diff --git a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts index c76dc9c547c..76464bc0011 100644 --- a/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts +++ b/packages/cli/src/modules/instance-ai/__tests__/instance-ai.service.test.ts @@ -3914,6 +3914,14 @@ describe('InstanceAiService — editor handoff context resources', () => { expect(source).toContain('buildContextResourcesBlock(contextAttachments)'); expect(source).not.toContain('buildContextResourcesBlock(workflowAttachments)'); }); + + it('traces the attached resources, which the raw message no longer shows', () => { + const source = InstanceAiService.toString(); + + // Assignment-form tolerant: the wiring is what matters, not whether the + // field is spread into the literal or set on it afterwards. + expect(source).toMatch(/resourceAttachments\s*[:=]\s*contextAttachments\.map/); + }); }); describe('InstanceAiService — workflow verification follow-up gate', () => { diff --git a/packages/cli/src/modules/instance-ai/instance-ai.service.ts b/packages/cli/src/modules/instance-ai/instance-ai.service.ts index 44536c82c8f..c59c4a4cbf8 100644 --- a/packages/cli/src/modules/instance-ai/instance-ai.service.ts +++ b/packages/cli/src/modules/instance-ai/instance-ai.service.ts @@ -200,6 +200,14 @@ function getErrorMessage(error: unknown): string { return error instanceof Error ? error.message : String(error); } +/** A resource attachment as the trace records it: the reference, not its contents. */ +type TracedResourceAttachment = { + type: InstanceAiResourceAttachment['type']; + id: string; + projectId?: string; + executionId?: string; +}; + /** Root-run outputs for a suspended segment — keep the LangSmith turn readable (AGENT-371). */ function buildSuspensionTraceOutputs(runId: string, suspension: SuspensionInfo | undefined) { const rawMessage = suspension?.suspendPayload.message; @@ -3295,18 +3303,33 @@ export class InstanceAiService { errorReporterExecutionToken = this.instanceAiErrorReporter.beginRun(runId); messageId = nanoid(); - const traceInput: Record = { - message, - ...(fileAttachments.length - ? { - attachments: fileAttachments.map((attachment) => ({ - mimeType: attachment.mimeType, - size: attachment.data.length, - })), - } - : {}), - ...(messageGroupId ? { messageGroupId } : {}), - }; + const traceInput: Record = { message }; + if (fileAttachments.length) { + traceInput.attachments = fileAttachments.map((attachment) => ({ + mimeType: attachment.mimeType, + size: attachment.data.length, + })); + } + // `message` is the user's raw text, so without this the trace has no + // record that the editor handed the agent a resource. + if (contextAttachments.length) { + traceInput.resourceAttachments = contextAttachments.map((attachment) => { + const resource: TracedResourceAttachment = { + type: attachment.type, + id: attachment.id, + }; + if (attachment.type === 'agent') { + resource.projectId = attachment.projectId; + } + if (attachment.type === 'workflow' && attachment.executionId) { + resource.executionId = attachment.executionId; + } + return resource; + }); + } + if (messageGroupId) { + traceInput.messageGroupId = messageGroupId; + } // Shared with createExecutionEnvironment so one ProxyTokenManager backs tracing + the run. const proxyRunConfig = await this.createProxyRunConfig(user);