@ouispec/agent-worker 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +31 -1
  2. package/dist/approvals/preview.d.ts +2 -2
  3. package/dist/approvals/preview.d.ts.map +1 -1
  4. package/dist/approvals/preview.js +20 -3
  5. package/dist/approvals/preview.js.map +1 -1
  6. package/dist/attachments/content.d.ts +68 -0
  7. package/dist/attachments/content.d.ts.map +1 -0
  8. package/dist/attachments/content.js +318 -0
  9. package/dist/attachments/content.js.map +1 -0
  10. package/dist/attachments/guard.d.ts +69 -0
  11. package/dist/attachments/guard.d.ts.map +1 -0
  12. package/dist/attachments/guard.js +86 -0
  13. package/dist/attachments/guard.js.map +1 -0
  14. package/dist/attachments/limits.d.ts +46 -0
  15. package/dist/attachments/limits.d.ts.map +1 -0
  16. package/dist/attachments/limits.js +64 -0
  17. package/dist/attachments/limits.js.map +1 -0
  18. package/dist/attachments/store.d.ts +85 -0
  19. package/dist/attachments/store.d.ts.map +1 -0
  20. package/dist/attachments/store.js +2 -0
  21. package/dist/attachments/store.js.map +1 -0
  22. package/dist/attachments/tools.d.ts +12 -0
  23. package/dist/attachments/tools.d.ts.map +1 -0
  24. package/dist/attachments/tools.js +216 -0
  25. package/dist/attachments/tools.js.map +1 -0
  26. package/dist/authz/tool-policy.d.ts +6 -0
  27. package/dist/authz/tool-policy.d.ts.map +1 -1
  28. package/dist/authz/tool-policy.js.map +1 -1
  29. package/dist/index.d.ts +7 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/orchestrator.d.ts.map +1 -1
  34. package/dist/orchestrator.js +202 -26
  35. package/dist/orchestrator.js.map +1 -1
  36. package/dist/prompt/clock.d.ts +0 -17
  37. package/dist/prompt/clock.d.ts.map +1 -1
  38. package/dist/prompt/clock.js +19 -2
  39. package/dist/prompt/clock.js.map +1 -1
  40. package/dist/prompt/user-text.d.ts +19 -0
  41. package/dist/prompt/user-text.d.ts.map +1 -0
  42. package/dist/prompt/user-text.js +20 -0
  43. package/dist/prompt/user-text.js.map +1 -0
  44. package/dist/runtime/turn-runner.d.ts +10 -2
  45. package/dist/runtime/turn-runner.d.ts.map +1 -1
  46. package/dist/runtime/turn-runner.js +41 -1
  47. package/dist/runtime/turn-runner.js.map +1 -1
  48. package/dist/runtime/types.d.ts +23 -2
  49. package/dist/runtime/types.d.ts.map +1 -1
  50. package/dist/stop/turn-stop.d.ts +6 -6
  51. package/dist/stop/turn-stop.d.ts.map +1 -1
  52. package/dist/stop/turn-stop.js +6 -2
  53. package/dist/stop/turn-stop.js.map +1 -1
  54. package/dist/tools/input-validation.d.ts.map +1 -1
  55. package/dist/tools/input-validation.js +3 -0
  56. package/dist/tools/input-validation.js.map +1 -1
  57. package/dist/tools/types.d.ts +12 -0
  58. package/dist/tools/types.d.ts.map +1 -1
  59. package/dist/tools/types.js.map +1 -1
  60. package/dist/turn-policy.d.ts +14 -0
  61. package/dist/turn-policy.d.ts.map +1 -1
  62. package/dist/turn-policy.js.map +1 -1
  63. package/dist/types.d.ts +26 -2
  64. package/dist/types.d.ts.map +1 -1
  65. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,kBAAkB,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACxJ,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAChH,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,YAAY,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnG,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,YAAY,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAI3F,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC/F,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,GACX,MAAM,oBAAoB,CAAC;AAG5B,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAInF,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AACtE,YAAY,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AACrF,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAK9D,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC1H,YAAY,EAAE,cAAc,EAAE,wBAAwB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAClH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,YAAY,EAAE,eAAe,EAAE,0BAA0B,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACxK,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACrH,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAClG,OAAO,EACL,SAAS,IAAI,oBAAoB,EACjC,SAAS,IAAI,oBAAoB,EACjC,SAAS,IAAI,oBAAoB,EACjC,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAC;AAC5D,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIpE,YAAY,EAAE,eAAe,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,eAAe,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvJ,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,wBAAwB,EAAE,6BAA6B,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAClI,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChJ,OAAO,EAAE,0BAA0B,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACpJ,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD,YAAY,EACV,eAAe,EACf,cAAc,EACd,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACjE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAGpE,OAAO,EAAE,qBAAqB,EAAE,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC1H,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAClH,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAGxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -29,6 +29,10 @@ export { buildApprovalPreview } from './approvals/preview.js';
29
29
  // turn. What the turn had produced is stored, then announced.
30
30
  export { createHttpTurnStopClient, watchTurnStop, stopOf, TurnStopped, DEFAULT_STOP_GRACE_MS } from './stop/turn-stop.js';
31
31
  export { stoppedTurnMessages } from './stop/partial.js';
32
+ export { AttachmentGuard } from './attachments/guard.js';
33
+ export { attachmentTools, ATTACHMENT_TOOLS, ATTACHMENT_TOOL_CLASS } from './attachments/tools.js';
34
+ export { VIEW_TOOL as ATTACHMENT_VIEW_TOOL, READ_TOOL as ATTACHMENT_READ_TOOL, LIST_TOOL as ATTACHMENT_LIST_TOOL, ATTACHMENT_DATA_NOTE, escapeAttachmentText, } from './attachments/content.js';
35
+ export * as attachmentLimits from './attachments/limits.js';
32
36
  export { createHttpUIActionChannel } from './ui/channel.js';
33
37
  export { readClientPage, withoutClientSnapshot, withoutClientUI, capabilityFingerprint, CLIENT_SNAPSHOT_KEY } from './ui/snapshot.js';
34
38
  export { DEFAULT_INDEX_CHARS, indexDiff, indexLine, indexText, pageActions, pageFingerprint, pageFromIndex, pageFromSurfaces, } from './ui/page-index.js';
@@ -43,7 +47,7 @@ export { PROMPT_CACHE_BREAKPOINTS, describeModel } from './model.js';
43
47
  export { defaultTurnPolicy } from './turn-policy.js';
44
48
  export { createHttpEmitAdapter } from './emit/http-adapter.js';
45
49
  // The runtime: one core, two host adapters (Lambda + SQS, container)
46
- export { createAgentTurnRunner, categorizeError, payloadRefusal } from './runtime/turn-runner.js';
50
+ export { createAgentTurnRunner, categorizeError, payloadRefusal, TURN_DEADLINE_EXCEEDED } from './runtime/turn-runner.js';
47
51
  export { assertAgentRuntimeConfig } from './runtime/config.js';
48
52
  export { createLambdaAgentHandler } from './lambda/handler.js';
49
53
  export { startContainerAgentWorker } from './container/server.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAmC,MAAM,YAAY,CAAC;AA0EtH;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,eAAe,CAAC,CA+gD1B"}
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAmC,MAAM,YAAY,CAAC;AAiFtH;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,eAAe,CAAC,CAwsD1B"}
@@ -30,6 +30,12 @@ import { readClientPage, withoutClientUI } from './ui/snapshot.js';
30
30
  import { KNOWLEDGE_TOOL, knowledgeTool, readClientKnowledge, withClientKnowledge } from './ui/knowledge.js';
31
31
  import { withNewestPageStateOnly } from './ui/newest-page-state.js';
32
32
  import { withClock } from './prompt/clock.js';
33
+ import { withUserText } from './prompt/user-text.js';
34
+ import { AttachmentGuard } from './attachments/guard.js';
35
+ import { ATTACHMENT_DATA_NOTE, turnAttachmentParts, withReferenceLines, withTurnAttachments, withoutLeftOut } from './attachments/content.js';
36
+ import { attachmentTools } from './attachments/tools.js';
37
+ import { acceptsMediaType, attachmentIdsIn, attachmentNameForModel, misplacedAttachmentInputs } from '@ouispec/agent-core';
38
+ const ATTACHMENT_LIST_HINT = 'attachment_list lists the files that can.';
33
39
  import { buildUITools, createPageSight, DefinitionUnavailable, fitNotes, jobWaitOf, UI_ACT_TOOL, UI_DESCRIBE_TOOL, UI_READ_TOOL, } from './ui/ui-tools.js';
34
40
  import { DEFAULT_INDEX_CHARS, indexText, pageFingerprint } from './ui/page-index.js';
35
41
  import { describeSchema } from './ui/outline.js';
@@ -119,6 +125,30 @@ export async function runAgentTurn(config, input) {
119
125
  let pageStatesNotRepeated = 0;
120
126
  // The knowledge the client sent for its page goes after the host's prompt.
121
127
  const knowledge = readClientKnowledge(input.context ?? null);
128
+ // ─── Files (ADR-0252 §2.11, §2.12) ─────────────────────────────────────────
129
+ // With the host's file area, the turn's files go to the model with its
130
+ // message, the attachment tools reach every file of the conversation, and the
131
+ // cost guard caps what the model is given of them.
132
+ const fileArea = config.attachments ?? null;
133
+ const fileOwner = { userId: input.userId, accountId: input.accountId, conversationId: input.conversationId };
134
+ let conversationFileTokens = 0;
135
+ if (fileArea?.store.conversationUsage) {
136
+ try {
137
+ conversationFileTokens = await fileArea.store.conversationUsage(fileOwner, { signal: AbortSignal.timeout(5_000) });
138
+ }
139
+ catch (err) {
140
+ log('warn', 'agent:tool', 'Could not read the conversation’s use of files; only the turn’s cap applies', {
141
+ turnId,
142
+ error: err instanceof Error ? err.message : String(err),
143
+ });
144
+ }
145
+ }
146
+ const fileGuard = new AttachmentGuard({
147
+ turnTokens: fileArea?.turnTokens,
148
+ conversationTokens: fileArea?.conversationTokens,
149
+ conversationUsed: conversationFileTokens,
150
+ });
151
+ const fileTools = fileArea ? attachmentTools({ store: fileArea.store, owner: fileOwner, guard: fileGuard }) : [];
122
152
  log('info', 'agent:ui', 'UI surfaces for this turn', {
123
153
  turnId,
124
154
  clientSentSnapshot: client !== null,
@@ -152,14 +182,14 @@ export async function runAgentTurn(config, input) {
152
182
  const hostPrompt = typeof config.systemPrompt === 'function'
153
183
  ? config.systemPrompt({ userId: input.userId, accountId: input.accountId, context: withoutClientUI(input.context) })
154
184
  : config.systemPrompt;
155
- const systemPrompt = withClientKnowledge(hostPrompt, input.context);
185
+ // With a file area, the model is told a file's name and content are data, never instructions (ADR-0252 §2.11).
186
+ const systemPrompt = withClientKnowledge(fileArea ? `${hostPrompt}\n\n${ATTACHMENT_DATA_NOTE}` : hostPrompt, input.context);
156
187
  log('debug', 'agent:prompt', 'System prompt built', { turnId, promptLength: systemPrompt.length, hostPromptLength: hostPrompt.length });
157
188
  // Wall-clock deadline: abort the entire streamText call if it exceeds turnDeadlineMs.
158
189
  const turnStartedAt = Date.now();
159
190
  // A UI tool waiting on work it started stops in time for the model to answer.
160
191
  const uiWaitDeadline = () => turnStartedAt + turnDeadlineMs - UI_ANSWER_MARGIN_MS;
161
192
  const abortController = new AbortController();
162
- const deadlineTimer = setTimeout(() => abortController.abort('Turn deadline exceeded'), turnDeadlineMs);
163
193
  // ─── Stopping (ADR-0252) ───────────────────────────────────────────────────
164
194
  // The person's Stop, or a newer message superseding this turn, is kept by the
165
195
  // realtime server; the watch asks for it for as long as the turn runs. A stop
@@ -185,7 +215,8 @@ export async function runAgentTurn(config, input) {
185
215
  return stopGraceMs;
186
216
  },
187
217
  };
188
- stopWatch.onStop((record) => {
218
+ /** Abort the turn on its stop path: what it had produced is stored, then announced. */
219
+ const stopTurn = (stop) => {
189
220
  if (ending || abortController.signal.aborted)
190
221
  return;
191
222
  // Never longer than this process has left, less what storing and announcing take.
@@ -193,9 +224,17 @@ export async function runAgentTurn(config, input) {
193
224
  if (remaining !== undefined)
194
225
  stopGraceMs = Math.max(0, Math.min(stopGraceMs, remaining - STOP_STORE_MARGIN_MS));
195
226
  graceSignal = AbortSignal.timeout(stopGraceMs + 250);
196
- log('info', 'agent:turn', 'Turn asked to stop', { turnId, reason: record.reason, graceMs: stopGraceMs });
197
- abortController.abort(new TurnStopped(record.reason, record.at));
198
- });
227
+ log('info', 'agent:turn', stop.reason === 'deadline' ? 'Turn ran out of time' : 'Turn asked to stop', {
228
+ turnId,
229
+ reason: stop.reason,
230
+ graceMs: stopGraceMs,
231
+ });
232
+ abortController.abort(stop);
233
+ };
234
+ stopWatch.onStop((record) => stopTurn(new TurnStopped(record.reason, record.at)));
235
+ // The turn's deadline ends it on the same path (ADR-0252 §6.4): what it did
236
+ // before it ran out of time is kept, marked, and the next turn is told.
237
+ const deadlineTimer = setTimeout(() => stopTurn(new TurnStopped('deadline', Date.now())), turnDeadlineMs);
199
238
  // Build tool execution context
200
239
  const toolCtx = {
201
240
  userId: input.userId,
@@ -213,6 +252,8 @@ export async function runAgentTurn(config, input) {
213
252
  const executedToolResults = [];
214
253
  // The pictures results came with, by call: given to the model with that call's result (ui/answer-image.ts).
215
254
  const answerImages = new Map();
255
+ // The text results came with (a file's text, read by attachment_read), by call: the model's only, like the pictures.
256
+ const answerTexts = new Map();
216
257
  // The worker's own record of the turn, kept as it runs: what a stopped turn
217
258
  // is stored from (stop/partial.ts). The model stream's own results do not
218
259
  // survive an abort, so nothing on the stop path reads them.
@@ -360,6 +401,65 @@ export async function runAgentTurn(config, input) {
360
401
  }, rawArgs);
361
402
  }
362
403
  const args = validation.value;
404
+ // ── Attached files named in the call (ADR-0252 §2.13) ──
405
+ // Each must be a file of this turn's conversation, ready, and of a type the
406
+ // input takes, checked before anything is dispatched; the page then
407
+ // resolves it through its own host. An action declaring a file where none
408
+ // can be checked is not run at all.
409
+ const misplaced = misplacedAttachmentInputs(schema);
410
+ if (misplaced.length > 0) {
411
+ return refused({
412
+ error: `"${t.name}" declares an attached file at ${misplaced.join(', ')}, where none can be checked: it cannot be run with a file.`,
413
+ notRun: true,
414
+ }, args);
415
+ }
416
+ const namedFiles = attachmentIdsIn(schema, args);
417
+ const callFiles = new Map();
418
+ if (namedFiles.length > 0) {
419
+ if (!fileArea) {
420
+ return refused({ error: `"${t.name}" takes an attached file, and this assistant cannot use attached files.`, notRun: true }, args);
421
+ }
422
+ const ids = [...new Set(namedFiles.map((f) => f.id))];
423
+ try {
424
+ for (const ref of await fileArea.store.describe(ids, fileOwner, { signal: abortController.signal }))
425
+ callFiles.set(ref.id, ref);
426
+ }
427
+ catch (err) {
428
+ log('warn', 'agent:tool', 'Could not check the files a call names; it was not run', {
429
+ turnId,
430
+ toolName: t.name,
431
+ error: err instanceof Error ? err.message : String(err),
432
+ });
433
+ return refused({ error: `The files "${t.name}" names could not be checked just now. It was not run; try again.`, notRun: true }, args);
434
+ }
435
+ const missing = ids.filter((id) => !callFiles.has(id) || callFiles.get(id).removed);
436
+ if (missing.length > 0) {
437
+ return refused({
438
+ error: `${missing.map((id) => `"${id}"`).join(', ')} ${missing.length === 1 ? 'is not a file' : 'are not files'} of this conversation that can be used. ${ATTACHMENT_LIST_HINT}`,
439
+ notRun: true,
440
+ }, args);
441
+ }
442
+ const pending = ids.filter((id) => callFiles.get(id).pending);
443
+ if (pending.length > 0) {
444
+ return refused({
445
+ error: `${pending.map((id) => `"${id}"`).join(', ')} ${pending.length === 1 ? 'is' : 'are'} still being checked, and can be used once that is done. It was not run.`,
446
+ notRun: true,
447
+ }, args);
448
+ }
449
+ // The type each input takes, checked here as the page checks it (OUI spec §7.3.11).
450
+ const wrongType = namedFiles.filter((f) => !acceptsMediaType(f.input, callFiles.get(f.id).mediaType));
451
+ if (wrongType.length > 0) {
452
+ return refused({
453
+ error: wrongType
454
+ .map((f) => {
455
+ const ref = callFiles.get(f.id);
456
+ return `${f.property}: ${attachmentNameForModel(ref.name)} (${f.id}) is ${ref.mediaType}; it takes ${f.input.mediaTypes.join(', ')}.`;
457
+ })
458
+ .join(' '),
459
+ notRun: true,
460
+ }, args);
461
+ }
462
+ }
363
463
  // Told to the model with the result, so it sends the value itself next time (below).
364
464
  const readFromText = validation.coerced ?? [];
365
465
  // The call as a repeat is judged by: its name and its exact input. Set for a UI action that changes the page.
@@ -415,6 +515,7 @@ export async function runAgentTurn(config, input) {
415
515
  turnId,
416
516
  hasSideEffects: isSideEffecting,
417
517
  toolKind: isUI ? 'ui' : 'backend',
518
+ ...(t.toolClass ? { toolClass: t.toolClass } : {}),
418
519
  effect: requirement.effect,
419
520
  destructive: requirement.destructive,
420
521
  });
@@ -434,7 +535,7 @@ export async function runAgentTurn(config, input) {
434
535
  }
435
536
  if ((requirement.required || policyRequiresApproval) && !approved) {
436
537
  ledger.record({ tool: t.name, ok: false, error: 'Not run: it waits for the user’s approval on the card' });
437
- return { text: await requestApproval(t, args, toolUseId, requirement), ran: false };
538
+ return { text: await requestApproval(t, args, toolUseId, requirement, callFiles), ran: false };
438
539
  }
439
540
  if (t.name === UI_DESCRIBE_TOOL)
440
541
  uiCounts.describes++;
@@ -544,6 +645,8 @@ export async function runAgentTurn(config, input) {
544
645
  // result, and nothing else's: it is not in the text above, the event or the record.
545
646
  if (isWrapped && result.image)
546
647
  answerImages.set(toolUseId, result.image);
648
+ if (isWrapped && typeof result.modelText === 'string')
649
+ answerTexts.set(toolUseId, result.modelText);
547
650
  return { text: JSON.stringify(modelPayload ?? { error: 'no result' }), ran: true };
548
651
  };
549
652
  /**
@@ -553,13 +656,15 @@ export async function runAgentTurn(config, input) {
553
656
  const toModelOutput = ({ toolCallId, output }) => {
554
657
  const text = typeof output === 'string' ? output : JSON.stringify(output ?? null);
555
658
  const image = answerImages.get(toolCallId);
556
- if (!image)
659
+ const fileText = answerTexts.get(toolCallId);
660
+ if (!image && fileText === undefined)
557
661
  return { type: 'text', value: text };
558
662
  return {
559
663
  type: 'content',
560
664
  value: [
561
665
  { type: 'text', text },
562
- { type: 'file', mediaType: image.mediaType, data: { type: 'data', data: image.base64 } },
666
+ ...(fileText !== undefined ? [{ type: 'text', text: fileText }] : []),
667
+ ...(image ? [{ type: 'file', mediaType: image.mediaType, data: { type: 'data', data: image.base64 } }] : []),
563
668
  ],
564
669
  };
565
670
  };
@@ -568,8 +673,9 @@ export async function runAgentTurn(config, input) {
568
673
  * stores it as a pending approval, shows the card, and tells the model only
569
674
  * that it is waiting. The model never sees a token.
570
675
  */
571
- const requestApproval = async (t, args, toolUseId, requirement) => {
572
- const preview = buildApprovalPreview(t, args);
676
+ const requestApproval = async (t, args, toolUseId, requirement, files) => {
677
+ // A file is shown by its name and size; the approval stays bound to its id (ADR-0252 §2.13).
678
+ const preview = buildApprovalPreview(t, args, files);
573
679
  approvalHold = { approvalId: toolUseId, title: preview.title };
574
680
  const refuse = (why) => {
575
681
  approvalHold = null;
@@ -711,9 +817,10 @@ export async function runAgentTurn(config, input) {
711
817
  // with the id of an action of the page, is withheld (ADR-0209 D6): for a
712
818
  // UI-only agent, the page is the authority.
713
819
  function hostTools(ui) {
820
+ const sdkOwned = new Set(fileTools.map((t) => t.name));
714
821
  if (!ui)
715
- return config.tools.tools;
716
- const taken = (name) => ui.actions.has(name) || name === UI_ACT_TOOL || name === UI_DESCRIBE_TOOL || name === UI_READ_TOOL;
822
+ return [...config.tools.tools.filter((t) => !sdkOwned.has(t.name)), ...fileTools];
823
+ const taken = (name) => sdkOwned.has(name) || ui.actions.has(name) || name === UI_ACT_TOOL || name === UI_DESCRIBE_TOOL || name === UI_READ_TOOL;
717
824
  const withheld = config.tools.tools.filter((t) => taken(t.name)).map((t) => t.name);
718
825
  if (withheld.length > 0) {
719
826
  log('error', 'agent:ui', 'Host tools collide with UI tools or action ids; the host tools are withheld this turn', {
@@ -721,7 +828,7 @@ export async function runAgentTurn(config, input) {
721
828
  withheld,
722
829
  });
723
830
  }
724
- return config.tools.tools.filter((t) => !taken(t.name));
831
+ return [...config.tools.tools.filter((t) => !taken(t.name)), ...fileTools];
725
832
  }
726
833
  /** Every tool a call can name: the host's, and the page's actions. What an approved call is looked up in. */
727
834
  function callableTools() {
@@ -791,6 +898,16 @@ export async function runAgentTurn(config, input) {
791
898
  return text;
792
899
  }
793
900
  }
901
+ /** A tool the model is given, as a turn policy sees it: its name, kind and class. */
902
+ function describeStepTool(name) {
903
+ if (name === UI_ACT_TOOL || name === UI_DESCRIBE_TOOL || name === UI_READ_TOOL)
904
+ return { name, kind: 'ui' };
905
+ // The SDK's own file tools take their names before the host's (hostTools).
906
+ const registered = fileTools.find((t) => t.name === name) ?? config.tools.tools.find((t) => t.name === name);
907
+ if (!registered)
908
+ return { name, kind: 'builtin' };
909
+ return { name, kind: registered.kind === 'ui' ? 'ui' : 'backend', ...(registered.toolClass ? { toolClass: registered.toolClass } : {}) };
910
+ }
794
911
  function assembleTools() {
795
912
  const ui = currentUI();
796
913
  const tools = {};
@@ -1044,15 +1161,48 @@ export async function runAgentTurn(config, input) {
1044
1161
  // The date and time on the user's clock travel with their message too: a
1045
1162
  // model has no clock, and the changing minute must stay out of the cached
1046
1163
  // system prompt (prompt/clock.ts).
1164
+ const turnFilesGiven = !!fileArea && (input.attachments?.length ?? 0) > 0;
1047
1165
  const messages = [
1048
1166
  ...withClock(withPageState(withNote(convertHistoryToCoreMessages(
1049
1167
  // An approved call's outcome, and an expired one's, is a later result of the call already in the
1050
1168
  // history: it takes that call's place.
1051
- [...(input.history ?? []), ...expired.outcomes, ...(continued?.outcome ? [continued.outcome] : [])], [input.content, continued?.note].filter(Boolean).join('\n\n')),
1169
+ [...(input.history ?? []), ...expired.outcomes, ...(continued?.outcome ? [continued.outcome] : [])], [input.content, continued?.note].filter(Boolean).join('\n\n'),
1170
+ // The turn's own message, when the history already holds it: its files are given below,
1171
+ // each with its line, so the history's copy of the message does not name them again.
1172
+ turnFilesGiven && input.history?.at(-1)?.role === 'user' ? input.history.length - 1 : null),
1052
1173
  // Said on the user's message whether or not the host's history already holds that message.
1053
1174
  expired.outcomes.length > 0 ? expiredNote(expired) : null), client, config.ui?.maxObservationChars ?? DEFAULT_PAGE_STATE_CHARS, config.ui?.maxIndexChars ?? DEFAULT_INDEX_CHARS), input.context ?? null),
1054
1175
  ...(continued?.messages ?? []),
1055
1176
  ];
1177
+ // The turn's files go with its message, after everything said about it (ADR-0252 §2.11).
1178
+ // A file that cannot be had is named with why; the turn goes on with the others.
1179
+ if (fileArea && turnFilesGiven) {
1180
+ try {
1181
+ const files = await turnAttachmentParts(input.attachments, {
1182
+ store: fileArea.store,
1183
+ owner: fileOwner,
1184
+ guard: fileGuard,
1185
+ pdfAsDocument: fileArea.pdfAsDocument ?? false,
1186
+ perMessage: fileArea.perMessage,
1187
+ signal: abortController.signal,
1188
+ });
1189
+ if (files.failures.length > 0) {
1190
+ log('warn', 'agent:tool', 'Some of the message’s files could not be loaded; the turn goes on without them', { turnId, failures: files.failures });
1191
+ }
1192
+ messages.splice(0, messages.length, ...withTurnAttachments(messages, files));
1193
+ }
1194
+ catch (err) {
1195
+ log('error', 'agent:tool', 'The message’s files could not be given; the turn goes on with their lines', {
1196
+ turnId,
1197
+ error: err instanceof Error ? err.message : String(err),
1198
+ });
1199
+ messages.splice(0, messages.length, ...withTurnAttachments(messages, {
1200
+ parts: [],
1201
+ notes: [withReferenceLines('', input.attachments), 'These files could not be loaded with this message.'],
1202
+ failures: [],
1203
+ }));
1204
+ }
1205
+ }
1056
1206
  // The system prompt goes in `instructions`, NOT in `messages`.
1057
1207
  //
1058
1208
  // ai v7 rejects a system-role entry in `messages` outright —
@@ -1125,6 +1275,11 @@ export async function runAgentTurn(config, input) {
1125
1275
  })),
1126
1276
  turnClass,
1127
1277
  allToolNames: [...Object.keys(aiTools), ...actionIds],
1278
+ // What each tool is, so a policy can choose by class (the attachment tools are `attachment`).
1279
+ allTools: [
1280
+ ...Object.keys(aiTools).map((name) => describeStepTool(name)),
1281
+ ...actionIds.map((name) => ({ name, kind: 'ui' })),
1282
+ ],
1128
1283
  });
1129
1284
  // What it constrained, in the model's terms: an action is run through
1130
1285
  // `ui_act`, which holds the step to the actions the policy allowed.
@@ -1155,9 +1310,19 @@ export async function runAgentTurn(config, input) {
1155
1310
  const trimmed = messages ? withNewestPageStateOnly(messages) : null;
1156
1311
  if (trimmed)
1157
1312
  pageStatesNotRepeated += trimmed.replaced;
1313
+ // The files this step would carry, within the turn's and the conversation's caps (ADR-0252 §2.11).
1314
+ const leftOutNow = fileGuard.beforeStep();
1315
+ if (leftOutNow.length > 0) {
1316
+ log('info', 'agent:tool', 'Files left out of the next step to keep within the cap', {
1317
+ turnId,
1318
+ attachments: leftOutNow.map((p) => ({ id: p.attachmentId, kind: p.kind, estimatedTokens: p.tokens })),
1319
+ });
1320
+ }
1321
+ const leftOut = fileGuard.leftOutParts();
1322
+ const stepMessages = leftOut.length > 0 && messages ? withoutLeftOut(trimmed?.messages ?? messages, leftOut) : trimmed?.messages;
1158
1323
  return {
1159
1324
  ...constraints,
1160
- ...(trimmed ? { messages: trimmed.messages } : {}),
1325
+ ...(stepMessages ? { messages: stepMessages } : {}),
1161
1326
  instructions: notes.length
1162
1327
  ? [instructions, ...notes.map((content) => ({ role: 'system', content }))]
1163
1328
  : instructions,
@@ -1292,6 +1457,7 @@ export async function runAgentTurn(config, input) {
1292
1457
  },
1293
1458
  }
1294
1459
  : {}),
1460
+ ...(fileArea ? { attachments: fileGuard.usage() } : {}),
1295
1461
  });
1296
1462
  // The turn's messages, in the host's format, for it to store.
1297
1463
  const newMessages = [...expired.persisted, ...(continued?.persisted ?? []), ...convertResponseToTurnMessages({ steps }, executedToolResults)];
@@ -1330,6 +1496,7 @@ export async function runAgentTurn(config, input) {
1330
1496
  ...(expired.claimed.length > 0 ? { settledApprovals: expired.claimed } : {}),
1331
1497
  maxRoundsReached: steps.length >= maxRounds,
1332
1498
  stopReason,
1499
+ ...(fileArea ? { attachments: fileGuard.usage() } : {}),
1333
1500
  };
1334
1501
  }
1335
1502
  finally {
@@ -1417,14 +1584,16 @@ export async function runAgentTurn(config, input) {
1417
1584
  cacheWriteTokens: usage.cacheWriteTokens,
1418
1585
  ...(unsettled.length > 0 ? { unsettledCalls: unsettled } : {}),
1419
1586
  ...(client ? { ui: { ...uiCounts, blindRefusals: sight.refusals(), stopped: sight.stopped() } } : {}),
1587
+ ...(fileArea ? { attachments: fileGuard.usage() } : {}),
1420
1588
  });
1421
1589
  // The store and the announcement each get a limit of their own when the host says how long
1422
1590
  // the process has left: a slow store must not run it to its hard end with nothing announced.
1591
+ /** Whether the work finished within its limit. */
1423
1592
  const within = async (what, keepBackMs, work) => {
1424
1593
  const remaining = input.remainingMs?.();
1425
1594
  if (remaining === undefined) {
1426
1595
  await work();
1427
- return;
1596
+ return true;
1428
1597
  }
1429
1598
  const limitMs = Math.max(STOP_STEP_FLOOR_MS, remaining - keepBackMs);
1430
1599
  let timer;
@@ -1443,14 +1612,20 @@ export async function runAgentTurn(config, input) {
1443
1612
  limitMs,
1444
1613
  remainingMs: remaining,
1445
1614
  });
1615
+ return false;
1446
1616
  }
1617
+ return true;
1447
1618
  };
1619
+ // Whether what the turn had produced was stored: a deadline stop that could not store it is
1620
+ // not a stopped turn but one that ran out of time with nothing kept (ADR-0252 §6.4).
1621
+ let stored = true;
1448
1622
  if (config.beforeTurnComplete) {
1449
1623
  try {
1450
1624
  // Kept back: time to announce after it.
1451
- await within('store', STOP_ANNOUNCE_MARGIN_MS, () => config.beforeTurnComplete({ rounds, usage, newMessages, stopped: marker }));
1625
+ stored = await within('store', STOP_ANNOUNCE_MARGIN_MS, () => config.beforeTurnComplete({ rounds, usage, newMessages, stopped: marker }));
1452
1626
  }
1453
1627
  catch (err) {
1628
+ stored = false;
1454
1629
  log('error', 'agent:turn', 'beforeTurnComplete failed; the stopped turn is announced without it', {
1455
1630
  turnId,
1456
1631
  error: err instanceof Error ? err.message : String(err),
@@ -1484,6 +1659,8 @@ export async function runAgentTurn(config, input) {
1484
1659
  maxRoundsReached: false,
1485
1660
  stopReason: stopped.reason,
1486
1661
  stopped: marker,
1662
+ ...(stored ? {} : { stored: false }),
1663
+ ...(fileArea ? { attachments: fileGuard.usage() } : {}),
1487
1664
  };
1488
1665
  }
1489
1666
  }
@@ -1492,8 +1669,7 @@ function withNote(messages, note) {
1492
1669
  const last = messages[messages.length - 1];
1493
1670
  if (!note || !last || last.role !== 'user')
1494
1671
  return messages;
1495
- const text = typeof last.content === 'string' ? last.content : last.content.map((p) => ('text' in p ? p.text : '')).join('');
1496
- return [...messages.slice(0, -1), { role: 'user', content: text ? `${text}\n\n${note}` : note }];
1672
+ return [...messages.slice(0, -1), withUserText(last, note)];
1497
1673
  }
1498
1674
  /**
1499
1675
  * Put the client's page on the user's message: what it offers, as an index of
@@ -1517,10 +1693,7 @@ function withPageState(messages, client, maxChars, maxIndexChars) {
1517
1693
  ...(notShown.length ? [`Not shown: ${notShown.join(' ')}`] : []),
1518
1694
  '</page_state>',
1519
1695
  ].join('\n');
1520
- const text = typeof last.content === 'string'
1521
- ? last.content
1522
- : last.content.map((p) => ('text' in p ? p.text : '')).join('');
1523
- return [...messages.slice(0, -1), { role: 'user', content: `${text}\n\n${pageState}` }];
1696
+ return [...messages.slice(0, -1), withUserText(last, pageState)];
1524
1697
  }
1525
1698
  /**
1526
1699
  * Convert our TurnHistoryMessage[] to AI SDK CoreMessage[].
@@ -1531,7 +1704,9 @@ function withPageState(messages, client, maxChars, maxIndexChars) {
1531
1704
  * AI_MissingToolResultsError crashes when persistence has gaps (e.g., stopWhen
1532
1705
  * triggered before a tool result was persisted).
1533
1706
  */
1534
- function convertHistoryToCoreMessages(history, currentContent) {
1707
+ function convertHistoryToCoreMessages(history, currentContent,
1708
+ /** The history's copy of the turn's own message, whose files the turn gives with their lines. */
1709
+ turnMessageAt = null) {
1535
1710
  const messages = [];
1536
1711
  // First pass: collect all tool_call_ids that have results in history
1537
1712
  // AND all tool call IDs from assistant messages (to validate results)
@@ -1574,7 +1749,8 @@ function convertHistoryToCoreMessages(history, currentContent) {
1574
1749
  for (let idx = 0; idx < history.length; idx++) {
1575
1750
  const msg = history[idx];
1576
1751
  if (msg.role === 'user') {
1577
- messages.push({ role: 'user', content: msg.content });
1752
+ // A message's files are in history as their reference lines, never their content (ADR-0252 §2.11).
1753
+ messages.push({ role: 'user', content: idx === turnMessageAt ? msg.content : withReferenceLines(msg.content, msg.attachments ?? undefined) });
1578
1754
  }
1579
1755
  else if (msg.role === 'assistant') {
1580
1756
  // A stopped turn's last message says so after its text (ADR-0252 §2.3).