@minpeter/pss-runtime 0.0.8 → 0.0.10
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 +21 -11
- package/dist/agent.d.ts +7 -7
- package/dist/agent.js.map +1 -1
- package/dist/hooks.d.ts +1 -1
- package/dist/index.d.ts +6 -7
- package/dist/index.js +1 -2
- package/dist/llm.d.ts +2 -5
- package/dist/llm.js.map +1 -1
- package/dist/session/events.d.ts +2 -41
- package/dist/session/history.js +2 -2
- package/dist/session/history.js.map +1 -1
- package/dist/session/input.d.ts +46 -0
- package/dist/session/mapping.js.map +1 -1
- package/dist/session/run.d.ts +1 -1
- package/dist/session/run.js +6 -6
- package/dist/session/run.js.map +1 -1
- package/dist/session/session.d.ts +2 -9
- package/dist/session/session.js +5 -9
- package/dist/session/session.js.map +1 -1
- package/dist/session/snapshot.js.map +1 -1
- package/dist/session/store/file.d.ts +3 -3
- package/dist/session/store/file.js +1 -1
- package/dist/session/store/file.js.map +1 -1
- package/dist/session/store/memory.d.ts +3 -3
- package/dist/session/store/memory.js +2 -2
- package/dist/session/store/memory.js.map +1 -1
- package/dist/session/store/types.d.ts +8 -5
- package/package.json +3 -3
- package/dist/agent-loop.d.ts +0 -32
package/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="../../assets/runtime-banner.png" alt="@minpeter/pss-runtime banner" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# @minpeter/pss-runtime
|
|
2
6
|
|
|
3
|
-
Minimal, platform-agnostic agent runtime with
|
|
4
|
-
opaque persistence contracts.
|
|
7
|
+
Minimal, platform-agnostic agent runtime with keyed sessions, synchronized
|
|
8
|
+
`run.events()`, and opaque persistence contracts.
|
|
5
9
|
|
|
6
10
|
## Core DX
|
|
7
11
|
|
|
@@ -15,14 +19,14 @@ const agent = await Agent.create({
|
|
|
15
19
|
});
|
|
16
20
|
|
|
17
21
|
const run = await agent.send("Hello");
|
|
18
|
-
for await (const event of run.
|
|
22
|
+
for await (const event of run.events()) {
|
|
19
23
|
console.log(event);
|
|
20
24
|
}
|
|
21
25
|
```
|
|
22
26
|
|
|
23
|
-
`run.
|
|
24
|
-
boundaries until the
|
|
25
|
-
consume the
|
|
27
|
+
`run.events()` is the run driver. The runtime stops at synchronized lifecycle
|
|
28
|
+
boundaries until the events consumer asks for the next event, so callers must
|
|
29
|
+
consume the events for the run to progress. This is what lets code react to
|
|
26
30
|
`turn-start`, `step-start`, and `step-end` before the next model snapshot is
|
|
27
31
|
created.
|
|
28
32
|
|
|
@@ -31,8 +35,8 @@ Per-key conversations use `session(key)`:
|
|
|
31
35
|
```ts
|
|
32
36
|
const roomSession = agent.session("room:123:user:456");
|
|
33
37
|
const run = await roomSession.send(["Context: user prefers short answers", "Hi"]);
|
|
34
|
-
for await (const event of run.
|
|
35
|
-
//
|
|
38
|
+
for await (const event of run.events()) {
|
|
39
|
+
// events for this single turn
|
|
36
40
|
}
|
|
37
41
|
```
|
|
38
42
|
|
|
@@ -73,7 +77,7 @@ state; it does not fetch remote media, decode files, or guarantee provider suppo
|
|
|
73
77
|
for every media type.
|
|
74
78
|
|
|
75
79
|
The public transcript protocol is `AgentEvent`: live runs emit runtime-defined
|
|
76
|
-
events through `run.
|
|
80
|
+
events through `run.events()`. Provider/model message history is internal
|
|
77
81
|
continuation state, not a public history API.
|
|
78
82
|
|
|
79
83
|
## Send and Steer
|
|
@@ -88,7 +92,7 @@ values. Active steering emits `runtime-input` events. A `runtime-input` is
|
|
|
88
92
|
runtime/API-originated input mapped internally to the model's user role. It is
|
|
89
93
|
distinct from human-origin `user-text` and `user-message` events.
|
|
90
94
|
|
|
91
|
-
Runtime input windows are tied to synchronized
|
|
95
|
+
Runtime input windows are tied to synchronized events:
|
|
92
96
|
|
|
93
97
|
- `turn-start`: input is appended after the original turn input and before the first model snapshot.
|
|
94
98
|
- `step-start`: input is appended before that same step's model snapshot.
|
|
@@ -102,7 +106,7 @@ const session = agent.session("room:123:user:456");
|
|
|
102
106
|
const run = await session.send("Draft a short answer.");
|
|
103
107
|
let addedSteer = false;
|
|
104
108
|
|
|
105
|
-
for await (const event of run.
|
|
109
|
+
for await (const event of run.events()) {
|
|
106
110
|
if (event.type === "assistant-text") {
|
|
107
111
|
process.stdout.write(event.text);
|
|
108
112
|
}
|
|
@@ -127,6 +131,12 @@ Stored session state is an opaque, versioned runtime snapshot for continuation.
|
|
|
127
131
|
Do not inspect it as a replay log; exact replay should be modeled separately as
|
|
128
132
|
an `AgentEvent` log if that capability is added later.
|
|
129
133
|
|
|
134
|
+
Custom stores own version generation. `load(key)` returns the opaque `state` with
|
|
135
|
+
the store-minted `version`; `commit(key, { state }, { expectedVersion })` receives
|
|
136
|
+
state only and should reject stale versions by returning `{ ok: false, reason:
|
|
137
|
+
"conflict" }`. On success, the store persists `{ state, version }` and returns the
|
|
138
|
+
new version to the runtime.
|
|
139
|
+
|
|
130
140
|
```ts
|
|
131
141
|
import type { SessionStore } from "@minpeter/pss-runtime";
|
|
132
142
|
import { MemorySessionStore } from "@minpeter/pss-runtime/session-store/memory";
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { AgentToolChoice,
|
|
1
|
+
import { AgentToolChoice, Llm } from "./llm.js";
|
|
2
|
+
import { AgentInput } from "./session/input.js";
|
|
3
|
+
import { AgentHooks } from "./hooks.js";
|
|
2
4
|
import { AgentRun } from "./session/run.js";
|
|
3
5
|
import { SessionStore } from "./session/store/types.js";
|
|
4
|
-
import {
|
|
5
|
-
import { AgentHooks } from "./hooks.js";
|
|
6
|
-
import { LanguageModel } from "ai";
|
|
6
|
+
import { LanguageModel, ToolSet } from "ai";
|
|
7
7
|
|
|
8
8
|
//#region src/agent.d.ts
|
|
9
|
-
interface
|
|
9
|
+
interface AgentLanguageModelOptions {
|
|
10
10
|
hooks?: AgentHooks;
|
|
11
11
|
instructions?: string;
|
|
12
12
|
llm?: never;
|
|
13
13
|
model: LanguageModel;
|
|
14
14
|
sessions?: AgentSessionOptions;
|
|
15
15
|
toolChoice?: AgentToolChoice;
|
|
16
|
-
tools?:
|
|
16
|
+
tools?: ToolSet;
|
|
17
17
|
}
|
|
18
18
|
interface AgentLlmOptions {
|
|
19
19
|
hooks?: AgentHooks;
|
|
@@ -33,7 +33,7 @@ interface SessionHandle {
|
|
|
33
33
|
send(input: AgentInput): Promise<AgentRun>;
|
|
34
34
|
steer(input: AgentInput): Promise<AgentRun>;
|
|
35
35
|
}
|
|
36
|
-
type AgentOptions =
|
|
36
|
+
type AgentOptions = AgentLanguageModelOptions | AgentLlmOptions;
|
|
37
37
|
declare class Agent {
|
|
38
38
|
#private;
|
|
39
39
|
private constructor();
|
package/dist/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 {
|
|
1
|
+
{"version":3,"file":"agent.js","names":["#hooks","#llm","#sessions","#store"],"sources":["../src/agent.ts"],"sourcesContent":["import type { LanguageModel, ToolSet } from \"ai\";\nimport type { AgentHooks } from \"./hooks\";\nimport { type AgentToolChoice, 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 AgentLanguageModelOptions {\n hooks?: AgentHooks;\n instructions?: string;\n llm?: never;\n model: LanguageModel;\n sessions?: AgentSessionOptions;\n toolChoice?: AgentToolChoice;\n tools?: ToolSet;\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 = AgentLanguageModelOptions | 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":";;;;AAyCA,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"}
|
package/dist/hooks.d.ts
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { AgentRun } from "./session/run.js";
|
|
4
|
-
import { CommitResult, ExpectedSessionVersion, SessionStore, StoredSession } from "./session/store/types.js";
|
|
5
|
-
import { AgentInput, SessionInput, UserInput } from "./session/session.js";
|
|
1
|
+
import { AgentToolChoice, AgentToolExecute, AgentToolExecutionOptions, LlmOutputPart, RuntimeCreateLlmOptions, RuntimeLlm, RuntimeLlmContext, RuntimeLlmOutput, createLlm } from "./llm.js";
|
|
2
|
+
import { AgentInput, SessionInput, UserInput, UserMessage, UserMessageContent, UserMessageContentPart, UserMessageFileData, UserMessageFilePart, UserMessageImagePart, UserMessageTextPart, UserText, UserTextContent } from "./session/input.js";
|
|
6
3
|
import { AgentAfterStepContext, AgentAfterTurnContext, AgentBeforeStepContext, AgentBeforeTurnContext, AgentHooks, AgentStepResult, AgentTurnResult } from "./hooks.js";
|
|
4
|
+
import { AgentEvent, AgentEventListener, AssistantReasoning, AssistantText, RuntimeInput, ToolCall, ToolResult } from "./session/events.js";
|
|
5
|
+
import { AgentRun } from "./session/run.js";
|
|
6
|
+
import { CommitResult, ExpectedSessionVersion, SessionStore, SessionStoreCommit, StoredSession } from "./session/store/types.js";
|
|
7
7
|
import { Agent, AgentOptions, AgentSessionOptions, SessionHandle } from "./agent.js";
|
|
8
|
-
|
|
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 };
|
|
8
|
+
export { Agent, type AgentAfterStepContext, type AgentAfterTurnContext, type AgentBeforeStepContext, type AgentBeforeTurnContext, type AgentEvent, type AgentEventListener, type AgentHooks, type AgentInput, type AgentOptions, type AgentRun, type AgentSessionOptions, type AgentStepResult, type AgentToolChoice, type AgentToolExecute, type AgentToolExecutionOptions, 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 SessionStoreCommit, 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 };
|
package/dist/index.js
CHANGED
package/dist/llm.d.ts
CHANGED
|
@@ -3,10 +3,7 @@ import { LanguageModel, ModelMessage, Tool, ToolExecutionOptions, ToolSet, gener
|
|
|
3
3
|
//#region src/llm.d.ts
|
|
4
4
|
type AgentToolExecutionOptions = ToolExecutionOptions<unknown>;
|
|
5
5
|
type AgentToolExecute = NonNullable<Tool["execute"]>;
|
|
6
|
-
type AgentTool = Tool;
|
|
7
|
-
type AgentTools = ToolSet;
|
|
8
6
|
type AgentToolChoice = "auto" | "required";
|
|
9
|
-
type AgentModel = LanguageModel;
|
|
10
7
|
type LlmOutput = Awaited<ReturnType<typeof generateText>>["responseMessages"];
|
|
11
8
|
type LlmOutputPart = LlmOutput[number];
|
|
12
9
|
interface LlmContext {
|
|
@@ -18,7 +15,7 @@ interface CreateLlmOptions {
|
|
|
18
15
|
instructions?: string;
|
|
19
16
|
model: LanguageModel;
|
|
20
17
|
toolChoice?: AgentToolChoice;
|
|
21
|
-
tools?:
|
|
18
|
+
tools?: ToolSet;
|
|
22
19
|
}
|
|
23
20
|
type RuntimeCreateLlmOptions = CreateLlmOptions;
|
|
24
21
|
type RuntimeLlm = Llm;
|
|
@@ -31,5 +28,5 @@ declare function createLlm({
|
|
|
31
28
|
tools
|
|
32
29
|
}: CreateLlmOptions): Llm;
|
|
33
30
|
//#endregion
|
|
34
|
-
export {
|
|
31
|
+
export { AgentToolChoice, AgentToolExecute, AgentToolExecutionOptions, Llm, LlmOutputPart, RuntimeCreateLlmOptions, RuntimeLlm, RuntimeLlmContext, RuntimeLlmOutput, createLlm };
|
|
35
32
|
//# sourceMappingURL=llm.d.ts.map
|
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
|
|
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 AgentToolChoice = \"auto\" | \"required\";\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?: ToolSet;\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":";;AAoCA,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"}
|
package/dist/session/events.d.ts
CHANGED
|
@@ -1,45 +1,6 @@
|
|
|
1
|
-
import { UserInput } from "./
|
|
1
|
+
import { UserInput, UserMessage, UserMessageContent, UserMessageContentPart, UserMessageFileData, UserMessageFilePart, UserMessageImagePart, UserMessageTextPart, UserText, UserTextContent } from "./input.js";
|
|
2
2
|
|
|
3
3
|
//#region src/session/events.d.ts
|
|
4
|
-
type UserTextContent = string | readonly string[];
|
|
5
|
-
interface UserText {
|
|
6
|
-
text: UserTextContent;
|
|
7
|
-
type: "user-text";
|
|
8
|
-
}
|
|
9
|
-
interface UserMessageTextPart {
|
|
10
|
-
text: string;
|
|
11
|
-
type: "text";
|
|
12
|
-
}
|
|
13
|
-
interface UserMessageImagePart {
|
|
14
|
-
image: string;
|
|
15
|
-
mediaType?: string;
|
|
16
|
-
type: "image";
|
|
17
|
-
}
|
|
18
|
-
type UserMessageFileData = string | {
|
|
19
|
-
data: string;
|
|
20
|
-
type: "data";
|
|
21
|
-
} | {
|
|
22
|
-
reference: Record<string, string>;
|
|
23
|
-
type: "reference";
|
|
24
|
-
} | {
|
|
25
|
-
text: string;
|
|
26
|
-
type: "text";
|
|
27
|
-
} | {
|
|
28
|
-
type: "url";
|
|
29
|
-
url: string;
|
|
30
|
-
};
|
|
31
|
-
interface UserMessageFilePart {
|
|
32
|
-
data: UserMessageFileData;
|
|
33
|
-
filename?: string;
|
|
34
|
-
mediaType: string;
|
|
35
|
-
type: "file";
|
|
36
|
-
}
|
|
37
|
-
type UserMessageContentPart = UserMessageFilePart | UserMessageImagePart | UserMessageTextPart;
|
|
38
|
-
type UserMessageContent = readonly UserMessageContentPart[];
|
|
39
|
-
interface UserMessage {
|
|
40
|
-
content: UserMessageContent;
|
|
41
|
-
type: "user-message";
|
|
42
|
-
}
|
|
43
4
|
interface RuntimeInput {
|
|
44
5
|
/**
|
|
45
6
|
* Runtime/API-originated model input inserted into the current turn.
|
|
@@ -85,5 +46,5 @@ type AgentEvent = /** User input was accepted into the session queue. */UserText
|
|
|
85
46
|
};
|
|
86
47
|
type AgentEventListener = (event: AgentEvent) => void;
|
|
87
48
|
//#endregion
|
|
88
|
-
export { AgentEvent, AgentEventListener, AssistantReasoning, AssistantText, RuntimeInput, ToolCall, ToolResult
|
|
49
|
+
export { AgentEvent, AgentEventListener, AssistantReasoning, AssistantText, RuntimeInput, ToolCall, ToolResult };
|
|
89
50
|
//# sourceMappingURL=events.d.ts.map
|
package/dist/session/history.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { userInputToModelMessage } from "./mapping.js";
|
|
2
2
|
//#region src/session/history.ts
|
|
3
|
-
var
|
|
3
|
+
var ModelMessageHistory = class {
|
|
4
4
|
#modelHistory = [];
|
|
5
5
|
#onChange;
|
|
6
6
|
constructor(history, onChange) {
|
|
@@ -28,6 +28,6 @@ var AgentModelHistory = class {
|
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
//#endregion
|
|
31
|
-
export {
|
|
31
|
+
export { ModelMessageHistory };
|
|
32
32
|
|
|
33
33
|
//# sourceMappingURL=history.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history.js","names":["#modelHistory","#onChange","#triggerChange"],"sources":["../../src/session/history.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport {
|
|
1
|
+
{"version":3,"file":"history.js","names":["#modelHistory","#onChange","#triggerChange"],"sources":["../../src/session/history.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport type { UserInput } from \"./input\";\nimport { userInputToModelMessage } from \"./mapping\";\n\nexport class ModelMessageHistory {\n readonly #modelHistory: ModelMessage[] = [];\n readonly #onChange?: (snapshot: ModelMessage[]) => void;\n\n constructor(\n history?: ModelMessage[],\n onChange?: (snapshot: ModelMessage[]) => void\n ) {\n if (history) {\n this.#modelHistory = structuredClone(history);\n }\n this.#onChange = onChange;\n }\n\n modelSnapshot(): ModelMessage[] {\n return structuredClone(this.#modelHistory);\n }\n\n appendUserInput(input: UserInput): void {\n this.#modelHistory.push(userInputToModelMessage(input));\n this.#triggerChange();\n }\n\n appendModelMessage(message: ModelMessage): void {\n this.#modelHistory.push(structuredClone(message));\n this.#triggerChange();\n }\n\n rollback(snapshot: ModelMessage[]): void {\n this.#modelHistory.length = 0;\n this.#modelHistory.push(...structuredClone(snapshot));\n this.#triggerChange();\n }\n\n #triggerChange(): void {\n this.#onChange?.(this.modelSnapshot());\n }\n}\n"],"mappings":";;AAIA,IAAa,sBAAb,MAAiC;CAC/B,gBAAyC,CAAC;CAC1C;CAEA,YACE,SACA,UACA;EACA,IAAI,SACF,KAAKA,gBAAgB,gBAAgB,OAAO;EAE9C,KAAKC,YAAY;CACnB;CAEA,gBAAgC;EAC9B,OAAO,gBAAgB,KAAKD,aAAa;CAC3C;CAEA,gBAAgB,OAAwB;EACtC,KAAKA,cAAc,KAAK,wBAAwB,KAAK,CAAC;EACtD,KAAKE,eAAe;CACtB;CAEA,mBAAmB,SAA6B;EAC9C,KAAKF,cAAc,KAAK,gBAAgB,OAAO,CAAC;EAChD,KAAKE,eAAe;CACtB;CAEA,SAAS,UAAgC;EACvC,KAAKF,cAAc,SAAS;EAC5B,KAAKA,cAAc,KAAK,GAAG,gBAAgB,QAAQ,CAAC;EACpD,KAAKE,eAAe;CACtB;CAEA,iBAAuB;EACrB,KAAKD,YAAY,KAAK,cAAc,CAAC;CACvC;AACF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/session/input.d.ts
|
|
2
|
+
type UserTextContent = string | readonly string[];
|
|
3
|
+
interface UserText {
|
|
4
|
+
text: UserTextContent;
|
|
5
|
+
type: "user-text";
|
|
6
|
+
}
|
|
7
|
+
interface UserMessageTextPart {
|
|
8
|
+
text: string;
|
|
9
|
+
type: "text";
|
|
10
|
+
}
|
|
11
|
+
interface UserMessageImagePart {
|
|
12
|
+
image: string;
|
|
13
|
+
mediaType?: string;
|
|
14
|
+
type: "image";
|
|
15
|
+
}
|
|
16
|
+
type UserMessageFileData = string | {
|
|
17
|
+
data: string;
|
|
18
|
+
type: "data";
|
|
19
|
+
} | {
|
|
20
|
+
reference: Record<string, string>;
|
|
21
|
+
type: "reference";
|
|
22
|
+
} | {
|
|
23
|
+
text: string;
|
|
24
|
+
type: "text";
|
|
25
|
+
} | {
|
|
26
|
+
type: "url";
|
|
27
|
+
url: string;
|
|
28
|
+
};
|
|
29
|
+
interface UserMessageFilePart {
|
|
30
|
+
data: UserMessageFileData;
|
|
31
|
+
filename?: string;
|
|
32
|
+
mediaType: string;
|
|
33
|
+
type: "file";
|
|
34
|
+
}
|
|
35
|
+
type UserMessageContentPart = UserMessageFilePart | UserMessageImagePart | UserMessageTextPart;
|
|
36
|
+
type UserMessageContent = readonly UserMessageContentPart[];
|
|
37
|
+
interface UserMessage {
|
|
38
|
+
content: UserMessageContent;
|
|
39
|
+
type: "user-message";
|
|
40
|
+
}
|
|
41
|
+
type UserInput = UserMessage | UserText;
|
|
42
|
+
type AgentInput = readonly string[] | readonly UserMessageContentPart[] | string | UserInput;
|
|
43
|
+
type SessionInput = AgentInput;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { AgentInput, SessionInput, UserInput, UserMessage, UserMessageContent, UserMessageContentPart, UserMessageFileData, UserMessageFilePart, UserMessageImagePart, UserMessageTextPart, UserText, UserTextContent };
|
|
46
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mapping.js","names":[],"sources":["../../src/session/mapping.ts"],"sourcesContent":["import type {\n AssistantContent,\n AssistantModelMessage,\n ModelMessage,\n ToolModelMessage,\n UserModelMessage,\n} from \"ai\";\nimport type {\n AssistantReasoning,\n AssistantText,\n ToolCall,\n ToolResult,\n UserMessage,\n UserMessageContent,\n UserMessageContentPart,\n UserMessageFileData,\n UserText,\n UserTextContent,\n} from \"./events\";\nimport type { UserInput } from \"./
|
|
1
|
+
{"version":3,"file":"mapping.js","names":[],"sources":["../../src/session/mapping.ts"],"sourcesContent":["import type {\n AssistantContent,\n AssistantModelMessage,\n ModelMessage,\n ToolModelMessage,\n UserModelMessage,\n} from \"ai\";\nimport type {\n AssistantReasoning,\n AssistantText,\n ToolCall,\n ToolResult,\n UserMessage,\n UserMessageContent,\n UserMessageContentPart,\n UserMessageFileData,\n UserText,\n UserTextContent,\n} from \"./events\";\nimport type { UserInput } from \"./input\";\n\ntype AssistantContentPart = Exclude<AssistantContent, string>[number];\ntype ToolContentPart = ToolModelMessage[\"content\"][number];\ntype ModelEvent = AssistantReasoning | AssistantText | ToolCall | ToolResult;\n\n// UserInput -> AI SDK UserModelMessage\nexport function userInputToModelMessage(input: UserInput): UserModelMessage {\n if (input.type === \"user-message\") {\n return userMessageToModelMessage(input);\n }\n\n return userTextToModelMessage(input);\n}\n\nexport function userTextToModelMessage(input: UserText): UserModelMessage {\n return { role: \"user\", content: userTextContentToUserContent(input.text) };\n}\n\nfunction userTextContentToUserContent(\n text: UserTextContent\n): UserModelMessage[\"content\"] {\n if (typeof text === \"string\") {\n return text;\n }\n\n return text.map((part) => ({ type: \"text\", text: part }));\n}\n\nexport function userMessageToModelMessage(\n input: UserMessage\n): UserModelMessage {\n return {\n role: \"user\",\n content: userMessageContentToUserContent(input.content),\n };\n}\n\nfunction userMessageContentToUserContent(\n content: UserMessageContent\n): Exclude<UserModelMessage[\"content\"], string> {\n return content.map(userMessageContentPartToUserContentPart);\n}\n\nfunction userMessageContentPartToUserContentPart(\n part: UserMessageContentPart\n): Exclude<UserModelMessage[\"content\"], string>[number] {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n\n if (part.type === \"image\") {\n return {\n type: \"file\",\n data: part.image,\n mediaType: part.mediaType ?? \"image\",\n };\n }\n\n return {\n type: \"file\",\n data: userMessageFileDataToFileData(part.data),\n mediaType: part.mediaType,\n ...(part.filename === undefined ? {} : { filename: part.filename }),\n };\n}\n\nfunction userMessageFileDataToFileData(\n data: UserMessageFileData\n): Extract<\n Exclude<UserModelMessage[\"content\"], string>[number],\n { type: \"file\" }\n>[\"data\"] {\n if (typeof data === \"string\") {\n return data;\n }\n\n if (data.type === \"url\") {\n return data.url;\n }\n\n if (data.type === \"data\") {\n return { type: \"data\", data: data.data };\n }\n\n if (data.type === \"reference\") {\n return { type: \"reference\", reference: { ...data.reference } };\n }\n\n return { type: \"text\", text: data.text };\n}\n\n// AI SDK ModelMessage -> public agent events\nexport function modelMessageToAgentEvents(message: ModelMessage): ModelEvent[] {\n if (message.role === \"assistant\") {\n return assistantReasoningFirstParts(assistantContentParts(message)).flatMap(\n assistantContentPartToEvents\n );\n }\n\n if (message.role === \"tool\") {\n return message.content.flatMap(toolContentPartToEvents);\n }\n\n return [];\n}\n\nfunction assistantContentParts(\n message: AssistantModelMessage\n): AssistantContentPart[] {\n return typeof message.content === \"string\"\n ? [{ type: \"text\", text: message.content }]\n : message.content;\n}\n\nfunction assistantReasoningFirstParts(\n parts: AssistantContentPart[]\n): AssistantContentPart[] {\n return [\n ...parts.filter((part) => part.type === \"reasoning\"),\n ...parts.filter((part) => part.type !== \"reasoning\"),\n ];\n}\n\nfunction assistantContentPartToEvents(\n part: AssistantContentPart\n): ModelEvent[] {\n if (part.type === \"text\") {\n return part.text ? [{ type: \"assistant-text\", text: part.text }] : [];\n }\n\n if (part.type === \"reasoning\") {\n return part.text ? [{ type: \"assistant-reasoning\", text: part.text }] : [];\n }\n\n if (part.type === \"tool-call\") {\n return [\n {\n type: \"tool-call\",\n input: part.input,\n toolCallId: part.toolCallId,\n toolName: part.toolName,\n },\n ];\n }\n\n return [];\n}\n\nfunction toolContentPartToEvents(part: ToolContentPart): ModelEvent[] {\n if (part.type === \"tool-result\") {\n return toolResultPartToEvents(part);\n }\n\n return [];\n}\n\nfunction toolResultPartToEvents(part: {\n output: unknown;\n toolCallId: string;\n toolName: string;\n type: \"tool-result\";\n}): ModelEvent[] {\n return [\n {\n type: \"tool-result\",\n output: part.output,\n toolCallId: part.toolCallId,\n toolName: part.toolName,\n },\n ];\n}\n"],"mappings":";AA0BA,SAAgB,wBAAwB,OAAoC;CAC1E,IAAI,MAAM,SAAS,gBACjB,OAAO,0BAA0B,KAAK;CAGxC,OAAO,uBAAuB,KAAK;AACrC;AAEA,SAAgB,uBAAuB,OAAmC;CACxE,OAAO;EAAE,MAAM;EAAQ,SAAS,6BAA6B,MAAM,IAAI;CAAE;AAC3E;AAEA,SAAS,6BACP,MAC6B;CAC7B,IAAI,OAAO,SAAS,UAClB,OAAO;CAGT,OAAO,KAAK,KAAK,UAAU;EAAE,MAAM;EAAQ,MAAM;CAAK,EAAE;AAC1D;AAEA,SAAgB,0BACd,OACkB;CAClB,OAAO;EACL,MAAM;EACN,SAAS,gCAAgC,MAAM,OAAO;CACxD;AACF;AAEA,SAAS,gCACP,SAC8C;CAC9C,OAAO,QAAQ,IAAI,uCAAuC;AAC5D;AAEA,SAAS,wCACP,MACsD;CACtD,IAAI,KAAK,SAAS,QAChB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAK;CAGzC,IAAI,KAAK,SAAS,SAChB,OAAO;EACL,MAAM;EACN,MAAM,KAAK;EACX,WAAW,KAAK,aAAa;CAC/B;CAGF,OAAO;EACL,MAAM;EACN,MAAM,8BAA8B,KAAK,IAAI;EAC7C,WAAW,KAAK;EAChB,GAAI,KAAK,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,KAAK,SAAS;CACnE;AACF;AAEA,SAAS,8BACP,MAIQ;CACR,IAAI,OAAO,SAAS,UAClB,OAAO;CAGT,IAAI,KAAK,SAAS,OAChB,OAAO,KAAK;CAGd,IAAI,KAAK,SAAS,QAChB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAK;CAGzC,IAAI,KAAK,SAAS,aAChB,OAAO;EAAE,MAAM;EAAa,WAAW,EAAE,GAAG,KAAK,UAAU;CAAE;CAG/D,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAK;AACzC;AAGA,SAAgB,0BAA0B,SAAqC;CAC7E,IAAI,QAAQ,SAAS,aACnB,OAAO,6BAA6B,sBAAsB,OAAO,CAAC,EAAE,QAClE,4BACF;CAGF,IAAI,QAAQ,SAAS,QACnB,OAAO,QAAQ,QAAQ,QAAQ,uBAAuB;CAGxD,OAAO,CAAC;AACV;AAEA,SAAS,sBACP,SACwB;CACxB,OAAO,OAAO,QAAQ,YAAY,WAC9B,CAAC;EAAE,MAAM;EAAQ,MAAM,QAAQ;CAAQ,CAAC,IACxC,QAAQ;AACd;AAEA,SAAS,6BACP,OACwB;CACxB,OAAO,CACL,GAAG,MAAM,QAAQ,SAAS,KAAK,SAAS,WAAW,GACnD,GAAG,MAAM,QAAQ,SAAS,KAAK,SAAS,WAAW,CACrD;AACF;AAEA,SAAS,6BACP,MACc;CACd,IAAI,KAAK,SAAS,QAChB,OAAO,KAAK,OAAO,CAAC;EAAE,MAAM;EAAkB,MAAM,KAAK;CAAK,CAAC,IAAI,CAAC;CAGtE,IAAI,KAAK,SAAS,aAChB,OAAO,KAAK,OAAO,CAAC;EAAE,MAAM;EAAuB,MAAM,KAAK;CAAK,CAAC,IAAI,CAAC;CAG3E,IAAI,KAAK,SAAS,aAChB,OAAO,CACL;EACE,MAAM;EACN,OAAO,KAAK;EACZ,YAAY,KAAK;EACjB,UAAU,KAAK;CACjB,CACF;CAGF,OAAO,CAAC;AACV;AAEA,SAAS,wBAAwB,MAAqC;CACpE,IAAI,KAAK,SAAS,eAChB,OAAO,uBAAuB,IAAI;CAGpC,OAAO,CAAC;AACV;AAEA,SAAS,uBAAuB,MAKf;CACf,OAAO,CACL;EACE,MAAM;EACN,QAAQ,KAAK;EACb,YAAY,KAAK;EACjB,UAAU,KAAK;CACjB,CACF;AACF"}
|
package/dist/session/run.d.ts
CHANGED
package/dist/session/run.js
CHANGED
|
@@ -5,7 +5,7 @@ var BufferedAgentRun = class {
|
|
|
5
5
|
#error;
|
|
6
6
|
#pendingAck;
|
|
7
7
|
#resultPending = false;
|
|
8
|
-
#
|
|
8
|
+
#eventsStarted = false;
|
|
9
9
|
#waiter;
|
|
10
10
|
emit(event) {
|
|
11
11
|
if (this.#closed) return;
|
|
@@ -37,9 +37,9 @@ var BufferedAgentRun = class {
|
|
|
37
37
|
value: void 0
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
if (this.#
|
|
42
|
-
this.#
|
|
40
|
+
events() {
|
|
41
|
+
if (this.#eventsStarted) throw new Error("AgentRun.events() can only be consumed once");
|
|
42
|
+
this.#eventsStarted = true;
|
|
43
43
|
const iterator = {
|
|
44
44
|
next: () => this.#next(),
|
|
45
45
|
return: () => {
|
|
@@ -56,7 +56,7 @@ var BufferedAgentRun = class {
|
|
|
56
56
|
#cancel() {
|
|
57
57
|
this.#settleQueuedAcks();
|
|
58
58
|
this.#events.length = 0;
|
|
59
|
-
this.close(void 0, "
|
|
59
|
+
this.close(void 0, "events return");
|
|
60
60
|
}
|
|
61
61
|
#enqueue(event) {
|
|
62
62
|
const waiter = this.#waiter;
|
|
@@ -68,7 +68,7 @@ var BufferedAgentRun = class {
|
|
|
68
68
|
this.#events.push(event);
|
|
69
69
|
}
|
|
70
70
|
#next() {
|
|
71
|
-
if (this.#resultPending || this.#waiter) return Promise.reject(/* @__PURE__ */ new Error("AgentRun.
|
|
71
|
+
if (this.#resultPending || this.#waiter) return Promise.reject(/* @__PURE__ */ new Error("AgentRun.events() does not allow concurrent next() calls"));
|
|
72
72
|
this.#settlePendingAck();
|
|
73
73
|
const event = this.#events.shift();
|
|
74
74
|
if (event) return new Promise((resolve) => this.#deliver(resolve, event));
|
package/dist/session/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","names":["#events","#closed","#enqueue","#error","#settlePendingAck","#waiter","#
|
|
1
|
+
{"version":3,"file":"run.js","names":["#events","#closed","#enqueue","#error","#settlePendingAck","#waiter","#eventsStarted","#next","#cancel","#settleQueuedAcks","#deliver","#resultPending","#pendingAck"],"sources":["../../src/session/run.ts"],"sourcesContent":["import type { AgentEvent } from \"./events\";\n\nexport interface AgentRun {\n events(): 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 #eventsStarted = 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 events(): AsyncIterable<AgentEvent> {\n if (this.#eventsStarted) {\n throw new Error(\"AgentRun.events() can only be consumed once\");\n }\n this.#eventsStarted = 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, \"events 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.events() 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,9 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AgentRun } from "./run.js";
|
|
3
|
-
//#region src/session/session.d.ts
|
|
4
|
-
type UserInput = UserMessage | UserText;
|
|
5
|
-
type AgentInput = readonly string[] | readonly UserMessageContentPart[] | string | UserInput;
|
|
6
|
-
type SessionInput = AgentInput;
|
|
7
|
-
//#endregion
|
|
8
|
-
export { AgentInput, SessionInput, UserInput };
|
|
9
|
-
//# sourceMappingURL=session.d.ts.map
|
|
1
|
+
import { AgentInput, SessionInput, UserInput } from "./input.js";
|
|
2
|
+
import { AgentRun } from "./run.js";
|
package/dist/session/session.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { runAgentLoop } from "../agent-loop.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ModelMessageHistory } 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;
|
|
7
6
|
var AgentSession = class {
|
|
8
7
|
#hooks;
|
|
9
8
|
#inputQueue = [];
|
|
@@ -12,7 +11,7 @@ var AgentSession = class {
|
|
|
12
11
|
#activeAbort;
|
|
13
12
|
#activeRun;
|
|
14
13
|
#activeRuntimeInput;
|
|
15
|
-
#history = new
|
|
14
|
+
#history = new ModelMessageHistory();
|
|
16
15
|
#killed = false;
|
|
17
16
|
#loadPromise;
|
|
18
17
|
#loaded = false;
|
|
@@ -92,7 +91,7 @@ var AgentSession = class {
|
|
|
92
91
|
async #replaceWithStoredSession() {
|
|
93
92
|
const stored = await this.#persistence.store.load(this.#persistence.key);
|
|
94
93
|
this.#storeVersion = stored?.version;
|
|
95
|
-
this.#history = new
|
|
94
|
+
this.#history = new ModelMessageHistory(decodeStoredSessionSnapshot(stored));
|
|
96
95
|
}
|
|
97
96
|
async #drainInputQueue() {
|
|
98
97
|
if (this.#running) return;
|
|
@@ -134,7 +133,7 @@ var AgentSession = class {
|
|
|
134
133
|
});
|
|
135
134
|
const runtimeInputAdded = await this.#drainRuntimeInput(run, runtimeInput, event.type);
|
|
136
135
|
if (event.type === "step-end") return { runtimeInputAdded };
|
|
137
|
-
return
|
|
136
|
+
return;
|
|
138
137
|
}
|
|
139
138
|
run.emit(event);
|
|
140
139
|
},
|
|
@@ -208,10 +207,7 @@ var AgentSession = class {
|
|
|
208
207
|
}
|
|
209
208
|
}
|
|
210
209
|
async #commitHistory() {
|
|
211
|
-
const result = await this.#persistence.store.commit(this.#persistence.key, {
|
|
212
|
-
state: encodeSessionSnapshot(this.#history.modelSnapshot()),
|
|
213
|
-
version: this.#storeVersion
|
|
214
|
-
}, { expectedVersion: this.#storeVersion ?? null });
|
|
210
|
+
const result = await this.#persistence.store.commit(this.#persistence.key, { state: encodeSessionSnapshot(this.#history.modelSnapshot()) }, { expectedVersion: this.#storeVersion ?? null });
|
|
215
211
|
if (!result.ok) {
|
|
216
212
|
await this.#replaceWithStoredSession();
|
|
217
213
|
throw new SessionCommitConflictError(this.#persistence.key);
|
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
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} from \"./events\";\nimport { ModelMessageHistory } from \"./history\";\nimport type { AgentInput, UserInput } from \"./input\";\nimport type { AgentRun } from \"./run\";\nimport { BufferedAgentRun } from \"./run\";\nimport { decodeStoredSessionSnapshot, encodeSessionSnapshot } from \"./snapshot\";\nimport type { SessionStore } from \"./store/types\";\n\nexport type { AgentInput, SessionInput, UserInput } from \"./input\";\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 ModelMessageHistory();\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 ModelMessageHistory(\n decodeStoredSessionSnapshot(stored)\n );\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 },\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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.js","names":[],"sources":["../../src/session/snapshot.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"snapshot.js","names":[],"sources":["../../src/session/snapshot.ts"],"sourcesContent":["import type { ModelMessage } from \"ai\";\nimport type { StoredSession } from \"./store/types\";\n\nexport interface AgentSessionSnapshotV1 {\n readonly history: ModelMessage[];\n readonly schemaVersion: 1;\n}\n\nexport type AgentSessionSnapshot = AgentSessionSnapshotV1;\n\nexport function encodeSessionSnapshot(\n history: ModelMessage[]\n): AgentSessionSnapshot {\n return { schemaVersion: 1, history: structuredClone(history) };\n}\n\nexport function decodeStoredSessionSnapshot(\n stored: StoredSession | null\n): ModelMessage[] {\n if (!stored) {\n return [];\n }\n\n const snapshot = stored.state;\n if (isSessionSnapshotV1(snapshot)) {\n return structuredClone(snapshot.history);\n }\n\n throw new Error(\"Unsupported stored session state\");\n}\n\nfunction isSessionSnapshotV1(value: unknown): value is AgentSessionSnapshotV1 {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"schemaVersion\" in value &&\n value.schemaVersion === 1 &&\n \"history\" in value &&\n Array.isArray(value.history)\n );\n}\n"],"mappings":";AAUA,SAAgB,sBACd,SACsB;CACtB,OAAO;EAAE,eAAe;EAAG,SAAS,gBAAgB,OAAO;CAAE;AAC/D;AAEA,SAAgB,4BACd,QACgB;CAChB,IAAI,CAAC,QACH,OAAO,CAAC;CAGV,MAAM,WAAW,OAAO;CACxB,IAAI,oBAAoB,QAAQ,GAC9B,OAAO,gBAAgB,SAAS,OAAO;CAGzC,MAAM,IAAI,MAAM,kCAAkC;AACpD;AAEA,SAAS,oBAAoB,OAAiD;CAC5E,OACE,UAAU,QACV,OAAO,UAAU,YACjB,mBAAmB,SACnB,MAAM,kBAAkB,KACxB,aAAa,SACb,MAAM,QAAQ,MAAM,OAAO;AAE/B"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { CommitResult, SessionStore, StoredSession } from "./types.js";
|
|
1
|
+
import { CommitResult, SessionStore, SessionStoreCommit, StoredSession } from "./types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/session/store/file.d.ts
|
|
4
4
|
declare class FileSessionStore implements SessionStore {
|
|
5
5
|
#private;
|
|
6
6
|
constructor(directory: string);
|
|
7
7
|
load(key: string): Promise<StoredSession | null>;
|
|
8
|
-
commit(key: string, next:
|
|
9
|
-
expectedVersion
|
|
8
|
+
commit(key: string, next: SessionStoreCommit, options: {
|
|
9
|
+
expectedVersion: string | null;
|
|
10
10
|
}): Promise<CommitResult>;
|
|
11
11
|
}
|
|
12
12
|
//#endregion
|
|
@@ -29,7 +29,7 @@ var FileSessionStore = class {
|
|
|
29
29
|
try {
|
|
30
30
|
const current = await this.load(key);
|
|
31
31
|
const currentVersion = current?.version ?? null;
|
|
32
|
-
if (options
|
|
32
|
+
if (options.expectedVersion !== currentVersion) return {
|
|
33
33
|
ok: false,
|
|
34
34
|
reason: "conflict"
|
|
35
35
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file.js","names":["#directory","#fileForKey"],"sources":["../../../src/session/store/file.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { setTimeout } from \"node:timers/promises\";\nimport type {
|
|
1
|
+
{"version":3,"file":"file.js","names":["#directory","#fileForKey"],"sources":["../../../src/session/store/file.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { setTimeout } from \"node:timers/promises\";\nimport type {\n CommitResult,\n SessionStore,\n SessionStoreCommit,\n StoredSession,\n} from \"./types\";\n\nconst LOCK_POLL_INTERVAL_MS = 10;\nconst LOCK_STALE_AFTER_MS = 30_000;\nconst LOCK_TIMEOUT_MS = 5000;\n\nexport class FileSessionStore implements SessionStore {\n readonly #directory: string;\n\n constructor(directory: string) {\n this.#directory = directory;\n }\n\n async load(key: string): Promise<StoredSession | null> {\n const file = this.#fileForKey(key);\n\n try {\n const parsed = JSON.parse(await readFile(file, \"utf8\")) as unknown;\n return parseStoredFileSession(parsed, file);\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return null;\n }\n if (error instanceof SyntaxError) {\n throw new Error(\n `Invalid FileSessionStore file ${JSON.stringify(\n file\n )}: invalid JSON (${error.message})`\n );\n }\n throw error;\n }\n }\n\n async commit(\n key: string,\n next: SessionStoreCommit,\n options: { expectedVersion: string | null }\n ): Promise<CommitResult> {\n const file = this.#fileForKey(key);\n const lockDirectory = `${file}.lock`;\n await mkdir(dirname(file), { recursive: true });\n await acquireFileLock(lockDirectory);\n try {\n const current = await this.load(key);\n const currentVersion = current?.version ?? null;\n\n if (options.expectedVersion !== currentVersion) {\n return { ok: false, reason: \"conflict\" };\n }\n\n const version = String((Number(current?.version ?? \"0\") || 0) + 1);\n const payload: StoredSession = structuredClone({\n state: next.state,\n version,\n });\n const tempFile = `${file}.${process.pid}.${randomUUID()}.tmp`;\n\n try {\n await writeFile(\n tempFile,\n `${JSON.stringify(payload, null, 2)}\\n`,\n \"utf8\"\n );\n await rename(tempFile, file);\n } catch (error) {\n await rm(tempFile, { force: true }).catch(() => undefined);\n throw error;\n }\n\n return { ok: true, version };\n } finally {\n await rm(lockDirectory, { force: true, recursive: true });\n }\n }\n\n #fileForKey(key: string): string {\n return join(\n this.#directory,\n `${Buffer.from(key).toString(\"base64url\")}.json`\n );\n }\n}\n\nfunction parseStoredFileSession(value: unknown, file: string): StoredSession {\n if (value === null || typeof value !== \"object\") {\n throw new Error(\n `Invalid FileSessionStore file ${JSON.stringify(\n file\n )}: expected an object`\n );\n }\n\n const candidate = value as Partial<StoredSession>;\n if (typeof candidate.version !== \"string\" || !(\"state\" in candidate)) {\n throw new Error(\n `Invalid FileSessionStore file ${JSON.stringify(\n file\n )}: expected state and string version`\n );\n }\n\n return structuredClone({\n state: candidate.state,\n version: candidate.version,\n });\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n\nasync function acquireFileLock(lockDirectory: string): Promise<void> {\n const startedAt = Date.now();\n while (Date.now() - startedAt < LOCK_TIMEOUT_MS) {\n try {\n await mkdir(lockDirectory);\n return;\n } catch (error) {\n if (!(isNodeError(error) && error.code === \"EEXIST\")) {\n throw error;\n }\n await removeStaleLock(lockDirectory);\n }\n\n await setTimeout(LOCK_POLL_INTERVAL_MS);\n }\n\n throw new Error(\n `Timed out waiting for FileSessionStore lock ${JSON.stringify(\n lockDirectory\n )}`\n );\n}\n\nasync function removeStaleLock(lockDirectory: string): Promise<void> {\n try {\n const stats = await stat(lockDirectory);\n if (Date.now() - stats.mtimeMs < LOCK_STALE_AFTER_MS) {\n return;\n }\n await rm(lockDirectory, { force: true, recursive: true });\n } catch (error) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return;\n }\n throw error;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { CommitResult, SessionStore, StoredSession } from "./types.js";
|
|
1
|
+
import { CommitResult, SessionStore, SessionStoreCommit, StoredSession } from "./types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/session/store/memory.d.ts
|
|
4
4
|
declare class MemorySessionStore implements SessionStore {
|
|
5
5
|
#private;
|
|
6
6
|
load(key: string): Promise<StoredSession | null>;
|
|
7
|
-
commit(key: string, next:
|
|
8
|
-
expectedVersion
|
|
7
|
+
commit(key: string, next: SessionStoreCommit, options: {
|
|
8
|
+
expectedVersion: string | null;
|
|
9
9
|
}): Promise<CommitResult>;
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
@@ -8,7 +8,7 @@ var MemorySessionStore = class {
|
|
|
8
8
|
}
|
|
9
9
|
commit(key, next, options) {
|
|
10
10
|
const currentVersion = this.#sessions.get(key)?.version ?? null;
|
|
11
|
-
if (options
|
|
11
|
+
if (options.expectedVersion !== currentVersion) return Promise.resolve({
|
|
12
12
|
ok: false,
|
|
13
13
|
reason: "conflict"
|
|
14
14
|
});
|
|
@@ -16,7 +16,7 @@ var MemorySessionStore = class {
|
|
|
16
16
|
const version = String(versionNumber);
|
|
17
17
|
this.#versions.set(key, versionNumber);
|
|
18
18
|
this.#sessions.set(key, structuredClone({
|
|
19
|
-
|
|
19
|
+
state: next.state,
|
|
20
20
|
version
|
|
21
21
|
}));
|
|
22
22
|
return Promise.resolve({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","names":["#sessions","#versions"],"sources":["../../../src/session/store/memory.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"memory.js","names":["#sessions","#versions"],"sources":["../../../src/session/store/memory.ts"],"sourcesContent":["import type {\n CommitResult,\n SessionStore,\n SessionStoreCommit,\n StoredSession,\n} from \"./types\";\n\nexport class MemorySessionStore implements SessionStore {\n readonly #sessions = new Map<string, StoredSession>();\n readonly #versions = new Map<string, number>();\n\n load(key: string): Promise<StoredSession | null> {\n const stored = this.#sessions.get(key);\n return Promise.resolve(stored ? structuredClone(stored) : null);\n }\n\n commit(\n key: string,\n next: SessionStoreCommit,\n options: { expectedVersion: string | null }\n ): Promise<CommitResult> {\n const current = this.#sessions.get(key);\n const currentVersion = current?.version ?? null;\n\n if (options.expectedVersion !== currentVersion) {\n return Promise.resolve({ ok: false, reason: \"conflict\" });\n }\n\n const versionNumber = (this.#versions.get(key) ?? 0) + 1;\n const version = String(versionNumber);\n this.#versions.set(key, versionNumber);\n this.#sessions.set(key, structuredClone({ state: next.state, version }));\n return Promise.resolve({ ok: true, version });\n }\n}\n"],"mappings":";AAOA,IAAa,qBAAb,MAAwD;CACtD,4BAAqB,IAAI,IAA2B;CACpD,4BAAqB,IAAI,IAAoB;CAE7C,KAAK,KAA4C;EAC/C,MAAM,SAAS,KAAKA,UAAU,IAAI,GAAG;EACrC,OAAO,QAAQ,QAAQ,SAAS,gBAAgB,MAAM,IAAI,IAAI;CAChE;CAEA,OACE,KACA,MACA,SACuB;EAEvB,MAAM,iBADU,KAAKA,UAAU,IAAI,GACN,GAAG,WAAW;EAE3C,IAAI,QAAQ,oBAAoB,gBAC9B,OAAO,QAAQ,QAAQ;GAAE,IAAI;GAAO,QAAQ;EAAW,CAAC;EAG1D,MAAM,iBAAiB,KAAKC,UAAU,IAAI,GAAG,KAAK,KAAK;EACvD,MAAM,UAAU,OAAO,aAAa;EACpC,KAAKA,UAAU,IAAI,KAAK,aAAa;EACrC,KAAKD,UAAU,IAAI,KAAK,gBAAgB;GAAE,OAAO,KAAK;GAAO;EAAQ,CAAC,CAAC;EACvE,OAAO,QAAQ,QAAQ;GAAE,IAAI;GAAM;EAAQ,CAAC;CAC9C;AACF"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
//#region src/session/store/types.d.ts
|
|
2
2
|
interface StoredSession {
|
|
3
|
-
state: unknown;
|
|
4
|
-
version
|
|
3
|
+
readonly state: unknown;
|
|
4
|
+
readonly version: string;
|
|
5
|
+
}
|
|
6
|
+
interface SessionStoreCommit {
|
|
7
|
+
readonly state: unknown;
|
|
5
8
|
}
|
|
6
9
|
type CommitResult = {
|
|
7
10
|
ok: true;
|
|
@@ -12,11 +15,11 @@ type CommitResult = {
|
|
|
12
15
|
};
|
|
13
16
|
type ExpectedSessionVersion = string | null;
|
|
14
17
|
interface SessionStore {
|
|
15
|
-
commit(key: string, next:
|
|
16
|
-
expectedVersion
|
|
18
|
+
commit(key: string, next: SessionStoreCommit, options: {
|
|
19
|
+
expectedVersion: ExpectedSessionVersion;
|
|
17
20
|
}): Promise<CommitResult>;
|
|
18
21
|
load(key: string): Promise<StoredSession | null>;
|
|
19
22
|
}
|
|
20
23
|
//#endregion
|
|
21
|
-
export { CommitResult, ExpectedSessionVersion, SessionStore, StoredSession };
|
|
24
|
+
export { CommitResult, ExpectedSessionVersion, SessionStore, SessionStoreCommit, StoredSession };
|
|
22
25
|
//# sourceMappingURL=types.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minpeter/pss-runtime",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Generic agent runtime for sessions, model loops, and
|
|
3
|
+
"version": "0.0.10",
|
|
4
|
+
"description": "Generic agent runtime for sessions, model loops, and synchronized run events.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"tsdown": "^0.22.0",
|
|
43
|
-
"vitest": "^4.1.
|
|
43
|
+
"vitest": "^4.1.7"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsdown",
|
package/dist/agent-loop.d.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Llm } from "./llm.js";
|
|
2
|
-
import { AgentEvent } from "./session/events.js";
|
|
3
|
-
import { AgentHooks } from "./hooks.js";
|
|
4
|
-
import { ModelMessage } from "ai";
|
|
5
|
-
|
|
6
|
-
//#region src/agent-loop.d.ts
|
|
7
|
-
interface ModelHistory {
|
|
8
|
-
appendModelMessage(message: ModelMessage): void;
|
|
9
|
-
modelSnapshot(): ModelMessage[];
|
|
10
|
-
}
|
|
11
|
-
interface RunAgentLoopOptions {
|
|
12
|
-
emit: AgentLoopEventListener;
|
|
13
|
-
history: ModelHistory;
|
|
14
|
-
hooks?: AgentHooks;
|
|
15
|
-
llm: Llm;
|
|
16
|
-
signal?: AbortSignal;
|
|
17
|
-
}
|
|
18
|
-
type AgentLoopResult = "completed" | "aborted";
|
|
19
|
-
interface AgentLoopBoundaryDecision {
|
|
20
|
-
readonly runtimeInputAdded?: boolean;
|
|
21
|
-
}
|
|
22
|
-
type AgentLoopEventListener = (event: AgentEvent) => AgentLoopBoundaryDecision | Promise<AgentLoopBoundaryDecision | undefined> | undefined;
|
|
23
|
-
declare function runAgentLoop({
|
|
24
|
-
emit,
|
|
25
|
-
history,
|
|
26
|
-
hooks,
|
|
27
|
-
llm,
|
|
28
|
-
signal
|
|
29
|
-
}: RunAgentLoopOptions): Promise<AgentLoopResult>;
|
|
30
|
-
//#endregion
|
|
31
|
-
export { AgentLoopResult, runAgentLoop };
|
|
32
|
-
//# sourceMappingURL=agent-loop.d.ts.map
|