@minpeter/pss-runtime 0.0.6 → 0.0.8

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.
package/README.md CHANGED
@@ -20,6 +20,12 @@ for await (const event of run.stream()) {
20
20
  }
21
21
  ```
22
22
 
23
+ `run.stream()` is the run driver. The runtime stops at synchronized lifecycle
24
+ boundaries until the stream consumer asks for the next event, so callers must
25
+ consume the stream for the run to progress. This is what lets code react to
26
+ `turn-start`, `step-start`, and `step-end` before the next model snapshot is
27
+ created.
28
+
23
29
  Per-key conversations use `session(key)`:
24
30
 
25
31
  ```ts
@@ -70,6 +76,48 @@ The public transcript protocol is `AgentEvent`: live runs emit runtime-defined
70
76
  events through `run.stream()`. Provider/model message history is internal
71
77
  continuation state, not a public history API.
72
78
 
79
+ ## Send and Steer
80
+
81
+ Use `session.send(input)` for a new user turn. If a run is already active, the
82
+ turn is queued until the active run finishes. Use `session.steer(input)` when the
83
+ input should steer the active run; if no run is active, it starts a normal run.
84
+
85
+ Both APIs accept the same input shapes: strings, arrays of strings,
86
+ `{ type: "user-text", text }`, and multipart `{ type: "user-message", content }`
87
+ values. Active steering emits `runtime-input` events. A `runtime-input` is
88
+ runtime/API-originated input mapped internally to the model's user role. It is
89
+ distinct from human-origin `user-text` and `user-message` events.
90
+
91
+ Runtime input windows are tied to synchronized stream events:
92
+
93
+ - `turn-start`: input is appended after the original turn input and before the first model snapshot.
94
+ - `step-start`: input is appended before that same step's model snapshot.
95
+ - `step-end`: input is appended before the next step and intentionally continues the current turn, even if the assistant text looked final.
96
+
97
+ Guard `step-end` insertion with a one-shot flag or a real condition. Adding input
98
+ on every `step-end` can keep the turn running indefinitely.
99
+
100
+ ```ts
101
+ const session = agent.session("room:123:user:456");
102
+ const run = await session.send("Draft a short answer.");
103
+ let addedSteer = false;
104
+
105
+ for await (const event of run.stream()) {
106
+ if (event.type === "assistant-text") {
107
+ process.stdout.write(event.text);
108
+ }
109
+
110
+ if (event.type === "step-end" && !addedSteer) {
111
+ addedSteer = true;
112
+ await session.steer("Also mention the main tradeoff.");
113
+ }
114
+ }
115
+ ```
116
+
117
+ `session.steer()` resolves when the input is accepted into the active run's
118
+ pending steering path or, when idle, when a new run is scheduled. It does not wait
119
+ for a later model snapshot.
120
+
73
121
  ## Session storage and portability
74
122
 
75
123
  The runtime owns full session state encoding and history compaction semantics.
@@ -1,5 +1,6 @@
1
1
  import { Llm } from "./llm.js";
2
- import { AgentEventListener } from "./session/events.js";
2
+ import { AgentEvent } from "./session/events.js";
3
+ import { AgentHooks } from "./hooks.js";
3
4
  import { ModelMessage } from "ai";
4
5
 
5
6
  //#region src/agent-loop.d.ts
@@ -8,15 +9,21 @@ interface ModelHistory {
8
9
  modelSnapshot(): ModelMessage[];
9
10
  }
10
11
  interface RunAgentLoopOptions {
11
- emit: AgentEventListener;
12
+ emit: AgentLoopEventListener;
12
13
  history: ModelHistory;
14
+ hooks?: AgentHooks;
13
15
  llm: Llm;
14
16
  signal?: AbortSignal;
15
17
  }
16
18
  type AgentLoopResult = "completed" | "aborted";
19
+ interface AgentLoopBoundaryDecision {
20
+ readonly runtimeInputAdded?: boolean;
21
+ }
22
+ type AgentLoopEventListener = (event: AgentEvent) => AgentLoopBoundaryDecision | Promise<AgentLoopBoundaryDecision | undefined> | undefined;
17
23
  declare function runAgentLoop({
18
24
  emit,
19
25
  history,
26
+ hooks,
20
27
  llm,
21
28
  signal
22
29
  }: RunAgentLoopOptions): Promise<AgentLoopResult>;
@@ -1,9 +1,20 @@
1
1
  import { modelMessageToAgentEvents } from "./session/mapping.js";
2
2
  //#region src/agent-loop.ts
3
- async function runAgentLoop({ emit, history, llm, signal = new AbortController().signal }) {
3
+ async function runAgentLoop({ emit, history, hooks, llm, signal = new AbortController().signal }) {
4
+ let stepIndex = 0;
4
5
  while (true) {
5
6
  if (signal.aborted) return "aborted";
6
- emit({ type: "step-start" });
7
+ await hooks?.beforeStep?.({
8
+ history: history.modelSnapshot(),
9
+ signal,
10
+ stepIndex
11
+ });
12
+ if (signal.aborted) return "aborted";
13
+ if (await emitBoundary({
14
+ emit,
15
+ event: { type: "step-start" },
16
+ signal
17
+ }) === "aborted") return "aborted";
7
18
  const output = await readLlmOutput({
8
19
  history,
9
20
  llm,
@@ -17,10 +28,51 @@ async function runAgentLoop({ emit, history, llm, signal = new AbortController()
17
28
  signal
18
29
  });
19
30
  if (result === "aborted") return "aborted";
20
- emit({ type: "step-end" });
21
- if (result === "completed") return "completed";
31
+ await runAfterStepHook(hooks, {
32
+ history: history.modelSnapshot(),
33
+ result,
34
+ signal,
35
+ stepIndex
36
+ });
37
+ const stepEndDecision = await emitBoundary({
38
+ emit,
39
+ event: { type: "step-end" },
40
+ signal
41
+ });
42
+ if (stepEndDecision === "aborted") return "aborted";
43
+ if (result === "completed" && !stepEndDecision?.runtimeInputAdded) return "completed";
44
+ stepIndex += 1;
22
45
  }
23
46
  }
47
+ async function emitBoundary({ emit, event, signal }) {
48
+ if (signal.aborted) return "aborted";
49
+ const abort = createAbortBoundary(signal);
50
+ try {
51
+ return await Promise.race([Promise.resolve(emit(event)), abort.promise]);
52
+ } catch (error) {
53
+ if (signal.aborted) return "aborted";
54
+ throw error;
55
+ } finally {
56
+ abort.dispose();
57
+ }
58
+ }
59
+ function createAbortBoundary(signal) {
60
+ let dispose = () => void 0;
61
+ const promise = new Promise((resolve) => {
62
+ const onAbort = () => resolve("aborted");
63
+ dispose = () => signal.removeEventListener("abort", onAbort);
64
+ signal.addEventListener("abort", onAbort, { once: true });
65
+ });
66
+ return {
67
+ dispose,
68
+ promise
69
+ };
70
+ }
71
+ async function runAfterStepHook(hooks, context) {
72
+ const hook = hooks?.afterStep;
73
+ if (!hook) return;
74
+ await Promise.allSettled([Promise.resolve().then(() => hook(context))]);
75
+ }
24
76
  async function readLlmOutput({ history, llm, signal }) {
25
77
  try {
26
78
  return await llm({
@@ -1 +1 @@
1
- {"version":3,"file":"agent-loop.js","names":[],"sources":["../src/agent-loop.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport type { Llm, LlmOutput } from \"./llm\";\nimport type { AgentEventListener } from \"./session/events\";\nimport { modelMessageToAgentEvents } from \"./session/mapping\";\n\ninterface ModelHistory {\n appendModelMessage(message: ModelMessage): void;\n modelSnapshot(): ModelMessage[];\n}\n\ninterface RunAgentLoopOptions {\n emit: AgentEventListener;\n history: ModelHistory;\n llm: Llm;\n signal?: AbortSignal;\n}\n\nexport type AgentLoopResult = \"completed\" | \"aborted\";\ntype StepOutputResult = \"continue\" | \"completed\" | \"aborted\";\n\nexport async function runAgentLoop({\n emit,\n history,\n llm,\n signal = new AbortController().signal,\n}: RunAgentLoopOptions): Promise<AgentLoopResult> {\n while (true) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n emit({ type: \"step-start\" });\n const output = await readLlmOutput({ history, llm, signal });\n\n if (output === \"aborted\") {\n return \"aborted\";\n }\n\n const result = appendStepOutput({ emit, history, output, signal });\n\n if (result === \"aborted\") {\n return \"aborted\";\n }\n\n emit({ type: \"step-end\" });\n\n if (result === \"completed\") {\n return \"completed\";\n }\n }\n}\n\nasync function readLlmOutput({\n history,\n llm,\n signal,\n}: Pick<RunAgentLoopOptions, \"history\" | \"llm\"> & {\n signal: AbortSignal;\n}): Promise<LlmOutput | \"aborted\"> {\n try {\n return await llm({ history: history.modelSnapshot(), signal });\n } catch (error) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n throw error;\n }\n}\n\nfunction appendStepOutput({\n emit,\n history,\n output,\n signal,\n}: Pick<RunAgentLoopOptions, \"emit\" | \"history\"> & {\n output: LlmOutput;\n signal: AbortSignal;\n}): StepOutputResult {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n let shouldContinue = false;\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\n for (const event of events) {\n emit(event);\n }\n\n if (events.some((event) => event.type === \"tool-call\")) {\n shouldContinue = true;\n }\n }\n\n return shouldContinue ? \"continue\" : \"completed\";\n}\n"],"mappings":";;AAoBA,eAAsB,aAAa,EACjC,MACA,SACA,KACA,SAAS,IAAI,gBAAgB,EAAE,UACiB;CAChD,OAAO,MAAM;EACX,IAAI,OAAO,SACT,OAAO;EAGT,KAAK,EAAE,MAAM,aAAa,CAAC;EAC3B,MAAM,SAAS,MAAM,cAAc;GAAE;GAAS;GAAK;EAAO,CAAC;EAE3D,IAAI,WAAW,WACb,OAAO;EAGT,MAAM,SAAS,iBAAiB;GAAE;GAAM;GAAS;GAAQ;EAAO,CAAC;EAEjE,IAAI,WAAW,WACb,OAAO;EAGT,KAAK,EAAE,MAAM,WAAW,CAAC;EAEzB,IAAI,WAAW,aACb,OAAO;CAEX;AACF;AAEA,eAAe,cAAc,EAC3B,SACA,KACA,UAGiC;CACjC,IAAI;EACF,OAAO,MAAM,IAAI;GAAE,SAAS,QAAQ,cAAc;GAAG;EAAO,CAAC;CAC/D,SAAS,OAAO;EACd,IAAI,OAAO,SACT,OAAO;EAGT,MAAM;CACR;AACF;AAEA,SAAS,iBAAiB,EACxB,MACA,SACA,QACA,UAImB;CACnB,IAAI,OAAO,SACT,OAAO;CAGT,IAAI,iBAAiB;CAErB,KAAK,MAAM,WAAW,QAAQ;EAC5B,IAAI,OAAO,SACT,OAAO;EAGT,QAAQ,mBAAmB,OAAO;EAClC,MAAM,SAAS,0BAA0B,OAAO;EAEhD,KAAK,MAAM,SAAS,QAClB,KAAK,KAAK;EAGZ,IAAI,OAAO,MAAM,UAAU,MAAM,SAAS,WAAW,GACnD,iBAAiB;CAErB;CAEA,OAAO,iBAAiB,aAAa;AACvC"}
1
+ {"version":3,"file":"agent-loop.js","names":[],"sources":["../src/agent-loop.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport type { AgentHooks, AgentStepResult } from \"./hooks\";\nimport type { Llm, LlmOutput } from \"./llm\";\nimport type { AgentEvent, AgentEventListener } from \"./session/events\";\nimport { modelMessageToAgentEvents } from \"./session/mapping\";\n\ninterface ModelHistory {\n appendModelMessage(message: ModelMessage): void;\n modelSnapshot(): ModelMessage[];\n}\n\ninterface RunAgentLoopOptions {\n emit: AgentLoopEventListener;\n history: ModelHistory;\n hooks?: AgentHooks;\n llm: Llm;\n signal?: AbortSignal;\n}\n\nexport type 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 = AgentStepResult | \"aborted\";\n\nexport async function runAgentLoop({\n emit,\n history,\n hooks,\n llm,\n signal = new AbortController().signal,\n}: RunAgentLoopOptions): Promise<AgentLoopResult> {\n let stepIndex = 0;\n\n while (true) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n await hooks?.beforeStep?.({\n history: history.modelSnapshot(),\n signal,\n stepIndex,\n });\n\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 output = await readLlmOutput({ history, llm, signal });\n\n if (output === \"aborted\") {\n return \"aborted\";\n }\n\n const result = appendStepOutput({ emit, history, output, signal });\n\n if (result === \"aborted\") {\n return \"aborted\";\n }\n\n await runAfterStepHook(hooks, {\n history: history.modelSnapshot(),\n result,\n signal,\n stepIndex,\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 stepIndex += 1;\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 runAfterStepHook(\n hooks: AgentHooks | undefined,\n context: Parameters<NonNullable<AgentHooks[\"afterStep\"]>>[0]\n): Promise<void> {\n const hook = hooks?.afterStep;\n if (!hook) {\n return;\n }\n\n await Promise.allSettled([Promise.resolve().then(() => hook(context))]);\n}\n\nasync function readLlmOutput({\n history,\n llm,\n signal,\n}: Pick<RunAgentLoopOptions, \"history\" | \"llm\"> & {\n signal: AbortSignal;\n}): Promise<LlmOutput | \"aborted\"> {\n try {\n return await llm({ history: history.modelSnapshot(), signal });\n } catch (error) {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n throw error;\n }\n}\n\nfunction appendStepOutput({\n emit,\n history,\n output,\n signal,\n}: { emit: AgentEventListener; history: ModelHistory } & {\n output: LlmOutput;\n signal: AbortSignal;\n}): StepOutputResult {\n if (signal.aborted) {\n return \"aborted\";\n }\n\n let shouldContinue = false;\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\n for (const event of events) {\n emit(event);\n }\n\n if (events.some((event) => event.type === \"tool-call\")) {\n shouldContinue = true;\n }\n }\n\n return shouldContinue ? \"continue\" : \"completed\";\n}\n"],"mappings":";;AAmCA,eAAsB,aAAa,EACjC,MACA,SACA,OACA,KACA,SAAS,IAAI,gBAAgB,EAAE,UACiB;CAChD,IAAI,YAAY;CAEhB,OAAO,MAAM;EACX,IAAI,OAAO,SACT,OAAO;EAGT,MAAM,OAAO,aAAa;GACxB,SAAS,QAAQ,cAAc;GAC/B;GACA;EACF,CAAC;EAED,IAAI,OAAO,SACT,OAAO;EAST,IAAI,MAN4B,aAAa;GAC3C;GACA,OAAO,EAAE,MAAM,aAAa;GAC5B;EACF,CAAC,MAEyB,WACxB,OAAO;EAGT,MAAM,SAAS,MAAM,cAAc;GAAE;GAAS;GAAK;EAAO,CAAC;EAE3D,IAAI,WAAW,WACb,OAAO;EAGT,MAAM,SAAS,iBAAiB;GAAE;GAAM;GAAS;GAAQ;EAAO,CAAC;EAEjE,IAAI,WAAW,WACb,OAAO;EAGT,MAAM,iBAAiB,OAAO;GAC5B,SAAS,QAAQ,cAAc;GAC/B;GACA;GACA;EACF,CAAC;EAED,MAAM,kBAAkB,MAAM,aAAa;GACzC;GACA,OAAO,EAAE,MAAM,WAAW;GAC1B;EACF,CAAC;EAED,IAAI,oBAAoB,WACtB,OAAO;EAMT,IAAI,WAAW,eAAe,CAAC,iBAAiB,mBAC9C,OAAO;EAGT,aAAa;CACf;AACF;AAEA,eAAe,aAAa,EAC1B,MACA,OACA,UAI6D;CAC7D,IAAI,OAAO,SACT,OAAO;CAGT,MAAM,QAAQ,oBAAoB,MAAM;CACxC,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CAAC,QAAQ,QAAQ,KAAK,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC;CACzE,SAAS,OAAO;EACd,IAAI,OAAO,SACT,OAAO;EAGT,MAAM;CACR,UAAU;EACR,MAAM,QAAQ;CAChB;AACF;AAEA,SAAS,oBAAoB,QAG3B;CACA,IAAI,gBAA4B,KAAA;CAEhC,MAAM,UAAU,IAAI,SAAoB,YAAY;EAClD,MAAM,gBAAgB,QAAQ,SAAS;EACvC,gBAAgB,OAAO,oBAAoB,SAAS,OAAO;EAC3D,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC1D,CAAC;CAED,OAAO;EAAE;EAAS;CAAQ;AAC5B;AAEA,eAAe,iBACb,OACA,SACe;CACf,MAAM,OAAO,OAAO;CACpB,IAAI,CAAC,MACH;CAGF,MAAM,QAAQ,WAAW,CAAC,QAAQ,QAAQ,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;AACxE;AAEA,eAAe,cAAc,EAC3B,SACA,KACA,UAGiC;CACjC,IAAI;EACF,OAAO,MAAM,IAAI;GAAE,SAAS,QAAQ,cAAc;GAAG;EAAO,CAAC;CAC/D,SAAS,OAAO;EACd,IAAI,OAAO,SACT,OAAO;EAGT,MAAM;CACR;AACF;AAEA,SAAS,iBAAiB,EACxB,MACA,SACA,QACA,UAImB;CACnB,IAAI,OAAO,SACT,OAAO;CAGT,IAAI,iBAAiB;CAErB,KAAK,MAAM,WAAW,QAAQ;EAC5B,IAAI,OAAO,SACT,OAAO;EAGT,QAAQ,mBAAmB,OAAO;EAClC,MAAM,SAAS,0BAA0B,OAAO;EAEhD,KAAK,MAAM,SAAS,QAClB,KAAK,KAAK;EAGZ,IAAI,OAAO,MAAM,UAAU,MAAM,SAAS,WAAW,GACnD,iBAAiB;CAErB;CAEA,OAAO,iBAAiB,aAAa;AACvC"}
package/dist/agent.d.ts CHANGED
@@ -1,22 +1,27 @@
1
- import { AgentTools, Llm } from "./llm.js";
1
+ import { AgentToolChoice, AgentTools, Llm } from "./llm.js";
2
2
  import { AgentRun } from "./session/run.js";
3
3
  import { SessionStore } from "./session/store/types.js";
4
4
  import { AgentInput } from "./session/session.js";
5
+ import { AgentHooks } from "./hooks.js";
5
6
  import { LanguageModel } from "ai";
6
7
 
7
8
  //#region src/agent.d.ts
8
9
  interface AgentModelOptions {
10
+ hooks?: AgentHooks;
9
11
  instructions?: string;
10
12
  llm?: never;
11
13
  model: LanguageModel;
12
14
  sessions?: AgentSessionOptions;
15
+ toolChoice?: AgentToolChoice;
13
16
  tools?: AgentTools;
14
17
  }
15
18
  interface AgentLlmOptions {
19
+ hooks?: AgentHooks;
16
20
  instructions?: never;
17
21
  llm: Llm;
18
22
  model?: never;
19
23
  sessions?: AgentSessionOptions;
24
+ toolChoice?: never;
20
25
  tools?: never;
21
26
  }
22
27
  interface AgentSessionOptions {
@@ -26,6 +31,7 @@ interface SessionHandle {
26
31
  interrupt(): void;
27
32
  kill(): void;
28
33
  send(input: AgentInput): Promise<AgentRun>;
34
+ steer(input: AgentInput): Promise<AgentRun>;
29
35
  }
30
36
  type AgentOptions = AgentModelOptions | AgentLlmOptions;
31
37
  declare class Agent {
package/dist/agent.js CHANGED
@@ -3,15 +3,18 @@ import { AgentSession } from "./session/session.js";
3
3
  import { MemorySessionStore } from "./session/store/memory.js";
4
4
  //#region src/agent.ts
5
5
  var Agent = class Agent {
6
+ #hooks;
6
7
  #llm;
7
8
  #sessions = /* @__PURE__ */ new Map();
8
9
  #store;
9
10
  constructor(options) {
10
11
  assertAgentOptions(options);
11
12
  this.#store = options.sessions?.store ?? new MemorySessionStore();
13
+ this.#hooks = options.hooks;
12
14
  this.#llm = hasCustomLlm(options) ? options.llm : createLlm({
13
15
  instructions: options.instructions,
14
16
  model: options.model,
17
+ toolChoice: options.toolChoice,
15
18
  tools: options.tools
16
19
  });
17
20
  }
@@ -27,14 +30,15 @@ var Agent = class Agent {
27
30
  const session = new AgentSession(this.#llm, {
28
31
  key,
29
32
  store: this.#store
30
- });
33
+ }, this.#hooks);
31
34
  const handle = {
32
35
  interrupt: () => session.interrupt(),
33
36
  kill: () => {
34
37
  session.kill();
35
38
  this.#sessions.delete(key);
36
39
  },
37
- send: (input) => session.send(input)
40
+ send: (input) => session.send(input),
41
+ steer: (input) => session.steer(input)
38
42
  };
39
43
  this.#sessions.set(key, handle);
40
44
  return handle;
package/dist/agent.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","names":["#llm","#sessions","#store"],"sources":["../src/agent.ts"],"sourcesContent":["import type { LanguageModel } from \"ai\";\nimport { type AgentTools, createLlm, type Llm } from \"./llm\";\nimport type { AgentRun } from \"./session/run\";\nimport { type AgentInput, AgentSession } from \"./session/session\";\nimport { MemorySessionStore } from \"./session/store/memory\";\nimport type { SessionStore } from \"./session/store/types\";\n\ninterface AgentModelOptions {\n instructions?: string;\n llm?: never;\n model: LanguageModel;\n sessions?: AgentSessionOptions;\n tools?: AgentTools;\n}\n\ninterface AgentLlmOptions {\n instructions?: never;\n llm: Llm;\n model?: never;\n sessions?: AgentSessionOptions;\n tools?: never;\n}\n\nexport interface AgentSessionOptions {\n store?: SessionStore;\n}\n\nexport interface SessionHandle {\n interrupt(): void;\n kill(): void;\n send(input: AgentInput): Promise<AgentRun>;\n}\n\nexport type AgentOptions = AgentModelOptions | AgentLlmOptions;\n\nexport class Agent {\n readonly #llm: Llm;\n readonly #sessions = new Map<string, SessionHandle>();\n readonly #store: SessionStore;\n\n private constructor(options: AgentOptions) {\n assertAgentOptions(options);\n\n this.#store = options.sessions?.store ?? new MemorySessionStore();\n this.#llm = hasCustomLlm(options)\n ? options.llm\n : createLlm({\n instructions: options.instructions,\n model: options.model,\n tools: options.tools,\n });\n }\n\n static create(options: AgentOptions): Promise<Agent> {\n return Promise.resolve().then(() => new Agent(options));\n }\n\n send(input: AgentInput): Promise<AgentRun> {\n return this.session(\"default\").send(input);\n }\n\n session(key: string): SessionHandle {\n const existing = this.#sessions.get(key);\n if (existing) {\n return existing;\n }\n\n const session = new AgentSession(this.#llm, { key, store: this.#store });\n const handle: SessionHandle = {\n interrupt: () => session.interrupt(),\n kill: () => {\n session.kill();\n this.#sessions.delete(key);\n },\n send: (input) => session.send(input),\n };\n this.#sessions.set(key, handle);\n return handle;\n }\n}\n\nfunction assertAgentOptions(options: unknown): asserts options is AgentOptions {\n if (options === null || typeof options !== \"object\") {\n throw new TypeError(\n \"Agent options are required. Provide either { model } or { llm }.\"\n );\n }\n\n const hasLlm = hasCustomLlm(options);\n const hasModel =\n \"model\" in options && options.model !== undefined && options.model !== null;\n\n if (hasLlm && hasModel) {\n throw new TypeError(\n \"Agent.create: provide either options.llm or options.model, not both.\"\n );\n }\n\n if (\"llm\" in options && options.llm !== undefined && !hasLlm) {\n throw new TypeError(\"Agent.create: invalid options.llm.\");\n }\n\n if (!(hasLlm || hasModel)) {\n throw new TypeError(\"Agent.create: missing options.model.\");\n }\n}\n\nfunction hasCustomLlm(options: object): options is AgentLlmOptions {\n return \"llm\" in options && typeof options.llm === \"function\";\n}\n"],"mappings":";;;;AAmCA,IAAa,QAAb,MAAa,MAAM;CACjB;CACA,4BAAqB,IAAI,IAA2B;CACpD;CAEA,YAAoB,SAAuB;EACzC,mBAAmB,OAAO;EAE1B,KAAKE,SAAS,QAAQ,UAAU,SAAS,IAAI,mBAAmB;EAChE,KAAKF,OAAO,aAAa,OAAO,IAC5B,QAAQ,MACR,UAAU;GACR,cAAc,QAAQ;GACtB,OAAO,QAAQ;GACf,OAAO,QAAQ;EACjB,CAAC;CACP;CAEA,OAAO,OAAO,SAAuC;EACnD,OAAO,QAAQ,QAAQ,EAAE,WAAW,IAAI,MAAM,OAAO,CAAC;CACxD;CAEA,KAAK,OAAsC;EACzC,OAAO,KAAK,QAAQ,SAAS,EAAE,KAAK,KAAK;CAC3C;CAEA,QAAQ,KAA4B;EAClC,MAAM,WAAW,KAAKC,UAAU,IAAI,GAAG;EACvC,IAAI,UACF,OAAO;EAGT,MAAM,UAAU,IAAI,aAAa,KAAKD,MAAM;GAAE;GAAK,OAAO,KAAKE;EAAO,CAAC;EACvE,MAAM,SAAwB;GAC5B,iBAAiB,QAAQ,UAAU;GACnC,YAAY;IACV,QAAQ,KAAK;IACb,KAAKD,UAAU,OAAO,GAAG;GAC3B;GACA,OAAO,UAAU,QAAQ,KAAK,KAAK;EACrC;EACA,KAAKA,UAAU,IAAI,KAAK,MAAM;EAC9B,OAAO;CACT;AACF;AAEA,SAAS,mBAAmB,SAAmD;CAC7E,IAAI,YAAY,QAAQ,OAAO,YAAY,UACzC,MAAM,IAAI,UACR,kEACF;CAGF,MAAM,SAAS,aAAa,OAAO;CACnC,MAAM,WACJ,WAAW,WAAW,QAAQ,UAAU,KAAA,KAAa,QAAQ,UAAU;CAEzE,IAAI,UAAU,UACZ,MAAM,IAAI,UACR,sEACF;CAGF,IAAI,SAAS,WAAW,QAAQ,QAAQ,KAAA,KAAa,CAAC,QACpD,MAAM,IAAI,UAAU,oCAAoC;CAG1D,IAAI,EAAE,UAAU,WACd,MAAM,IAAI,UAAU,sCAAsC;AAE9D;AAEA,SAAS,aAAa,SAA6C;CACjE,OAAO,SAAS,WAAW,OAAO,QAAQ,QAAQ;AACpD"}
1
+ {"version":3,"file":"agent.js","names":["#hooks","#llm","#sessions","#store"],"sources":["../src/agent.ts"],"sourcesContent":["import type { LanguageModel } from \"ai\";\nimport type { AgentHooks } from \"./hooks\";\nimport {\n type AgentToolChoice,\n type AgentTools,\n createLlm,\n type Llm,\n} from \"./llm\";\nimport type { AgentRun } from \"./session/run\";\nimport { type AgentInput, AgentSession } from \"./session/session\";\nimport { MemorySessionStore } from \"./session/store/memory\";\nimport type { SessionStore } from \"./session/store/types\";\n\ninterface AgentModelOptions {\n hooks?: AgentHooks;\n instructions?: string;\n llm?: never;\n model: LanguageModel;\n sessions?: AgentSessionOptions;\n toolChoice?: AgentToolChoice;\n tools?: AgentTools;\n}\n\ninterface AgentLlmOptions {\n hooks?: AgentHooks;\n instructions?: never;\n llm: Llm;\n model?: never;\n sessions?: AgentSessionOptions;\n toolChoice?: never;\n tools?: never;\n}\n\nexport interface AgentSessionOptions {\n store?: SessionStore;\n}\n\nexport interface SessionHandle {\n interrupt(): void;\n kill(): void;\n send(input: AgentInput): Promise<AgentRun>;\n steer(input: AgentInput): Promise<AgentRun>;\n}\n\nexport type AgentOptions = AgentModelOptions | AgentLlmOptions;\n\nexport class Agent {\n readonly #hooks?: AgentHooks;\n readonly #llm: Llm;\n readonly #sessions = new Map<string, SessionHandle>();\n readonly #store: SessionStore;\n\n private constructor(options: AgentOptions) {\n assertAgentOptions(options);\n\n this.#store = options.sessions?.store ?? new MemorySessionStore();\n this.#hooks = options.hooks;\n this.#llm = hasCustomLlm(options)\n ? options.llm\n : createLlm({\n instructions: options.instructions,\n model: options.model,\n toolChoice: options.toolChoice,\n tools: options.tools,\n });\n }\n\n static create(options: AgentOptions): Promise<Agent> {\n return Promise.resolve().then(() => new Agent(options));\n }\n\n send(input: AgentInput): Promise<AgentRun> {\n return this.session(\"default\").send(input);\n }\n\n session(key: string): SessionHandle {\n const existing = this.#sessions.get(key);\n if (existing) {\n return existing;\n }\n\n const session = new AgentSession(\n this.#llm,\n { key, store: this.#store },\n this.#hooks\n );\n const handle: SessionHandle = {\n interrupt: () => session.interrupt(),\n kill: () => {\n session.kill();\n this.#sessions.delete(key);\n },\n send: (input) => session.send(input),\n steer: (input) => session.steer(input),\n };\n this.#sessions.set(key, handle);\n return handle;\n }\n}\n\nfunction assertAgentOptions(options: unknown): asserts options is AgentOptions {\n if (options === null || typeof options !== \"object\") {\n throw new TypeError(\n \"Agent options are required. Provide either { model } or { llm }.\"\n );\n }\n\n const hasLlm = hasCustomLlm(options);\n const hasModel =\n \"model\" in options && options.model !== undefined && options.model !== null;\n\n if (hasLlm && hasModel) {\n throw new TypeError(\n \"Agent.create: provide either options.llm or options.model, not both.\"\n );\n }\n\n if (\"llm\" in options && options.llm !== undefined && !hasLlm) {\n throw new TypeError(\"Agent.create: invalid options.llm.\");\n }\n\n if (!(hasLlm || hasModel)) {\n throw new TypeError(\"Agent.create: missing options.model.\");\n }\n}\n\nfunction hasCustomLlm(options: object): options is AgentLlmOptions {\n return \"llm\" in options && typeof options.llm === \"function\";\n}\n"],"mappings":";;;;AA8CA,IAAa,QAAb,MAAa,MAAM;CACjB;CACA;CACA,4BAAqB,IAAI,IAA2B;CACpD;CAEA,YAAoB,SAAuB;EACzC,mBAAmB,OAAO;EAE1B,KAAKG,SAAS,QAAQ,UAAU,SAAS,IAAI,mBAAmB;EAChE,KAAKH,SAAS,QAAQ;EACtB,KAAKC,OAAO,aAAa,OAAO,IAC5B,QAAQ,MACR,UAAU;GACR,cAAc,QAAQ;GACtB,OAAO,QAAQ;GACf,YAAY,QAAQ;GACpB,OAAO,QAAQ;EACjB,CAAC;CACP;CAEA,OAAO,OAAO,SAAuC;EACnD,OAAO,QAAQ,QAAQ,EAAE,WAAW,IAAI,MAAM,OAAO,CAAC;CACxD;CAEA,KAAK,OAAsC;EACzC,OAAO,KAAK,QAAQ,SAAS,EAAE,KAAK,KAAK;CAC3C;CAEA,QAAQ,KAA4B;EAClC,MAAM,WAAW,KAAKC,UAAU,IAAI,GAAG;EACvC,IAAI,UACF,OAAO;EAGT,MAAM,UAAU,IAAI,aAClB,KAAKD,MACL;GAAE;GAAK,OAAO,KAAKE;EAAO,GAC1B,KAAKH,MACP;EACA,MAAM,SAAwB;GAC5B,iBAAiB,QAAQ,UAAU;GACnC,YAAY;IACV,QAAQ,KAAK;IACb,KAAKE,UAAU,OAAO,GAAG;GAC3B;GACA,OAAO,UAAU,QAAQ,KAAK,KAAK;GACnC,QAAQ,UAAU,QAAQ,MAAM,KAAK;EACvC;EACA,KAAKA,UAAU,IAAI,KAAK,MAAM;EAC9B,OAAO;CACT;AACF;AAEA,SAAS,mBAAmB,SAAmD;CAC7E,IAAI,YAAY,QAAQ,OAAO,YAAY,UACzC,MAAM,IAAI,UACR,kEACF;CAGF,MAAM,SAAS,aAAa,OAAO;CACnC,MAAM,WACJ,WAAW,WAAW,QAAQ,UAAU,KAAA,KAAa,QAAQ,UAAU;CAEzE,IAAI,UAAU,UACZ,MAAM,IAAI,UACR,sEACF;CAGF,IAAI,SAAS,WAAW,QAAQ,QAAQ,KAAA,KAAa,CAAC,QACpD,MAAM,IAAI,UAAU,oCAAoC;CAG1D,IAAI,EAAE,UAAU,WACd,MAAM,IAAI,UAAU,sCAAsC;AAE9D;AAEA,SAAS,aAAa,SAA6C;CACjE,OAAO,SAAS,WAAW,OAAO,QAAQ,QAAQ;AACpD"}
@@ -0,0 +1,32 @@
1
+ import { RuntimeLlmContext } from "./llm.js";
2
+ import { UserInput } from "./session/session.js";
3
+
4
+ //#region src/hooks.d.ts
5
+ type AgentTurnResult = "aborted" | "completed";
6
+ type AgentStepResult = "completed" | "continue";
7
+ type MaybePromise<T> = PromiseLike<T> | T;
8
+ interface AgentBeforeTurnContext {
9
+ readonly history: RuntimeLlmContext["history"];
10
+ readonly input: UserInput;
11
+ readonly signal: AbortSignal;
12
+ }
13
+ interface AgentAfterTurnContext extends AgentBeforeTurnContext {
14
+ readonly result: AgentTurnResult;
15
+ }
16
+ interface AgentBeforeStepContext {
17
+ readonly history: RuntimeLlmContext["history"];
18
+ readonly signal: AbortSignal;
19
+ readonly stepIndex: number;
20
+ }
21
+ interface AgentAfterStepContext extends AgentBeforeStepContext {
22
+ readonly result: AgentStepResult;
23
+ }
24
+ interface AgentHooks {
25
+ afterStep?(context: AgentAfterStepContext): MaybePromise<void>;
26
+ afterTurn?(context: AgentAfterTurnContext): MaybePromise<void>;
27
+ beforeStep?(context: AgentBeforeStepContext): MaybePromise<void>;
28
+ beforeTurn?(context: AgentBeforeTurnContext): MaybePromise<void>;
29
+ }
30
+ //#endregion
31
+ export { AgentAfterStepContext, AgentAfterTurnContext, AgentBeforeStepContext, AgentBeforeTurnContext, AgentHooks, AgentStepResult, AgentTurnResult };
32
+ //# sourceMappingURL=hooks.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { AgentModel, AgentTool, AgentToolExecute, AgentToolExecutionOptions, AgentTools, LlmOutputPart, RuntimeCreateLlmOptions, RuntimeLlm, RuntimeLlmContext, RuntimeLlmOutput, createLlm } from "./llm.js";
2
- import { AgentEvent, AgentEventListener, AssistantReasoning, AssistantText, ToolCall, ToolResult, UserMessage, UserMessageContent, UserMessageContentPart, UserMessageFileData, UserMessageFilePart, UserMessageImagePart, UserMessageTextPart, UserText, UserTextContent } from "./session/events.js";
1
+ import { AgentModel, AgentTool, AgentToolChoice, AgentToolExecute, AgentToolExecutionOptions, AgentTools, LlmOutputPart, RuntimeCreateLlmOptions, RuntimeLlm, RuntimeLlmContext, RuntimeLlmOutput, createLlm } from "./llm.js";
2
+ import { AgentEvent, AgentEventListener, AssistantReasoning, AssistantText, RuntimeInput, ToolCall, ToolResult, UserMessage, UserMessageContent, UserMessageContentPart, UserMessageFileData, UserMessageFilePart, UserMessageImagePart, UserMessageTextPart, UserText, UserTextContent } from "./session/events.js";
3
3
  import { AgentRun } from "./session/run.js";
4
4
  import { CommitResult, ExpectedSessionVersion, SessionStore, StoredSession } from "./session/store/types.js";
5
5
  import { AgentInput, SessionInput, UserInput } from "./session/session.js";
6
+ import { AgentAfterStepContext, AgentAfterTurnContext, AgentBeforeStepContext, AgentBeforeTurnContext, AgentHooks, AgentStepResult, AgentTurnResult } from "./hooks.js";
6
7
  import { Agent, AgentOptions, AgentSessionOptions, SessionHandle } from "./agent.js";
7
8
  import { AgentLoopResult, runAgentLoop } from "./agent-loop.js";
8
- export { Agent, type AgentEvent, type AgentEventListener, type AgentInput, type AgentLoopResult, type AgentModel, type AgentOptions, type AgentRun, type AgentSessionOptions, type AgentTool, type AgentToolExecute, type AgentToolExecutionOptions, type AgentTools, type AssistantReasoning, type AssistantText, type CommitResult, type ExpectedSessionVersion, type LlmOutputPart, type RuntimeCreateLlmOptions, type RuntimeLlm, type RuntimeLlmContext, type RuntimeLlmOutput, type SessionHandle, type SessionInput, type SessionStore, type StoredSession, type ToolCall, type ToolResult, type UserInput, type UserMessage, type UserMessageContent, type UserMessageContentPart, type UserMessageFileData, type UserMessageFilePart, type UserMessageImagePart, type UserMessageTextPart, type UserText, type UserTextContent, createLlm, runAgentLoop };
9
+ export { Agent, type AgentAfterStepContext, type AgentAfterTurnContext, type AgentBeforeStepContext, type AgentBeforeTurnContext, type AgentEvent, type AgentEventListener, type AgentHooks, type AgentInput, type AgentLoopResult, type AgentModel, type AgentOptions, type AgentRun, type AgentSessionOptions, type AgentStepResult, type AgentTool, type AgentToolChoice, type AgentToolExecute, type AgentToolExecutionOptions, type AgentTools, type AgentTurnResult, type AssistantReasoning, type AssistantText, type CommitResult, type ExpectedSessionVersion, type LlmOutputPart, type RuntimeCreateLlmOptions, type RuntimeInput, type RuntimeLlm, type RuntimeLlmContext, type RuntimeLlmOutput, type SessionHandle, type SessionInput, type SessionStore, type StoredSession, type ToolCall, type ToolResult, type UserInput, type UserMessage, type UserMessageContent, type UserMessageContentPart, type UserMessageFileData, type UserMessageFilePart, type UserMessageImagePart, type UserMessageTextPart, type UserText, type UserTextContent, createLlm, runAgentLoop };
package/dist/llm.d.ts CHANGED
@@ -5,6 +5,7 @@ type AgentToolExecutionOptions = ToolExecutionOptions<unknown>;
5
5
  type AgentToolExecute = NonNullable<Tool["execute"]>;
6
6
  type AgentTool = Tool;
7
7
  type AgentTools = ToolSet;
8
+ type AgentToolChoice = "auto" | "required";
8
9
  type AgentModel = LanguageModel;
9
10
  type LlmOutput = Awaited<ReturnType<typeof generateText>>["responseMessages"];
10
11
  type LlmOutputPart = LlmOutput[number];
@@ -16,6 +17,7 @@ type Llm = (context: LlmContext) => Promise<LlmOutput>;
16
17
  interface CreateLlmOptions {
17
18
  instructions?: string;
18
19
  model: LanguageModel;
20
+ toolChoice?: AgentToolChoice;
19
21
  tools?: AgentTools;
20
22
  }
21
23
  type RuntimeCreateLlmOptions = CreateLlmOptions;
@@ -25,8 +27,9 @@ type RuntimeLlmOutput = LlmOutput;
25
27
  declare function createLlm({
26
28
  model,
27
29
  instructions,
30
+ toolChoice,
28
31
  tools
29
32
  }: CreateLlmOptions): Llm;
30
33
  //#endregion
31
- export { AgentModel, AgentTool, AgentToolExecute, AgentToolExecutionOptions, AgentTools, Llm, LlmOutputPart, RuntimeCreateLlmOptions, RuntimeLlm, RuntimeLlmContext, RuntimeLlmOutput, createLlm };
34
+ export { AgentModel, AgentTool, AgentToolChoice, AgentToolExecute, AgentToolExecutionOptions, AgentTools, Llm, LlmOutputPart, RuntimeCreateLlmOptions, RuntimeLlm, RuntimeLlmContext, RuntimeLlmOutput, createLlm };
32
35
  //# sourceMappingURL=llm.d.ts.map
package/dist/llm.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { generateText } from "ai";
2
2
  //#region src/llm.ts
3
- function createLlm({ model, instructions, tools }) {
3
+ function createLlm({ model, instructions, toolChoice, tools }) {
4
4
  return async ({ history, signal }) => {
5
5
  const { responseMessages } = await generateText({
6
6
  abortSignal: signal,
7
7
  instructions,
8
8
  messages: [...history],
9
9
  model,
10
+ toolChoice,
10
11
  tools
11
12
  });
12
13
  return responseMessages;
package/dist/llm.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"llm.js","names":[],"sources":["../src/llm.ts"],"sourcesContent":["import type {\n LanguageModel,\n ModelMessage,\n Tool,\n ToolExecutionOptions,\n ToolSet,\n} from \"ai\";\nimport { generateText } from \"ai\";\n\nexport type AgentToolExecutionOptions = ToolExecutionOptions<unknown>;\nexport type AgentToolExecute = NonNullable<Tool[\"execute\"]>;\nexport type AgentTool = Tool;\nexport type AgentTools = ToolSet;\nexport type AgentModel = LanguageModel;\nexport type AgentMessage = ModelMessage;\nexport type LlmOutput = Awaited<\n ReturnType<typeof generateText>\n>[\"responseMessages\"];\nexport type LlmOutputPart = LlmOutput[number];\n\nexport interface LlmContext {\n history: readonly ModelMessage[];\n signal: AbortSignal;\n}\n\nexport type Llm = (context: LlmContext) => Promise<LlmOutput>;\n\nexport interface CreateLlmOptions {\n instructions?: string;\n model: LanguageModel;\n tools?: AgentTools;\n}\n\nexport type RuntimeCreateLlmOptions = CreateLlmOptions;\nexport type RuntimeLlm = Llm;\nexport type RuntimeLlmContext = LlmContext;\nexport type RuntimeLlmOutput = LlmOutput;\n\nexport function createLlm({\n model,\n instructions,\n tools,\n}: CreateLlmOptions): Llm {\n return async ({ history, signal }) => {\n const { responseMessages } = await generateText({\n abortSignal: signal,\n instructions,\n messages: [...history],\n model,\n tools,\n });\n\n return responseMessages;\n };\n}\n"],"mappings":";;AAsCA,SAAgB,UAAU,EACxB,OACA,cACA,SACwB;CACxB,OAAO,OAAO,EAAE,SAAS,aAAa;EACpC,MAAM,EAAE,qBAAqB,MAAM,aAAa;GAC9C,aAAa;GACb;GACA,UAAU,CAAC,GAAG,OAAO;GACrB;GACA;EACF,CAAC;EAED,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"llm.js","names":[],"sources":["../src/llm.ts"],"sourcesContent":["import type {\n LanguageModel,\n ModelMessage,\n Tool,\n ToolExecutionOptions,\n ToolSet,\n} from \"ai\";\nimport { generateText } from \"ai\";\n\nexport type AgentToolExecutionOptions = ToolExecutionOptions<unknown>;\nexport type AgentToolExecute = NonNullable<Tool[\"execute\"]>;\nexport type AgentTool = Tool;\nexport type AgentTools = ToolSet;\nexport type AgentToolChoice = \"auto\" | \"required\";\nexport type AgentModel = LanguageModel;\nexport type AgentMessage = ModelMessage;\nexport type LlmOutput = Awaited<\n ReturnType<typeof generateText>\n>[\"responseMessages\"];\nexport type LlmOutputPart = LlmOutput[number];\n\nexport interface LlmContext {\n history: readonly ModelMessage[];\n signal: AbortSignal;\n}\n\nexport type Llm = (context: LlmContext) => Promise<LlmOutput>;\n\nexport interface CreateLlmOptions {\n instructions?: string;\n model: LanguageModel;\n toolChoice?: AgentToolChoice;\n tools?: AgentTools;\n}\n\nexport type RuntimeCreateLlmOptions = CreateLlmOptions;\nexport type RuntimeLlm = Llm;\nexport type RuntimeLlmContext = LlmContext;\nexport type RuntimeLlmOutput = LlmOutput;\n\nexport function createLlm({\n model,\n instructions,\n toolChoice,\n tools,\n}: CreateLlmOptions): Llm {\n return async ({ history, signal }) => {\n const { responseMessages } = await generateText({\n abortSignal: signal,\n instructions,\n messages: [...history],\n model,\n toolChoice,\n tools,\n });\n\n return responseMessages;\n };\n}\n"],"mappings":";;AAwCA,SAAgB,UAAU,EACxB,OACA,cACA,YACA,SACwB;CACxB,OAAO,OAAO,EAAE,SAAS,aAAa;EACpC,MAAM,EAAE,qBAAqB,MAAM,aAAa;GAC9C,aAAa;GACb;GACA,UAAU,CAAC,GAAG,OAAO;GACrB;GACA;GACA;EACF,CAAC;EAED,OAAO;CACT;AACF"}
@@ -1,3 +1,5 @@
1
+ import { UserInput } from "./session.js";
2
+
1
3
  //#region src/session/events.d.ts
2
4
  type UserTextContent = string | readonly string[];
3
5
  interface UserText {
@@ -38,6 +40,15 @@ interface UserMessage {
38
40
  content: UserMessageContent;
39
41
  type: "user-message";
40
42
  }
43
+ interface RuntimeInput {
44
+ /**
45
+ * Runtime/API-originated model input inserted into the current turn.
46
+ * This is distinct from human-originated user-text and user-message input.
47
+ */
48
+ input: UserInput;
49
+ placement: "turn-start" | "step-start" | "step-end";
50
+ type: "runtime-input";
51
+ }
41
52
  interface AssistantText {
42
53
  text: string;
43
54
  type: "assistant-text";
@@ -58,7 +69,7 @@ interface ToolResult {
58
69
  toolName: string;
59
70
  type: "tool-result";
60
71
  }
61
- type AgentEvent = /** User input was accepted into the session queue. */UserMessage | UserText /** User multipart input was accepted into the session queue. */ | UserMessage /** A queued user input started running as a turn. */ | {
72
+ type AgentEvent = /** User input was accepted into the session queue. */UserText /** User multipart input was accepted into the session queue. */ | UserMessage /** Runtime/API-originated input inserted into the current turn, not human input. */ | RuntimeInput /** A queued user input started running as a turn. */ | {
62
73
  type: "turn-start";
63
74
  } /** The active turn was interrupted before normal completion. */ | {
64
75
  type: "turn-abort";
@@ -74,5 +85,5 @@ type AgentEvent = /** User input was accepted into the session queue. */UserMess
74
85
  };
75
86
  type AgentEventListener = (event: AgentEvent) => void;
76
87
  //#endregion
77
- export { AgentEvent, AgentEventListener, AssistantReasoning, AssistantText, ToolCall, ToolResult, UserMessage, UserMessageContent, UserMessageContentPart, UserMessageFileData, UserMessageFilePart, UserMessageImagePart, UserMessageTextPart, UserText, UserTextContent };
88
+ export { AgentEvent, AgentEventListener, AssistantReasoning, AssistantText, RuntimeInput, ToolCall, ToolResult, UserMessage, UserMessageContent, UserMessageContentPart, UserMessageFileData, UserMessageFilePart, UserMessageImagePart, UserMessageTextPart, UserText, UserTextContent };
78
89
  //# sourceMappingURL=events.d.ts.map
@@ -1,35 +1,41 @@
1
1
  //#region src/session/run.ts
2
2
  var BufferedAgentRun = class {
3
3
  #events = [];
4
- #waiters = [];
5
4
  #closed = false;
6
5
  #error;
6
+ #pendingAck;
7
+ #resultPending = false;
7
8
  #streamStarted = false;
9
+ #waiter;
8
10
  emit(event) {
9
11
  if (this.#closed) return;
10
- const waiter = this.#waiters.shift();
11
- if (waiter) {
12
- waiter.resolve({
13
- done: false,
14
- value: event
12
+ this.#enqueue({ event: structuredClone(event) });
13
+ }
14
+ emitBoundary(event) {
15
+ if (this.#closed) return Promise.resolve();
16
+ return new Promise((resolve) => {
17
+ this.#enqueue({
18
+ ack: resolve,
19
+ event: structuredClone(event)
15
20
  });
16
- return;
17
- }
18
- this.#events.push(structuredClone(event));
21
+ });
19
22
  }
20
- close(error) {
23
+ close(error, _reason = "the run is closed") {
21
24
  if (this.#closed) return;
22
25
  this.#closed = true;
23
26
  this.#error = error;
24
- while (this.#waiters.length > 0) {
25
- const waiter = this.#waiters.shift();
26
- if (!waiter) continue;
27
- if (error) waiter.reject(error);
28
- else waiter.resolve({
29
- done: true,
30
- value: void 0
31
- });
27
+ this.#settlePendingAck();
28
+ if (!this.#waiter) return;
29
+ const waiter = this.#waiter;
30
+ this.#waiter = void 0;
31
+ if (error) {
32
+ waiter.reject(error);
33
+ return;
32
34
  }
35
+ waiter.resolve({
36
+ done: true,
37
+ value: void 0
38
+ });
33
39
  }
34
40
  stream() {
35
41
  if (this.#streamStarted) throw new Error("AgentRun.stream() can only be consumed once");
@@ -48,15 +54,24 @@ var BufferedAgentRun = class {
48
54
  return iterator;
49
55
  }
50
56
  #cancel() {
57
+ this.#settleQueuedAcks();
51
58
  this.#events.length = 0;
52
- this.close();
59
+ this.close(void 0, "stream return");
60
+ }
61
+ #enqueue(event) {
62
+ const waiter = this.#waiter;
63
+ if (waiter) {
64
+ this.#waiter = void 0;
65
+ this.#deliver(waiter.resolve, event);
66
+ return;
67
+ }
68
+ this.#events.push(event);
53
69
  }
54
70
  #next() {
71
+ if (this.#resultPending || this.#waiter) return Promise.reject(/* @__PURE__ */ new Error("AgentRun.stream() does not allow concurrent next() calls"));
72
+ this.#settlePendingAck();
55
73
  const event = this.#events.shift();
56
- if (event) return Promise.resolve({
57
- done: false,
58
- value: event
59
- });
74
+ if (event) return new Promise((resolve) => this.#deliver(resolve, event));
60
75
  if (this.#closed) {
61
76
  if (this.#error) return Promise.reject(this.#error);
62
77
  return Promise.resolve({
@@ -65,12 +80,31 @@ var BufferedAgentRun = class {
65
80
  });
66
81
  }
67
82
  return new Promise((resolve, reject) => {
68
- this.#waiters.push({
83
+ this.#waiter = {
69
84
  reject,
70
85
  resolve
71
- });
86
+ };
87
+ });
88
+ }
89
+ #deliver(resolve, { ack, event }) {
90
+ this.#resultPending = true;
91
+ queueMicrotask(() => {
92
+ this.#resultPending = false;
93
+ });
94
+ this.#pendingAck = ack;
95
+ resolve({
96
+ done: false,
97
+ value: event
72
98
  });
73
99
  }
100
+ #settlePendingAck() {
101
+ const ack = this.#pendingAck;
102
+ this.#pendingAck = void 0;
103
+ ack?.();
104
+ }
105
+ #settleQueuedAcks() {
106
+ for (const event of this.#events) event.ack?.();
107
+ }
74
108
  };
75
109
  //#endregion
76
110
  export { BufferedAgentRun };
@@ -1 +1 @@
1
- {"version":3,"file":"run.js","names":["#events","#waiters","#closed","#error","#streamStarted","#next","#cancel"],"sources":["../../src/session/run.ts"],"sourcesContent":["import type { AgentEvent } from \"./events\";\n\nexport interface AgentRun {\n stream(): AsyncIterable<AgentEvent>;\n}\n\nexport class BufferedAgentRun implements AgentRun {\n readonly #events: AgentEvent[] = [];\n readonly #waiters: Array<{\n reject: (error: unknown) => void;\n resolve: (value: IteratorResult<AgentEvent>) => void;\n }> = [];\n #closed = false;\n #error: unknown;\n #streamStarted = false;\n\n emit(event: AgentEvent): void {\n if (this.#closed) {\n return;\n }\n\n const waiter = this.#waiters.shift();\n if (waiter) {\n waiter.resolve({ done: false, value: event });\n return;\n }\n\n this.#events.push(structuredClone(event));\n }\n\n close(error?: unknown): void {\n if (this.#closed) {\n return;\n }\n\n this.#closed = true;\n this.#error = error;\n\n while (this.#waiters.length > 0) {\n const waiter = this.#waiters.shift();\n if (!waiter) {\n continue;\n }\n\n if (error) {\n waiter.reject(error);\n } else {\n waiter.resolve({ done: true, value: undefined });\n }\n }\n }\n\n stream(): AsyncIterable<AgentEvent> {\n if (this.#streamStarted) {\n throw new Error(\"AgentRun.stream() can only be consumed once\");\n }\n this.#streamStarted = true;\n\n const iterator: AsyncIterableIterator<AgentEvent> = {\n next: () => this.#next(),\n return: () => {\n this.#cancel();\n return Promise.resolve({ done: true, value: undefined });\n },\n [Symbol.asyncIterator]: () => iterator,\n };\n return iterator;\n }\n\n #cancel(): void {\n this.#events.length = 0;\n this.close();\n }\n\n #next(): Promise<IteratorResult<AgentEvent>> {\n const event = this.#events.shift();\n if (event) {\n return Promise.resolve({ done: false, value: event });\n }\n\n if (this.#closed) {\n if (this.#error) {\n return Promise.reject(this.#error);\n }\n return Promise.resolve({ done: true, value: undefined });\n }\n\n return new Promise((resolve, reject) => {\n this.#waiters.push({ reject, resolve });\n });\n }\n}\n"],"mappings":";AAMA,IAAa,mBAAb,MAAkD;CAChD,UAAiC,CAAC;CAClC,WAGK,CAAC;CACN,UAAU;CACV;CACA,iBAAiB;CAEjB,KAAK,OAAyB;EAC5B,IAAI,KAAKE,SACP;EAGF,MAAM,SAAS,KAAKD,SAAS,MAAM;EACnC,IAAI,QAAQ;GACV,OAAO,QAAQ;IAAE,MAAM;IAAO,OAAO;GAAM,CAAC;GAC5C;EACF;EAEA,KAAKD,QAAQ,KAAK,gBAAgB,KAAK,CAAC;CAC1C;CAEA,MAAM,OAAuB;EAC3B,IAAI,KAAKE,SACP;EAGF,KAAKA,UAAU;EACf,KAAKC,SAAS;EAEd,OAAO,KAAKF,SAAS,SAAS,GAAG;GAC/B,MAAM,SAAS,KAAKA,SAAS,MAAM;GACnC,IAAI,CAAC,QACH;GAGF,IAAI,OACF,OAAO,OAAO,KAAK;QAEnB,OAAO,QAAQ;IAAE,MAAM;IAAM,OAAO,KAAA;GAAU,CAAC;EAEnD;CACF;CAEA,SAAoC;EAClC,IAAI,KAAKG,gBACP,MAAM,IAAI,MAAM,6CAA6C;EAE/D,KAAKA,iBAAiB;EAEtB,MAAM,WAA8C;GAClD,YAAY,KAAKC,MAAM;GACvB,cAAc;IACZ,KAAKC,QAAQ;IACb,OAAO,QAAQ,QAAQ;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU,CAAC;GACzD;IACC,OAAO,sBAAsB;EAChC;EACA,OAAO;CACT;CAEA,UAAgB;EACd,KAAKN,QAAQ,SAAS;EACtB,KAAK,MAAM;CACb;CAEA,QAA6C;EAC3C,MAAM,QAAQ,KAAKA,QAAQ,MAAM;EACjC,IAAI,OACF,OAAO,QAAQ,QAAQ;GAAE,MAAM;GAAO,OAAO;EAAM,CAAC;EAGtD,IAAI,KAAKE,SAAS;GAChB,IAAI,KAAKC,QACP,OAAO,QAAQ,OAAO,KAAKA,MAAM;GAEnC,OAAO,QAAQ,QAAQ;IAAE,MAAM;IAAM,OAAO,KAAA;GAAU,CAAC;EACzD;EAEA,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,KAAKF,SAAS,KAAK;IAAE;IAAQ;GAAQ,CAAC;EACxC,CAAC;CACH;AACF"}
1
+ {"version":3,"file":"run.js","names":["#events","#closed","#enqueue","#error","#settlePendingAck","#waiter","#streamStarted","#next","#cancel","#settleQueuedAcks","#deliver","#resultPending","#pendingAck"],"sources":["../../src/session/run.ts"],"sourcesContent":["import type { AgentEvent } from \"./events\";\n\nexport interface AgentRun {\n stream(): AsyncIterable<AgentEvent>;\n}\n\ninterface QueuedEvent {\n readonly ack?: () => void;\n readonly event: AgentEvent;\n}\n\ninterface NextWaiter {\n readonly reject: (error: unknown) => void;\n readonly resolve: (value: IteratorResult<AgentEvent>) => void;\n}\n\nexport class BufferedAgentRun implements AgentRun {\n readonly #events: QueuedEvent[] = [];\n #closed = false;\n #error: unknown;\n #pendingAck: (() => void) | undefined;\n #resultPending = false;\n #streamStarted = false;\n #waiter: NextWaiter | undefined;\n\n emit(event: AgentEvent): void {\n if (this.#closed) {\n return;\n }\n\n this.#enqueue({ event: structuredClone(event) });\n }\n\n emitBoundary(event: AgentEvent): Promise<void> {\n if (this.#closed) {\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n this.#enqueue({ ack: resolve, event: structuredClone(event) });\n });\n }\n\n close(error?: unknown, _reason = \"the run is closed\"): void {\n if (this.#closed) {\n return;\n }\n\n this.#closed = true;\n this.#error = error;\n this.#settlePendingAck();\n\n if (!this.#waiter) {\n return;\n }\n\n const waiter = this.#waiter;\n this.#waiter = undefined;\n if (error) {\n waiter.reject(error);\n return;\n }\n waiter.resolve({ done: true, value: undefined });\n }\n\n stream(): AsyncIterable<AgentEvent> {\n if (this.#streamStarted) {\n throw new Error(\"AgentRun.stream() can only be consumed once\");\n }\n this.#streamStarted = true;\n\n const iterator: AsyncIterableIterator<AgentEvent> = {\n next: () => this.#next(),\n return: () => {\n this.#cancel();\n return Promise.resolve({ done: true, value: undefined });\n },\n [Symbol.asyncIterator]: () => iterator,\n };\n return iterator;\n }\n\n #cancel(): void {\n this.#settleQueuedAcks();\n this.#events.length = 0;\n this.close(undefined, \"stream return\");\n }\n\n #enqueue(event: QueuedEvent): void {\n const waiter = this.#waiter;\n if (waiter) {\n this.#waiter = undefined;\n this.#deliver(waiter.resolve, event);\n return;\n }\n\n this.#events.push(event);\n }\n\n #next(): Promise<IteratorResult<AgentEvent>> {\n if (this.#resultPending || this.#waiter) {\n return Promise.reject(\n new Error(\"AgentRun.stream() does not allow concurrent next() calls\")\n );\n }\n\n this.#settlePendingAck();\n\n const event = this.#events.shift();\n if (event) {\n return new Promise((resolve) => this.#deliver(resolve, event));\n }\n\n if (this.#closed) {\n if (this.#error) {\n return Promise.reject(this.#error);\n }\n return Promise.resolve({ done: true, value: undefined });\n }\n\n return new Promise((resolve, reject) => {\n this.#waiter = { reject, resolve };\n });\n }\n\n #deliver(\n resolve: (value: IteratorResult<AgentEvent>) => void,\n { ack, event }: QueuedEvent\n ): void {\n this.#resultPending = true;\n queueMicrotask(() => {\n this.#resultPending = false;\n });\n this.#pendingAck = ack;\n resolve({ done: false, value: event });\n }\n\n #settlePendingAck(): void {\n const ack = this.#pendingAck;\n this.#pendingAck = undefined;\n ack?.();\n }\n\n #settleQueuedAcks(): void {\n for (const event of this.#events) {\n event.ack?.();\n }\n }\n}\n"],"mappings":";AAgBA,IAAa,mBAAb,MAAkD;CAChD,UAAkC,CAAC;CACnC,UAAU;CACV;CACA;CACA,iBAAiB;CACjB,iBAAiB;CACjB;CAEA,KAAK,OAAyB;EAC5B,IAAI,KAAKC,SACP;EAGF,KAAKC,SAAS,EAAE,OAAO,gBAAgB,KAAK,EAAE,CAAC;CACjD;CAEA,aAAa,OAAkC;EAC7C,IAAI,KAAKD,SACP,OAAO,QAAQ,QAAQ;EAGzB,OAAO,IAAI,SAAS,YAAY;GAC9B,KAAKC,SAAS;IAAE,KAAK;IAAS,OAAO,gBAAgB,KAAK;GAAE,CAAC;EAC/D,CAAC;CACH;CAEA,MAAM,OAAiB,UAAU,qBAA2B;EAC1D,IAAI,KAAKD,SACP;EAGF,KAAKA,UAAU;EACf,KAAKE,SAAS;EACd,KAAKC,kBAAkB;EAEvB,IAAI,CAAC,KAAKC,SACR;EAGF,MAAM,SAAS,KAAKA;EACpB,KAAKA,UAAU,KAAA;EACf,IAAI,OAAO;GACT,OAAO,OAAO,KAAK;GACnB;EACF;EACA,OAAO,QAAQ;GAAE,MAAM;GAAM,OAAO,KAAA;EAAU,CAAC;CACjD;CAEA,SAAoC;EAClC,IAAI,KAAKC,gBACP,MAAM,IAAI,MAAM,6CAA6C;EAE/D,KAAKA,iBAAiB;EAEtB,MAAM,WAA8C;GAClD,YAAY,KAAKC,MAAM;GACvB,cAAc;IACZ,KAAKC,QAAQ;IACb,OAAO,QAAQ,QAAQ;KAAE,MAAM;KAAM,OAAO,KAAA;IAAU,CAAC;GACzD;IACC,OAAO,sBAAsB;EAChC;EACA,OAAO;CACT;CAEA,UAAgB;EACd,KAAKC,kBAAkB;EACvB,KAAKT,QAAQ,SAAS;EACtB,KAAK,MAAM,KAAA,GAAW,eAAe;CACvC;CAEA,SAAS,OAA0B;EACjC,MAAM,SAAS,KAAKK;EACpB,IAAI,QAAQ;GACV,KAAKA,UAAU,KAAA;GACf,KAAKK,SAAS,OAAO,SAAS,KAAK;GACnC;EACF;EAEA,KAAKV,QAAQ,KAAK,KAAK;CACzB;CAEA,QAA6C;EAC3C,IAAI,KAAKW,kBAAkB,KAAKN,SAC9B,OAAO,QAAQ,uBACb,IAAI,MAAM,0DAA0D,CACtE;EAGF,KAAKD,kBAAkB;EAEvB,MAAM,QAAQ,KAAKJ,QAAQ,MAAM;EACjC,IAAI,OACF,OAAO,IAAI,SAAS,YAAY,KAAKU,SAAS,SAAS,KAAK,CAAC;EAG/D,IAAI,KAAKT,SAAS;GAChB,IAAI,KAAKE,QACP,OAAO,QAAQ,OAAO,KAAKA,MAAM;GAEnC,OAAO,QAAQ,QAAQ;IAAE,MAAM;IAAM,OAAO,KAAA;GAAU,CAAC;EACzD;EAEA,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,KAAKE,UAAU;IAAE;IAAQ;GAAQ;EACnC,CAAC;CACH;CAEA,SACE,SACA,EAAE,KAAK,SACD;EACN,KAAKM,iBAAiB;EACtB,qBAAqB;GACnB,KAAKA,iBAAiB;EACxB,CAAC;EACD,KAAKC,cAAc;EACnB,QAAQ;GAAE,MAAM;GAAO,OAAO;EAAM,CAAC;CACvC;CAEA,oBAA0B;EACxB,MAAM,MAAM,KAAKA;EACjB,KAAKA,cAAc,KAAA;EACnB,MAAM;CACR;CAEA,oBAA0B;EACxB,KAAK,MAAM,SAAS,KAAKZ,SACvB,MAAM,MAAM;CAEhB;AACF"}
@@ -1,6 +1,5 @@
1
1
  import { UserMessage, UserMessageContentPart, UserText } from "./events.js";
2
2
  import { AgentRun } from "./run.js";
3
-
4
3
  //#region src/session/session.d.ts
5
4
  type UserInput = UserMessage | UserText;
6
5
  type AgentInput = readonly string[] | readonly UserMessageContentPart[] | string | UserInput;
@@ -3,18 +3,23 @@ import { AgentModelHistory } from "./history.js";
3
3
  import { BufferedAgentRun } from "./run.js";
4
4
  import { decodeStoredSessionSnapshot, encodeSessionSnapshot } from "./snapshot.js";
5
5
  //#region src/session/session.ts
6
+ const noBoundaryDecision = void 0;
6
7
  var AgentSession = class {
8
+ #hooks;
7
9
  #inputQueue = [];
8
10
  #llm;
9
11
  #persistence;
10
12
  #activeAbort;
13
+ #activeRun;
14
+ #activeRuntimeInput;
11
15
  #history = new AgentModelHistory();
12
16
  #killed = false;
13
17
  #loadPromise;
14
18
  #loaded = false;
15
19
  #running = false;
16
20
  #storeVersion;
17
- constructor(llm, persistence) {
21
+ constructor(llm, persistence, hooks) {
22
+ this.#hooks = hooks;
18
23
  this.#llm = llm;
19
24
  this.#persistence = persistence;
20
25
  }
@@ -22,12 +27,17 @@ var AgentSession = class {
22
27
  if (this.#killed) throw sessionKilledError();
23
28
  await this.#ensureLoaded();
24
29
  if (this.#killed) throw sessionKilledError();
30
+ const runtimeInput = {
31
+ pending: Promise.resolve(),
32
+ queue: []
33
+ };
25
34
  const acceptedInput = normalizeAgentInput(input);
26
35
  const run = new BufferedAgentRun();
27
36
  run.emit(acceptedInput);
28
37
  this.#inputQueue.push({
29
38
  input: structuredClone(acceptedInput),
30
- run
39
+ run,
40
+ runtimeInput
31
41
  });
32
42
  this.#drainInputQueue().catch((error) => {
33
43
  run.emit({
@@ -38,6 +48,14 @@ var AgentSession = class {
38
48
  });
39
49
  return run;
40
50
  }
51
+ async steer(input) {
52
+ if (this.#killed) throw sessionKilledError();
53
+ const runtimeInput = this.#activeRuntimeInput;
54
+ const run = this.#activeRun;
55
+ if (!(runtimeInput && run)) return this.send(input);
56
+ await this.#addSteeringInput(runtimeInput, input);
57
+ return run;
58
+ }
41
59
  interrupt() {
42
60
  this.#activeAbort?.abort();
43
61
  }
@@ -45,13 +63,15 @@ var AgentSession = class {
45
63
  if (this.#killed) return;
46
64
  this.#killed = true;
47
65
  this.#activeAbort?.abort();
66
+ this.#closeRuntimeInput(this.#activeRuntimeInput, sessionKilledError().message);
48
67
  while (this.#inputQueue.length > 0) {
49
68
  const item = this.#inputQueue.shift();
69
+ this.#closeRuntimeInput(item?.runtimeInput, sessionKilledError().message);
50
70
  item?.run.emit({
51
71
  type: "turn-error",
52
72
  message: sessionKilledError().message
53
73
  });
54
- item?.run.close();
74
+ item?.run.close(void 0, sessionKilledError().message);
55
75
  }
56
76
  }
57
77
  async #ensureLoaded() {
@@ -86,27 +106,63 @@ var AgentSession = class {
86
106
  this.#running = false;
87
107
  }
88
108
  }
89
- async #processQueuedInput({ input, run }) {
90
- this.#activeAbort = new AbortController();
109
+ async #processQueuedInput({ input, run, runtimeInput }) {
110
+ const activeAbort = new AbortController();
111
+ this.#activeAbort = activeAbort;
112
+ this.#activeRun = run;
113
+ this.#activeRuntimeInput = runtimeInput;
91
114
  const historySnapshot = this.#history.modelSnapshot();
92
115
  try {
93
- run.emit({ type: "turn-start" });
116
+ await this.#withSteeringPlacement(runtimeInput, "turn-start", async () => {
117
+ await this.#hooks?.beforeTurn?.({
118
+ history: this.#history.modelSnapshot(),
119
+ input,
120
+ signal: activeAbort.signal
121
+ });
122
+ });
123
+ await this.#withRuntimeInputWindow(runtimeInput, "turn-start", async () => {
124
+ await run.emitBoundary({ type: "turn-start" });
125
+ });
94
126
  this.#history.appendUserInput(input);
95
127
  await this.#commitHistory();
128
+ await this.#drainRuntimeInput(run, runtimeInput, "turn-start");
96
129
  const result = await runAgentLoop({
97
- emit: (event) => run.emit(event),
130
+ emit: async (event) => {
131
+ if (event.type === "step-start" || event.type === "step-end") {
132
+ await this.#withRuntimeInputWindow(runtimeInput, event.type, async () => {
133
+ await run.emitBoundary(event);
134
+ });
135
+ const runtimeInputAdded = await this.#drainRuntimeInput(run, runtimeInput, event.type);
136
+ if (event.type === "step-end") return { runtimeInputAdded };
137
+ return noBoundaryDecision;
138
+ }
139
+ run.emit(event);
140
+ },
98
141
  history: this.#history,
142
+ hooks: this.#hooksForRuntimeInput(runtimeInput),
99
143
  llm: this.#llm,
100
- signal: this.#activeAbort.signal
144
+ signal: activeAbort.signal
101
145
  });
102
146
  await this.#commitHistory();
103
- run.emit({ type: result === "aborted" ? "turn-abort" : "turn-end" });
147
+ const terminalEvent = result === "aborted" ? "turn-abort" : "turn-end";
148
+ this.#closeRuntimeInput(runtimeInput, terminalEvent);
149
+ this.#activeRuntimeInput = void 0;
150
+ this.#activeRun = void 0;
151
+ await runAfterTurnHook(this.#hooks, {
152
+ history: this.#history.modelSnapshot(),
153
+ input,
154
+ result,
155
+ signal: activeAbort.signal
156
+ });
157
+ run.emit({ type: terminalEvent });
104
158
  } catch (error) {
105
159
  if (error instanceof SessionCommitConflictError) {
106
160
  run.emit({
107
161
  type: "turn-error",
108
162
  message: error.message
109
163
  });
164
+ this.#closeRuntimeInput(runtimeInput, "a session commit conflict");
165
+ this.#activeAbort = void 0;
110
166
  return;
111
167
  }
112
168
  this.#history.rollback(historySnapshot);
@@ -117,15 +173,38 @@ var AgentSession = class {
117
173
  type: "turn-error",
118
174
  message: `${errorMessage(error)}; history rollback persistence failed: ${errorMessage(rollbackError)}`
119
175
  });
176
+ this.#closeRuntimeInput(runtimeInput, "turn-error");
177
+ this.#activeAbort = void 0;
120
178
  return;
121
179
  }
122
180
  run.emit({
123
181
  type: "turn-error",
124
182
  message: errorMessage(error)
125
183
  });
184
+ this.#closeRuntimeInput(runtimeInput, "turn-error");
126
185
  } finally {
127
- run.close();
186
+ this.#closeRuntimeInput(runtimeInput);
128
187
  this.#activeAbort = void 0;
188
+ this.#activeRun = void 0;
189
+ this.#activeRuntimeInput = void 0;
190
+ run.close(void 0, runtimeInput.closedReason);
191
+ }
192
+ }
193
+ #addSteeringInput(runtimeInput, input) {
194
+ const next = runtimeInput.pending.then(() => {
195
+ if (runtimeInput.closedReason) throw runtimeInputClosedError(runtimeInput.closedReason);
196
+ runtimeInput.queue.push({
197
+ input: normalizeAgentInput(input),
198
+ placement: runtimeInput.steerPlacement ?? runtimeInput.placement ?? "step-end"
199
+ });
200
+ });
201
+ runtimeInput.pending = next.catch(() => void 0);
202
+ return next;
203
+ }
204
+ #closeRuntimeInput(runtimeInput, reason = "the run reached a terminal state") {
205
+ if (!runtimeInput?.closedReason && runtimeInput) {
206
+ runtimeInput.closedReason = reason;
207
+ runtimeInput.placement = void 0;
129
208
  }
130
209
  }
131
210
  async #commitHistory() {
@@ -139,7 +218,66 @@ var AgentSession = class {
139
218
  }
140
219
  this.#storeVersion = result.version;
141
220
  }
221
+ async #withRuntimeInputWindow(runtimeInput, placement, callback) {
222
+ const previousSteerPlacement = runtimeInput.steerPlacement;
223
+ runtimeInput.placement = placement;
224
+ runtimeInput.steerPlacement = placement;
225
+ try {
226
+ return await callback();
227
+ } finally {
228
+ runtimeInput.placement = void 0;
229
+ runtimeInput.steerPlacement = previousSteerPlacement;
230
+ }
231
+ }
232
+ async #withSteeringPlacement(runtimeInput, placement, callback) {
233
+ const previousSteerPlacement = runtimeInput.steerPlacement;
234
+ runtimeInput.steerPlacement = placement;
235
+ try {
236
+ return await callback();
237
+ } finally {
238
+ runtimeInput.steerPlacement = previousSteerPlacement;
239
+ }
240
+ }
241
+ #hooksForRuntimeInput(runtimeInput) {
242
+ const hooks = this.#hooks;
243
+ if (!hooks) return;
244
+ return {
245
+ ...hooks,
246
+ afterStep: (context) => this.#withSteeringPlacement(runtimeInput, "step-end", async () => {
247
+ await hooks.afterStep?.(context);
248
+ }),
249
+ beforeStep: (context) => this.#withSteeringPlacement(runtimeInput, "step-start", async () => {
250
+ await hooks.beforeStep?.(context);
251
+ })
252
+ };
253
+ }
254
+ async #drainRuntimeInput(run, runtimeInput, placement) {
255
+ let added = false;
256
+ let next = shiftRuntimeInput(runtimeInput, placement);
257
+ while (next) {
258
+ added = true;
259
+ run.emit({
260
+ type: "runtime-input",
261
+ input: next.input,
262
+ placement
263
+ });
264
+ this.#history.appendUserInput(next.input);
265
+ await this.#commitHistory();
266
+ next = shiftRuntimeInput(runtimeInput, placement);
267
+ }
268
+ return added;
269
+ }
142
270
  };
271
+ function shiftRuntimeInput(runtimeInput, placement) {
272
+ const index = runtimeInput.queue.findIndex((input) => input.placement === placement);
273
+ if (index === -1) return;
274
+ return runtimeInput.queue.splice(index, 1)[0];
275
+ }
276
+ async function runAfterTurnHook(hooks, context) {
277
+ const hook = hooks?.afterTurn;
278
+ if (!hook) return;
279
+ await Promise.allSettled([Promise.resolve().then(() => hook(context))]);
280
+ }
143
281
  function normalizeAgentInput(input) {
144
282
  if (typeof input === "string") return {
145
283
  type: "user-text",
@@ -194,6 +332,9 @@ function errorMessage(error) {
194
332
  function sessionKilledError() {
195
333
  return /* @__PURE__ */ new Error("Session killed");
196
334
  }
335
+ function runtimeInputClosedError(reason) {
336
+ return /* @__PURE__ */ new Error(`session.steer() cannot be used after ${reason}`);
337
+ }
197
338
  var SessionCommitConflictError = class extends Error {
198
339
  constructor(key) {
199
340
  super(`Session ${JSON.stringify(key)} commit conflict`);
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","names":["#inputQueue","#llm","#persistence","#killed","#ensureLoaded","#drainInputQueue","#activeAbort","#loaded","#loadPromise","#loadSessionState","#replaceWithStoredSession","#storeVersion","#history","#running","#processQueuedInput","#commitHistory"],"sources":["../../src/session/session.ts"],"sourcesContent":["import { runAgentLoop } from \"../agent-loop\";\nimport type { Llm } from \"../llm\";\nimport type { UserMessage, UserMessageContentPart, UserText } from \"./events\";\nimport { AgentModelHistory } from \"./history\";\nimport type { AgentRun } from \"./run\";\nimport { BufferedAgentRun } from \"./run\";\nimport { decodeStoredSessionSnapshot, encodeSessionSnapshot } from \"./snapshot\";\nimport type { SessionStore } from \"./store/types\";\n\nexport type UserInput = UserMessage | UserText;\nexport type AgentInput =\n | readonly string[]\n | readonly UserMessageContentPart[]\n | string\n | UserInput;\nexport type SessionInput = AgentInput;\nexport type { AgentRun } from \"./run\";\n\ninterface SessionPersistenceOptions {\n readonly key: string;\n readonly store: SessionStore;\n}\n\ninterface QueuedInput {\n readonly input: UserInput;\n readonly run: BufferedAgentRun;\n}\n\nexport class AgentSession {\n readonly #inputQueue: QueuedInput[] = [];\n readonly #llm: Llm;\n readonly #persistence: SessionPersistenceOptions;\n #activeAbort?: AbortController;\n #history = new AgentModelHistory();\n #killed = false;\n #loadPromise?: Promise<void>;\n #loaded = false;\n #running = false;\n #storeVersion: string | undefined;\n\n constructor(llm: Llm, persistence: SessionPersistenceOptions) {\n this.#llm = llm;\n this.#persistence = persistence;\n }\n\n async send(input: AgentInput): Promise<AgentRun> {\n if (this.#killed) {\n throw sessionKilledError();\n }\n\n await this.#ensureLoaded();\n\n if (this.#killed) {\n throw sessionKilledError();\n }\n\n const acceptedInput = normalizeAgentInput(input);\n const run = new BufferedAgentRun();\n run.emit(acceptedInput);\n this.#inputQueue.push({ input: structuredClone(acceptedInput), run });\n this.#drainInputQueue().catch((error: unknown) => {\n run.emit({ type: \"turn-error\", message: errorMessage(error) });\n run.close();\n });\n return run;\n }\n\n interrupt(): void {\n this.#activeAbort?.abort();\n }\n\n kill(): void {\n if (this.#killed) {\n return;\n }\n\n this.#killed = true;\n this.#activeAbort?.abort();\n\n while (this.#inputQueue.length > 0) {\n const item = this.#inputQueue.shift();\n item?.run.emit({\n type: \"turn-error\",\n message: sessionKilledError().message,\n });\n item?.run.close();\n }\n }\n\n async #ensureLoaded(): Promise<void> {\n if (this.#loaded) {\n return;\n }\n\n this.#loadPromise ??= this.#loadSessionState();\n try {\n await this.#loadPromise;\n } catch (error) {\n this.#loadPromise = undefined;\n throw error;\n }\n }\n\n async #loadSessionState(): Promise<void> {\n if (this.#loaded) {\n return;\n }\n\n await this.#replaceWithStoredSession();\n this.#loaded = true;\n }\n\n async #replaceWithStoredSession(): Promise<void> {\n const stored = await this.#persistence.store.load(this.#persistence.key);\n this.#storeVersion = stored?.version;\n this.#history = new AgentModelHistory(decodeStoredSessionSnapshot(stored));\n }\n\n async #drainInputQueue(): Promise<void> {\n if (this.#running) {\n return;\n }\n\n this.#running = true;\n try {\n while (!this.#killed && this.#inputQueue.length > 0) {\n const item = this.#inputQueue.shift();\n if (item) {\n await this.#processQueuedInput(item);\n }\n }\n } finally {\n this.#running = false;\n }\n }\n\n async #processQueuedInput({ input, run }: QueuedInput): Promise<void> {\n this.#activeAbort = new AbortController();\n const historySnapshot = this.#history.modelSnapshot();\n\n try {\n run.emit({ type: \"turn-start\" });\n this.#history.appendUserInput(input);\n await this.#commitHistory();\n\n const result = await runAgentLoop({\n emit: (event) => run.emit(event),\n history: this.#history,\n llm: this.#llm,\n signal: this.#activeAbort.signal,\n });\n\n await this.#commitHistory();\n run.emit({ type: result === \"aborted\" ? \"turn-abort\" : \"turn-end\" });\n } catch (error) {\n if (error instanceof SessionCommitConflictError) {\n run.emit({ type: \"turn-error\", message: error.message });\n return;\n }\n\n this.#history.rollback(historySnapshot);\n try {\n await this.#commitHistory();\n } catch (rollbackError) {\n run.emit({\n type: \"turn-error\",\n message: `${errorMessage(error)}; history rollback persistence failed: ${errorMessage(\n rollbackError\n )}`,\n });\n return;\n }\n run.emit({ type: \"turn-error\", message: errorMessage(error) });\n } finally {\n run.close();\n this.#activeAbort = undefined;\n }\n }\n\n async #commitHistory(): Promise<void> {\n const result = await this.#persistence.store.commit(\n this.#persistence.key,\n {\n state: encodeSessionSnapshot(this.#history.modelSnapshot()),\n version: this.#storeVersion,\n },\n { expectedVersion: this.#storeVersion ?? null }\n );\n\n if (!result.ok) {\n await this.#replaceWithStoredSession();\n throw new SessionCommitConflictError(this.#persistence.key);\n }\n\n this.#storeVersion = result.version;\n }\n}\n\nexport function normalizeAgentInput(input: AgentInput): UserInput {\n if (typeof input === \"string\") {\n return {\n type: \"user-text\",\n text: input,\n };\n }\n\n if (isStringArrayInput(input)) {\n return {\n type: \"user-text\",\n text: structuredClone(input) as readonly string[],\n };\n }\n\n if (isArrayInput(input)) {\n assertUserMessageContent(input);\n return {\n type: \"user-message\",\n content: structuredClone(input) as readonly UserMessageContentPart[],\n };\n }\n\n if (isUserMessage(input)) {\n assertUserMessageContent(input.content);\n }\n\n return structuredClone(input);\n}\n\nfunction isStringArrayInput(input: AgentInput): input is readonly string[] {\n return isArrayInput(input) && input.every((part) => typeof part === \"string\");\n}\n\nfunction isArrayInput(\n input: AgentInput\n): input is readonly string[] | readonly UserMessageContentPart[] {\n return Array.isArray(input);\n}\n\nfunction isUserMessage(input: UserInput): input is UserMessage {\n return input.type === \"user-message\";\n}\n\nfunction assertUserMessageContent(\n input: readonly unknown[]\n): asserts input is readonly UserMessageContentPart[] {\n for (const part of input) {\n if (!isUserMessageContentPart(part)) {\n throw new TypeError(\n 'Agent input content parts must be { type: \"text\", text }, { type: \"image\", image }, or { type: \"file\", data, mediaType }.'\n );\n }\n }\n}\n\nfunction isUserMessageContentPart(\n part: unknown\n): part is UserMessageContentPart {\n if (part === null || typeof part !== \"object\" || !(\"type\" in part)) {\n return false;\n }\n\n if (part.type === \"text\") {\n return \"text\" in part && typeof part.text === \"string\";\n }\n\n if (part.type === \"image\") {\n return (\n \"image\" in part &&\n typeof part.image === \"string\" &&\n (!(\"mediaType\" in part) || typeof part.mediaType === \"string\")\n );\n }\n\n if (part.type === \"file\") {\n return (\n \"data\" in part &&\n isUserMessageFileData(part.data) &&\n \"mediaType\" in part &&\n typeof part.mediaType === \"string\" &&\n (!(\"filename\" in part) || typeof part.filename === \"string\")\n );\n }\n\n return false;\n}\n\nfunction isUserMessageFileData(data: unknown): boolean {\n if (typeof data === \"string\") {\n return true;\n }\n\n if (data === null || typeof data !== \"object\" || !(\"type\" in data)) {\n return false;\n }\n\n if (data.type === \"data\") {\n return \"data\" in data && typeof data.data === \"string\";\n }\n\n if (data.type === \"reference\") {\n return (\n \"reference\" in data &&\n data.reference !== null &&\n typeof data.reference === \"object\" &&\n Object.values(data.reference).every((value) => typeof value === \"string\")\n );\n }\n\n if (data.type === \"text\") {\n return \"text\" in data && typeof data.text === \"string\";\n }\n\n if (data.type === \"url\") {\n return \"url\" in data && typeof data.url === \"string\";\n }\n\n return false;\n}\n\nfunction errorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n\n return String(error);\n}\n\nfunction sessionKilledError(): Error {\n return new Error(\"Session killed\");\n}\n\nclass SessionCommitConflictError extends Error {\n constructor(key: string) {\n super(`Session ${JSON.stringify(key)} commit conflict`);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session.js","names":["#hooks","#inputQueue","#llm","#persistence","#killed","#ensureLoaded","#drainInputQueue","#activeRuntimeInput","#activeRun","#addSteeringInput","#activeAbort","#closeRuntimeInput","#loaded","#loadPromise","#loadSessionState","#replaceWithStoredSession","#storeVersion","#history","#running","#processQueuedInput","#withSteeringPlacement","#withRuntimeInputWindow","#commitHistory","#drainRuntimeInput","#hooksForRuntimeInput"],"sources":["../../src/session/session.ts"],"sourcesContent":["import { runAgentLoop } from \"../agent-loop\";\nimport type { AgentHooks } from \"../hooks\";\nimport type { Llm } from \"../llm\";\nimport type {\n RuntimeInput,\n UserMessage,\n UserMessageContentPart,\n UserText,\n} from \"./events\";\nimport { AgentModelHistory } from \"./history\";\nimport type { AgentRun } from \"./run\";\nimport { BufferedAgentRun } from \"./run\";\nimport { decodeStoredSessionSnapshot, encodeSessionSnapshot } from \"./snapshot\";\nimport type { SessionStore } from \"./store/types\";\n\nexport type UserInput = UserMessage | UserText;\nexport type AgentInput =\n | readonly string[]\n | readonly UserMessageContentPart[]\n | string\n | UserInput;\nexport type SessionInput = AgentInput;\nexport type { AgentRun } from \"./run\";\n\ninterface SessionPersistenceOptions {\n readonly key: string;\n readonly store: SessionStore;\n}\n\ninterface QueuedInput {\n readonly input: UserInput;\n readonly run: BufferedAgentRun;\n readonly runtimeInput: RuntimeInputState;\n}\n\ntype RuntimeInputPlacement = RuntimeInput[\"placement\"];\nconst noBoundaryDecision = undefined;\n\ninterface QueuedRuntimeInput {\n readonly input: UserInput;\n readonly placement: RuntimeInputPlacement;\n}\n\ninterface RuntimeInputState {\n closedReason?: string;\n pending: Promise<void>;\n placement?: RuntimeInputPlacement;\n readonly queue: QueuedRuntimeInput[];\n steerPlacement?: RuntimeInputPlacement;\n}\n\nexport class AgentSession {\n readonly #hooks?: AgentHooks;\n readonly #inputQueue: QueuedInput[] = [];\n readonly #llm: Llm;\n readonly #persistence: SessionPersistenceOptions;\n #activeAbort?: AbortController;\n #activeRun?: BufferedAgentRun;\n #activeRuntimeInput?: RuntimeInputState;\n #history = new AgentModelHistory();\n #killed = false;\n #loadPromise?: Promise<void>;\n #loaded = false;\n #running = false;\n #storeVersion: string | undefined;\n\n constructor(\n llm: Llm,\n persistence: SessionPersistenceOptions,\n hooks?: AgentHooks\n ) {\n this.#hooks = hooks;\n this.#llm = llm;\n this.#persistence = persistence;\n }\n\n async send(input: AgentInput): Promise<AgentRun> {\n if (this.#killed) {\n throw sessionKilledError();\n }\n\n await this.#ensureLoaded();\n\n if (this.#killed) {\n throw sessionKilledError();\n }\n\n const runtimeInput: RuntimeInputState = {\n pending: Promise.resolve(),\n queue: [],\n };\n const acceptedInput = normalizeAgentInput(input);\n const run = new BufferedAgentRun();\n run.emit(acceptedInput);\n this.#inputQueue.push({\n input: structuredClone(acceptedInput),\n run,\n runtimeInput,\n });\n this.#drainInputQueue().catch((error: unknown) => {\n run.emit({ type: \"turn-error\", message: errorMessage(error) });\n run.close();\n });\n return run;\n }\n\n async steer(input: AgentInput): Promise<AgentRun> {\n if (this.#killed) {\n throw sessionKilledError();\n }\n\n const runtimeInput = this.#activeRuntimeInput;\n const run = this.#activeRun;\n if (!(runtimeInput && run)) {\n return this.send(input);\n }\n\n await this.#addSteeringInput(runtimeInput, input);\n return run;\n }\n\n interrupt(): void {\n this.#activeAbort?.abort();\n }\n\n kill(): void {\n if (this.#killed) {\n return;\n }\n\n this.#killed = true;\n this.#activeAbort?.abort();\n this.#closeRuntimeInput(\n this.#activeRuntimeInput,\n sessionKilledError().message\n );\n\n while (this.#inputQueue.length > 0) {\n const item = this.#inputQueue.shift();\n this.#closeRuntimeInput(item?.runtimeInput, sessionKilledError().message);\n item?.run.emit({\n type: \"turn-error\",\n message: sessionKilledError().message,\n });\n item?.run.close(undefined, sessionKilledError().message);\n }\n }\n\n async #ensureLoaded(): Promise<void> {\n if (this.#loaded) {\n return;\n }\n\n this.#loadPromise ??= this.#loadSessionState();\n try {\n await this.#loadPromise;\n } catch (error) {\n this.#loadPromise = undefined;\n throw error;\n }\n }\n\n async #loadSessionState(): Promise<void> {\n if (this.#loaded) {\n return;\n }\n\n await this.#replaceWithStoredSession();\n this.#loaded = true;\n }\n\n async #replaceWithStoredSession(): Promise<void> {\n const stored = await this.#persistence.store.load(this.#persistence.key);\n this.#storeVersion = stored?.version;\n this.#history = new AgentModelHistory(decodeStoredSessionSnapshot(stored));\n }\n\n async #drainInputQueue(): Promise<void> {\n if (this.#running) {\n return;\n }\n\n this.#running = true;\n try {\n while (!this.#killed && this.#inputQueue.length > 0) {\n const item = this.#inputQueue.shift();\n if (item) {\n await this.#processQueuedInput(item);\n }\n }\n } finally {\n this.#running = false;\n }\n }\n\n async #processQueuedInput({\n input,\n run,\n runtimeInput,\n }: QueuedInput): Promise<void> {\n const activeAbort = new AbortController();\n this.#activeAbort = activeAbort;\n this.#activeRun = run;\n this.#activeRuntimeInput = runtimeInput;\n const historySnapshot = this.#history.modelSnapshot();\n\n try {\n await this.#withSteeringPlacement(\n runtimeInput,\n \"turn-start\",\n async () => {\n await this.#hooks?.beforeTurn?.({\n history: this.#history.modelSnapshot(),\n input,\n signal: activeAbort.signal,\n });\n }\n );\n await this.#withRuntimeInputWindow(\n runtimeInput,\n \"turn-start\",\n async () => {\n await run.emitBoundary({ type: \"turn-start\" });\n }\n );\n this.#history.appendUserInput(input);\n await this.#commitHistory();\n await this.#drainRuntimeInput(run, runtimeInput, \"turn-start\");\n\n const result = await runAgentLoop({\n emit: async (event) => {\n if (event.type === \"step-start\" || event.type === \"step-end\") {\n await this.#withRuntimeInputWindow(\n runtimeInput,\n event.type,\n async () => {\n await run.emitBoundary(event);\n }\n );\n const runtimeInputAdded = await this.#drainRuntimeInput(\n run,\n runtimeInput,\n event.type\n );\n\n if (event.type === \"step-end\") {\n return { runtimeInputAdded };\n }\n return noBoundaryDecision;\n }\n\n run.emit(event);\n },\n history: this.#history,\n hooks: this.#hooksForRuntimeInput(runtimeInput),\n llm: this.#llm,\n signal: activeAbort.signal,\n });\n\n await this.#commitHistory();\n const terminalEvent = result === \"aborted\" ? \"turn-abort\" : \"turn-end\";\n this.#closeRuntimeInput(runtimeInput, terminalEvent);\n this.#activeRuntimeInput = undefined;\n this.#activeRun = undefined;\n await runAfterTurnHook(this.#hooks, {\n history: this.#history.modelSnapshot(),\n input,\n result,\n signal: activeAbort.signal,\n });\n run.emit({ type: terminalEvent });\n } catch (error) {\n if (error instanceof SessionCommitConflictError) {\n run.emit({ type: \"turn-error\", message: error.message });\n this.#closeRuntimeInput(runtimeInput, \"a session commit conflict\");\n this.#activeAbort = undefined;\n return;\n }\n\n this.#history.rollback(historySnapshot);\n try {\n await this.#commitHistory();\n } catch (rollbackError) {\n run.emit({\n type: \"turn-error\",\n message: `${errorMessage(error)}; history rollback persistence failed: ${errorMessage(\n rollbackError\n )}`,\n });\n this.#closeRuntimeInput(runtimeInput, \"turn-error\");\n this.#activeAbort = undefined;\n return;\n }\n run.emit({ type: \"turn-error\", message: errorMessage(error) });\n this.#closeRuntimeInput(runtimeInput, \"turn-error\");\n } finally {\n this.#closeRuntimeInput(runtimeInput);\n this.#activeAbort = undefined;\n this.#activeRun = undefined;\n this.#activeRuntimeInput = undefined;\n run.close(undefined, runtimeInput.closedReason);\n }\n }\n\n #addSteeringInput(\n runtimeInput: RuntimeInputState,\n input: AgentInput\n ): Promise<void> {\n const next = runtimeInput.pending.then(() => {\n if (runtimeInput.closedReason) {\n throw runtimeInputClosedError(runtimeInput.closedReason);\n }\n\n runtimeInput.queue.push({\n input: normalizeAgentInput(input),\n placement:\n runtimeInput.steerPlacement ?? runtimeInput.placement ?? \"step-end\",\n });\n });\n runtimeInput.pending = next.catch(() => undefined);\n return next;\n }\n\n #closeRuntimeInput(\n runtimeInput: RuntimeInputState | undefined,\n reason = \"the run reached a terminal state\"\n ): void {\n if (!runtimeInput?.closedReason && runtimeInput) {\n runtimeInput.closedReason = reason;\n runtimeInput.placement = undefined;\n }\n }\n\n async #commitHistory(): Promise<void> {\n const result = await this.#persistence.store.commit(\n this.#persistence.key,\n {\n state: encodeSessionSnapshot(this.#history.modelSnapshot()),\n version: this.#storeVersion,\n },\n { expectedVersion: this.#storeVersion ?? null }\n );\n\n if (!result.ok) {\n await this.#replaceWithStoredSession();\n throw new SessionCommitConflictError(this.#persistence.key);\n }\n\n this.#storeVersion = result.version;\n }\n\n async #withRuntimeInputWindow<T>(\n runtimeInput: RuntimeInputState,\n placement: RuntimeInputPlacement,\n callback: () => Promise<T>\n ): Promise<T> {\n const previousSteerPlacement = runtimeInput.steerPlacement;\n runtimeInput.placement = placement;\n runtimeInput.steerPlacement = placement;\n try {\n return await callback();\n } finally {\n runtimeInput.placement = undefined;\n runtimeInput.steerPlacement = previousSteerPlacement;\n }\n }\n\n async #withSteeringPlacement<T>(\n runtimeInput: RuntimeInputState,\n placement: RuntimeInputPlacement,\n callback: () => Promise<T>\n ): Promise<T> {\n const previousSteerPlacement = runtimeInput.steerPlacement;\n runtimeInput.steerPlacement = placement;\n try {\n return await callback();\n } finally {\n runtimeInput.steerPlacement = previousSteerPlacement;\n }\n }\n\n #hooksForRuntimeInput(\n runtimeInput: RuntimeInputState\n ): AgentHooks | undefined {\n const hooks = this.#hooks;\n if (!hooks) {\n return;\n }\n\n return {\n ...hooks,\n afterStep: (context) =>\n this.#withSteeringPlacement(runtimeInput, \"step-end\", async () => {\n await hooks.afterStep?.(context);\n }),\n beforeStep: (context) =>\n this.#withSteeringPlacement(runtimeInput, \"step-start\", async () => {\n await hooks.beforeStep?.(context);\n }),\n };\n }\n\n async #drainRuntimeInput(\n run: BufferedAgentRun,\n runtimeInput: RuntimeInputState,\n placement: RuntimeInputPlacement\n ): Promise<boolean> {\n let added = false;\n let next = shiftRuntimeInput(runtimeInput, placement);\n while (next) {\n added = true;\n run.emit({ type: \"runtime-input\", input: next.input, placement });\n this.#history.appendUserInput(next.input);\n await this.#commitHistory();\n next = shiftRuntimeInput(runtimeInput, placement);\n }\n\n return added;\n }\n}\n\nfunction shiftRuntimeInput(\n runtimeInput: RuntimeInputState,\n placement: RuntimeInputPlacement\n): QueuedRuntimeInput | undefined {\n const index = runtimeInput.queue.findIndex(\n (input) => input.placement === placement\n );\n if (index === -1) {\n return;\n }\n\n return runtimeInput.queue.splice(index, 1)[0];\n}\n\nasync function runAfterTurnHook(\n hooks: AgentHooks | undefined,\n context: Parameters<NonNullable<AgentHooks[\"afterTurn\"]>>[0]\n): Promise<void> {\n const hook = hooks?.afterTurn;\n if (!hook) {\n return;\n }\n\n await Promise.allSettled([Promise.resolve().then(() => hook(context))]);\n}\n\nexport function normalizeAgentInput(input: AgentInput): UserInput {\n if (typeof input === \"string\") {\n return {\n type: \"user-text\",\n text: input,\n };\n }\n\n if (isStringArrayInput(input)) {\n return {\n type: \"user-text\",\n text: structuredClone(input) as readonly string[],\n };\n }\n\n if (isArrayInput(input)) {\n assertUserMessageContent(input);\n return {\n type: \"user-message\",\n content: structuredClone(input) as readonly UserMessageContentPart[],\n };\n }\n\n if (isUserMessage(input)) {\n assertUserMessageContent(input.content);\n }\n\n return structuredClone(input);\n}\n\nfunction isStringArrayInput(input: AgentInput): input is readonly string[] {\n return isArrayInput(input) && input.every((part) => typeof part === \"string\");\n}\n\nfunction isArrayInput(\n input: AgentInput\n): input is readonly string[] | readonly UserMessageContentPart[] {\n return Array.isArray(input);\n}\n\nfunction isUserMessage(input: UserInput): input is UserMessage {\n return input.type === \"user-message\";\n}\n\nfunction assertUserMessageContent(\n input: readonly unknown[]\n): asserts input is readonly UserMessageContentPart[] {\n for (const part of input) {\n if (!isUserMessageContentPart(part)) {\n throw new TypeError(\n 'Agent input content parts must be { type: \"text\", text }, { type: \"image\", image }, or { type: \"file\", data, mediaType }.'\n );\n }\n }\n}\n\nfunction isUserMessageContentPart(\n part: unknown\n): part is UserMessageContentPart {\n if (part === null || typeof part !== \"object\" || !(\"type\" in part)) {\n return false;\n }\n\n if (part.type === \"text\") {\n return \"text\" in part && typeof part.text === \"string\";\n }\n\n if (part.type === \"image\") {\n return (\n \"image\" in part &&\n typeof part.image === \"string\" &&\n (!(\"mediaType\" in part) || typeof part.mediaType === \"string\")\n );\n }\n\n if (part.type === \"file\") {\n return (\n \"data\" in part &&\n isUserMessageFileData(part.data) &&\n \"mediaType\" in part &&\n typeof part.mediaType === \"string\" &&\n (!(\"filename\" in part) || typeof part.filename === \"string\")\n );\n }\n\n return false;\n}\n\nfunction isUserMessageFileData(data: unknown): boolean {\n if (typeof data === \"string\") {\n return true;\n }\n\n if (data === null || typeof data !== \"object\" || !(\"type\" in data)) {\n return false;\n }\n\n if (data.type === \"data\") {\n return \"data\" in data && typeof data.data === \"string\";\n }\n\n if (data.type === \"reference\") {\n return (\n \"reference\" in data &&\n data.reference !== null &&\n typeof data.reference === \"object\" &&\n Object.values(data.reference).every((value) => typeof value === \"string\")\n );\n }\n\n if (data.type === \"text\") {\n return \"text\" in data && typeof data.text === \"string\";\n }\n\n if (data.type === \"url\") {\n return \"url\" in data && typeof data.url === \"string\";\n }\n\n return false;\n}\n\nfunction errorMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message;\n }\n\n return String(error);\n}\n\nfunction sessionKilledError(): Error {\n return new Error(\"Session killed\");\n}\n\nfunction runtimeInputClosedError(reason: string): Error {\n return new Error(`session.steer() cannot be used after ${reason}`);\n}\n\nclass SessionCommitConflictError extends Error {\n constructor(key: string) {\n super(`Session ${JSON.stringify(key)} commit conflict`);\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minpeter/pss-runtime",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Generic agent runtime for sessions, model loops, and event streams.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",