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

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 (145) hide show
  1. package/README.md +172 -85
  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 +9 -8
  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 +4 -19
  9. package/dist/agent-options.js +2 -8
  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 +19 -89
  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 +9 -5
  66. package/dist/index.js +6 -2
  67. package/dist/llm-tool-execution.js.map +1 -1
  68. package/dist/llm.d.ts +1 -21
  69. package/dist/llm.js +12 -14
  70. package/dist/llm.js.map +1 -1
  71. package/dist/plugins.d.ts +27 -5
  72. package/dist/plugins.js +35 -6
  73. package/dist/plugins.js.map +1 -1
  74. package/dist/session/delegate-input.d.ts +9 -0
  75. package/dist/session/delegate-input.js +16 -0
  76. package/dist/session/delegate-input.js.map +1 -0
  77. package/dist/session/events.d.ts +43 -25
  78. package/dist/session/events.js +41 -0
  79. package/dist/session/events.js.map +1 -0
  80. package/dist/session/input-meta-types.d.ts +10 -0
  81. package/dist/session/input-meta.d.ts +13 -0
  82. package/dist/session/input-meta.js +45 -0
  83. package/dist/session/input-meta.js.map +1 -0
  84. package/dist/session/input.d.ts +4 -0
  85. package/dist/session/mapping.js +4 -2
  86. package/dist/session/mapping.js.map +1 -1
  87. package/dist/session/runtime-input-emit.js +41 -0
  88. package/dist/session/runtime-input-emit.js.map +1 -0
  89. package/dist/session/runtime-input.js +5 -1
  90. package/dist/session/runtime-input.js.map +1 -1
  91. package/dist/session/session-events.js +20 -6
  92. package/dist/session/session-events.js.map +1 -1
  93. package/dist/session/session-notification.js +3 -2
  94. package/dist/session/session-notification.js.map +1 -1
  95. package/dist/session/session-runtime-drain.js +3 -9
  96. package/dist/session/session-runtime-drain.js.map +1 -1
  97. package/dist/session/session-turn-processor.js +10 -20
  98. package/dist/session/session-turn-processor.js.map +1 -1
  99. package/dist/session/session.js +15 -8
  100. package/dist/session/session.js.map +1 -1
  101. package/package.json +6 -1
  102. package/dist/agent-child-runs.js +0 -16
  103. package/dist/agent-child-runs.js.map +0 -1
  104. package/dist/agent-host-capabilities.js +0 -9
  105. package/dist/agent-host-capabilities.js.map +0 -1
  106. package/dist/agent-validation.js +0 -35
  107. package/dist/agent-validation.js.map +0 -1
  108. package/dist/child-session-cleanups.js +0 -61
  109. package/dist/child-session-cleanups.js.map +0 -1
  110. package/dist/execution/run.js +0 -55
  111. package/dist/execution/run.js.map +0 -1
  112. package/dist/subagent-background-child-run-state.js +0 -51
  113. package/dist/subagent-background-child-run-state.js.map +0 -1
  114. package/dist/subagent-background-child-run.js +0 -103
  115. package/dist/subagent-background-child-run.js.map +0 -1
  116. package/dist/subagent-background-in-process.js +0 -98
  117. package/dist/subagent-background-in-process.js.map +0 -1
  118. package/dist/subagent-background-notification-inbox.js +0 -106
  119. package/dist/subagent-background-notification-inbox.js.map +0 -1
  120. package/dist/subagent-background-notify.js +0 -136
  121. package/dist/subagent-background-notify.js.map +0 -1
  122. package/dist/subagent-background-resume-group.js +0 -99
  123. package/dist/subagent-background-resume-group.js.map +0 -1
  124. package/dist/subagent-background-runner.js +0 -115
  125. package/dist/subagent-background-runner.js.map +0 -1
  126. package/dist/subagent-background-schedule.js +0 -43
  127. package/dist/subagent-background-schedule.js.map +0 -1
  128. package/dist/subagent-child-run.js +0 -68
  129. package/dist/subagent-child-run.js.map +0 -1
  130. package/dist/subagent-job-cancel.js +0 -84
  131. package/dist/subagent-job-cancel.js.map +0 -1
  132. package/dist/subagent-job-observer.js +0 -19
  133. package/dist/subagent-job-observer.js.map +0 -1
  134. package/dist/subagent-job-output.js +0 -87
  135. package/dist/subagent-job-output.js.map +0 -1
  136. package/dist/subagent-job-state.js +0 -66
  137. package/dist/subagent-job-state.js.map +0 -1
  138. package/dist/subagent-jobs.js +0 -96
  139. package/dist/subagent-jobs.js.map +0 -1
  140. package/dist/subagent-prompt-schema.js +0 -114
  141. package/dist/subagent-prompt-schema.js.map +0 -1
  142. package/dist/subagent-run.js +0 -111
  143. package/dist/subagent-run.js.map +0 -1
  144. package/dist/subagents.js +0 -125
  145. package/dist/subagents.js.map +0 -1
package/README.md CHANGED
@@ -50,19 +50,18 @@ 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
55
- adapter yourself:
53
+ `LanguageModel` object and configure runtime-owned prompting through
54
+ `instructions`, `tools`, and `toolChoice`:
56
55
 
57
56
  ```ts
58
- import { Agent, type RuntimeLlm } from "@minpeter/pss-runtime";
57
+ import { openai } from "@ai-sdk/openai";
58
+ import { Agent } from "@minpeter/pss-runtime";
59
59
 
60
- const runtimeModel: RuntimeLlm = async ({ history }) => [
61
- { role: "assistant", content: `Seen ${history.length} messages.` },
62
- ];
60
+ const model = openai("gpt-4.1-mini");
63
61
 
64
62
  const agent = new Agent({
65
- model: runtimeModel,
63
+ instructions: "Answer with concise operational notes.",
64
+ model,
66
65
  });
67
66
  ```
68
67
 
@@ -116,69 +115,154 @@ The public transcript protocol is `AgentEvent`: live runs emit runtime-defined
116
115
  events through `run.events()`. Provider/model message history is internal
117
116
  continuation state, not a public history API.
118
117
 
119
- ## Subagents
118
+ ## Delegation
120
119
 
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.
120
+ Delegation is app-owned. Build ordinary tools that call another `Agent`,
121
+ `session.send(...)`, `session.notify(...)`, or host-owned background work, then
122
+ return the compact result shape your product wants the model to see.
125
123
 
126
124
  ```ts
127
- const researcher = new Agent({
128
- name: "researcher",
129
- description: "Researches facts and returns concise evidence.",
125
+ const reader = new Agent({
126
+ instructions: "Read knowledge-base files and cite paths.",
130
127
  model,
131
- instructions: "Research facts and return concise evidence.",
128
+ namespace: "reader",
132
129
  });
133
130
 
134
131
  const coordinator = new Agent({
132
+ instructions: "Coordinate work and delegate knowledge-base reads.",
135
133
  model,
136
- instructions: "Coordinate work and delegate when useful.",
137
- subagents: [researcher],
134
+ namespace: "coordinator",
135
+ tools: {
136
+ delegate_to_reader: tool({
137
+ description: "Ask the reader agent to inspect the knowledge base.",
138
+ execute: async ({ prompt }) => {
139
+ const run = await reader.session("kb").send(prompt);
140
+ const text: string[] = [];
141
+ for await (const event of run.events()) {
142
+ if (event.type === "assistant-text") {
143
+ text.push(event.text);
144
+ }
145
+ }
146
+ return { result: text.join("\n") };
147
+ },
148
+ inputSchema,
149
+ }),
150
+ },
138
151
  });
139
152
  ```
140
153
 
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.
154
+ For background delegation, let your host own task ids, scheduling, output
155
+ storage, and notification resume. The runtime provides generic execution stores,
156
+ notifications, `Agent.resume(...)`, and `run.events()`; it does not generate
157
+ delegation tools or own child-agent lifecycle semantics. See
158
+ the sync and background example packages for app-owned blocking and background
159
+ delegation patterns.
160
+
161
+ ## Plugins
162
+
163
+ Pass `plugins: [...]` on `Agent` to observe or intercept runtime events. Each
164
+ plugin exposes one handler:
148
165
 
149
166
  ```ts
150
- delegate_to_researcher({
151
- prompt: "Find the current release notes and summarize the evidence.",
167
+ import type { AgentPlugin } from "@minpeter/pss-runtime";
168
+ import { Agent } from "@minpeter/pss-runtime";
169
+
170
+ const tracePlugin: AgentPlugin = {
171
+ name: "trace",
172
+ on: ({ event }) => {
173
+ if (event.type === "turn-end") {
174
+ console.log("turn finished");
175
+ }
176
+ },
177
+ };
178
+
179
+ const agent = new Agent({
180
+ model,
181
+ plugins: [tracePlugin],
152
182
  });
153
183
  ```
154
184
 
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`.
185
+ ### Observe vs intercept
186
+
187
+ For most events, `on` is observe-only: return nothing (or `{ action: "continue" }`)
188
+ and the runtime emits the event unchanged.
189
+
190
+ Three input event types support intercept returns:
191
+
192
+ - `user-text`
193
+ - `user-message`
194
+ - `runtime-input`
195
+
196
+ Return one of:
197
+
198
+ - `{ action: "continue" }` — emit the current event (default when omitted)
199
+ - `{ action: "transform", event }` — emit a replacement input event
200
+ - `{ action: "handled" }` — skip emit; for `session.send`, close the run without
201
+ starting a turn
202
+
203
+ Plugins run in registration order. Each `transform` updates the event seen by
204
+ later plugins, so transforms chain sequentially.
205
+
206
+ ### Input `meta.source`
207
+
208
+ The runtime attaches `meta` on input events at API boundaries. Plugins can route
209
+ on `event.meta?.source`:
210
+
211
+ | `source` | Boundary |
212
+ |----------|----------|
213
+ | `send` | `session.send()` / `agent.send()` |
214
+ | `steer` | `session.steer()` and drained steering queue |
215
+ | `notify` | `session.notify()` runtime input |
216
+ | `delegate` | parent `delegate_to_*` child `session.send()` |
217
+
218
+ `meta` appears on `run.events()` for input events but is stripped before session
219
+ history persistence and model mapping. It never reaches the LLM prompt.
220
+
221
+ ### Delegate prompt wrapping
222
+
223
+ Child agents receive delegated prompts with `meta.source === "delegate"`. Wrap or
224
+ rewrite them with a plugin instead of agent-level prompt shims:
161
225
 
162
226
  ```ts
163
- delegate_to_researcher({
164
- prompt: "Compare the API designs.",
165
- run_in_background: true,
166
- });
227
+ import type { AgentPlugin, UserText } from "@minpeter/pss-runtime";
228
+ import { Agent } from "@minpeter/pss-runtime";
167
229
 
168
- // After the completion reminder arrives:
169
- background_output({ task_id: "bg_...", block: true });
170
- background_cancel({ task_id: "bg_..." });
230
+ const pokeTagsPlugin: AgentPlugin = {
231
+ name: "poke-tags",
232
+ on: ({ event }) => {
233
+ if (event.type !== "user-text" || event.meta?.source !== "delegate") {
234
+ return;
235
+ }
236
+
237
+ const text =
238
+ typeof event.text === "string" ? event.text : event.text.join("\n");
239
+
240
+ return {
241
+ action: "transform",
242
+ event: {
243
+ ...event,
244
+ text: `<poke>\n${text}\n</poke>`,
245
+ } satisfies UserText,
246
+ };
247
+ },
248
+ };
249
+
250
+ const executionAgent = new Agent({
251
+ namespace: "execution",
252
+ plugins: [pokeTagsPlugin],
253
+ model,
254
+ });
171
255
  ```
172
256
 
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.
257
+ The parent coordinator stays unchanged; only the nested child agent carries the
258
+ plugin.
259
+
260
+ ### Migration
261
+
262
+ - **`plugins[].events.on`** deprecated. Use top-level `plugins[].on`. The legacy
263
+ handler still receives every event but intercept returns are ignored (observe-only).
264
+ - **`wrapDelegatePrompt`** removed. Use a child `plugins[].on` handler that checks
265
+ `meta.source === "delegate"` and returns `transform`, as above.
182
266
 
183
267
  ## Send, Host Resume, and Steer
184
268
 
@@ -268,9 +352,9 @@ const agent = new Agent({
268
352
  });
269
353
  ```
270
354
 
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:
355
+ For durable sessions, use the exported file POC. Set a stable `namespace` so
356
+ reconstructed agents map the same app-owned session keys back to the same
357
+ transcripts:
274
358
 
275
359
  ```ts
276
360
  import { FileSessionStore } from "@minpeter/pss-runtime/session-store/file";
@@ -284,15 +368,20 @@ const agent = new Agent({
284
368
  });
285
369
  ```
286
370
 
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.
371
+ Hosts that need durable runs pass `host:` into `Agent`. The execution subpath
372
+ keeps the durable surface split by responsibility, so hosts can implement only
373
+ the capabilities they need: `SessionHost`, `RunHost`, `CheckpointHost`,
374
+ `EventHost`, `NotificationHost`, `BackgroundSchedulerHost`, and
375
+ `ExecutionTransactionHost`. `ExecutionHost` remains the aggregate contract for
376
+ in-process or full-store hosts, while `DurableBackgroundHost` and
377
+ `DurableNotificationResumeHost` describe the smaller durable surfaces required
378
+ for background scheduling and notification resume.
291
379
 
292
380
  ```ts
293
381
  import { Agent } from "@minpeter/pss-runtime";
294
382
  import {
295
383
  createInMemoryExecutionHost,
384
+ type DurableBackgroundHost,
296
385
  type ExecutionHost,
297
386
  } from "@minpeter/pss-runtime/execution";
298
387
 
@@ -304,10 +393,15 @@ const agent = new Agent({
304
393
  namespace: "support-agent",
305
394
  });
306
395
 
307
- const durableHost: ExecutionHost = {
308
- capabilities: { backgroundSubagents: "durable" },
309
- scheduler,
310
- store,
396
+ const durableHost: DurableBackgroundHost = {
397
+ capabilities: {},
398
+ backgroundScheduler,
399
+ checkpointStore,
400
+ eventStore,
401
+ notificationInbox,
402
+ runStore,
403
+ sessionStore,
404
+ transaction,
311
405
  };
312
406
  ```
313
407
 
@@ -315,25 +409,20 @@ const durableHost: ExecutionHost = {
315
409
 
316
410
  The runtime supports both long-running Node.js processes and edge hosts that
317
411
  reconstruct runtime objects between turns. The same public DX stays centered on
318
- `new Agent({ subagents: [...] })`; host-specific durability and scheduling live
412
+ `new Agent({ model, tools, host })`; host-specific durability and scheduling live
319
413
  behind the `host` boundary.
320
414
 
321
415
  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.
416
+ `FileSessionStore` persists session snapshots only; app-owned background work
417
+ needs its own durable task/output storage if it must survive process restarts.
326
418
 
327
419
  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.
420
+ objects per turn and persist opaque session state through a durable
421
+ `sessionStore`.
422
+ Use `@minpeter/pss-runtime/cloudflare` for the packaged Cloudflare Durable
423
+ Object adapter. See the sync example package for blocking app-owned delegation
424
+ and the background example package for durable background delegation in a local
425
+ interactive CLI.
337
426
 
338
427
  The same core API supports room/user/session routing through stable session keys.
339
428
 
@@ -348,11 +437,10 @@ storage is durable across hibernation/restores, while in-memory state remains
348
437
  request-local. Do not store canonical agent session or run state in memory
349
438
  attachments.
350
439
 
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(...)`.
440
+ Durable background workflows require host-owned task ids, attempts, leases,
441
+ checkpoints, cancellation, scheduling, session snapshots, and completion
442
+ notifications. The Cloudflare adapter persists scheduled runs and session
443
+ prompts, sets alarms, and resumes work through `Agent.resume(...)`.
356
444
 
357
445
  ## Checkpoints and Cancellation
358
446
 
@@ -368,14 +456,13 @@ calls. Tools are checkpointed before and after execution and receive stable
368
456
  `attempt`, `idempotencyKey`, `retryPolicy`, `signal`, and public `toolCallId`
369
457
  values. The `@minpeter/pss-runtime/execution` entrypoint also exposes the same
370
458
  low-level tool execution checkpoint types for custom resume runners built
371
- directly on `createLlm`.
459
+ directly on AI SDK `LanguageModel` objects.
372
460
 
373
461
  These checkpoints are rollback boundaries, not a complete host adapter by
374
462
  themselves. Edge hosts still need durable scheduling, leases, resume workers,
375
463
  and notification resume handling; externally visible side-effect tools still need
376
464
  idempotent execution or a manual recovery flow.
377
465
 
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.
466
+ Cancellation is persisted before aborting active work. `delete()` and `dispose()`
467
+ stop the current session's in-process work; durable hosts remain responsible for
468
+ 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"}
@@ -1,6 +1,7 @@
1
1
  import { modelMessageToAgentEvents } from "./session/mapping.js";
2
+ import { generateModelStep } from "./llm.js";
2
3
  //#region src/agent-loop.ts
3
- async function runAgentLoop({ captureObserverEvents = captureNoObserverEvents, emit, history, llm, signal = new AbortController().signal, toolExecution }) {
4
+ async function runAgentLoop({ captureObserverEvents = captureNoObserverEvents, emit, history, model, signal = new AbortController().signal, toolExecution }) {
4
5
  while (true) {
5
6
  if (signal.aborted) return "aborted";
6
7
  if (await emitBoundary({
@@ -8,9 +9,9 @@ async function runAgentLoop({ captureObserverEvents = captureNoObserverEvents, e
8
9
  event: { type: "step-start" },
9
10
  signal
10
11
  }) === "aborted") return "aborted";
11
- const capturedOutput = await captureObserverEvents(() => readLlmOutput({
12
+ const capturedOutput = await captureObserverEvents(() => readModelOutput({
12
13
  history,
13
- llm,
14
+ model,
14
15
  signal,
15
16
  toolExecution
16
17
  }));
@@ -65,10 +66,11 @@ async function captureNoObserverEvents(callback) {
65
66
  };
66
67
  }
67
68
  function releaseNoObserverEvents() {}
68
- async function readLlmOutput({ history, llm, signal, toolExecution }) {
69
+ async function readModelOutput({ history, model, signal, toolExecution }) {
69
70
  try {
70
- return await llm({
71
+ return await generateModelStep({
71
72
  history: history.modelSnapshot(),
73
+ ...model,
72
74
  signal,
73
75
  toolExecution
74
76
  });
@@ -122,9 +124,8 @@ async function appendStepOutput({ emit, history, observerEvents, output, signal
122
124
  await flushObserverEvents();
123
125
  return shouldContinue ? "continue" : "completed";
124
126
  }
125
- function isLaunchOrBlockingObserverEvent(event) {
126
- if (event.type === "subagent-job-update") return false;
127
- return !(event.type === "subagent-job-end" && event.task_id);
127
+ function isLaunchOrBlockingObserverEvent(_event) {
128
+ return true;
128
129
  }
129
130
  //#endregion
130
131
  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 {\n generateModelStep,\n type ModelGenerationOptions,\n type ModelStepOutput,\n} 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 model: ModelGenerationOptions;\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 model,\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 readModelOutput({ history, model, 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 readModelOutput({\n history,\n model,\n signal,\n toolExecution,\n}: Pick<RunAgentLoopOptions, \"history\" | \"model\"> & {\n signal: AbortSignal;\n toolExecution?: RuntimeToolExecutionContext;\n}): Promise<ModelStepOutput | \"aborted\"> {\n try {\n return await generateModelStep({\n history: history.modelSnapshot(),\n ...model,\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<ModelStepOutput | \"aborted\">;\n output: ModelStepOutput;\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: ModelStepOutput;\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,20 @@
1
- import { AgentToolChoice, RuntimeLlm } from "./llm.js";
1
+ import { AgentToolChoice } 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
- interface AgentRuntimeModelOptions {
21
- readonly description?: string;
22
- readonly host?: AgentHost;
23
- readonly instructions?: never;
24
- readonly model: RuntimeLlm;
25
- readonly name?: string;
26
- readonly namespace?: string;
27
- readonly plugins?: readonly AgentPlugin[];
28
- readonly subagents?: never;
29
- readonly toolChoice?: never;
30
- readonly tools?: never;
31
- }
32
- type AgentOptions = AgentLanguageModelOptions | AgentRuntimeModelOptions;
16
+ type AgentOptions = AgentLanguageModelOptions;
17
+ type AgentConstructionOptions = AgentOptions;
33
18
  //#endregion
34
- export { AgentOptions };
19
+ export { AgentConstructionOptions, AgentOptions };
35
20
  //# sourceMappingURL=agent-options.d.ts.map
@@ -1,16 +1,10 @@
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
- if (typeof options.model !== "function" && (typeof options.model !== "object" || options.model === null)) throw new TypeError("Agent: invalid options.model.");
9
- }
10
- function hasRuntimeModel(options) {
11
- return typeof options.model === "function";
5
+ if (typeof options.model !== "object" || options.model === null) throw new TypeError("Agent: invalid options.model.");
12
6
  }
13
7
  //#endregion
14
- export { assertAgentOptions, hasRuntimeModel };
8
+ export { assertAgentOptions };
15
9
 
16
10
  //# 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 } from \"./llm\";\nimport type { AgentPlugin } from \"./plugins\";\n\nexport interface 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\nexport type AgentModelOptions = Pick<\n AgentLanguageModelOptions,\n \"instructions\" | \"model\" | \"toolChoice\" | \"tools\"\n>;\nexport type AgentOptions = AgentLanguageModelOptions;\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 (typeof options.model !== \"object\" || options.model === null) {\n throw new TypeError(\"Agent: invalid options.model.\");\n }\n}\n"],"mappings":";AAuBA,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,IAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,UAAU,MACzD,MAAM,IAAI,UAAU,+BAA+B;AAEvD"}