@minpeter/pss-runtime 0.1.0-next.2 → 0.1.0-next.3

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 (141) hide show
  1. package/README.md +166 -78
  2. package/dist/agent-host-session-store.js +2 -4
  3. package/dist/agent-host-session-store.js.map +1 -1
  4. package/dist/agent-loop.js +2 -3
  5. package/dist/agent-loop.js.map +1 -1
  6. package/dist/agent-namespace.js +3 -7
  7. package/dist/agent-namespace.js.map +1 -1
  8. package/dist/agent-options.d.ts +2 -8
  9. package/dist/agent-options.js +4 -4
  10. package/dist/agent-options.js.map +1 -1
  11. package/dist/agent-resume.js +2 -82
  12. package/dist/agent-resume.js.map +1 -1
  13. package/dist/agent-session-entry.d.ts +1 -1
  14. package/dist/agent.d.ts +4 -4
  15. package/dist/agent.js +16 -70
  16. package/dist/agent.js.map +1 -1
  17. package/dist/cloudflare/cloudflare-agent-context.d.ts +40 -0
  18. package/dist/cloudflare/cloudflare-agent-context.js +37 -0
  19. package/dist/cloudflare/cloudflare-agent-context.js.map +1 -0
  20. package/dist/cloudflare/cloudflare-alarm-budget.d.ts +18 -0
  21. package/dist/cloudflare/cloudflare-alarm-budget.js +77 -0
  22. package/dist/cloudflare/cloudflare-alarm-budget.js.map +1 -0
  23. package/dist/cloudflare/cloudflare-alarm-drainer.d.ts +45 -0
  24. package/dist/cloudflare/cloudflare-alarm-drainer.js +103 -0
  25. package/dist/cloudflare/cloudflare-alarm-drainer.js.map +1 -0
  26. package/dist/cloudflare/cloudflare-alarm-run-drain.d.ts +13 -0
  27. package/dist/cloudflare/cloudflare-alarm-run-drain.js +81 -0
  28. package/dist/cloudflare/cloudflare-alarm-run-drain.js.map +1 -0
  29. package/dist/cloudflare/cloudflare-alarm-work.js +110 -0
  30. package/dist/cloudflare/cloudflare-alarm-work.js.map +1 -0
  31. package/dist/cloudflare/cloudflare-checkpoint-store.js +39 -0
  32. package/dist/cloudflare/cloudflare-checkpoint-store.js.map +1 -0
  33. package/dist/cloudflare/cloudflare-durable-object-fetch.d.ts +21 -0
  34. package/dist/cloudflare/cloudflare-durable-object-fetch.js +11 -0
  35. package/dist/cloudflare/cloudflare-durable-object-fetch.js.map +1 -0
  36. package/dist/cloudflare/cloudflare-event-store.js +33 -0
  37. package/dist/cloudflare/cloudflare-event-store.js.map +1 -0
  38. package/dist/cloudflare/cloudflare-execution-session-store.js +40 -0
  39. package/dist/cloudflare/cloudflare-execution-session-store.js.map +1 -0
  40. package/dist/cloudflare/cloudflare-execution-store.js +35 -0
  41. package/dist/cloudflare/cloudflare-execution-store.js.map +1 -0
  42. package/dist/cloudflare/cloudflare-host.d.ts +61 -0
  43. package/dist/cloudflare/cloudflare-host.js +113 -0
  44. package/dist/cloudflare/cloudflare-host.js.map +1 -0
  45. package/dist/cloudflare/cloudflare-notification-store.js +59 -0
  46. package/dist/cloudflare/cloudflare-notification-store.js.map +1 -0
  47. package/dist/cloudflare/cloudflare-run-store.js +81 -0
  48. package/dist/cloudflare/cloudflare-run-store.js.map +1 -0
  49. package/dist/cloudflare/cloudflare-store-utils.js +43 -0
  50. package/dist/cloudflare/cloudflare-store-utils.js.map +1 -0
  51. package/dist/cloudflare/durable-object-storage.d.ts +20 -0
  52. package/dist/cloudflare/durable-object-storage.js +76 -0
  53. package/dist/cloudflare/durable-object-storage.js.map +1 -0
  54. package/dist/cloudflare/index.d.ts +7 -0
  55. package/dist/cloudflare/index.js +6 -0
  56. package/dist/execution/capabilities.d.ts +40 -0
  57. package/dist/execution/host.d.ts +9 -0
  58. package/dist/execution/host.js +49 -1
  59. package/dist/execution/host.js.map +1 -1
  60. package/dist/execution/index.d.ts +3 -1
  61. package/dist/execution/index.js +2 -1
  62. package/dist/execution/memory.js +1 -1
  63. package/dist/execution/memory.js.map +1 -1
  64. package/dist/execution/types.d.ts +5 -10
  65. package/dist/index.d.ts +8 -4
  66. package/dist/index.js +6 -1
  67. package/dist/plugins.d.ts +25 -3
  68. package/dist/plugins.js +35 -6
  69. package/dist/plugins.js.map +1 -1
  70. package/dist/session/delegate-input.d.ts +9 -0
  71. package/dist/session/delegate-input.js +16 -0
  72. package/dist/session/delegate-input.js.map +1 -0
  73. package/dist/session/events.d.ts +43 -25
  74. package/dist/session/events.js +41 -0
  75. package/dist/session/events.js.map +1 -0
  76. package/dist/session/input-meta-types.d.ts +10 -0
  77. package/dist/session/input-meta.d.ts +13 -0
  78. package/dist/session/input-meta.js +45 -0
  79. package/dist/session/input-meta.js.map +1 -0
  80. package/dist/session/input.d.ts +4 -0
  81. package/dist/session/mapping.js +4 -2
  82. package/dist/session/mapping.js.map +1 -1
  83. package/dist/session/runtime-input-emit.js +41 -0
  84. package/dist/session/runtime-input-emit.js.map +1 -0
  85. package/dist/session/runtime-input.js +5 -1
  86. package/dist/session/runtime-input.js.map +1 -1
  87. package/dist/session/session-events.js +20 -6
  88. package/dist/session/session-events.js.map +1 -1
  89. package/dist/session/session-notification.js +3 -2
  90. package/dist/session/session-notification.js.map +1 -1
  91. package/dist/session/session-runtime-drain.js +3 -9
  92. package/dist/session/session-runtime-drain.js.map +1 -1
  93. package/dist/session/session-turn-processor.js +7 -17
  94. package/dist/session/session-turn-processor.js.map +1 -1
  95. package/dist/session/session.js +11 -4
  96. package/dist/session/session.js.map +1 -1
  97. package/package.json +6 -1
  98. package/dist/agent-child-runs.js +0 -16
  99. package/dist/agent-child-runs.js.map +0 -1
  100. package/dist/agent-host-capabilities.js +0 -9
  101. package/dist/agent-host-capabilities.js.map +0 -1
  102. package/dist/agent-validation.js +0 -35
  103. package/dist/agent-validation.js.map +0 -1
  104. package/dist/child-session-cleanups.js +0 -61
  105. package/dist/child-session-cleanups.js.map +0 -1
  106. package/dist/execution/run.js +0 -55
  107. package/dist/execution/run.js.map +0 -1
  108. package/dist/subagent-background-child-run-state.js +0 -51
  109. package/dist/subagent-background-child-run-state.js.map +0 -1
  110. package/dist/subagent-background-child-run.js +0 -103
  111. package/dist/subagent-background-child-run.js.map +0 -1
  112. package/dist/subagent-background-in-process.js +0 -98
  113. package/dist/subagent-background-in-process.js.map +0 -1
  114. package/dist/subagent-background-notification-inbox.js +0 -106
  115. package/dist/subagent-background-notification-inbox.js.map +0 -1
  116. package/dist/subagent-background-notify.js +0 -136
  117. package/dist/subagent-background-notify.js.map +0 -1
  118. package/dist/subagent-background-resume-group.js +0 -99
  119. package/dist/subagent-background-resume-group.js.map +0 -1
  120. package/dist/subagent-background-runner.js +0 -115
  121. package/dist/subagent-background-runner.js.map +0 -1
  122. package/dist/subagent-background-schedule.js +0 -43
  123. package/dist/subagent-background-schedule.js.map +0 -1
  124. package/dist/subagent-child-run.js +0 -68
  125. package/dist/subagent-child-run.js.map +0 -1
  126. package/dist/subagent-job-cancel.js +0 -84
  127. package/dist/subagent-job-cancel.js.map +0 -1
  128. package/dist/subagent-job-observer.js +0 -19
  129. package/dist/subagent-job-observer.js.map +0 -1
  130. package/dist/subagent-job-output.js +0 -87
  131. package/dist/subagent-job-output.js.map +0 -1
  132. package/dist/subagent-job-state.js +0 -66
  133. package/dist/subagent-job-state.js.map +0 -1
  134. package/dist/subagent-jobs.js +0 -96
  135. package/dist/subagent-jobs.js.map +0 -1
  136. package/dist/subagent-prompt-schema.js +0 -114
  137. package/dist/subagent-prompt-schema.js.map +0 -1
  138. package/dist/subagent-run.js +0 -111
  139. package/dist/subagent-run.js.map +0 -1
  140. package/dist/subagents.js +0 -125
  141. package/dist/subagents.js.map +0 -1
package/README.md CHANGED
@@ -50,8 +50,8 @@ consume the events for the run to progress. This is what lets code react to
50
50
  created.
51
51
 
52
52
  `model` is the single public constructor key for model execution. Pass an AI SDK
53
- `LanguageModel` for the managed runtime path with `instructions`, `tools`, and
54
- `subagents`, or pass a custom `RuntimeLlm` function when you want to own the model
53
+ `LanguageModel` for the managed runtime path with `instructions` and `tools`, or
54
+ pass a custom `RuntimeLlm` function when you want to own the model
55
55
  adapter yourself:
56
56
 
57
57
  ```ts
@@ -116,69 +116,154 @@ The public transcript protocol is `AgentEvent`: live runs emit runtime-defined
116
116
  events through `run.events()`. Provider/model message history is internal
117
117
  continuation state, not a public history API.
118
118
 
119
- ## Subagents
119
+ ## Delegation
120
120
 
121
- Compose specialist agents by constructing them first and passing them as an
122
- array. Top-level agents may omit metadata, but agents used as subagents need a
123
- stable `name` and `description` so the runtime can expose clear model-facing
124
- delegate tools.
121
+ Delegation is app-owned. Build ordinary tools that call another `Agent`,
122
+ `session.send(...)`, `session.notify(...)`, or host-owned background work, then
123
+ return the compact result shape your product wants the model to see.
125
124
 
126
125
  ```ts
127
- const researcher = new Agent({
128
- name: "researcher",
129
- description: "Researches facts and returns concise evidence.",
126
+ const reader = new Agent({
127
+ instructions: "Read knowledge-base files and cite paths.",
130
128
  model,
131
- instructions: "Research facts and return concise evidence.",
129
+ namespace: "reader",
132
130
  });
133
131
 
134
132
  const coordinator = new Agent({
133
+ instructions: "Coordinate work and delegate knowledge-base reads.",
135
134
  model,
136
- instructions: "Coordinate work and delegate when useful.",
137
- subagents: [researcher],
135
+ namespace: "coordinator",
136
+ tools: {
137
+ delegate_to_reader: tool({
138
+ description: "Ask the reader agent to inspect the knowledge base.",
139
+ execute: async ({ prompt }) => {
140
+ const run = await reader.session("kb").send(prompt);
141
+ const text: string[] = [];
142
+ for await (const event of run.events()) {
143
+ if (event.type === "assistant-text") {
144
+ text.push(event.text);
145
+ }
146
+ }
147
+ return { result: text.join("\n") };
148
+ },
149
+ inputSchema,
150
+ }),
151
+ },
138
152
  });
139
153
  ```
140
154
 
141
- For each subagent, the parent model receives a generated
142
- `delegate_to_<name>` tool. The tool accepts `prompt`, optional `description`,
143
- optional `sessionKey` suffix, and `run_in_background`. A provided `sessionKey`
144
- is always scoped under the parent session and subagent name; the model cannot
145
- select an arbitrary child session key. Omitting `run_in_background` defaults to
146
- blocking behavior and returns compact child text, not the full child event
147
- stream.
155
+ For background delegation, let your host own task ids, scheduling, output
156
+ storage, and notification resume. The runtime provides generic execution stores,
157
+ notifications, `Agent.resume(...)`, and `run.events()`; it does not generate
158
+ delegation tools or own child-agent lifecycle semantics. See
159
+ the sync and background example packages for app-owned blocking and background
160
+ delegation patterns.
161
+
162
+ ## Plugins
163
+
164
+ Pass `plugins: [...]` on `Agent` to observe or intercept runtime events. Each
165
+ plugin exposes one handler:
148
166
 
149
167
  ```ts
150
- delegate_to_researcher({
151
- prompt: "Find the current release notes and summarize the evidence.",
168
+ import type { AgentPlugin } from "@minpeter/pss-runtime";
169
+ import { Agent } from "@minpeter/pss-runtime";
170
+
171
+ const tracePlugin: AgentPlugin = {
172
+ name: "trace",
173
+ on: ({ event }) => {
174
+ if (event.type === "turn-end") {
175
+ console.log("turn finished");
176
+ }
177
+ },
178
+ };
179
+
180
+ const agent = new Agent({
181
+ model,
182
+ plugins: [tracePlugin],
152
183
  });
153
184
  ```
154
185
 
155
- When the model sets `run_in_background: true`, the parent run can finish while
156
- the child keeps working. The launch result includes a `bg_...` `task_id` and
157
- tells the model to wait for a `<system-reminder>` before retrieving output.
158
- When background work finishes, the session is notified with compact runtime
159
- input. Hosts that support durable background work should resume the parent session
160
- through the notification inbox and drain the returned `AgentRun`.
186
+ ### Observe vs intercept
187
+
188
+ For most events, `on` is observe-only: return nothing (or `{ action: "continue" }`)
189
+ and the runtime emits the event unchanged.
190
+
191
+ Three input event types support intercept returns:
192
+
193
+ - `user-text`
194
+ - `user-message`
195
+ - `runtime-input`
196
+
197
+ Return one of:
198
+
199
+ - `{ action: "continue" }` — emit the current event (default when omitted)
200
+ - `{ action: "transform", event }` — emit a replacement input event
201
+ - `{ action: "handled" }` — skip emit; for `session.send`, close the run without
202
+ starting a turn
203
+
204
+ Plugins run in registration order. Each `transform` updates the event seen by
205
+ later plugins, so transforms chain sequentially.
206
+
207
+ ### Input `meta.source`
208
+
209
+ The runtime attaches `meta` on input events at API boundaries. Plugins can route
210
+ on `event.meta?.source`:
211
+
212
+ | `source` | Boundary |
213
+ |----------|----------|
214
+ | `send` | `session.send()` / `agent.send()` |
215
+ | `steer` | `session.steer()` and drained steering queue |
216
+ | `notify` | `session.notify()` runtime input |
217
+ | `delegate` | parent `delegate_to_*` child `session.send()` |
218
+
219
+ `meta` appears on `run.events()` for input events but is stripped before session
220
+ history persistence and model mapping. It never reaches the LLM prompt.
221
+
222
+ ### Delegate prompt wrapping
223
+
224
+ Child agents receive delegated prompts with `meta.source === "delegate"`. Wrap or
225
+ rewrite them with a plugin instead of agent-level prompt shims:
161
226
 
162
227
  ```ts
163
- delegate_to_researcher({
164
- prompt: "Compare the API designs.",
165
- run_in_background: true,
166
- });
228
+ import type { AgentPlugin, UserText } from "@minpeter/pss-runtime";
229
+ import { Agent } from "@minpeter/pss-runtime";
167
230
 
168
- // After the completion reminder arrives:
169
- background_output({ task_id: "bg_...", block: true });
170
- background_cancel({ task_id: "bg_..." });
231
+ const pokeTagsPlugin: AgentPlugin = {
232
+ name: "poke-tags",
233
+ on: ({ event }) => {
234
+ if (event.type !== "user-text" || event.meta?.source !== "delegate") {
235
+ return;
236
+ }
237
+
238
+ const text =
239
+ typeof event.text === "string" ? event.text : event.text.join("\n");
240
+
241
+ return {
242
+ action: "transform",
243
+ event: {
244
+ ...event,
245
+ text: `<poke>\n${text}\n</poke>`,
246
+ } satisfies UserText,
247
+ };
248
+ },
249
+ };
250
+
251
+ const executionAgent = new Agent({
252
+ namespace: "execution",
253
+ plugins: [pokeTagsPlugin],
254
+ model,
255
+ });
171
256
  ```
172
257
 
173
- The parent model context stays compact by default: completion reminders include
174
- the task id, subagent name, description, and retrieval instruction. Full child
175
- traces are not injected into the parent transcript by default. Background jobs
176
- run in task-scoped child sessions. In-memory job handles are cleaned up after
177
- completed output retrieval, while durable hosts keep compact completed output
178
- available through the execution store for reconstructed agents. Background jobs
179
- launched during the same parent turn share an internal completion barrier:
180
- successful jobs notify once when the group settles, while failed jobs notify
181
- immediately.
258
+ The parent coordinator stays unchanged; only the nested child agent carries the
259
+ plugin.
260
+
261
+ ### Migration
262
+
263
+ - **`plugins[].events.on`** deprecated. Use top-level `plugins[].on`. The legacy
264
+ handler still receives every event but intercept returns are ignored (observe-only).
265
+ - **`wrapDelegatePrompt`** removed. Use a child `plugins[].on` handler that checks
266
+ `meta.source === "delegate"` and returns `transform`, as above.
182
267
 
183
268
  ## Send, Host Resume, and Steer
184
269
 
@@ -268,9 +353,9 @@ const agent = new Agent({
268
353
  });
269
354
  ```
270
355
 
271
- For durable sessions, use the exported file POC. Set a stable `namespace` when
272
- subagents also use durable stores, so reconstructed agents map the same parent
273
- session and child `sessionKey` suffixes back to the same child transcripts:
356
+ For durable sessions, use the exported file POC. Set a stable `namespace` so
357
+ reconstructed agents map the same app-owned session keys back to the same
358
+ transcripts:
274
359
 
275
360
  ```ts
276
361
  import { FileSessionStore } from "@minpeter/pss-runtime/session-store/file";
@@ -284,15 +369,20 @@ const agent = new Agent({
284
369
  });
285
370
  ```
286
371
 
287
- Hosts that need durable runs pass `host:` into `Agent`. A durable host owns
288
- execution state, scheduling, and its session snapshot store through
289
- `store.sessions`; `ExecutionHost` does not accept a separate `sessionStore`
290
- override.
372
+ Hosts that need durable runs pass `host:` into `Agent`. The execution subpath
373
+ keeps the durable surface split by responsibility, so hosts can implement only
374
+ the capabilities they need: `SessionHost`, `RunHost`, `CheckpointHost`,
375
+ `EventHost`, `NotificationHost`, `BackgroundSchedulerHost`, and
376
+ `ExecutionTransactionHost`. `ExecutionHost` remains the aggregate contract for
377
+ in-process or full-store hosts, while `DurableBackgroundHost` and
378
+ `DurableNotificationResumeHost` describe the smaller durable surfaces required
379
+ for background scheduling and notification resume.
291
380
 
292
381
  ```ts
293
382
  import { Agent } from "@minpeter/pss-runtime";
294
383
  import {
295
384
  createInMemoryExecutionHost,
385
+ type DurableBackgroundHost,
296
386
  type ExecutionHost,
297
387
  } from "@minpeter/pss-runtime/execution";
298
388
 
@@ -304,10 +394,15 @@ const agent = new Agent({
304
394
  namespace: "support-agent",
305
395
  });
306
396
 
307
- const durableHost: ExecutionHost = {
308
- capabilities: { backgroundSubagents: "durable" },
309
- scheduler,
310
- store,
397
+ const durableHost: DurableBackgroundHost = {
398
+ capabilities: {},
399
+ backgroundScheduler,
400
+ checkpointStore,
401
+ eventStore,
402
+ notificationInbox,
403
+ runStore,
404
+ sessionStore,
405
+ transaction,
311
406
  };
312
407
  ```
313
408
 
@@ -315,25 +410,20 @@ const durableHost: ExecutionHost = {
315
410
 
316
411
  The runtime supports both long-running Node.js processes and edge hosts that
317
412
  reconstruct runtime objects between turns. The same public DX stays centered on
318
- `new Agent({ subagents: [...] })`; host-specific durability and scheduling live
413
+ `new Agent({ model, tools, host })`; host-specific durability and scheduling live
319
414
  behind the `host` boundary.
320
415
 
321
416
  Long-running Node.js can keep an `Agent` and `SessionHandle` alive across turns.
322
- The default in-memory host advertises in-process background subagent capability,
323
- so `run_in_background`, `background_output`, and `background_cancel` remain
324
- available while that process owns the work. `FileSessionStore` persists session
325
- snapshots only; it does not make background work durable by itself.
417
+ `FileSessionStore` persists session snapshots only; app-owned background work
418
+ needs its own durable task/output storage if it must survive process restarts.
326
419
 
327
420
  Cloudflare Durable Objects and similar edge hosts should reconstruct `Agent`
328
- objects per turn and persist opaque session state through `store.sessions`.
329
- When a host does not advertise background subagent capability, the runtime hides
330
- background tools from the parent model and exposes blocking delegation only.
331
- This avoids pretending that in-memory background work survived a hibernation,
332
- isolate restart, or request deadline.
333
-
334
- See `examples/cloudflare-edge-subagent` for an edge-hosted turn loop with a
335
- Worker/Durable Object-shaped host, and `examples/subagent` for a long-running
336
- local background subagent flow.
421
+ objects per turn and persist opaque session state through a durable
422
+ `sessionStore`.
423
+ Use `@minpeter/pss-runtime/cloudflare` for the packaged Cloudflare Durable
424
+ Object adapter. See the sync example package for blocking app-owned delegation
425
+ and the background example package for durable background delegation in a local
426
+ interactive CLI.
337
427
 
338
428
  The same core API supports room/user/session routing through stable session keys.
339
429
 
@@ -348,11 +438,10 @@ storage is durable across hibernation/restores, while in-memory state remains
348
438
  request-local. Do not store canonical agent session or run state in memory
349
439
  attachments.
350
440
 
351
- Durable background subagents require a host capability that owns task ids,
352
- attempts, leases, checkpoints, cancellation, scheduling, and completion
353
- notifications. The Cloudflare edge example includes a Worker/Durable
354
- Object-shaped host that persists scheduled runs and session prompts, sets alarms, and
355
- resumes work through `Agent.resume(...)`.
441
+ Durable background workflows require host-owned task ids, attempts, leases,
442
+ checkpoints, cancellation, scheduling, session snapshots, and completion
443
+ notifications. The Cloudflare adapter persists scheduled runs and session
444
+ prompts, sets alarms, and resumes work through `Agent.resume(...)`.
356
445
 
357
446
  ## Checkpoints and Cancellation
358
447
 
@@ -375,7 +464,6 @@ themselves. Edge hosts still need durable scheduling, leases, resume workers,
375
464
  and notification resume handling; externally visible side-effect tools still need
376
465
  idempotent execution or a manual recovery flow.
377
466
 
378
- Cancellation is persisted before aborting active work. Parent `delete()` and
379
- `kill()` mark linked child runs as `cancelled` through the execution store before
380
- falling back to process-local cleanup, so stale child completions cannot enqueue
381
- new parent notifications.
467
+ Cancellation is persisted before aborting active work. `delete()` and `dispose()`
468
+ stop the current session's in-process work; durable hosts remain responsible for
469
+ any app-owned background run cancellation, cleanup, and notification policy.
@@ -1,10 +1,8 @@
1
- import { executionHost } from "./execution/host.js";
1
+ import { sessionHost } from "./execution/host.js";
2
2
  import { MemorySessionStore } from "./session/store/memory.js";
3
3
  //#region src/agent-host-session-store.ts
4
4
  function sessionStoreForHost(host) {
5
- if ("sessionStore" in host && host.sessionStore) return host.sessionStore;
6
- const hostExecution = executionHost(host);
7
- return hostExecution ? hostExecution.store.sessions : new MemorySessionStore();
5
+ return sessionHost(host).sessionStore ?? new MemorySessionStore();
8
6
  }
9
7
  //#endregion
10
8
  export { sessionStoreForHost };
@@ -1 +1 @@
1
- {"version":3,"file":"agent-host-session-store.js","names":[],"sources":["../src/agent-host-session-store.ts"],"sourcesContent":["import { executionHost } from \"./execution/host\";\nimport type { AgentHost } from \"./execution/types\";\nimport { MemorySessionStore } from \"./session/store/memory\";\nimport type { SessionStore } from \"./session/store/types\";\n\nexport function sessionStoreForHost(host: AgentHost): SessionStore {\n if (\"sessionStore\" in host && host.sessionStore) {\n return host.sessionStore;\n }\n\n const hostExecution = executionHost(host);\n return hostExecution\n ? hostExecution.store.sessions\n : new MemorySessionStore();\n}\n"],"mappings":";;;AAKA,SAAgB,oBAAoB,MAA+B;CACjE,IAAI,kBAAkB,QAAQ,KAAK,cACjC,OAAO,KAAK;CAGd,MAAM,gBAAgB,cAAc,IAAI;CACxC,OAAO,gBACH,cAAc,MAAM,WACpB,IAAI,mBAAmB;AAC7B"}
1
+ {"version":3,"file":"agent-host-session-store.js","names":[],"sources":["../src/agent-host-session-store.ts"],"sourcesContent":["import { sessionHost } from \"./execution/host\";\nimport type { AgentHost } from \"./execution/types\";\nimport { MemorySessionStore } from \"./session/store/memory\";\nimport type { SessionStore } from \"./session/store/types\";\n\nexport function sessionStoreForHost(host: AgentHost): SessionStore {\n return sessionHost(host).sessionStore ?? new MemorySessionStore();\n}\n"],"mappings":";;;AAKA,SAAgB,oBAAoB,MAA+B;CACjE,OAAO,YAAY,IAAI,EAAE,gBAAgB,IAAI,mBAAmB;AAClE"}
@@ -122,9 +122,8 @@ async function appendStepOutput({ emit, history, observerEvents, output, signal
122
122
  await flushObserverEvents();
123
123
  return shouldContinue ? "continue" : "completed";
124
124
  }
125
- function isLaunchOrBlockingObserverEvent(event) {
126
- if (event.type === "subagent-job-update") return false;
127
- return !(event.type === "subagent-job-end" && event.task_id);
125
+ function isLaunchOrBlockingObserverEvent(_event) {
126
+ return true;
128
127
  }
129
128
  //#endregion
130
129
  export { runAgentLoop };
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.js","names":[],"sources":["../src/agent-loop.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport type { RuntimeLlm, RuntimeLlmOutput } from \"./llm\";\nimport type { RuntimeToolExecutionContext } from \"./llm-tool-execution\";\nimport type { AgentEvent } from \"./session/events\";\nimport { modelMessageToAgentEvents } from \"./session/mapping\";\n\ninterface ModelHistory {\n appendModelMessage(message: ModelMessage): void;\n modelSnapshot(): ModelMessage[];\n}\n\ninterface RunAgentLoopOptions {\n captureObserverEvents?: ObserverEventCapture;\n emit: AgentLoopEventListener;\n history: ModelHistory;\n llm: RuntimeLlm;\n signal?: AbortSignal;\n toolExecution?: RuntimeToolExecutionContext;\n}\n\ntype AgentLoopResult = \"completed\" | \"aborted\";\ntype AgentLoopBoundaryEvent = Extract<\n AgentEvent,\n { type: \"step-end\" } | { type: \"step-start\" }\n>;\ninterface AgentLoopBoundaryDecision {\n readonly runtimeInputAdded?: boolean;\n}\ntype AgentLoopEventListener = (\n event: AgentEvent\n) =>\n | AgentLoopBoundaryDecision\n | Promise<AgentLoopBoundaryDecision | undefined>\n | undefined;\ntype StepOutputResult = \"aborted\" | \"completed\" | \"continue\";\ninterface ObserverEventCaptureResult<T> {\n readonly events: AgentEvent[];\n readonly release: () => void;\n readonly value: T;\n}\ntype ObserverEventCapture = <T>(\n callback: () => Promise<T>\n) => Promise<ObserverEventCaptureResult<T>>;\n\nexport async function runAgentLoop({\n captureObserverEvents = captureNoObserverEvents,\n emit,\n history,\n llm,\n signal = new AbortController().signal,\n toolExecution,\n}: RunAgentLoopOptions): Promise<AgentLoopResult> {\n while (true) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n const stepStartDecision = await emitBoundary({\n emit,\n event: { type: \"step-start\" },\n signal,\n });\n\n if (stepStartDecision === \"aborted\") {\n return \"aborted\";\n }\n\n const capturedOutput = await captureObserverEvents(() =>\n readLlmOutput({ history, llm, signal, toolExecution })\n );\n const output = capturedOutput.value;\n\n if (output === \"aborted\") {\n return \"aborted\";\n }\n\n const result = await appendCapturedStepOutput({\n capturedOutput,\n emit,\n history,\n output,\n signal,\n });\n\n if (result === \"aborted\") {\n return \"aborted\";\n }\n\n const stepEndDecision = await emitBoundary({\n emit,\n event: { type: \"step-end\" },\n signal,\n });\n\n if (stepEndDecision === \"aborted\") {\n return \"aborted\";\n }\n\n // Runtime input after step-end intentionally forces another inference step,\n // even after final-looking assistant text. Unconditional insertion on every\n // step-end can create an unbounded loop.\n if (result === \"completed\" && !stepEndDecision?.runtimeInputAdded) {\n return \"completed\";\n }\n }\n}\n\nasync function emitBoundary({\n emit,\n event,\n signal,\n}: Pick<RunAgentLoopOptions, \"emit\"> & {\n event: AgentLoopBoundaryEvent;\n signal: AbortSignal;\n}): Promise<AgentLoopBoundaryDecision | \"aborted\" | undefined> {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n const abort = createAbortBoundary(signal);\n try {\n return await Promise.race([Promise.resolve(emit(event)), abort.promise]);\n } catch (error) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n throw error;\n } finally {\n abort.dispose();\n }\n}\n\nfunction createAbortBoundary(signal: AbortSignal): {\n dispose: () => void;\n promise: Promise<\"aborted\">;\n} {\n let dispose: () => void = () => undefined;\n\n const promise = new Promise<\"aborted\">((resolve) => {\n const onAbort = () => resolve(\"aborted\");\n dispose = () => signal.removeEventListener(\"abort\", onAbort);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n\n return { dispose, promise };\n}\n\nasync function captureNoObserverEvents<T>(callback: () => Promise<T>): Promise<{\n readonly events: AgentEvent[];\n readonly release: () => void;\n readonly value: T;\n}> {\n return {\n events: [],\n release: releaseNoObserverEvents,\n value: await callback(),\n };\n}\n\nfunction releaseNoObserverEvents(): void {\n return;\n}\n\nasync function readLlmOutput({\n history,\n llm,\n signal,\n toolExecution,\n}: Pick<RunAgentLoopOptions, \"history\" | \"llm\"> & {\n signal: AbortSignal;\n toolExecution?: RuntimeToolExecutionContext;\n}): Promise<RuntimeLlmOutput | \"aborted\"> {\n try {\n return await llm({\n history: history.modelSnapshot(),\n signal,\n toolExecution,\n });\n } catch (error) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n throw error;\n }\n}\n\nasync function appendCapturedStepOutput({\n capturedOutput,\n emit,\n history,\n output,\n signal,\n}: Pick<RunAgentLoopOptions, \"emit\"> & { history: ModelHistory } & {\n capturedOutput: ObserverEventCaptureResult<RuntimeLlmOutput | \"aborted\">;\n output: RuntimeLlmOutput;\n signal: AbortSignal;\n}): Promise<StepOutputResult> {\n try {\n return await appendStepOutput({\n emit,\n history,\n observerEvents: capturedOutput.events,\n output,\n signal,\n });\n } finally {\n capturedOutput.release();\n }\n}\n\nasync function appendStepOutput({\n emit,\n history,\n observerEvents,\n output,\n signal,\n}: Pick<RunAgentLoopOptions, \"emit\"> & { history: ModelHistory } & {\n observerEvents: AgentEvent[];\n output: RuntimeLlmOutput;\n signal: AbortSignal;\n}): Promise<StepOutputResult> {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n let shouldContinue = false;\n const pendingObserverEvents = observerEvents;\n const flushObserverEvents = async (\n shouldFlush: (event: AgentEvent) => boolean = () => true\n ) => {\n for (let index = 0; index < pendingObserverEvents.length; ) {\n const event = pendingObserverEvents[index];\n if (!(event && shouldFlush(event))) {\n index += 1;\n continue;\n }\n pendingObserverEvents.splice(index, 1);\n await emit(event);\n }\n };\n\n for (const message of output) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n history.appendModelMessage(message);\n const events = modelMessageToAgentEvents(message);\n const hasToolResult = events.some((event) => event.type === \"tool-result\");\n\n for (const event of events) {\n await emit(event);\n if (event.type === \"tool-call\") {\n shouldContinue = true;\n await flushObserverEvents(isLaunchOrBlockingObserverEvent);\n }\n }\n\n if (hasToolResult) {\n await flushObserverEvents();\n }\n }\n\n await flushObserverEvents();\n\n return shouldContinue ? \"continue\" : \"completed\";\n}\n\nfunction isLaunchOrBlockingObserverEvent(event: AgentEvent): boolean {\n if (event.type === \"subagent-job-update\") {\n return false;\n }\n\n return !(event.type === \"subagent-job-end\" && event.task_id);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-loop.js","names":[],"sources":["../src/agent-loop.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport type { RuntimeLlm, RuntimeLlmOutput } from \"./llm\";\nimport type { RuntimeToolExecutionContext } from \"./llm-tool-execution\";\nimport type { AgentEvent } from \"./session/events\";\nimport { modelMessageToAgentEvents } from \"./session/mapping\";\n\ninterface ModelHistory {\n appendModelMessage(message: ModelMessage): void;\n modelSnapshot(): ModelMessage[];\n}\n\ninterface RunAgentLoopOptions {\n captureObserverEvents?: ObserverEventCapture;\n emit: AgentLoopEventListener;\n history: ModelHistory;\n llm: RuntimeLlm;\n signal?: AbortSignal;\n toolExecution?: RuntimeToolExecutionContext;\n}\n\ntype AgentLoopResult = \"completed\" | \"aborted\";\ntype AgentLoopBoundaryEvent = Extract<\n AgentEvent,\n { type: \"step-end\" } | { type: \"step-start\" }\n>;\ninterface AgentLoopBoundaryDecision {\n readonly runtimeInputAdded?: boolean;\n}\ntype AgentLoopEventListener = (\n event: AgentEvent\n) =>\n | AgentLoopBoundaryDecision\n | Promise<AgentLoopBoundaryDecision | undefined>\n | undefined;\ntype StepOutputResult = \"aborted\" | \"completed\" | \"continue\";\ninterface ObserverEventCaptureResult<T> {\n readonly events: AgentEvent[];\n readonly release: () => void;\n readonly value: T;\n}\ntype ObserverEventCapture = <T>(\n callback: () => Promise<T>\n) => Promise<ObserverEventCaptureResult<T>>;\n\nexport async function runAgentLoop({\n captureObserverEvents = captureNoObserverEvents,\n emit,\n history,\n llm,\n signal = new AbortController().signal,\n toolExecution,\n}: RunAgentLoopOptions): Promise<AgentLoopResult> {\n while (true) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n const stepStartDecision = await emitBoundary({\n emit,\n event: { type: \"step-start\" },\n signal,\n });\n\n if (stepStartDecision === \"aborted\") {\n return \"aborted\";\n }\n\n const capturedOutput = await captureObserverEvents(() =>\n readLlmOutput({ history, llm, signal, toolExecution })\n );\n const output = capturedOutput.value;\n\n if (output === \"aborted\") {\n return \"aborted\";\n }\n\n const result = await appendCapturedStepOutput({\n capturedOutput,\n emit,\n history,\n output,\n signal,\n });\n\n if (result === \"aborted\") {\n return \"aborted\";\n }\n\n const stepEndDecision = await emitBoundary({\n emit,\n event: { type: \"step-end\" },\n signal,\n });\n\n if (stepEndDecision === \"aborted\") {\n return \"aborted\";\n }\n\n // Runtime input after step-end intentionally forces another inference step,\n // even after final-looking assistant text. Unconditional insertion on every\n // step-end can create an unbounded loop.\n if (result === \"completed\" && !stepEndDecision?.runtimeInputAdded) {\n return \"completed\";\n }\n }\n}\n\nasync function emitBoundary({\n emit,\n event,\n signal,\n}: Pick<RunAgentLoopOptions, \"emit\"> & {\n event: AgentLoopBoundaryEvent;\n signal: AbortSignal;\n}): Promise<AgentLoopBoundaryDecision | \"aborted\" | undefined> {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n const abort = createAbortBoundary(signal);\n try {\n return await Promise.race([Promise.resolve(emit(event)), abort.promise]);\n } catch (error) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n throw error;\n } finally {\n abort.dispose();\n }\n}\n\nfunction createAbortBoundary(signal: AbortSignal): {\n dispose: () => void;\n promise: Promise<\"aborted\">;\n} {\n let dispose: () => void = () => undefined;\n\n const promise = new Promise<\"aborted\">((resolve) => {\n const onAbort = () => resolve(\"aborted\");\n dispose = () => signal.removeEventListener(\"abort\", onAbort);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n\n return { dispose, promise };\n}\n\nasync function captureNoObserverEvents<T>(callback: () => Promise<T>): Promise<{\n readonly events: AgentEvent[];\n readonly release: () => void;\n readonly value: T;\n}> {\n return {\n events: [],\n release: releaseNoObserverEvents,\n value: await callback(),\n };\n}\n\nfunction releaseNoObserverEvents(): void {\n return;\n}\n\nasync function readLlmOutput({\n history,\n llm,\n signal,\n toolExecution,\n}: Pick<RunAgentLoopOptions, \"history\" | \"llm\"> & {\n signal: AbortSignal;\n toolExecution?: RuntimeToolExecutionContext;\n}): Promise<RuntimeLlmOutput | \"aborted\"> {\n try {\n return await llm({\n history: history.modelSnapshot(),\n signal,\n toolExecution,\n });\n } catch (error) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n throw error;\n }\n}\n\nasync function appendCapturedStepOutput({\n capturedOutput,\n emit,\n history,\n output,\n signal,\n}: Pick<RunAgentLoopOptions, \"emit\"> & { history: ModelHistory } & {\n capturedOutput: ObserverEventCaptureResult<RuntimeLlmOutput | \"aborted\">;\n output: RuntimeLlmOutput;\n signal: AbortSignal;\n}): Promise<StepOutputResult> {\n try {\n return await appendStepOutput({\n emit,\n history,\n observerEvents: capturedOutput.events,\n output,\n signal,\n });\n } finally {\n capturedOutput.release();\n }\n}\n\nasync function appendStepOutput({\n emit,\n history,\n observerEvents,\n output,\n signal,\n}: Pick<RunAgentLoopOptions, \"emit\"> & { history: ModelHistory } & {\n observerEvents: AgentEvent[];\n output: RuntimeLlmOutput;\n signal: AbortSignal;\n}): Promise<StepOutputResult> {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n let shouldContinue = false;\n const pendingObserverEvents = observerEvents;\n const flushObserverEvents = async (\n shouldFlush: (event: AgentEvent) => boolean = () => true\n ) => {\n for (let index = 0; index < pendingObserverEvents.length; ) {\n const event = pendingObserverEvents[index];\n if (!(event && shouldFlush(event))) {\n index += 1;\n continue;\n }\n pendingObserverEvents.splice(index, 1);\n await emit(event);\n }\n };\n\n for (const message of output) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n history.appendModelMessage(message);\n const events = modelMessageToAgentEvents(message);\n const hasToolResult = events.some((event) => event.type === \"tool-result\");\n\n for (const event of events) {\n await emit(event);\n if (event.type === \"tool-call\") {\n shouldContinue = true;\n await flushObserverEvents(isLaunchOrBlockingObserverEvent);\n }\n }\n\n if (hasToolResult) {\n await flushObserverEvents();\n }\n }\n\n await flushObserverEvents();\n\n return shouldContinue ? \"continue\" : \"completed\";\n}\n\nfunction isLaunchOrBlockingObserverEvent(_event: AgentEvent): boolean {\n return true;\n}\n"],"mappings":"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"}
@@ -8,17 +8,13 @@ function agentNamespace(namespace) {
8
8
  function namespacePart(value) {
9
9
  return encodeURIComponent(value);
10
10
  }
11
- function parentSessionNamespace({ generation, sessionKey, sessionNamespace }) {
12
- return `${sessionNamespace}:session:${namespacePart(sessionKey)}:generation:${generation}`;
13
- }
14
11
  function ownsAgentNamespace(ownerNamespace, sessionNamespace) {
15
12
  return ownerNamespace === sessionNamespace || ownerNamespace?.startsWith(`${sessionNamespace}:session:`) === true;
16
13
  }
17
- function stableAgentNamespace({ name, namespace }) {
18
- const stableNamespace = namespace ?? name;
19
- return stableNamespace ? agentNamespace(stableNamespace) : randomAgentNamespace();
14
+ function stableAgentNamespace({ namespace }) {
15
+ return namespace ? agentNamespace(namespace) : randomAgentNamespace();
20
16
  }
21
17
  //#endregion
22
- export { ownsAgentNamespace, parentSessionNamespace, stableAgentNamespace };
18
+ export { ownsAgentNamespace, stableAgentNamespace };
23
19
 
24
20
  //# sourceMappingURL=agent-namespace.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-namespace.js","names":[],"sources":["../src/agent-namespace.ts"],"sourcesContent":["export function randomAgentNamespace(): string {\n return agentNamespace(crypto.randomUUID());\n}\n\nexport function agentNamespace(namespace: string): string {\n return `agent:${namespacePart(namespace)}`;\n}\n\nexport function namespacePart(value: string): string {\n return encodeURIComponent(value);\n}\n\nexport function parentSessionNamespace({\n generation,\n sessionKey,\n sessionNamespace,\n}: {\n readonly generation: number;\n readonly sessionKey: string;\n readonly sessionNamespace: string;\n}): string {\n return `${sessionNamespace}:session:${namespacePart(\n sessionKey\n )}:generation:${generation}`;\n}\n\nexport function ownsAgentNamespace(\n ownerNamespace: string | undefined,\n sessionNamespace: string\n): boolean {\n return (\n ownerNamespace === sessionNamespace ||\n ownerNamespace?.startsWith(`${sessionNamespace}:session:`) === true\n );\n}\n\nexport function stableAgentNamespace({\n name,\n namespace,\n}: {\n readonly name?: string;\n readonly namespace?: string;\n}): string {\n const stableNamespace = namespace ?? name;\n return stableNamespace\n ? agentNamespace(stableNamespace)\n : randomAgentNamespace();\n}\n"],"mappings":";AAAA,SAAgB,uBAA+B;CAC7C,OAAO,eAAe,OAAO,WAAW,CAAC;AAC3C;AAEA,SAAgB,eAAe,WAA2B;CACxD,OAAO,SAAS,cAAc,SAAS;AACzC;AAEA,SAAgB,cAAc,OAAuB;CACnD,OAAO,mBAAmB,KAAK;AACjC;AAEA,SAAgB,uBAAuB,EACrC,YACA,YACA,oBAKS;CACT,OAAO,GAAG,iBAAiB,WAAW,cACpC,UACF,EAAE,cAAc;AAClB;AAEA,SAAgB,mBACd,gBACA,kBACS;CACT,OACE,mBAAmB,oBACnB,gBAAgB,WAAW,GAAG,iBAAiB,UAAU,MAAM;AAEnE;AAEA,SAAgB,qBAAqB,EACnC,MACA,aAIS;CACT,MAAM,kBAAkB,aAAa;CACrC,OAAO,kBACH,eAAe,eAAe,IAC9B,qBAAqB;AAC3B"}
1
+ {"version":3,"file":"agent-namespace.js","names":[],"sources":["../src/agent-namespace.ts"],"sourcesContent":["export function randomAgentNamespace(): string {\n return agentNamespace(crypto.randomUUID());\n}\n\nexport function agentNamespace(namespace: string): string {\n return `agent:${namespacePart(namespace)}`;\n}\n\nexport function namespacePart(value: string): string {\n return encodeURIComponent(value);\n}\n\nexport function parentSessionNamespace({\n generation,\n sessionKey,\n sessionNamespace,\n}: {\n readonly generation: number;\n readonly sessionKey: string;\n readonly sessionNamespace: string;\n}): string {\n return `${sessionNamespace}:session:${namespacePart(\n sessionKey\n )}:generation:${generation}`;\n}\n\nexport function ownsAgentNamespace(\n ownerNamespace: string | undefined,\n sessionNamespace: string\n): boolean {\n return (\n ownerNamespace === sessionNamespace ||\n ownerNamespace?.startsWith(`${sessionNamespace}:session:`) === true\n );\n}\n\nexport function stableAgentNamespace({\n namespace,\n}: {\n readonly namespace?: string;\n}): string {\n return namespace ? agentNamespace(namespace) : randomAgentNamespace();\n}\n"],"mappings":";AAAA,SAAgB,uBAA+B;CAC7C,OAAO,eAAe,OAAO,WAAW,CAAC;AAC3C;AAEA,SAAgB,eAAe,WAA2B;CACxD,OAAO,SAAS,cAAc,SAAS;AACzC;AAEA,SAAgB,cAAc,OAAuB;CACnD,OAAO,mBAAmB,KAAK;AACjC;AAgBA,SAAgB,mBACd,gBACA,kBACS;CACT,OACE,mBAAmB,oBACnB,gBAAgB,WAAW,GAAG,iBAAiB,UAAU,MAAM;AAEnE;AAEA,SAAgB,qBAAqB,EACnC,aAGS;CACT,OAAO,YAAY,eAAe,SAAS,IAAI,qBAAqB;AACtE"}
@@ -1,35 +1,29 @@
1
1
  import { AgentToolChoice, RuntimeLlm } from "./llm.js";
2
2
  import { AgentHost } from "./execution/types.js";
3
3
  import { AgentPlugin } from "./plugins.js";
4
- import { Agent } from "./agent.js";
5
4
  import { LanguageModel, ToolSet } from "ai";
6
5
 
7
6
  //#region src/agent-options.d.ts
8
7
  interface AgentLanguageModelOptions {
9
- readonly description?: string;
10
8
  readonly host?: AgentHost;
11
9
  readonly instructions?: string;
12
10
  readonly model: LanguageModel;
13
- readonly name?: string;
14
11
  readonly namespace?: string;
15
12
  readonly plugins?: readonly AgentPlugin[];
16
- readonly subagents?: readonly Agent[];
17
13
  readonly toolChoice?: AgentToolChoice;
18
14
  readonly tools?: ToolSet;
19
15
  }
20
16
  interface AgentRuntimeModelOptions {
21
- readonly description?: string;
22
17
  readonly host?: AgentHost;
23
18
  readonly instructions?: never;
24
19
  readonly model: RuntimeLlm;
25
- readonly name?: string;
26
20
  readonly namespace?: string;
27
21
  readonly plugins?: readonly AgentPlugin[];
28
- readonly subagents?: never;
29
22
  readonly toolChoice?: never;
30
23
  readonly tools?: never;
31
24
  }
32
25
  type AgentOptions = AgentLanguageModelOptions | AgentRuntimeModelOptions;
26
+ type AgentConstructionOptions = AgentOptions;
33
27
  //#endregion
34
- export { AgentOptions };
28
+ export { AgentConstructionOptions, AgentOptions };
35
29
  //# sourceMappingURL=agent-options.d.ts.map
@@ -1,16 +1,16 @@
1
1
  //#region src/agent-options.ts
2
2
  function assertAgentOptions(options) {
3
3
  if (options === null || typeof options !== "object") throw new TypeError("Agent options are required. Provide { model }.");
4
- if ("sessions" in options) throw new TypeError("Agent: unsupported options.sessions. Use host: { sessionStore } and namespace instead.");
5
- if ("runtime" in options) throw new TypeError("Agent: unsupported options.runtime. Use host.");
6
- if ("llm" in options) throw new TypeError("Agent: unsupported options.llm. Use model for both AI SDK models and custom RuntimeLlm functions.");
7
4
  if (!("model" in options && options.model != null)) throw new TypeError("Agent: missing options.model.");
8
5
  if (typeof options.model !== "function" && (typeof options.model !== "object" || options.model === null)) throw new TypeError("Agent: invalid options.model.");
9
6
  }
10
7
  function hasRuntimeModel(options) {
11
8
  return typeof options.model === "function";
12
9
  }
10
+ function hasLanguageModel(options) {
11
+ return typeof options.model !== "function";
12
+ }
13
13
  //#endregion
14
- export { assertAgentOptions, hasRuntimeModel };
14
+ export { assertAgentOptions, hasLanguageModel, hasRuntimeModel };
15
15
 
16
16
  //# sourceMappingURL=agent-options.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-options.js","names":[],"sources":["../src/agent-options.ts"],"sourcesContent":["import type { LanguageModel, ToolSet } from \"ai\";\nimport type { Agent } from \"./agent\";\nimport type { AgentHost } from \"./execution/types\";\nimport type { AgentToolChoice, RuntimeLlm } from \"./llm\";\nimport type { AgentPlugin } from \"./plugins\";\n\ninterface AgentLanguageModelOptions {\n readonly description?: string;\n readonly host?: AgentHost;\n readonly instructions?: string;\n readonly model: LanguageModel;\n readonly name?: string;\n readonly namespace?: string;\n readonly plugins?: readonly AgentPlugin[];\n readonly subagents?: readonly Agent[];\n readonly toolChoice?: AgentToolChoice;\n readonly tools?: ToolSet;\n}\n\ninterface AgentRuntimeModelOptions {\n readonly description?: string;\n readonly host?: AgentHost;\n readonly instructions?: never;\n readonly model: RuntimeLlm;\n readonly name?: string;\n readonly namespace?: string;\n readonly plugins?: readonly AgentPlugin[];\n readonly subagents?: never;\n readonly toolChoice?: never;\n readonly tools?: never;\n}\n\nexport type AgentModelOptions = Pick<\n AgentLanguageModelOptions,\n \"instructions\" | \"model\" | \"toolChoice\"\n>;\nexport type AgentOptions = AgentLanguageModelOptions | AgentRuntimeModelOptions;\n\nexport function assertAgentOptions(\n options: unknown\n): asserts options is AgentOptions {\n if (options === null || typeof options !== \"object\") {\n throw new TypeError(\"Agent options are required. Provide { model }.\");\n }\n\n if (\"sessions\" in options) {\n throw new TypeError(\n \"Agent: unsupported options.sessions. Use host: { sessionStore } and namespace instead.\"\n );\n }\n\n if (\"runtime\" in options) {\n throw new TypeError(\"Agent: unsupported options.runtime. Use host.\");\n }\n\n if (\"llm\" in options) {\n throw new TypeError(\n \"Agent: unsupported options.llm. Use model for both AI SDK models and custom RuntimeLlm functions.\"\n );\n }\n\n const hasModel = \"model\" in options && options.model != null;\n\n if (!hasModel) {\n throw new TypeError(\"Agent: missing options.model.\");\n }\n\n if (\n typeof options.model !== \"function\" &&\n (typeof options.model !== \"object\" || options.model === null)\n ) {\n throw new TypeError(\"Agent: invalid options.model.\");\n }\n}\n\nexport function hasRuntimeModel(\n options: AgentOptions\n): options is AgentRuntimeModelOptions {\n return typeof options.model === \"function\";\n}\n"],"mappings":";AAsCA,SAAgB,mBACd,SACiC;CACjC,IAAI,YAAY,QAAQ,OAAO,YAAY,UACzC,MAAM,IAAI,UAAU,gDAAgD;CAGtE,IAAI,cAAc,SAChB,MAAM,IAAI,UACR,wFACF;CAGF,IAAI,aAAa,SACf,MAAM,IAAI,UAAU,+CAA+C;CAGrE,IAAI,SAAS,SACX,MAAM,IAAI,UACR,mGACF;CAKF,IAAI,EAFa,WAAW,WAAW,QAAQ,SAAS,OAGtD,MAAM,IAAI,UAAU,+BAA+B;CAGrD,IACE,OAAO,QAAQ,UAAU,eACxB,OAAO,QAAQ,UAAU,YAAY,QAAQ,UAAU,OAExD,MAAM,IAAI,UAAU,+BAA+B;AAEvD;AAEA,SAAgB,gBACd,SACqC;CACrC,OAAO,OAAO,QAAQ,UAAU;AAClC"}
1
+ {"version":3,"file":"agent-options.js","names":[],"sources":["../src/agent-options.ts"],"sourcesContent":["import type { LanguageModel, ToolSet } from \"ai\";\nimport type { AgentHost } from \"./execution/types\";\nimport type { AgentToolChoice, RuntimeLlm } from \"./llm\";\nimport type { AgentPlugin } from \"./plugins\";\n\ninterface AgentLanguageModelOptions {\n readonly host?: AgentHost;\n readonly instructions?: string;\n readonly model: LanguageModel;\n readonly namespace?: string;\n readonly plugins?: readonly AgentPlugin[];\n readonly toolChoice?: AgentToolChoice;\n readonly tools?: ToolSet;\n}\n\ninterface AgentRuntimeModelOptions {\n readonly host?: AgentHost;\n readonly instructions?: never;\n readonly model: RuntimeLlm;\n readonly namespace?: string;\n readonly plugins?: readonly AgentPlugin[];\n readonly toolChoice?: never;\n readonly tools?: never;\n}\n\nexport type AgentModelOptions = Pick<\n AgentLanguageModelOptions,\n \"instructions\" | \"model\" | \"toolChoice\"\n>;\nexport type AgentOptions = AgentLanguageModelOptions | AgentRuntimeModelOptions;\n\nexport type AgentConstructionOptions = AgentOptions;\n\nexport function assertAgentOptions(\n options: unknown\n): asserts options is AgentConstructionOptions {\n if (options === null || typeof options !== \"object\") {\n throw new TypeError(\"Agent options are required. Provide { model }.\");\n }\n\n const hasModel = \"model\" in options && options.model != null;\n\n if (!hasModel) {\n throw new TypeError(\"Agent: missing options.model.\");\n }\n\n if (\n typeof options.model !== \"function\" &&\n (typeof options.model !== \"object\" || options.model === null)\n ) {\n throw new TypeError(\"Agent: invalid options.model.\");\n }\n}\n\nexport function hasRuntimeModel(\n options: AgentConstructionOptions\n): options is AgentRuntimeModelOptions {\n return typeof options.model === \"function\";\n}\n\nexport function hasLanguageModel(\n options: AgentConstructionOptions\n): options is AgentLanguageModelOptions {\n return typeof options.model !== \"function\";\n}\n"],"mappings":";AAiCA,SAAgB,mBACd,SAC6C;CAC7C,IAAI,YAAY,QAAQ,OAAO,YAAY,UACzC,MAAM,IAAI,UAAU,gDAAgD;CAKtE,IAAI,EAFa,WAAW,WAAW,QAAQ,SAAS,OAGtD,MAAM,IAAI,UAAU,+BAA+B;CAGrD,IACE,OAAO,QAAQ,UAAU,eACxB,OAAO,QAAQ,UAAU,YAAY,QAAQ,UAAU,OAExD,MAAM,IAAI,UAAU,+BAA+B;AAEvD;AAEA,SAAgB,gBACd,SACqC;CACrC,OAAO,OAAO,QAAQ,UAAU;AAClC;AAEA,SAAgB,iBACd,SACsC;CACtC,OAAO,OAAO,QAAQ,UAAU;AAClC"}
@@ -1,20 +1,9 @@
1
- import { readDurableBackgroundChildRunState } from "./subagent-background-child-run-state.js";
2
1
  import { ownsAgentNamespace } from "./agent-namespace.js";
3
- import { StoredAgentRun } from "./execution/run.js";
4
- import { BufferedAgentRun } from "./session/run.js";
5
- import { buildDurableResumeGroups } from "./subagent-background-resume-group.js";
6
- import { runBackgroundJob } from "./subagent-background-runner.js";
7
2
  //#region src/agent-resume.ts
8
- const defaultResumeLeaseMs = 3e5;
9
- async function resumeAgentRun({ host, ownerNamespace, resumeNotification, runId, subagents }) {
3
+ async function resumeAgentRun({ host, ownerNamespace, resumeNotification, runId }) {
10
4
  const run = await host.store.runs.get(runId);
11
5
  if (!run) return null;
12
6
  if (!canAccessRun(run, ownerNamespace)) return null;
13
- if (run.kind === "background-subagent") return await resumeBackgroundSubagentRun({
14
- host,
15
- run,
16
- subagents
17
- });
18
7
  if (run.kind === "notification" && run.dedupeKey) {
19
8
  const idempotencyKey = run.dedupeKey;
20
9
  const claimed = await claimRun(host, run);
@@ -59,84 +48,15 @@ async function completeNotificationRun(host, runId) {
59
48
  status: "completed"
60
49
  });
61
50
  }
62
- async function resumeBackgroundSubagentRun({ host, run, subagents }) {
63
- const claimed = await claimRun(host, run);
64
- if (!claimed) return null;
65
- const state = readDurableBackgroundChildRunState(await host.store.checkpoints.latest(run.runId));
66
- if (!state) throw new AgentResumeError(run.runId, "missing background run state");
67
- const subagent = subagents.find((candidate) => candidate.name === state.subagent);
68
- if (!subagent) throw new AgentResumeError(run.runId, `missing subagent ${JSON.stringify(state.subagent)}`);
69
- const childSession = subagent.session(claimed.sessionKey);
70
- const job = {
71
- abort: () => childSession.interrupt(),
72
- childRunId: claimed.runId,
73
- childRunLeaseId: claimed.lease?.leaseId,
74
- cleanup: () => Promise.resolve(),
75
- dedupeKey: claimed.dedupeKey,
76
- delegateToolCallId: state.delegateToolCallId,
77
- description: state.description,
78
- executionHost: host,
79
- groupId: state.groupId,
80
- id: claimed.publicTaskId ?? claimed.runId,
81
- ownerNamespace: claimed.ownerNamespace,
82
- parentRunId: claimed.parentRunId,
83
- parentSessionKey: state.parentSessionKey,
84
- promise: Promise.resolve(),
85
- sessionKey: claimed.sessionKey,
86
- settled: false,
87
- status: "running",
88
- subagent: state.subagent
89
- };
90
- const jobs = new Map([[job.id, job]]);
91
- job.promise = runBackgroundJob({
92
- childSession,
93
- groups: await buildDurableResumeGroups({
94
- currentJob: job,
95
- host,
96
- run: claimed,
97
- state,
98
- jobs
99
- }),
100
- jobs,
101
- job,
102
- parentSession: durableParentSession(host, run.runId),
103
- prompt: state.prompt
104
- }).finally(() => {
105
- job.settled = true;
106
- });
107
- await job.promise;
108
- return new StoredAgentRun({
109
- eventStore: host.store.events,
110
- runId: run.runId
111
- });
112
- }
113
51
  async function claimRun(host, run) {
114
52
  const claim = await host.store.runs.claim(run.runId, {
115
53
  attempt: (run.lease?.attempt ?? 0) + 1,
116
54
  leaseId: crypto.randomUUID(),
117
- leaseMs: defaultResumeLeaseMs,
55
+ leaseMs: 3e5,
118
56
  nowMs: Date.now()
119
57
  });
120
58
  return claim.ok ? claim.record : null;
121
59
  }
122
- function durableParentSession(host, runId) {
123
- return {
124
- emitObserverEvent: (event) => host.store.events.append(runId, event).then(),
125
- enqueueRuntimeInput: () => void 0,
126
- notify: () => Promise.resolve(emptyRun())
127
- };
128
- }
129
- function emptyRun() {
130
- const run = new BufferedAgentRun();
131
- run.close();
132
- return run;
133
- }
134
- var AgentResumeError = class extends Error {
135
- constructor(runId, reason) {
136
- super(`Cannot resume agent run ${runId}: ${reason}`);
137
- this.name = "AgentResumeError";
138
- }
139
- };
140
60
  //#endregion
141
61
  export { resumeAgentRun };
142
62