zidane 5.5.0 → 5.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-CvImMxMQ.d.ts → agent-CMAklak7.d.ts} +98 -3
- package/dist/agent-CMAklak7.d.ts.map +1 -0
- package/dist/chat.d.ts +283 -5
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +203 -3
- package/dist/chat.js.map +1 -0
- package/dist/{contexts-DhmMlT2W.js → contexts-BOtMvzli.js} +1 -1
- package/dist/{contexts-DhmMlT2W.js.map → contexts-BOtMvzli.js.map} +1 -1
- package/dist/contexts.js +1 -1
- package/dist/{errors-CDwtPIMX.js → errors-C5VSakmT.js} +1 -1
- package/dist/{errors-CDwtPIMX.js.map → errors-C5VSakmT.js.map} +1 -1
- package/dist/{index-B0uc2C5x.d.ts → index-CF5QwBiz.d.ts} +2 -2
- package/dist/{index-B0uc2C5x.d.ts.map → index-CF5QwBiz.d.ts.map} +1 -1
- package/dist/{index-CtXksgqb.d.ts → index-kroGomhj.d.ts} +2 -2
- package/dist/index-kroGomhj.d.ts.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +12 -12
- package/dist/{interpolate-BaaKaKzN.js → interpolate-Cvjy8gpk.js} +2 -2
- package/dist/{interpolate-BaaKaKzN.js.map → interpolate-Cvjy8gpk.js.map} +1 -1
- package/dist/{login-iTy-0wYz.js → login-B_kfoGMP.js} +4 -4
- package/dist/{login-iTy-0wYz.js.map → login-B_kfoGMP.js.map} +1 -1
- package/dist/{mcp-CNUbvbsy.js → mcp-BE43Viwi.js} +3 -3
- package/dist/{mcp-CNUbvbsy.js.map → mcp-BE43Viwi.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-fTR19Ga6.js → messages-BBWakTN6.js} +2 -2
- package/dist/{messages-fTR19Ga6.js.map → messages-BBWakTN6.js.map} +1 -1
- package/dist/{presets-h6UWhghO.js → presets-BDvBZuYI.js} +2 -2
- package/dist/{presets-h6UWhghO.js.map → presets-BDvBZuYI.js.map} +1 -1
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +1 -1
- package/dist/{providers-G0VBZK9j.js → providers-CsUyN_FJ.js} +3 -3
- package/dist/{providers-G0VBZK9j.js.map → providers-CsUyN_FJ.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +2 -2
- package/dist/restate.d.ts +148 -0
- package/dist/restate.d.ts.map +1 -0
- package/dist/restate.js +152 -0
- package/dist/restate.js.map +1 -0
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session/sqlite.js +1 -1
- package/dist/{session-CbkiJDlH.js → session-DzfRacU_.js} +2 -2
- package/dist/{session-CbkiJDlH.js.map → session-DzfRacU_.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +2 -2
- package/dist/skills.js +1 -1
- package/dist/{stats-DgOvY7wd.js → stats-Lc3zL3RM.js} +1 -1
- package/dist/{stats-DgOvY7wd.js.map → stats-Lc3zL3RM.js.map} +1 -1
- package/dist/{tools-D_icxa-V.js → tools-Bbd0Ivwn.js} +136 -73
- package/dist/tools-Bbd0Ivwn.js.map +1 -0
- package/dist/tools.d.ts +2 -2
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-3FFw2xuk.d.ts → transcript-anchors-C79AszkC.d.ts} +155 -66
- package/dist/transcript-anchors-C79AszkC.d.ts.map +1 -0
- package/dist/tui.d.ts +2 -2
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +37 -16
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CtgBlBHn.js → turn-operations-CGf7wWF0.js} +851 -16
- package/dist/turn-operations-CGf7wWF0.js.map +1 -0
- package/dist/{types-IcokUOyC.js → types-oKPBdCmL.js} +7 -2
- package/dist/types-oKPBdCmL.js.map +1 -0
- package/dist/types.d.ts +3 -3
- package/dist/types.js +4 -4
- package/docs/TUI.md +72 -1
- package/package.json +10 -1
- package/dist/agent-CvImMxMQ.d.ts.map +0 -1
- package/dist/index-CtXksgqb.d.ts.map +0 -1
- package/dist/tools-D_icxa-V.js.map +0 -1
- package/dist/transcript-anchors-3FFw2xuk.d.ts.map +0 -1
- package/dist/turn-operations-CtgBlBHn.js.map +0 -1
- package/dist/types-IcokUOyC.js.map +0 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { I as SessionStore, Pt as AgentClock, lt as StreamOptions, ot as Provider, y as ToolDef } from "./agent-CMAklak7.js";
|
|
2
|
+
|
|
3
|
+
//#region src/restate/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Minimal structural types mirroring the subset of `@restatedev/restate-sdk`
|
|
6
|
+
* the adapter touches. Kept in-house so:
|
|
7
|
+
*
|
|
8
|
+
* 1. `zidane/restate` typechecks **without** the SDK installed — useful in
|
|
9
|
+
* monorepos that vend `zidane` upstream of Restate.
|
|
10
|
+
* 2. The contract is explicit at the seam: a handful of methods, not the
|
|
11
|
+
* full SDK surface. Drift between SDK versions surfaces as a single
|
|
12
|
+
* type error here instead of cascading through wrapper internals.
|
|
13
|
+
*
|
|
14
|
+
* Consumers wiring the real SDK pass `restate.Context` / `restate.ObjectContext`
|
|
15
|
+
* directly — they're structurally assignable to the shapes below.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Subset of `restate.Context.run` used by the adapter.
|
|
19
|
+
* The SDK's full signature accepts a richer options bag; this captures
|
|
20
|
+
* what the adapter exercises.
|
|
21
|
+
*/
|
|
22
|
+
interface RestateRunOptions {
|
|
23
|
+
/** Max retry attempts before surfacing a terminal error. */
|
|
24
|
+
maxRetryAttempts?: number;
|
|
25
|
+
/** Retry policy passthrough. SDK-shaped object; opaque to the adapter. */
|
|
26
|
+
retryPolicy?: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Structural subset of `restate.Context` covering everything the
|
|
30
|
+
* adapter's pure wrappers need.
|
|
31
|
+
*
|
|
32
|
+
* `run` journals one side effect; on replay the journaled value returns
|
|
33
|
+
* without re-executing `fn`. `date.now()` is async because the SDK
|
|
34
|
+
* routes it through `ctx.run` internally — replay returns the journaled
|
|
35
|
+
* timestamp. `rand.uuidv4()` is sync because the SDK seeds an in-memory
|
|
36
|
+
* RNG from the invocation id, which is itself replay-stable.
|
|
37
|
+
*/
|
|
38
|
+
interface RestateContextLike {
|
|
39
|
+
run: <T>(name: string, fn: () => Promise<T> | T, options?: RestateRunOptions) => Promise<T>;
|
|
40
|
+
date: {
|
|
41
|
+
now: () => Promise<number>;
|
|
42
|
+
};
|
|
43
|
+
rand: {
|
|
44
|
+
uuidv4: () => string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Structural subset of `restate.ObjectContext` — adds keyed K/V state
|
|
49
|
+
* scoped to the virtual-object instance the handler is bound to.
|
|
50
|
+
*
|
|
51
|
+
* `get` returns `null` for missing keys (SDK contract). `set` returns
|
|
52
|
+
* void; the SDK journals the mutation transparently.
|
|
53
|
+
*/
|
|
54
|
+
interface RestateObjectContextLike extends RestateContextLike {
|
|
55
|
+
key: string;
|
|
56
|
+
get: <T>(name: string) => Promise<T | null>;
|
|
57
|
+
set: <T>(name: string, value: T) => void;
|
|
58
|
+
clear: (name: string) => void;
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/restate/clock.d.ts
|
|
62
|
+
/**
|
|
63
|
+
* Wrap a Restate context's `date.now()` + `rand.uuidv4()` into the
|
|
64
|
+
* {@link AgentClock} shape Zidane consumes for journaled metadata.
|
|
65
|
+
*
|
|
66
|
+
* The returned clock is a thin live view onto `ctx` — each call hits
|
|
67
|
+
* the SDK afresh. No caching, no closure-captured values. Restate's
|
|
68
|
+
* journal handles determinism on its end.
|
|
69
|
+
*/
|
|
70
|
+
declare function restateClock(ctx: RestateContextLike): AgentClock;
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/restate/provider.d.ts
|
|
73
|
+
interface RestateProviderOptions {
|
|
74
|
+
/**
|
|
75
|
+
* `ctx.run` options forwarded on every wrapped stream call. Defaults
|
|
76
|
+
* to `{ maxRetryAttempts: 3 }` — provider transients (rate limits,
|
|
77
|
+
* 5xx, connection resets) get retried inside the journal entry. Once
|
|
78
|
+
* the cap is reached the failure becomes terminal and surfaces
|
|
79
|
+
* through the loop's normal `AgentProviderError` path.
|
|
80
|
+
*/
|
|
81
|
+
runOptions?: RestateRunOptions;
|
|
82
|
+
/**
|
|
83
|
+
* Override the journal-entry name. Receives the 1-indexed sequence
|
|
84
|
+
* number for this wrapper instance and the `StreamOptions` of the
|
|
85
|
+
* call. Default: `llm-call-<n>`. Override to inject extra structure
|
|
86
|
+
* (e.g. `${agentName}/llm-call-${n}`) when multiple agents share a
|
|
87
|
+
* service.
|
|
88
|
+
*/
|
|
89
|
+
entryName?: (seq: number, opts: StreamOptions) => string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Wrap a Zidane `Provider` so its `stream()` method journals each turn's
|
|
93
|
+
* response inside `ctx.run`. All other Provider methods (`formatTools`,
|
|
94
|
+
* `userMessage`, `toolResultsMessage`, `classifyError`, …) pass through
|
|
95
|
+
* unchanged — they're pure functions over already-deterministic inputs.
|
|
96
|
+
*/
|
|
97
|
+
declare function restateProvider(inner: Provider, ctx: RestateContextLike, options?: RestateProviderOptions): Provider;
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/restate/session.d.ts
|
|
100
|
+
/**
|
|
101
|
+
* Construct a `SessionStore` backed by the bound virtual-object's K/V
|
|
102
|
+
* state. The store is single-tenant — every method ignores the
|
|
103
|
+
* `sessionId` argument because the virtual object is already keyed by
|
|
104
|
+
* `ctx.key`. Calling `appendTurns('other-id', …)` writes to the
|
|
105
|
+
* CURRENT object's state, not the one named in the argument. Bind one
|
|
106
|
+
* virtual-object key per session and the contract holds.
|
|
107
|
+
*/
|
|
108
|
+
declare function restateSessionStore(ctx: RestateObjectContextLike): SessionStore;
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/restate/tool.d.ts
|
|
111
|
+
interface RestateToolOptions {
|
|
112
|
+
/**
|
|
113
|
+
* `ctx.run` options forwarded on every wrapped tool call. Defaults to
|
|
114
|
+
* `{ maxRetryAttempts: 1 }` — tool errors are typically deterministic
|
|
115
|
+
* (validation failures, missing files) so blind retries waste budget.
|
|
116
|
+
* Override per-tool for network-heavy custom tools that benefit from
|
|
117
|
+
* SDK-level retries.
|
|
118
|
+
*/
|
|
119
|
+
runOptions?: RestateRunOptions;
|
|
120
|
+
/**
|
|
121
|
+
* Override the journal-entry name. Receives the canonical tool name
|
|
122
|
+
* and the call's input. Default: `tool-<name>-<seq>` where `<seq>` is
|
|
123
|
+
* a per-wrapper monotonic counter. Override to inject the model's
|
|
124
|
+
* `callId` (read it off `ToolContext.callId` inside `execute` if you
|
|
125
|
+
* need an LLM-visible key — but the default counter is sufficient
|
|
126
|
+
* for journal correctness).
|
|
127
|
+
*/
|
|
128
|
+
entryName?: (seq: number, name: string, input: Record<string, unknown>) => string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Wrap a single `ToolDef` so its `execute` runs inside `ctx.run`. The
|
|
132
|
+
* tool's `spec` and `isConcurrencySafe` pass through unchanged.
|
|
133
|
+
*/
|
|
134
|
+
declare function restateTool(inner: ToolDef, ctx: RestateContextLike, options?: RestateToolOptions): ToolDef;
|
|
135
|
+
/**
|
|
136
|
+
* Wrap an entire tool map at once — convenience for the common case of
|
|
137
|
+
* "journal every tool the agent has". Pass-through for tools whose
|
|
138
|
+
* names are listed in `opts.exclude` (e.g. fully-deterministic local
|
|
139
|
+
* helpers where journaling is pure overhead).
|
|
140
|
+
*
|
|
141
|
+
* The returned map is a fresh object; the input is untouched.
|
|
142
|
+
*/
|
|
143
|
+
declare function wrapAgentTools(tools: Record<string, ToolDef>, ctx: RestateContextLike, opts?: RestateToolOptions & {
|
|
144
|
+
exclude?: readonly string[];
|
|
145
|
+
}): Record<string, ToolDef>;
|
|
146
|
+
//#endregion
|
|
147
|
+
export { type RestateContextLike, type RestateObjectContextLike, type RestateProviderOptions, type RestateRunOptions, type RestateToolOptions, restateClock, restateProvider, restateSessionStore, restateTool, wrapAgentTools };
|
|
148
|
+
//# sourceMappingURL=restate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restate.d.ts","names":[],"sources":["../src/restate/types.ts","../src/restate/clock.ts","../src/restate/provider.ts","../src/restate/session.ts","../src/restate/tool.ts"],"mappings":";;;;;;AAmBA;;;;;AAiBA;;;;;;;;;;UAjBiB,iBAAA;EAuBW;EArB1B,gBAAA;EAgBM;EAdN,WAAA;AAAA;;;;;;;;;;;UAae,kBAAA;EACf,GAAA,MACE,IAAA,UACA,EAAA,QAAU,OAAA,CAAQ,CAAA,IAAK,CAAA,EACvB,OAAA,GAAU,iBAAA,KACP,OAAA,CAAQ,CAAA;EACb,IAAA;IAAQ,GAAA,QAAW,OAAA;EAAA;EACnB,IAAA;IAAQ,MAAA;EAAA;AAAA;;;;;;;;UAUO,wBAAA,SAAiC,kBAAA;EAChD,GAAA;EACA,GAAA,MAAS,IAAA,aAAiB,OAAA,CAAQ,CAAA;EAClC,GAAA,MAAS,IAAA,UAAc,KAAA,EAAO,CAAA;EAC9B,KAAA,GAAQ,IAAA;AAAA;;;;;;;;;;;iBCnCM,YAAA,CAAa,GAAA,EAAK,kBAAA,GAAqB,UAAA;;;UCGtC,sBAAA;EFcK;;;;;;;EENpB,UAAA,GAAa,iBAAA;EFSL;;;;;;AAWV;EEZE,SAAA,IAAa,GAAA,UAAa,IAAA,EAAM,aAAA;AAAA;;;;;;;iBASlB,eAAA,CACd,KAAA,EAAO,QAAA,EACP,GAAA,EAAK,kBAAA,EACL,OAAA,GAAS,sBAAA,GACR,QAAA;;;;;;;;;;;iBCKa,mBAAA,CAAoB,GAAA,EAAK,wBAAA,GAA2B,YAAA;;;UC/BnD,kBAAA;EJYH;;;;;;;EIJZ,UAAA,GAAa,iBAAA;EJOL;;;AAUV;;;;;EIRE,SAAA,IAAa,GAAA,UAAa,IAAA,UAAc,KAAA,EAAO,MAAA;AAAA;;;;;iBAOjC,WAAA,CACd,KAAA,EAAO,OAAA,EACP,GAAA,EAAK,kBAAA,EACL,OAAA,GAAS,kBAAA,GACR,OAAA;;;;;;;;;iBA2Ba,cAAA,CACd,KAAA,EAAO,MAAA,SAAe,OAAA,GACtB,GAAA,EAAK,kBAAA,EACL,IAAA,GAAM,kBAAA;EAAuB,OAAA;AAAA,IAC5B,MAAA,SAAe,OAAA"}
|
package/dist/restate.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
//#region src/restate/clock.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wrap a Restate context's `date.now()` + `rand.uuidv4()` into the
|
|
4
|
+
* {@link AgentClock} shape Zidane consumes for journaled metadata.
|
|
5
|
+
*
|
|
6
|
+
* The returned clock is a thin live view onto `ctx` — each call hits
|
|
7
|
+
* the SDK afresh. No caching, no closure-captured values. Restate's
|
|
8
|
+
* journal handles determinism on its end.
|
|
9
|
+
*/
|
|
10
|
+
function restateClock(ctx) {
|
|
11
|
+
return {
|
|
12
|
+
now: () => ctx.date.now(),
|
|
13
|
+
randomUUID: () => ctx.rand.uuidv4()
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/restate/provider.ts
|
|
18
|
+
/**
|
|
19
|
+
* Wrap a Zidane `Provider` so its `stream()` method journals each turn's
|
|
20
|
+
* response inside `ctx.run`. All other Provider methods (`formatTools`,
|
|
21
|
+
* `userMessage`, `toolResultsMessage`, `classifyError`, …) pass through
|
|
22
|
+
* unchanged — they're pure functions over already-deterministic inputs.
|
|
23
|
+
*/
|
|
24
|
+
function restateProvider(inner, ctx, options = {}) {
|
|
25
|
+
const runOpts = options.runOptions ?? { maxRetryAttempts: 3 };
|
|
26
|
+
const nameFor = options.entryName ?? ((seq) => `llm-call-${seq}`);
|
|
27
|
+
let seq = 0;
|
|
28
|
+
return {
|
|
29
|
+
...inner,
|
|
30
|
+
stream(streamOpts, callbacks) {
|
|
31
|
+
seq += 1;
|
|
32
|
+
return ctx.run(nameFor(seq, streamOpts), () => inner.stream(streamOpts, callbacks), runOpts);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/restate/session.ts
|
|
38
|
+
const KEY_DATA = "session-data";
|
|
39
|
+
const KEY_TURNS = "session-turns";
|
|
40
|
+
const KEY_RUNS = "session-runs";
|
|
41
|
+
/**
|
|
42
|
+
* Construct a `SessionStore` backed by the bound virtual-object's K/V
|
|
43
|
+
* state. The store is single-tenant — every method ignores the
|
|
44
|
+
* `sessionId` argument because the virtual object is already keyed by
|
|
45
|
+
* `ctx.key`. Calling `appendTurns('other-id', …)` writes to the
|
|
46
|
+
* CURRENT object's state, not the one named in the argument. Bind one
|
|
47
|
+
* virtual-object key per session and the contract holds.
|
|
48
|
+
*/
|
|
49
|
+
function restateSessionStore(ctx) {
|
|
50
|
+
return {
|
|
51
|
+
/**
|
|
52
|
+
* Turn-id mint backed by Restate's seeded RNG. Critical: without
|
|
53
|
+
* this, `Session.generateTurnId` falls through to
|
|
54
|
+
* `crypto.randomUUID()` (see `src/session/index.ts`), so every
|
|
55
|
+
* assistant / tool-results turn id drifts across replays even
|
|
56
|
+
* though `AgentClock.randomUUID` is wired correctly — the loop
|
|
57
|
+
* calls `session?.generateTurnId() ?? clock.randomUUID()`, and a
|
|
58
|
+
* session that always returns a value short-circuits the fallback.
|
|
59
|
+
*/
|
|
60
|
+
generateTurnId: () => ctx.rand.uuidv4(),
|
|
61
|
+
async load(_sessionId) {
|
|
62
|
+
const header = await ctx.get(KEY_DATA);
|
|
63
|
+
if (!header) return null;
|
|
64
|
+
const turns = await ctx.get(KEY_TURNS) ?? [];
|
|
65
|
+
const runs = await ctx.get(KEY_RUNS) ?? [];
|
|
66
|
+
return {
|
|
67
|
+
...header,
|
|
68
|
+
turns,
|
|
69
|
+
runs
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
async save(session) {
|
|
73
|
+
const { turns, runs, ...header } = session;
|
|
74
|
+
ctx.set(KEY_DATA, header);
|
|
75
|
+
ctx.set(KEY_TURNS, turns);
|
|
76
|
+
ctx.set(KEY_RUNS, runs);
|
|
77
|
+
},
|
|
78
|
+
async delete(_sessionId) {
|
|
79
|
+
ctx.clear(KEY_DATA);
|
|
80
|
+
ctx.clear(KEY_TURNS);
|
|
81
|
+
ctx.clear(KEY_RUNS);
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* Always empty — virtual objects don't support cross-key
|
|
85
|
+
* enumeration. Maintain a separate index handler if discovery
|
|
86
|
+
* matters.
|
|
87
|
+
*/
|
|
88
|
+
async list() {
|
|
89
|
+
return [];
|
|
90
|
+
},
|
|
91
|
+
async appendTurns(_sessionId, turns) {
|
|
92
|
+
const existing = await ctx.get(KEY_TURNS) ?? [];
|
|
93
|
+
ctx.set(KEY_TURNS, [...existing, ...turns]);
|
|
94
|
+
},
|
|
95
|
+
async getTurns(_sessionId, from = 0, limit) {
|
|
96
|
+
return (await ctx.get(KEY_TURNS) ?? []).slice(from, limit !== void 0 ? from + limit : void 0);
|
|
97
|
+
},
|
|
98
|
+
async updateRun(_sessionId, run) {
|
|
99
|
+
const runs = await ctx.get(KEY_RUNS) ?? [];
|
|
100
|
+
const idx = runs.findIndex((r) => r.id === run.id);
|
|
101
|
+
const next = [...runs];
|
|
102
|
+
if (idx >= 0) next[idx] = run;
|
|
103
|
+
else next.push(run);
|
|
104
|
+
ctx.set(KEY_RUNS, next);
|
|
105
|
+
},
|
|
106
|
+
async updateStatus(_sessionId, status) {
|
|
107
|
+
const header = await ctx.get(KEY_DATA);
|
|
108
|
+
if (header) ctx.set(KEY_DATA, {
|
|
109
|
+
...header,
|
|
110
|
+
status,
|
|
111
|
+
updatedAt: await ctx.date.now()
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/restate/tool.ts
|
|
118
|
+
/**
|
|
119
|
+
* Wrap a single `ToolDef` so its `execute` runs inside `ctx.run`. The
|
|
120
|
+
* tool's `spec` and `isConcurrencySafe` pass through unchanged.
|
|
121
|
+
*/
|
|
122
|
+
function restateTool(inner, ctx, options = {}) {
|
|
123
|
+
const runOpts = options.runOptions ?? { maxRetryAttempts: 1 };
|
|
124
|
+
const nameFor = options.entryName ?? ((seq, name) => `tool-${name}-${seq}`);
|
|
125
|
+
const canonicalName = inner.spec.name;
|
|
126
|
+
let seq = 0;
|
|
127
|
+
return {
|
|
128
|
+
...inner,
|
|
129
|
+
execute: (input, toolCtx) => {
|
|
130
|
+
seq += 1;
|
|
131
|
+
return ctx.run(nameFor(seq, canonicalName, input), () => inner.execute(input, toolCtx), runOpts);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Wrap an entire tool map at once — convenience for the common case of
|
|
137
|
+
* "journal every tool the agent has". Pass-through for tools whose
|
|
138
|
+
* names are listed in `opts.exclude` (e.g. fully-deterministic local
|
|
139
|
+
* helpers where journaling is pure overhead).
|
|
140
|
+
*
|
|
141
|
+
* The returned map is a fresh object; the input is untouched.
|
|
142
|
+
*/
|
|
143
|
+
function wrapAgentTools(tools, ctx, opts = {}) {
|
|
144
|
+
const excludeSet = new Set(opts.exclude ?? []);
|
|
145
|
+
const wrapped = {};
|
|
146
|
+
for (const [name, def] of Object.entries(tools)) wrapped[name] = excludeSet.has(name) ? def : restateTool(def, ctx, opts);
|
|
147
|
+
return wrapped;
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { restateClock, restateProvider, restateSessionStore, restateTool, wrapAgentTools };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=restate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restate.js","names":[],"sources":["../src/restate/clock.ts","../src/restate/provider.ts","../src/restate/session.ts","../src/restate/tool.ts"],"sourcesContent":["/**\n * Restate-backed {@link AgentClock} — journals `now()` + `randomUUID()` so\n * every `SessionTurn.id` / `SessionTurn.createdAt` / `runId` / synthetic\n * turn id stays byte-identical across replays of the same invocation.\n *\n * Pass to `agent.run({ clock: restateClock(ctx) })` or bake it into\n * `createAgent({ clock: restateClock(ctx) })` for the duration of a\n * handler invocation. The clock holds a reference to `ctx`; do not reuse\n * a clock instance across invocations.\n */\n\nimport type { AgentClock } from '../types'\nimport type { RestateContextLike } from './types'\n\n/**\n * Wrap a Restate context's `date.now()` + `rand.uuidv4()` into the\n * {@link AgentClock} shape Zidane consumes for journaled metadata.\n *\n * The returned clock is a thin live view onto `ctx` — each call hits\n * the SDK afresh. No caching, no closure-captured values. Restate's\n * journal handles determinism on its end.\n */\nexport function restateClock(ctx: RestateContextLike): AgentClock {\n return {\n now: () => ctx.date.now(),\n randomUUID: () => ctx.rand.uuidv4(),\n }\n}\n","/**\n * Restate-backed Provider wrapper.\n *\n * Wraps `Provider.stream` so every assistant turn (the LLM call as a\n * whole) is journaled. The model is invoked once per turn — the live\n * stream still fires `onText` / `onThinking` callbacks for UX during the\n * original execution. On replay, `ctx.run` returns the journaled\n * `TurnResult` immediately and the inner provider is NEVER re-invoked →\n * no duplicate billed tokens, no duplicate stream chunks.\n *\n * Replay caveat: stream callbacks don't re-fire on replay. Consumers\n * that paint live UI from `stream:text` should reconstruct transcripts\n * from `session.turns` via `eventsFromTurns` (zidane/chat) instead of\n * relying on hook re-emission. The chat layer already does this.\n *\n * Journal-entry naming: a monotonic per-wrapper counter (`llm-call-1`,\n * `llm-call-2`, …). One wrapper instance per Restate handler invocation\n * resets the counter; Restate's journal is invocation-scoped so names\n * don't need to be globally unique. The counter ticks the same way on\n * replay because the wrapper is constructed fresh each attempt.\n */\n\nimport type { Provider, StreamCallbacks, StreamOptions, TurnResult } from '../providers'\nimport type { RestateContextLike, RestateRunOptions } from './types'\n\nexport interface RestateProviderOptions {\n /**\n * `ctx.run` options forwarded on every wrapped stream call. Defaults\n * to `{ maxRetryAttempts: 3 }` — provider transients (rate limits,\n * 5xx, connection resets) get retried inside the journal entry. Once\n * the cap is reached the failure becomes terminal and surfaces\n * through the loop's normal `AgentProviderError` path.\n */\n runOptions?: RestateRunOptions\n /**\n * Override the journal-entry name. Receives the 1-indexed sequence\n * number for this wrapper instance and the `StreamOptions` of the\n * call. Default: `llm-call-<n>`. Override to inject extra structure\n * (e.g. `${agentName}/llm-call-${n}`) when multiple agents share a\n * service.\n */\n entryName?: (seq: number, opts: StreamOptions) => string\n}\n\n/**\n * Wrap a Zidane `Provider` so its `stream()` method journals each turn's\n * response inside `ctx.run`. All other Provider methods (`formatTools`,\n * `userMessage`, `toolResultsMessage`, `classifyError`, …) pass through\n * unchanged — they're pure functions over already-deterministic inputs.\n */\nexport function restateProvider(\n inner: Provider,\n ctx: RestateContextLike,\n options: RestateProviderOptions = {},\n): Provider {\n const runOpts: RestateRunOptions = options.runOptions ?? { maxRetryAttempts: 3 }\n const nameFor = options.entryName ?? ((seq: number) => `llm-call-${seq}`)\n let seq = 0\n\n return {\n ...inner,\n stream(streamOpts: StreamOptions, callbacks: StreamCallbacks): Promise<TurnResult> {\n seq += 1\n return ctx.run<TurnResult>(\n nameFor(seq, streamOpts),\n () => inner.stream(streamOpts, callbacks),\n runOpts,\n )\n },\n }\n}\n","/**\n * Restate-backed `SessionStore` — turn / run state lives in the bound\n * virtual object's K/V state instead of SQLite / file / remote HTTP.\n *\n * Trade-offs vs. journal-only replay\n * ----------------------------------\n * If you only care about durable execution within ONE invocation,\n * skip this — the journaled `ctx.run` entries (LLM calls, tool\n * executions) are enough to replay the loop to byte-identical state\n * after a crash. The in-memory `createMemoryStore()` is the right\n * choice; turns rematerialize from the journal each replay.\n *\n * Pick this store when EXTERNAL consumers (TUI, web GUI, support\n * tooling) need to read a session's history WITHOUT re-entering the\n * agent handler. Virtual-object state is exposed via the SDK's read\n * APIs (`ctx.get` from another handler, `restate-cli`, the Restate\n * web UI) — same data, different access path. The journal is\n * invocation-local; this state is the externally-visible projection\n * of it.\n *\n * Storage layout\n * --------------\n * One virtual-object key per session, three K/V slots:\n *\n * `data` → `SessionData` minus the `turns` array (small header)\n * `turns` → `SessionTurn[]` (the conversation history)\n * `runs` → `SessionRun[]` (run lifecycle records)\n *\n * Splitting `turns` / `runs` out keeps the header light for `load()` —\n * a handler that only needs metadata doesn't pull the full history.\n *\n * Listing / discovery\n * -------------------\n * `list()` returns `[]` and `delete()` no-ops — virtual objects don't\n * expose cross-key enumeration. Use a separate index handler if you\n * need it (one virtual object per `projectRoot` that maintains an\n * indexed list of session ids; this store doesn't own that concern).\n * `generateSessionId` is omitted so the agent falls back to its\n * default UUID generator — for a session bound 1:1 to a virtual-\n * object key, pass `id: ctx.key` to `createSession()` instead and\n * skip ID generation entirely.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '../session'\nimport type { SessionTurn } from '../types'\nimport type { RestateObjectContextLike } from './types'\n\nconst KEY_DATA = 'session-data'\nconst KEY_TURNS = 'session-turns'\nconst KEY_RUNS = 'session-runs'\n\n/**\n * Construct a `SessionStore` backed by the bound virtual-object's K/V\n * state. The store is single-tenant — every method ignores the\n * `sessionId` argument because the virtual object is already keyed by\n * `ctx.key`. Calling `appendTurns('other-id', …)` writes to the\n * CURRENT object's state, not the one named in the argument. Bind one\n * virtual-object key per session and the contract holds.\n */\nexport function restateSessionStore(ctx: RestateObjectContextLike): SessionStore {\n return {\n /**\n * Turn-id mint backed by Restate's seeded RNG. Critical: without\n * this, `Session.generateTurnId` falls through to\n * `crypto.randomUUID()` (see `src/session/index.ts`), so every\n * assistant / tool-results turn id drifts across replays even\n * though `AgentClock.randomUUID` is wired correctly — the loop\n * calls `session?.generateTurnId() ?? clock.randomUUID()`, and a\n * session that always returns a value short-circuits the fallback.\n */\n generateTurnId: () => ctx.rand.uuidv4(),\n\n async load(_sessionId: string): Promise<SessionData | null> {\n const header = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n if (!header)\n return null\n const turns = (await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? []\n const runs = (await ctx.get<SessionRun[]>(KEY_RUNS)) ?? []\n return { ...header, turns, runs }\n },\n\n async save(session: SessionData): Promise<void> {\n const { turns, runs, ...header } = session\n ctx.set(KEY_DATA, header)\n ctx.set(KEY_TURNS, turns)\n ctx.set(KEY_RUNS, runs)\n },\n\n async delete(_sessionId: string): Promise<void> {\n ctx.clear(KEY_DATA)\n ctx.clear(KEY_TURNS)\n ctx.clear(KEY_RUNS)\n },\n\n /**\n * Always empty — virtual objects don't support cross-key\n * enumeration. Maintain a separate index handler if discovery\n * matters.\n */\n async list(): Promise<string[]> {\n return []\n },\n\n async appendTurns(_sessionId: string, turns: SessionTurn[]): Promise<void> {\n const existing = (await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? []\n ctx.set(KEY_TURNS, [...existing, ...turns])\n },\n\n async getTurns(_sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n const turns = (await ctx.get<SessionTurn[]>(KEY_TURNS)) ?? []\n return turns.slice(from, limit !== undefined ? from + limit : undefined)\n },\n\n async updateRun(_sessionId: string, run: SessionRun): Promise<void> {\n const runs = (await ctx.get<SessionRun[]>(KEY_RUNS)) ?? []\n const idx = runs.findIndex(r => r.id === run.id)\n const next = [...runs]\n if (idx >= 0)\n next[idx] = run\n else\n next.push(run)\n ctx.set(KEY_RUNS, next)\n },\n\n async updateStatus(_sessionId: string, status: SessionData['status']): Promise<void> {\n const header = await ctx.get<Omit<SessionData, 'turns' | 'runs'>>(KEY_DATA)\n if (header) {\n ctx.set(KEY_DATA, { ...header, status, updatedAt: await ctx.date.now() })\n }\n },\n }\n}\n","/**\n * Restate-backed tool wrappers.\n *\n * Wraps `ToolDef.execute` so every tool invocation is journaled. The\n * tool body runs once on the original execution; on replay `ctx.run`\n * returns the journaled result without re-executing. Side effects\n * (`write_file` to disk, network POSTs in custom tools) happen\n * **exactly once** across crashes + retries — the entire reason for\n * pairing Zidane with Restate.\n *\n * `tool:gate`, `tool:transform`, `tool:after` hooks fire OUTSIDE the\n * journaled boundary, so they re-execute on replay. Built-in handlers\n * are deterministic given the journaled input + output (truncation,\n * image-stripping, `<edit-outcomes>` merge — all pure). Consumer hooks\n * that hit external services should themselves wrap side effects in\n * `ctx.run` so the substitution / annotation is replay-stable.\n *\n * `isConcurrencySafe` is preserved — Restate's per-key serialization\n * doesn't preclude in-handler parallelism. The loop's scheduler still\n * fans out safe tools up to `behavior.maxConcurrentTools`, each call\n * lands in its own journal entry. Order-of-journal-completion does NOT\n * affect replay correctness: Restate journals by call-site name, not\n * by completion order.\n */\n\nimport type { ToolDef } from '../tools/types'\nimport type { RestateContextLike, RestateRunOptions } from './types'\n\nexport interface RestateToolOptions {\n /**\n * `ctx.run` options forwarded on every wrapped tool call. Defaults to\n * `{ maxRetryAttempts: 1 }` — tool errors are typically deterministic\n * (validation failures, missing files) so blind retries waste budget.\n * Override per-tool for network-heavy custom tools that benefit from\n * SDK-level retries.\n */\n runOptions?: RestateRunOptions\n /**\n * Override the journal-entry name. Receives the canonical tool name\n * and the call's input. Default: `tool-<name>-<seq>` where `<seq>` is\n * a per-wrapper monotonic counter. Override to inject the model's\n * `callId` (read it off `ToolContext.callId` inside `execute` if you\n * need an LLM-visible key — but the default counter is sufficient\n * for journal correctness).\n */\n entryName?: (seq: number, name: string, input: Record<string, unknown>) => string\n}\n\n/**\n * Wrap a single `ToolDef` so its `execute` runs inside `ctx.run`. The\n * tool's `spec` and `isConcurrencySafe` pass through unchanged.\n */\nexport function restateTool(\n inner: ToolDef,\n ctx: RestateContextLike,\n options: RestateToolOptions = {},\n): ToolDef {\n const runOpts: RestateRunOptions = options.runOptions ?? { maxRetryAttempts: 1 }\n const nameFor = options.entryName ?? ((seq: number, name: string) => `tool-${name}-${seq}`)\n const canonicalName = inner.spec.name\n let seq = 0\n\n return {\n ...inner,\n execute: (input, toolCtx) => {\n seq += 1\n return ctx.run(\n nameFor(seq, canonicalName, input),\n () => inner.execute(input, toolCtx),\n runOpts,\n )\n },\n }\n}\n\n/**\n * Wrap an entire tool map at once — convenience for the common case of\n * \"journal every tool the agent has\". Pass-through for tools whose\n * names are listed in `opts.exclude` (e.g. fully-deterministic local\n * helpers where journaling is pure overhead).\n *\n * The returned map is a fresh object; the input is untouched.\n */\nexport function wrapAgentTools(\n tools: Record<string, ToolDef>,\n ctx: RestateContextLike,\n opts: RestateToolOptions & { exclude?: readonly string[] } = {},\n): Record<string, ToolDef> {\n const excludeSet = new Set(opts.exclude ?? [])\n const wrapped: Record<string, ToolDef> = {}\n for (const [name, def] of Object.entries(tools)) {\n wrapped[name] = excludeSet.has(name) ? def : restateTool(def, ctx, opts)\n }\n return wrapped\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,aAAa,KAAqC;CAChE,OAAO;EACL,WAAW,IAAI,KAAK,KAAK;EACzB,kBAAkB,IAAI,KAAK,QAAQ;EACpC;;;;;;;;;;ACwBH,SAAgB,gBACd,OACA,KACA,UAAkC,EAAE,EAC1B;CACV,MAAM,UAA6B,QAAQ,cAAc,EAAE,kBAAkB,GAAG;CAChF,MAAM,UAAU,QAAQ,eAAe,QAAgB,YAAY;CACnE,IAAI,MAAM;CAEV,OAAO;EACL,GAAG;EACH,OAAO,YAA2B,WAAiD;GACjF,OAAO;GACP,OAAO,IAAI,IACT,QAAQ,KAAK,WAAW,QAClB,MAAM,OAAO,YAAY,UAAU,EACzC,QACD;;EAEJ;;;;ACtBH,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,WAAW;;;;;;;;;AAUjB,SAAgB,oBAAoB,KAA6C;CAC/E,OAAO;;;;;;;;;;EAUL,sBAAsB,IAAI,KAAK,QAAQ;EAEvC,MAAM,KAAK,YAAiD;GAC1D,MAAM,SAAS,MAAM,IAAI,IAAyC,SAAS;GAC3E,IAAI,CAAC,QACH,OAAO;GACT,MAAM,QAAS,MAAM,IAAI,IAAmB,UAAU,IAAK,EAAE;GAC7D,MAAM,OAAQ,MAAM,IAAI,IAAkB,SAAS,IAAK,EAAE;GAC1D,OAAO;IAAE,GAAG;IAAQ;IAAO;IAAM;;EAGnC,MAAM,KAAK,SAAqC;GAC9C,MAAM,EAAE,OAAO,MAAM,GAAG,WAAW;GACnC,IAAI,IAAI,UAAU,OAAO;GACzB,IAAI,IAAI,WAAW,MAAM;GACzB,IAAI,IAAI,UAAU,KAAK;;EAGzB,MAAM,OAAO,YAAmC;GAC9C,IAAI,MAAM,SAAS;GACnB,IAAI,MAAM,UAAU;GACpB,IAAI,MAAM,SAAS;;;;;;;EAQrB,MAAM,OAA0B;GAC9B,OAAO,EAAE;;EAGX,MAAM,YAAY,YAAoB,OAAqC;GACzE,MAAM,WAAY,MAAM,IAAI,IAAmB,UAAU,IAAK,EAAE;GAChE,IAAI,IAAI,WAAW,CAAC,GAAG,UAAU,GAAG,MAAM,CAAC;;EAG7C,MAAM,SAAS,YAAoB,OAAO,GAAG,OAAwC;GAEnF,QADe,MAAM,IAAI,IAAmB,UAAU,IAAK,EAAE,EAChD,MAAM,MAAM,UAAU,KAAA,IAAY,OAAO,QAAQ,KAAA,EAAU;;EAG1E,MAAM,UAAU,YAAoB,KAAgC;GAClE,MAAM,OAAQ,MAAM,IAAI,IAAkB,SAAS,IAAK,EAAE;GAC1D,MAAM,MAAM,KAAK,WAAU,MAAK,EAAE,OAAO,IAAI,GAAG;GAChD,MAAM,OAAO,CAAC,GAAG,KAAK;GACtB,IAAI,OAAO,GACT,KAAK,OAAO;QAEZ,KAAK,KAAK,IAAI;GAChB,IAAI,IAAI,UAAU,KAAK;;EAGzB,MAAM,aAAa,YAAoB,QAA8C;GACnF,MAAM,SAAS,MAAM,IAAI,IAAyC,SAAS;GAC3E,IAAI,QACF,IAAI,IAAI,UAAU;IAAE,GAAG;IAAQ;IAAQ,WAAW,MAAM,IAAI,KAAK,KAAK;IAAE,CAAC;;EAG9E;;;;;;;;AC9EH,SAAgB,YACd,OACA,KACA,UAA8B,EAAE,EACvB;CACT,MAAM,UAA6B,QAAQ,cAAc,EAAE,kBAAkB,GAAG;CAChF,MAAM,UAAU,QAAQ,eAAe,KAAa,SAAiB,QAAQ,KAAK,GAAG;CACrF,MAAM,gBAAgB,MAAM,KAAK;CACjC,IAAI,MAAM;CAEV,OAAO;EACL,GAAG;EACH,UAAU,OAAO,YAAY;GAC3B,OAAO;GACP,OAAO,IAAI,IACT,QAAQ,KAAK,eAAe,MAAM,QAC5B,MAAM,QAAQ,OAAO,QAAQ,EACnC,QACD;;EAEJ;;;;;;;;;;AAWH,SAAgB,eACd,OACA,KACA,OAA6D,EAAE,EACtC;CACzB,MAAM,aAAa,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;CAC9C,MAAM,UAAmC,EAAE;CAC3C,KAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,MAAM,EAC7C,QAAQ,QAAQ,WAAW,IAAI,KAAK,GAAG,MAAM,YAAY,KAAK,KAAK,KAAK;CAE1E,OAAO"}
|
package/dist/session/sqlite.d.ts
CHANGED
package/dist/session/sqlite.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./messages-
|
|
1
|
+
import "./messages-BBWakTN6.js";
|
|
2
2
|
//#region src/session/file-map.ts
|
|
3
3
|
function toMeta(data) {
|
|
4
4
|
return {
|
|
@@ -474,4 +474,4 @@ function generateId() {
|
|
|
474
474
|
//#endregion
|
|
475
475
|
export { createFileMapStore as a, createMemoryStore as i, loadSession as n, createRemoteStore as r, createSession as t };
|
|
476
476
|
|
|
477
|
-
//# sourceMappingURL=session-
|
|
477
|
+
//# sourceMappingURL=session-DzfRacU_.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-CbkiJDlH.js","names":[],"sources":["../src/session/file-map.ts","../src/session/memory.ts","../src/session/remote.ts","../src/session/index.ts"],"sourcesContent":["/**\n * File-map session store.\n *\n * Wraps a narrow 3-method adapter (`get` / `save` / `delete`) that exchanges a flat\n * map of filename → string content. Useful for embedding zidane sessions inside\n * host-provided session backends that only speak in file maps (not zidane's native\n * `SessionStore` shape).\n *\n * Serialization format:\n * - `turns.jsonl` — one `SessionTurn` per line.\n * - `meta.json` — session metadata (id, agentId, status, runs, metadata, timestamps).\n *\n * JSONL for turns keeps history inspectable with tools like `jq` and resilient to\n * partial corruption — parse up to the first bad line and you still have a valid\n * prefix. Metadata lives in its own file so large turn logs don't bloat the\n * metadata path.\n *\n * Scope: each `createFileMapStore` handles a **single session** — the adapter's\n * file map holds at most one zidane session at a time. This matches how host SDKs\n * scope their session stores per conversation.\n *\n * Divergences from the built-in memory / sqlite stores:\n * - `appendTurns` / `updateStatus` / `updateRun` auto-create a minimal `SessionData`\n * record on first write, instead of silently no-oping when the session hasn't been\n * explicitly `save()`-ed. This matches the host-SDK integration path where\n * `createSession(...)` → `agent.run(...)` directly without an explicit `save()` call.\n * - `updateRun` inserts the run if not found in the cached record (rather than\n * silently dropping). Run records therefore always reach the adapter.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\n/**\n * Host-provided file-map adapter. Three methods exchanging `Record<string, string>`\n * payloads — the whole persistence surface the wrapper needs.\n */\nexport interface FileMapAdapter {\n /** Load the current file map. Returns an empty `files` record when nothing is persisted. */\n get: () => Promise<{ files: Record<string, string> }>\n /** Replace the persisted file map. Full-rewrite semantics. */\n save: (files: Record<string, string>) => Promise<void>\n /** Delete all persisted state. */\n delete: () => Promise<void>\n}\n\nexport interface FileMapStoreOptions {\n /** Filename for the JSONL turns log. Default: `turns.jsonl`. */\n turnsFile?: string\n /** Filename for the metadata JSON. Default: `meta.json`. */\n metaFile?: string\n}\n\ninterface MetaShape {\n id: string\n agentId?: string\n runs: SessionRun[]\n status: SessionData['status']\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\nfunction toMeta(data: SessionData): MetaShape {\n return {\n id: data.id,\n agentId: data.agentId,\n runs: data.runs,\n status: data.status,\n metadata: data.metadata,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n }\n}\n\nfunction toData(meta: MetaShape, turns: SessionTurn[]): SessionData {\n return {\n id: meta.id,\n agentId: meta.agentId,\n turns,\n runs: meta.runs,\n status: meta.status,\n metadata: meta.metadata,\n createdAt: meta.createdAt,\n updatedAt: meta.updatedAt,\n }\n}\n\nfunction parseTurnsJsonl(jsonl: string): SessionTurn[] {\n if (!jsonl)\n return []\n const turns: SessionTurn[] = []\n for (const line of jsonl.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed)\n continue\n try {\n turns.push(JSON.parse(trimmed) as SessionTurn)\n }\n catch {\n // Skip malformed lines — preserves the valid prefix on partial corruption.\n }\n }\n return turns\n}\n\nfunction serializeTurnsJsonl(turns: SessionTurn[]): string {\n if (turns.length === 0)\n return ''\n return `${turns.map(t => JSON.stringify(t)).join('\\n')}\\n`\n}\n\n/**\n * Create a single-session `SessionStore` backed by a file-map adapter.\n *\n * @example\n * ```ts\n * const session = await createSession({\n * store: createFileMapStore(hostSessionStore),\n * })\n * ```\n */\nexport function createFileMapStore(\n adapter: FileMapAdapter,\n options: FileMapStoreOptions = {},\n): SessionStore {\n const turnsFile = options.turnsFile ?? 'turns.jsonl'\n const metaFile = options.metaFile ?? 'meta.json'\n\n // Cached view of the persisted session. Populated lazily on first access so the\n // factory itself doesn't do I/O.\n let cached: SessionData | null = null\n let hydrated = false\n\n async function hydrate(): Promise<void> {\n if (hydrated)\n return\n const { files } = await adapter.get()\n const metaRaw = files[metaFile]\n if (metaRaw) {\n let meta: MetaShape | null = null\n try {\n meta = JSON.parse(metaRaw) as MetaShape\n }\n catch {\n meta = null\n }\n if (meta) {\n cached = toData(meta, parseTurnsJsonl(files[turnsFile] ?? ''))\n }\n }\n hydrated = true\n }\n\n async function persist(data: SessionData): Promise<void> {\n const meta = toMeta(data)\n await adapter.save({\n [metaFile]: JSON.stringify(meta, null, 2),\n [turnsFile]: serializeTurnsJsonl(data.turns),\n })\n }\n\n // Ensure `cached` exists for `sessionId`, creating a minimal record when first written.\n // Returns false when `cached` already holds a different sessionId (request ignored).\n async function ensureCachedFor(sessionId: string): Promise<boolean> {\n await hydrate()\n if (cached) {\n return cached.id === sessionId\n }\n const now = Date.now()\n cached = {\n id: sessionId,\n turns: [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n hydrated = true\n return true\n }\n\n return {\n async load(sessionId: string): Promise<SessionData | null> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return null\n return structuredClone(cached)\n },\n\n async save(data: SessionData): Promise<void> {\n cached = structuredClone(data)\n hydrated = true\n await persist(cached)\n },\n\n async delete(sessionId: string): Promise<void> {\n await hydrate()\n if (cached && cached.id !== sessionId)\n return\n cached = null\n await adapter.delete()\n },\n\n async list(filter): Promise<string[]> {\n await hydrate()\n if (!cached)\n return []\n if (filter?.agentId && cached.agentId !== filter.agentId)\n return []\n // file-map stores exactly one session, so the projectRoot filter\n // either keeps it (match / axis off) or drops it entirely.\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null && cached.projectRoot != null)\n return []\n if (typeof v === 'string' && cached.projectRoot !== v)\n return []\n }\n return [cached.id]\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.turns.push(...structuredClone(turns))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return []\n const slice = cached.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(slice) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n const idx = cached!.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n cached!.runs[idx] = structuredClone(run)\n else\n cached!.runs.push(structuredClone(run))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.status = status\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n }\n}\n","/**\n * In-memory session store.\n * Useful for development and testing. Data is lost when the process exits.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport function createMemoryStore(): SessionStore {\n const sessions = new Map<string, SessionData>()\n\n return {\n async load(sessionId: string) {\n const data = sessions.get(sessionId)\n return data ? structuredClone(data) : null\n },\n\n async save(session: SessionData) {\n sessions.set(session.id, structuredClone(session))\n },\n\n async delete(sessionId: string) {\n sessions.delete(sessionId)\n },\n\n async list(filter) {\n let ids = Array.from(sessions.keys())\n if (filter?.agentId) {\n ids = ids.filter(id => sessions.get(id)?.agentId === filter.agentId)\n }\n // `projectRoot` mirrors the SQLite store's tri-state contract:\n // string → only sessions tagged with that root\n // null → only UNTAGGED sessions (legacy / pre-v3 rows)\n // undefined → axis ignored (tagged + untagged both returned)\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n ids = ids.filter(id => sessions.get(id)?.projectRoot == null)\n else if (typeof v === 'string')\n ids = ids.filter(id => sessions.get(id)?.projectRoot === v)\n }\n if (filter?.limit) {\n ids = ids.slice(0, filter.limit)\n }\n return ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const data = sessions.get(sessionId)\n if (data) {\n data.turns.push(...structuredClone(turns))\n data.updatedAt = Date.now()\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const data = sessions.get(sessionId)\n if (!data)\n return []\n const sliced = data.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(sliced) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const data = sessions.get(sessionId)\n if (data) {\n // Upsert — see the matching comment in sqlite.ts's\n // `txnUpdateRun` for the failure mode. tl;dr: the agent's\n // `startRun()` only updates in-memory state; the store first\n // sees the run via `updateRun()` at finalize time, so an\n // update-only impl silently drops every new run.\n const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n data.runs[idx] = structuredClone(run)\n else\n data.runs.push(structuredClone(run))\n data.updatedAt = Date.now()\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = sessions.get(sessionId)\n if (data) {\n data.status = status\n data.updatedAt = Date.now()\n }\n },\n }\n}\n","/**\n * Remote session store via HTTP API.\n *\n * Expects a REST API with:\n * GET {url}/sessions/{id} -> SessionData | 404\n * PUT {url}/sessions/{id} -> save SessionData\n * DELETE {url}/sessions/{id} -> delete\n * GET {url}/sessions?agentId=&limit=&projectRoot= -> { ids: string[] }\n * `projectRoot=__null__` is the wire encoding for \"untagged only\".\n * POST {url}/sessions/{id}/turns -> append turns\n * GET {url}/sessions/{id}/turns?from=&limit= -> SessionTurn[]\n * PUT {url}/sessions/{id}/runs/{runId} -> update run\n * PATCH {url}/sessions/{id} -> { status }\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport interface RemoteStoreOptions {\n /** Base URL of the session API */\n url: string\n /** Optional headers (e.g. for authentication) */\n headers?: Record<string, string>\n}\n\nconst TRAILING_SLASH = /\\/$/\n\nexport function createRemoteStore(options: RemoteStoreOptions): SessionStore {\n const baseUrl = options.url.replace(TRAILING_SLASH, '')\n const defaultHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n }\n\n async function request(path: string, init?: RequestInit): Promise<Response> {\n const url = `${baseUrl}${path}`\n const res = await fetch(url, {\n ...init,\n headers: { ...defaultHeaders, ...init?.headers },\n })\n return res\n }\n\n return {\n async load(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`)\n if (!res.ok) {\n if (res.status === 404)\n return null\n throw new Error(`Remote session load failed: ${res.status} ${res.statusText}`)\n }\n return await res.json() as SessionData\n },\n\n async save(session: SessionData) {\n const res = await request(`/sessions/${encodeURIComponent(session.id)}`, {\n method: 'PUT',\n body: JSON.stringify(session),\n })\n if (!res.ok) {\n throw new Error(`Remote session save failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async delete(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'DELETE',\n })\n if (!res.ok && res.status !== 404) {\n throw new Error(`Remote session delete failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async list(filter) {\n const params = new URLSearchParams()\n if (filter?.agentId)\n params.set('agentId', filter.agentId)\n if (filter?.limit)\n params.set('limit', String(filter.limit))\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n params.set('projectRoot', '__null__')\n else if (typeof v === 'string')\n params.set('projectRoot', v)\n }\n\n const query = params.toString()\n const path = query ? `/sessions?${query}` : '/sessions'\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote session list failed: ${res.status} ${res.statusText}`)\n }\n\n const body = await res.json() as { ids: string[] }\n return body.ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}/turns`, {\n method: 'POST',\n body: JSON.stringify(turns),\n })\n if (!res.ok) {\n throw new Error(`Remote appendTurns failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const params = new URLSearchParams()\n if (from)\n params.set('from', String(from))\n if (limit !== undefined)\n params.set('limit', String(limit))\n\n const query = params.toString()\n const path = `/sessions/${encodeURIComponent(sessionId)}/turns${query ? `?${query}` : ''}`\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote getTurns failed: ${res.status} ${res.statusText}`)\n }\n\n return await res.json() as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const res = await request(\n `/sessions/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(run.id)}`,\n {\n method: 'PUT',\n body: JSON.stringify(run),\n },\n )\n if (!res.ok) {\n throw new Error(`Remote updateRun failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'PATCH',\n body: JSON.stringify({ status }),\n })\n if (!res.ok) {\n throw new Error(`Remote updateStatus failed: ${res.status} ${res.statusText}`)\n }\n },\n }\n}\n","/**\n * Session management for agents.\n *\n * A session tracks identity, turn history, and run metadata.\n * Plug in any storage backend by implementing the SessionStore interface,\n * or use one of the built-in stores: memory, sqlite, remote.\n */\n\nimport type { SessionTurn, TurnUsage } from '../types'\n\nexport type { SessionContentBlock, SessionMessage, SessionTurn } from '../types'\nexport { createFileMapStore } from './file-map'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SessionRun {\n id: string\n startedAt: number\n endedAt?: number\n prompt: string\n status: 'running' | 'completed' | 'aborted' | 'error'\n turns?: number\n tokensIn?: number\n tokensOut?: number\n error?: string\n /** Per-turn usage breakdown */\n turnUsage?: TurnUsage[]\n /** Total usage across all turns */\n totalUsage?: TurnUsage\n /** Estimated cost in USD */\n cost?: number\n /**\n * The run that spawned this one, when the agent is a subagent sharing its\n * parent's session. Undefined on top-level `agent.run()`. Consumers can walk\n * `runs` by `parentRunId` to reconstruct the subagent tree.\n */\n parentRunId?: string\n /**\n * Zero-based subagent depth. 0 = top-level run, 1 = direct child, …\n * Recorded here so hosts can query/filter by level without walking the tree.\n */\n depth?: number\n}\n\nexport interface SessionData {\n id: string\n agentId?: string\n /**\n * Absolute path of the project this session belongs to — typically\n * the git root resolved from `cwd` at creation time, falling back to\n * `cwd` itself when not in a git repo. Set ONCE on creation and never\n * mutated thereafter (the session \"belongs\" to that project forever).\n *\n * Used by the TUI's sessions list to filter rows by current project,\n * so the user only sees conversations relevant to where they are\n * working — without needing one `.{prefix}/` directory per project.\n *\n * `undefined` on pre-tagging legacy sessions; the chat layer treats\n * those as \"untagged\" and hides them unless `Settings.showAllProjects`\n * is on.\n */\n projectRoot?: string\n turns: SessionTurn[]\n runs: SessionRun[]\n status: 'idle' | 'running' | 'completed' | 'error'\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\n// ---------------------------------------------------------------------------\n// SessionStore interface (pluggable backend)\n// ---------------------------------------------------------------------------\n\nexport interface SessionStore {\n /** Optional: generate a session ID server-side (e.g. Supabase UUID). */\n generateSessionId?: () => string | Promise<string>\n\n /** Optional: generate a turn ID server-side. */\n generateTurnId?: () => string | Promise<string>\n\n /** Load a session by ID. Returns null if not found. */\n load: (sessionId: string) => Promise<SessionData | null>\n\n /** Save a session (create or update, full document). */\n save: (session: SessionData) => Promise<void>\n\n /** Delete a session. */\n delete: (sessionId: string) => Promise<void>\n\n /**\n * List session IDs, optionally filtered. `projectRoot` restricts to\n * sessions whose `SessionData.projectRoot` matches exactly — untagged\n * (legacy) sessions are NOT returned under that filter; pass `null`\n * explicitly to ask for untagged ones, or omit the field to ignore\n * the axis entirely. `agentId` filters by recorded agent; the two\n * conditions AND together when both are set.\n */\n list: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<string[]>\n\n /** Append new turns to a session (incremental, avoids full re-save). */\n appendTurns: (sessionId: string, turns: SessionTurn[]) => Promise<void>\n\n /** Return a slice of turns for a session. */\n getTurns: (sessionId: string, from?: number, limit?: number) => Promise<SessionTurn[]>\n\n /**\n * Persist a run record (called after completeRun / abortRun / errorRun).\n *\n * **Upsert semantics required.** The agent's run lifecycle calls\n * `session.startRun()` to register a new run in IN-MEMORY state and\n * then `updateRun()` at finalize. The store doesn't see the run any\n * earlier — `appendTurns` is the only intermediate call, and it\n * persists turns but not runs. An update-only implementation would\n * silently drop every newly-created run record at finalize, leaving\n * the on-disk `runs[]` missing entries that the persisted turns\n * reference (which `eventsFromTurns` then mis-classifies via\n * `ancestryOf` → broken depth, broken `child-N` labels, broken\n * `userPrompts` history).\n *\n * Implementations MUST insert when the id doesn't exist and update\n * when it does. The bundled `sqlite` / `memory` / `file-map` stores\n * all follow this contract; remote stores using HTTP PUT get upsert\n * for free via REST semantics.\n */\n updateRun: (sessionId: string, run: SessionRun) => Promise<void>\n\n /** Update the top-level status of a session. */\n updateStatus: (sessionId: string, status: SessionData['status']) => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Session (live instance wrapping a SessionData)\n// ---------------------------------------------------------------------------\n\nexport interface Session {\n /** Session ID */\n readonly id: string\n\n /** Agent ID (optional label) */\n readonly agentId?: string\n\n /**\n * Project this session was created under — see {@link SessionData.projectRoot}.\n * Set once on creation; surfaces here for read-only inspection.\n */\n readonly projectRoot?: string\n\n /** Current turn history */\n readonly turns: SessionTurn[]\n\n /**\n * True when this session has no turns yet.\n *\n * Use this as a first-prompt signal when setting up a run — e.g. writing initial\n * configuration only on fresh sessions. Equivalent to `turns.length === 0`.\n */\n readonly isEmpty: boolean\n\n /** Top-level session status */\n readonly status: SessionData['status']\n\n /** All runs in this session */\n readonly runs: SessionRun[]\n\n /** Arbitrary metadata */\n readonly metadata: Record<string, unknown>\n\n /**\n * Start tracking a new run. `extras.parentRunId` + `extras.depth` are\n * populated by the spawn tool when a child agent shares its parent's\n * session; regular top-level `agent.run()` calls omit them.\n */\n startRun: (runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) => void\n\n /** Mark a run as completed */\n completeRun: (runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as aborted */\n /**\n * Optional `stats` lets the agent backfill the run's token totals when\n * the abort happened *after* the loop accumulated meaningful usage —\n * common when the user presses esc mid-streaming. Without it, the run\n * record reads `0 in / 0 out` on reload regardless of how much was\n * spent before the abort. Same shape as `completeRun`'s stats so the\n * persisted `totalUsage` aggregate stays consistent across paths.\n */\n abortRun: (runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as errored */\n /** Optional `stats` — same rationale as `abortRun.stats`. */\n errorRun: (runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Append turns to in-memory history AND persist via store.appendTurns (if store present) */\n appendTurns: (turns: SessionTurn[]) => Promise<void>\n\n /** Replace all turns in-memory (does not persist — use save() for that) */\n setTurns: (turns: SessionTurn[]) => void\n\n /**\n * Replace all runs in-memory (does not persist — use save() for that).\n * Mirrors {@link setTurns} for the fork / restore case: callers that\n * bootstrap a session from an externally-derived snapshot (e.g.\n * `onForkTurn` copying parent runs into a child session) need this so\n * the cloned runs land in `data.runs` before the first `save()`.\n * Production agent runs continue to mutate runs via `startRun` /\n * `completeRun` / `updateRun`; this is the bulk-replace escape hatch.\n */\n setRuns: (runs: SessionRun[]) => void\n\n /** Update the session status in memory AND via store.updateStatus (if store present) */\n updateStatus: (status: SessionData['status']) => Promise<void>\n\n /** Persist an updated run record via store.updateRun (if store present) */\n updateRun: (run: SessionRun) => Promise<void>\n\n /** Generate a turn ID using store.generateTurnId if available, else crypto.randomUUID() */\n generateTurnId: () => string | Promise<string>\n\n /** Set metadata key */\n setMeta: (key: string, value: unknown) => void\n\n /** Persist the full session document to the store */\n save: () => Promise<void>\n\n /** Serialize to SessionData */\n toJSON: () => SessionData\n}\n\n// ---------------------------------------------------------------------------\n// createSession\n// ---------------------------------------------------------------------------\n\nexport interface CreateSessionOptions {\n /** Session ID. If omitted and store provides generateSessionId, that is used. */\n id?: string\n /** Agent ID label */\n agentId?: string\n /**\n * Project tag — see {@link SessionData.projectRoot}. Stamped once on\n * creation; ignored when `_data` is set (restoring an existing\n * session preserves whatever was already persisted there). The TUI\n * resolves this from `findGitRoot(cwd) ?? cwd` so sessions started\n * from the same repo (no matter which subdir) share one tag.\n */\n projectRoot?: string\n /** Initial metadata */\n metadata?: Record<string, unknown>\n /** Storage backend (optional, enables save/load) */\n store?: SessionStore\n // @internal: restore from existing data (bypasses id/agentId/metadata options)\n _data?: SessionData\n}\n\n/**\n * Create a new session.\n * Async so stores that generate IDs server-side (e.g. Supabase) can be supported.\n */\nexport async function createSession(options: CreateSessionOptions = {}): Promise<Session> {\n const store = options.store\n const now = Date.now()\n\n let sessionId = options.id\n if (!sessionId && store?.generateSessionId) {\n sessionId = await store.generateSessionId()\n }\n if (!sessionId) {\n sessionId = generateId()\n }\n\n const data: SessionData = options._data ?? {\n id: sessionId,\n agentId: options.agentId,\n // Stamp the project tag at creation only — restored sessions (the\n // `_data` branch above) keep whatever was already persisted, even\n // if `options.projectRoot` differs. A session's project identity\n // is sticky for its lifetime; loading the same session from a\n // different cwd doesn't re-home it.\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n turns: [],\n runs: [],\n status: 'idle',\n metadata: options.metadata ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n function touch() {\n data.updatedAt = Date.now()\n }\n\n function findRun(runId: string): SessionRun | undefined {\n return data.runs.find(r => r.id === runId)\n }\n\n /**\n * Apply per-run usage stats onto a SessionRun. Shared by `completeRun`,\n * `abortRun`, and `errorRun` so the on-disk shape stays consistent across\n * exit paths — historically only `completeRun` filled these in, and\n * aborted/errored runs rendered `0 in / 0 out` on reload regardless of\n * how much was actually consumed.\n */\n function applyRunStats(run: SessionRun, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n run.turns = stats.turns\n run.tokensIn = stats.tokensIn\n run.tokensOut = stats.tokensOut\n if (stats.turnUsage) {\n run.turnUsage = stats.turnUsage\n const total = stats.turnUsage.reduce((acc, t) => ({\n input: acc.input + t.input,\n output: acc.output + t.output,\n cacheCreation: (acc.cacheCreation ?? 0) + (t.cacheCreation ?? 0),\n cacheRead: (acc.cacheRead ?? 0) + (t.cacheRead ?? 0),\n thinking: (acc.thinking ?? 0) + (t.thinking ?? 0),\n }), { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, thinking: 0 })\n run.totalUsage = {\n input: total.input,\n output: total.output,\n ...(total.cacheCreation ? { cacheCreation: total.cacheCreation } : {}),\n ...(total.cacheRead ? { cacheRead: total.cacheRead } : {}),\n ...(total.thinking ? { thinking: total.thinking } : {}),\n }\n }\n if (stats.cost !== undefined)\n run.cost = stats.cost\n }\n\n const session: Session = {\n get id() { return data.id },\n get agentId() { return data.agentId },\n get projectRoot() { return data.projectRoot },\n get turns() { return data.turns },\n get isEmpty() { return data.turns.length === 0 },\n get status() { return data.status },\n get runs() { return data.runs },\n get metadata() { return data.metadata },\n\n startRun(runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) {\n data.runs.push({\n id: runId,\n startedAt: Date.now(),\n prompt: prompt ?? '',\n status: 'running',\n ...(extras?.parentRunId ? { parentRunId: extras.parentRunId } : {}),\n ...(typeof extras?.depth === 'number' ? { depth: extras.depth } : {}),\n })\n touch()\n },\n\n completeRun(runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'completed'\n run.endedAt = Date.now()\n applyRunStats(run, stats)\n }\n touch()\n },\n\n abortRun(runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'aborted'\n run.endedAt = Date.now()\n // Backfill tokens when available so an aborted run's session\n // ledger reflects actual consumption rather than `0 in / 0 out`.\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n errorRun(runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'error'\n run.endedAt = Date.now()\n run.error = error\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n async appendTurns(turns: SessionTurn[]) {\n data.turns.push(...turns)\n touch()\n if (store) {\n await store.appendTurns(data.id, turns)\n }\n },\n\n setTurns(turns: SessionTurn[]) {\n data.turns = turns\n touch()\n },\n\n setRuns(runs: SessionRun[]) {\n data.runs = runs\n touch()\n },\n\n async updateStatus(status: SessionData['status']) {\n data.status = status\n touch()\n if (store) {\n await store.updateStatus(data.id, status)\n }\n },\n\n async updateRun(run: SessionRun) {\n if (store) {\n await store.updateRun(data.id, run)\n }\n },\n\n generateTurnId() {\n if (store?.generateTurnId) {\n return store.generateTurnId()\n }\n return crypto.randomUUID()\n },\n\n setMeta(key: string, value: unknown) {\n data.metadata[key] = value\n touch()\n },\n\n async save() {\n if (!store) {\n throw new Error('No SessionStore configured. Pass a store to createSession() to enable persistence.')\n }\n await store.save(data)\n },\n\n toJSON() {\n return structuredClone(data)\n },\n }\n\n return session\n}\n\n/**\n * Load an existing session from a store.\n */\nexport async function loadSession(store: SessionStore, sessionId: string): Promise<Session | null> {\n const loaded = await store.load(sessionId)\n if (!loaded)\n return null\n\n return createSession({ store, _data: loaded })\n}\n\n// ---------------------------------------------------------------------------\n// Re-export stores\n// ---------------------------------------------------------------------------\n\nexport type { FileMapAdapter, FileMapStoreOptions } from './file-map'\nexport { createMemoryStore } from './memory'\nexport { autoDetectAndConvert, fromAnthropic, fromOpenAI, toAnthropic, toOpenAI } from './messages'\nexport { createRemoteStore } from './remote'\nexport type { RemoteStoreOptions } from './remote'\n\n// NOTE: `createSqliteStore` is intentionally NOT re-exported here. It lives behind\n// the dedicated `zidane/session/sqlite` subpath so that non-Bun consumers don't\n// transitively evaluate `bun:sqlite` when they import from `zidane/session`.\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction generateId(): string {\n return `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-DzfRacU_.js","names":[],"sources":["../src/session/file-map.ts","../src/session/memory.ts","../src/session/remote.ts","../src/session/index.ts"],"sourcesContent":["/**\n * File-map session store.\n *\n * Wraps a narrow 3-method adapter (`get` / `save` / `delete`) that exchanges a flat\n * map of filename → string content. Useful for embedding zidane sessions inside\n * host-provided session backends that only speak in file maps (not zidane's native\n * `SessionStore` shape).\n *\n * Serialization format:\n * - `turns.jsonl` — one `SessionTurn` per line.\n * - `meta.json` — session metadata (id, agentId, status, runs, metadata, timestamps).\n *\n * JSONL for turns keeps history inspectable with tools like `jq` and resilient to\n * partial corruption — parse up to the first bad line and you still have a valid\n * prefix. Metadata lives in its own file so large turn logs don't bloat the\n * metadata path.\n *\n * Scope: each `createFileMapStore` handles a **single session** — the adapter's\n * file map holds at most one zidane session at a time. This matches how host SDKs\n * scope their session stores per conversation.\n *\n * Divergences from the built-in memory / sqlite stores:\n * - `appendTurns` / `updateStatus` / `updateRun` auto-create a minimal `SessionData`\n * record on first write, instead of silently no-oping when the session hasn't been\n * explicitly `save()`-ed. This matches the host-SDK integration path where\n * `createSession(...)` → `agent.run(...)` directly without an explicit `save()` call.\n * - `updateRun` inserts the run if not found in the cached record (rather than\n * silently dropping). Run records therefore always reach the adapter.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\n/**\n * Host-provided file-map adapter. Three methods exchanging `Record<string, string>`\n * payloads — the whole persistence surface the wrapper needs.\n */\nexport interface FileMapAdapter {\n /** Load the current file map. Returns an empty `files` record when nothing is persisted. */\n get: () => Promise<{ files: Record<string, string> }>\n /** Replace the persisted file map. Full-rewrite semantics. */\n save: (files: Record<string, string>) => Promise<void>\n /** Delete all persisted state. */\n delete: () => Promise<void>\n}\n\nexport interface FileMapStoreOptions {\n /** Filename for the JSONL turns log. Default: `turns.jsonl`. */\n turnsFile?: string\n /** Filename for the metadata JSON. Default: `meta.json`. */\n metaFile?: string\n}\n\ninterface MetaShape {\n id: string\n agentId?: string\n runs: SessionRun[]\n status: SessionData['status']\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\nfunction toMeta(data: SessionData): MetaShape {\n return {\n id: data.id,\n agentId: data.agentId,\n runs: data.runs,\n status: data.status,\n metadata: data.metadata,\n createdAt: data.createdAt,\n updatedAt: data.updatedAt,\n }\n}\n\nfunction toData(meta: MetaShape, turns: SessionTurn[]): SessionData {\n return {\n id: meta.id,\n agentId: meta.agentId,\n turns,\n runs: meta.runs,\n status: meta.status,\n metadata: meta.metadata,\n createdAt: meta.createdAt,\n updatedAt: meta.updatedAt,\n }\n}\n\nfunction parseTurnsJsonl(jsonl: string): SessionTurn[] {\n if (!jsonl)\n return []\n const turns: SessionTurn[] = []\n for (const line of jsonl.split('\\n')) {\n const trimmed = line.trim()\n if (!trimmed)\n continue\n try {\n turns.push(JSON.parse(trimmed) as SessionTurn)\n }\n catch {\n // Skip malformed lines — preserves the valid prefix on partial corruption.\n }\n }\n return turns\n}\n\nfunction serializeTurnsJsonl(turns: SessionTurn[]): string {\n if (turns.length === 0)\n return ''\n return `${turns.map(t => JSON.stringify(t)).join('\\n')}\\n`\n}\n\n/**\n * Create a single-session `SessionStore` backed by a file-map adapter.\n *\n * @example\n * ```ts\n * const session = await createSession({\n * store: createFileMapStore(hostSessionStore),\n * })\n * ```\n */\nexport function createFileMapStore(\n adapter: FileMapAdapter,\n options: FileMapStoreOptions = {},\n): SessionStore {\n const turnsFile = options.turnsFile ?? 'turns.jsonl'\n const metaFile = options.metaFile ?? 'meta.json'\n\n // Cached view of the persisted session. Populated lazily on first access so the\n // factory itself doesn't do I/O.\n let cached: SessionData | null = null\n let hydrated = false\n\n async function hydrate(): Promise<void> {\n if (hydrated)\n return\n const { files } = await adapter.get()\n const metaRaw = files[metaFile]\n if (metaRaw) {\n let meta: MetaShape | null = null\n try {\n meta = JSON.parse(metaRaw) as MetaShape\n }\n catch {\n meta = null\n }\n if (meta) {\n cached = toData(meta, parseTurnsJsonl(files[turnsFile] ?? ''))\n }\n }\n hydrated = true\n }\n\n async function persist(data: SessionData): Promise<void> {\n const meta = toMeta(data)\n await adapter.save({\n [metaFile]: JSON.stringify(meta, null, 2),\n [turnsFile]: serializeTurnsJsonl(data.turns),\n })\n }\n\n // Ensure `cached` exists for `sessionId`, creating a minimal record when first written.\n // Returns false when `cached` already holds a different sessionId (request ignored).\n async function ensureCachedFor(sessionId: string): Promise<boolean> {\n await hydrate()\n if (cached) {\n return cached.id === sessionId\n }\n const now = Date.now()\n cached = {\n id: sessionId,\n turns: [],\n runs: [],\n status: 'idle',\n metadata: {},\n createdAt: now,\n updatedAt: now,\n }\n hydrated = true\n return true\n }\n\n return {\n async load(sessionId: string): Promise<SessionData | null> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return null\n return structuredClone(cached)\n },\n\n async save(data: SessionData): Promise<void> {\n cached = structuredClone(data)\n hydrated = true\n await persist(cached)\n },\n\n async delete(sessionId: string): Promise<void> {\n await hydrate()\n if (cached && cached.id !== sessionId)\n return\n cached = null\n await adapter.delete()\n },\n\n async list(filter): Promise<string[]> {\n await hydrate()\n if (!cached)\n return []\n if (filter?.agentId && cached.agentId !== filter.agentId)\n return []\n // file-map stores exactly one session, so the projectRoot filter\n // either keeps it (match / axis off) or drops it entirely.\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null && cached.projectRoot != null)\n return []\n if (typeof v === 'string' && cached.projectRoot !== v)\n return []\n }\n return [cached.id]\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.turns.push(...structuredClone(turns))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number): Promise<SessionTurn[]> {\n await hydrate()\n if (!cached || cached.id !== sessionId)\n return []\n const slice = cached.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(slice) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n const idx = cached!.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n cached!.runs[idx] = structuredClone(run)\n else\n cached!.runs.push(structuredClone(run))\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']): Promise<void> {\n const ok = await ensureCachedFor(sessionId)\n if (!ok)\n return\n cached!.status = status\n cached!.updatedAt = Date.now()\n await persist(cached!)\n },\n }\n}\n","/**\n * In-memory session store.\n * Useful for development and testing. Data is lost when the process exits.\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport function createMemoryStore(): SessionStore {\n const sessions = new Map<string, SessionData>()\n\n return {\n async load(sessionId: string) {\n const data = sessions.get(sessionId)\n return data ? structuredClone(data) : null\n },\n\n async save(session: SessionData) {\n sessions.set(session.id, structuredClone(session))\n },\n\n async delete(sessionId: string) {\n sessions.delete(sessionId)\n },\n\n async list(filter) {\n let ids = Array.from(sessions.keys())\n if (filter?.agentId) {\n ids = ids.filter(id => sessions.get(id)?.agentId === filter.agentId)\n }\n // `projectRoot` mirrors the SQLite store's tri-state contract:\n // string → only sessions tagged with that root\n // null → only UNTAGGED sessions (legacy / pre-v3 rows)\n // undefined → axis ignored (tagged + untagged both returned)\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n ids = ids.filter(id => sessions.get(id)?.projectRoot == null)\n else if (typeof v === 'string')\n ids = ids.filter(id => sessions.get(id)?.projectRoot === v)\n }\n if (filter?.limit) {\n ids = ids.slice(0, filter.limit)\n }\n return ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const data = sessions.get(sessionId)\n if (data) {\n data.turns.push(...structuredClone(turns))\n data.updatedAt = Date.now()\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const data = sessions.get(sessionId)\n if (!data)\n return []\n const sliced = data.turns.slice(from, limit !== undefined ? from + limit : undefined)\n return structuredClone(sliced) as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const data = sessions.get(sessionId)\n if (data) {\n // Upsert — see the matching comment in sqlite.ts's\n // `txnUpdateRun` for the failure mode. tl;dr: the agent's\n // `startRun()` only updates in-memory state; the store first\n // sees the run via `updateRun()` at finalize time, so an\n // update-only impl silently drops every new run.\n const idx = data.runs.findIndex(r => r.id === run.id)\n if (idx >= 0)\n data.runs[idx] = structuredClone(run)\n else\n data.runs.push(structuredClone(run))\n data.updatedAt = Date.now()\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const data = sessions.get(sessionId)\n if (data) {\n data.status = status\n data.updatedAt = Date.now()\n }\n },\n }\n}\n","/**\n * Remote session store via HTTP API.\n *\n * Expects a REST API with:\n * GET {url}/sessions/{id} -> SessionData | 404\n * PUT {url}/sessions/{id} -> save SessionData\n * DELETE {url}/sessions/{id} -> delete\n * GET {url}/sessions?agentId=&limit=&projectRoot= -> { ids: string[] }\n * `projectRoot=__null__` is the wire encoding for \"untagged only\".\n * POST {url}/sessions/{id}/turns -> append turns\n * GET {url}/sessions/{id}/turns?from=&limit= -> SessionTurn[]\n * PUT {url}/sessions/{id}/runs/{runId} -> update run\n * PATCH {url}/sessions/{id} -> { status }\n */\n\nimport type { SessionData, SessionRun, SessionStore } from '.'\nimport type { SessionTurn } from '../types'\n\nexport interface RemoteStoreOptions {\n /** Base URL of the session API */\n url: string\n /** Optional headers (e.g. for authentication) */\n headers?: Record<string, string>\n}\n\nconst TRAILING_SLASH = /\\/$/\n\nexport function createRemoteStore(options: RemoteStoreOptions): SessionStore {\n const baseUrl = options.url.replace(TRAILING_SLASH, '')\n const defaultHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n }\n\n async function request(path: string, init?: RequestInit): Promise<Response> {\n const url = `${baseUrl}${path}`\n const res = await fetch(url, {\n ...init,\n headers: { ...defaultHeaders, ...init?.headers },\n })\n return res\n }\n\n return {\n async load(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`)\n if (!res.ok) {\n if (res.status === 404)\n return null\n throw new Error(`Remote session load failed: ${res.status} ${res.statusText}`)\n }\n return await res.json() as SessionData\n },\n\n async save(session: SessionData) {\n const res = await request(`/sessions/${encodeURIComponent(session.id)}`, {\n method: 'PUT',\n body: JSON.stringify(session),\n })\n if (!res.ok) {\n throw new Error(`Remote session save failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async delete(sessionId: string) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'DELETE',\n })\n if (!res.ok && res.status !== 404) {\n throw new Error(`Remote session delete failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async list(filter) {\n const params = new URLSearchParams()\n if (filter?.agentId)\n params.set('agentId', filter.agentId)\n if (filter?.limit)\n params.set('limit', String(filter.limit))\n if (filter && 'projectRoot' in filter) {\n const v = filter.projectRoot\n if (v === null)\n params.set('projectRoot', '__null__')\n else if (typeof v === 'string')\n params.set('projectRoot', v)\n }\n\n const query = params.toString()\n const path = query ? `/sessions?${query}` : '/sessions'\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote session list failed: ${res.status} ${res.statusText}`)\n }\n\n const body = await res.json() as { ids: string[] }\n return body.ids\n },\n\n async appendTurns(sessionId: string, turns: SessionTurn[]) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}/turns`, {\n method: 'POST',\n body: JSON.stringify(turns),\n })\n if (!res.ok) {\n throw new Error(`Remote appendTurns failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async getTurns(sessionId: string, from = 0, limit?: number) {\n const params = new URLSearchParams()\n if (from)\n params.set('from', String(from))\n if (limit !== undefined)\n params.set('limit', String(limit))\n\n const query = params.toString()\n const path = `/sessions/${encodeURIComponent(sessionId)}/turns${query ? `?${query}` : ''}`\n const res = await request(path)\n\n if (!res.ok) {\n throw new Error(`Remote getTurns failed: ${res.status} ${res.statusText}`)\n }\n\n return await res.json() as SessionTurn[]\n },\n\n async updateRun(sessionId: string, run: SessionRun) {\n const res = await request(\n `/sessions/${encodeURIComponent(sessionId)}/runs/${encodeURIComponent(run.id)}`,\n {\n method: 'PUT',\n body: JSON.stringify(run),\n },\n )\n if (!res.ok) {\n throw new Error(`Remote updateRun failed: ${res.status} ${res.statusText}`)\n }\n },\n\n async updateStatus(sessionId: string, status: SessionData['status']) {\n const res = await request(`/sessions/${encodeURIComponent(sessionId)}`, {\n method: 'PATCH',\n body: JSON.stringify({ status }),\n })\n if (!res.ok) {\n throw new Error(`Remote updateStatus failed: ${res.status} ${res.statusText}`)\n }\n },\n }\n}\n","/**\n * Session management for agents.\n *\n * A session tracks identity, turn history, and run metadata.\n * Plug in any storage backend by implementing the SessionStore interface,\n * or use one of the built-in stores: memory, sqlite, remote.\n */\n\nimport type { SessionTurn, TurnUsage } from '../types'\n\nexport type { SessionContentBlock, SessionMessage, SessionTurn } from '../types'\nexport { createFileMapStore } from './file-map'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SessionRun {\n id: string\n startedAt: number\n endedAt?: number\n prompt: string\n status: 'running' | 'completed' | 'aborted' | 'error'\n turns?: number\n tokensIn?: number\n tokensOut?: number\n error?: string\n /** Per-turn usage breakdown */\n turnUsage?: TurnUsage[]\n /** Total usage across all turns */\n totalUsage?: TurnUsage\n /** Estimated cost in USD */\n cost?: number\n /**\n * The run that spawned this one, when the agent is a subagent sharing its\n * parent's session. Undefined on top-level `agent.run()`. Consumers can walk\n * `runs` by `parentRunId` to reconstruct the subagent tree.\n */\n parentRunId?: string\n /**\n * Zero-based subagent depth. 0 = top-level run, 1 = direct child, …\n * Recorded here so hosts can query/filter by level without walking the tree.\n */\n depth?: number\n}\n\nexport interface SessionData {\n id: string\n agentId?: string\n /**\n * Absolute path of the project this session belongs to — typically\n * the git root resolved from `cwd` at creation time, falling back to\n * `cwd` itself when not in a git repo. Set ONCE on creation and never\n * mutated thereafter (the session \"belongs\" to that project forever).\n *\n * Used by the TUI's sessions list to filter rows by current project,\n * so the user only sees conversations relevant to where they are\n * working — without needing one `.{prefix}/` directory per project.\n *\n * `undefined` on pre-tagging legacy sessions; the chat layer treats\n * those as \"untagged\" and hides them unless `Settings.showAllProjects`\n * is on.\n */\n projectRoot?: string\n turns: SessionTurn[]\n runs: SessionRun[]\n status: 'idle' | 'running' | 'completed' | 'error'\n metadata: Record<string, unknown>\n createdAt: number\n updatedAt: number\n}\n\n// ---------------------------------------------------------------------------\n// SessionStore interface (pluggable backend)\n// ---------------------------------------------------------------------------\n\nexport interface SessionStore {\n /** Optional: generate a session ID server-side (e.g. Supabase UUID). */\n generateSessionId?: () => string | Promise<string>\n\n /** Optional: generate a turn ID server-side. */\n generateTurnId?: () => string | Promise<string>\n\n /** Load a session by ID. Returns null if not found. */\n load: (sessionId: string) => Promise<SessionData | null>\n\n /** Save a session (create or update, full document). */\n save: (session: SessionData) => Promise<void>\n\n /** Delete a session. */\n delete: (sessionId: string) => Promise<void>\n\n /**\n * List session IDs, optionally filtered. `projectRoot` restricts to\n * sessions whose `SessionData.projectRoot` matches exactly — untagged\n * (legacy) sessions are NOT returned under that filter; pass `null`\n * explicitly to ask for untagged ones, or omit the field to ignore\n * the axis entirely. `agentId` filters by recorded agent; the two\n * conditions AND together when both are set.\n */\n list: (filter?: { agentId?: string, limit?: number, projectRoot?: string | null }) => Promise<string[]>\n\n /** Append new turns to a session (incremental, avoids full re-save). */\n appendTurns: (sessionId: string, turns: SessionTurn[]) => Promise<void>\n\n /** Return a slice of turns for a session. */\n getTurns: (sessionId: string, from?: number, limit?: number) => Promise<SessionTurn[]>\n\n /**\n * Persist a run record (called after completeRun / abortRun / errorRun).\n *\n * **Upsert semantics required.** The agent's run lifecycle calls\n * `session.startRun()` to register a new run in IN-MEMORY state and\n * then `updateRun()` at finalize. The store doesn't see the run any\n * earlier — `appendTurns` is the only intermediate call, and it\n * persists turns but not runs. An update-only implementation would\n * silently drop every newly-created run record at finalize, leaving\n * the on-disk `runs[]` missing entries that the persisted turns\n * reference (which `eventsFromTurns` then mis-classifies via\n * `ancestryOf` → broken depth, broken `child-N` labels, broken\n * `userPrompts` history).\n *\n * Implementations MUST insert when the id doesn't exist and update\n * when it does. The bundled `sqlite` / `memory` / `file-map` stores\n * all follow this contract; remote stores using HTTP PUT get upsert\n * for free via REST semantics.\n */\n updateRun: (sessionId: string, run: SessionRun) => Promise<void>\n\n /** Update the top-level status of a session. */\n updateStatus: (sessionId: string, status: SessionData['status']) => Promise<void>\n}\n\n// ---------------------------------------------------------------------------\n// Session (live instance wrapping a SessionData)\n// ---------------------------------------------------------------------------\n\nexport interface Session {\n /** Session ID */\n readonly id: string\n\n /** Agent ID (optional label) */\n readonly agentId?: string\n\n /**\n * Project this session was created under — see {@link SessionData.projectRoot}.\n * Set once on creation; surfaces here for read-only inspection.\n */\n readonly projectRoot?: string\n\n /** Current turn history */\n readonly turns: SessionTurn[]\n\n /**\n * True when this session has no turns yet.\n *\n * Use this as a first-prompt signal when setting up a run — e.g. writing initial\n * configuration only on fresh sessions. Equivalent to `turns.length === 0`.\n */\n readonly isEmpty: boolean\n\n /** Top-level session status */\n readonly status: SessionData['status']\n\n /** All runs in this session */\n readonly runs: SessionRun[]\n\n /** Arbitrary metadata */\n readonly metadata: Record<string, unknown>\n\n /**\n * Start tracking a new run. `extras.parentRunId` + `extras.depth` are\n * populated by the spawn tool when a child agent shares its parent's\n * session; regular top-level `agent.run()` calls omit them.\n */\n startRun: (runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) => void\n\n /** Mark a run as completed */\n completeRun: (runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as aborted */\n /**\n * Optional `stats` lets the agent backfill the run's token totals when\n * the abort happened *after* the loop accumulated meaningful usage —\n * common when the user presses esc mid-streaming. Without it, the run\n * record reads `0 in / 0 out` on reload regardless of how much was\n * spent before the abort. Same shape as `completeRun`'s stats so the\n * persisted `totalUsage` aggregate stays consistent across paths.\n */\n abortRun: (runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Mark a run as errored */\n /** Optional `stats` — same rationale as `abortRun.stats`. */\n errorRun: (runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) => void\n\n /** Append turns to in-memory history AND persist via store.appendTurns (if store present) */\n appendTurns: (turns: SessionTurn[]) => Promise<void>\n\n /** Replace all turns in-memory (does not persist — use save() for that) */\n setTurns: (turns: SessionTurn[]) => void\n\n /**\n * Replace all runs in-memory (does not persist — use save() for that).\n * Mirrors {@link setTurns} for the fork / restore case: callers that\n * bootstrap a session from an externally-derived snapshot (e.g.\n * `onForkTurn` copying parent runs into a child session) need this so\n * the cloned runs land in `data.runs` before the first `save()`.\n * Production agent runs continue to mutate runs via `startRun` /\n * `completeRun` / `updateRun`; this is the bulk-replace escape hatch.\n */\n setRuns: (runs: SessionRun[]) => void\n\n /** Update the session status in memory AND via store.updateStatus (if store present) */\n updateStatus: (status: SessionData['status']) => Promise<void>\n\n /** Persist an updated run record via store.updateRun (if store present) */\n updateRun: (run: SessionRun) => Promise<void>\n\n /** Generate a turn ID using store.generateTurnId if available, else crypto.randomUUID() */\n generateTurnId: () => string | Promise<string>\n\n /** Set metadata key */\n setMeta: (key: string, value: unknown) => void\n\n /** Persist the full session document to the store */\n save: () => Promise<void>\n\n /** Serialize to SessionData */\n toJSON: () => SessionData\n}\n\n// ---------------------------------------------------------------------------\n// createSession\n// ---------------------------------------------------------------------------\n\nexport interface CreateSessionOptions {\n /** Session ID. If omitted and store provides generateSessionId, that is used. */\n id?: string\n /** Agent ID label */\n agentId?: string\n /**\n * Project tag — see {@link SessionData.projectRoot}. Stamped once on\n * creation; ignored when `_data` is set (restoring an existing\n * session preserves whatever was already persisted there). The TUI\n * resolves this from `findGitRoot(cwd) ?? cwd` so sessions started\n * from the same repo (no matter which subdir) share one tag.\n */\n projectRoot?: string\n /** Initial metadata */\n metadata?: Record<string, unknown>\n /** Storage backend (optional, enables save/load) */\n store?: SessionStore\n // @internal: restore from existing data (bypasses id/agentId/metadata options)\n _data?: SessionData\n}\n\n/**\n * Create a new session.\n * Async so stores that generate IDs server-side (e.g. Supabase) can be supported.\n */\nexport async function createSession(options: CreateSessionOptions = {}): Promise<Session> {\n const store = options.store\n const now = Date.now()\n\n let sessionId = options.id\n if (!sessionId && store?.generateSessionId) {\n sessionId = await store.generateSessionId()\n }\n if (!sessionId) {\n sessionId = generateId()\n }\n\n const data: SessionData = options._data ?? {\n id: sessionId,\n agentId: options.agentId,\n // Stamp the project tag at creation only — restored sessions (the\n // `_data` branch above) keep whatever was already persisted, even\n // if `options.projectRoot` differs. A session's project identity\n // is sticky for its lifetime; loading the same session from a\n // different cwd doesn't re-home it.\n ...(options.projectRoot ? { projectRoot: options.projectRoot } : {}),\n turns: [],\n runs: [],\n status: 'idle',\n metadata: options.metadata ?? {},\n createdAt: now,\n updatedAt: now,\n }\n\n function touch() {\n data.updatedAt = Date.now()\n }\n\n function findRun(runId: string): SessionRun | undefined {\n return data.runs.find(r => r.id === runId)\n }\n\n /**\n * Apply per-run usage stats onto a SessionRun. Shared by `completeRun`,\n * `abortRun`, and `errorRun` so the on-disk shape stays consistent across\n * exit paths — historically only `completeRun` filled these in, and\n * aborted/errored runs rendered `0 in / 0 out` on reload regardless of\n * how much was actually consumed.\n */\n function applyRunStats(run: SessionRun, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n run.turns = stats.turns\n run.tokensIn = stats.tokensIn\n run.tokensOut = stats.tokensOut\n if (stats.turnUsage) {\n run.turnUsage = stats.turnUsage\n const total = stats.turnUsage.reduce((acc, t) => ({\n input: acc.input + t.input,\n output: acc.output + t.output,\n cacheCreation: (acc.cacheCreation ?? 0) + (t.cacheCreation ?? 0),\n cacheRead: (acc.cacheRead ?? 0) + (t.cacheRead ?? 0),\n thinking: (acc.thinking ?? 0) + (t.thinking ?? 0),\n }), { input: 0, output: 0, cacheCreation: 0, cacheRead: 0, thinking: 0 })\n run.totalUsage = {\n input: total.input,\n output: total.output,\n ...(total.cacheCreation ? { cacheCreation: total.cacheCreation } : {}),\n ...(total.cacheRead ? { cacheRead: total.cacheRead } : {}),\n ...(total.thinking ? { thinking: total.thinking } : {}),\n }\n }\n if (stats.cost !== undefined)\n run.cost = stats.cost\n }\n\n const session: Session = {\n get id() { return data.id },\n get agentId() { return data.agentId },\n get projectRoot() { return data.projectRoot },\n get turns() { return data.turns },\n get isEmpty() { return data.turns.length === 0 },\n get status() { return data.status },\n get runs() { return data.runs },\n get metadata() { return data.metadata },\n\n startRun(runId: string, prompt?: string, extras?: { parentRunId?: string, depth?: number }) {\n data.runs.push({\n id: runId,\n startedAt: Date.now(),\n prompt: prompt ?? '',\n status: 'running',\n ...(extras?.parentRunId ? { parentRunId: extras.parentRunId } : {}),\n ...(typeof extras?.depth === 'number' ? { depth: extras.depth } : {}),\n })\n touch()\n },\n\n completeRun(runId: string, stats: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'completed'\n run.endedAt = Date.now()\n applyRunStats(run, stats)\n }\n touch()\n },\n\n abortRun(runId: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'aborted'\n run.endedAt = Date.now()\n // Backfill tokens when available so an aborted run's session\n // ledger reflects actual consumption rather than `0 in / 0 out`.\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n errorRun(runId: string, error: string, stats?: { turns: number, tokensIn: number, tokensOut: number, turnUsage?: TurnUsage[], cost?: number }) {\n const run = findRun(runId)\n if (run) {\n run.status = 'error'\n run.endedAt = Date.now()\n run.error = error\n if (stats)\n applyRunStats(run, stats)\n }\n touch()\n },\n\n async appendTurns(turns: SessionTurn[]) {\n data.turns.push(...turns)\n touch()\n if (store) {\n await store.appendTurns(data.id, turns)\n }\n },\n\n setTurns(turns: SessionTurn[]) {\n data.turns = turns\n touch()\n },\n\n setRuns(runs: SessionRun[]) {\n data.runs = runs\n touch()\n },\n\n async updateStatus(status: SessionData['status']) {\n data.status = status\n touch()\n if (store) {\n await store.updateStatus(data.id, status)\n }\n },\n\n async updateRun(run: SessionRun) {\n if (store) {\n await store.updateRun(data.id, run)\n }\n },\n\n generateTurnId() {\n if (store?.generateTurnId) {\n return store.generateTurnId()\n }\n return crypto.randomUUID()\n },\n\n setMeta(key: string, value: unknown) {\n data.metadata[key] = value\n touch()\n },\n\n async save() {\n if (!store) {\n throw new Error('No SessionStore configured. Pass a store to createSession() to enable persistence.')\n }\n await store.save(data)\n },\n\n toJSON() {\n return structuredClone(data)\n },\n }\n\n return session\n}\n\n/**\n * Load an existing session from a store.\n */\nexport async function loadSession(store: SessionStore, sessionId: string): Promise<Session | null> {\n const loaded = await store.load(sessionId)\n if (!loaded)\n return null\n\n return createSession({ store, _data: loaded })\n}\n\n// ---------------------------------------------------------------------------\n// Re-export stores\n// ---------------------------------------------------------------------------\n\nexport type { FileMapAdapter, FileMapStoreOptions } from './file-map'\nexport { createMemoryStore } from './memory'\nexport { autoDetectAndConvert, fromAnthropic, fromOpenAI, toAnthropic, toOpenAI } from './messages'\nexport { createRemoteStore } from './remote'\nexport type { RemoteStoreOptions } from './remote'\n\n// NOTE: `createSqliteStore` is intentionally NOT re-exported here. It lives behind\n// the dedicated `zidane/session/sqlite` subpath so that non-Bun consumers don't\n// transitively evaluate `bun:sqlite` when they import from `zidane/session`.\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction generateId(): string {\n return `ses_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`\n}\n"],"mappings":"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"}
|
package/dist/session.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as fromOpenAI, B as createRemoteStore, F as SessionRun, I as SessionStore, J as autoDetectAndConvert,
|
|
1
|
+
import { $ as fromOpenAI, B as createRemoteStore, F as SessionRun, I as SessionStore, J as autoDetectAndConvert, Jt as SessionContentBlock, L as createSession, M as CreateSessionOptions, N as Session, P as SessionData, Q as fromAnthropic, Qt as SessionTurn, R as loadSession, Zt as SessionMessage, at as createFileMapStore, et as toAnthropic, it as FileMapStoreOptions, nt as createMemoryStore, rt as FileMapAdapter, tt as toOpenAI, z as RemoteStoreOptions } from "./agent-CMAklak7.js";
|
|
2
2
|
export { CreateSessionOptions, FileMapAdapter, FileMapStoreOptions, RemoteStoreOptions, Session, SessionContentBlock, SessionData, SessionMessage, SessionRun, SessionStore, SessionTurn, autoDetectAndConvert, createFileMapStore, createMemoryStore, createRemoteStore, createSession, fromAnthropic, fromOpenAI, loadSession, toAnthropic, toOpenAI };
|
package/dist/session.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { c as fromAnthropic, d as toOpenAI, i as autoDetectAndConvert, l as fromOpenAI, u as toAnthropic } from "./messages-
|
|
2
|
-
import { a as createFileMapStore, i as createMemoryStore, n as loadSession, r as createRemoteStore, t as createSession } from "./session-
|
|
1
|
+
import { c as fromAnthropic, d as toOpenAI, i as autoDetectAndConvert, l as fromOpenAI, u as toAnthropic } from "./messages-BBWakTN6.js";
|
|
2
|
+
import { a as createFileMapStore, i as createMemoryStore, n as loadSession, r as createRemoteStore, t as createSession } from "./session-DzfRacU_.js";
|
|
3
3
|
export { autoDetectAndConvert, createFileMapStore, createMemoryStore, createRemoteStore, createSession, fromAnthropic, fromOpenAI, loadSession, toAnthropic, toOpenAI };
|
package/dist/skills.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { A as SkillSource, D as SkillConfig, O as SkillDiagnostic, c as DeactivationReason, d as createSkillActivationState, j as SkillsConfig, k as SkillResource, l as SkillActivationState, o as ActivationVia, s as ActiveSkill, u as SkillActivationStateOptions } from "./agent-
|
|
2
|
-
import { S as installAllowedToolsGate, _ as inferSource, a as SkillValidationResult, b as buildCatalog, c as parseAllowedToolPattern, d as validateSkillName, f as resolveSkills, g as getDefaultScanPaths, h as discoverSkills, i as SkillValidationIssue, l as validateResourcePath, m as SourcedScanPath, n as writeSkillToDisk, o as isToolAllowedByUnion, p as interpolateShellCommands, r as writeSkillsToDisk, s as matchesAllowedTool, t as defineSkill, u as validateSkillForWrite, v as parseFrontmatter, x as IMPLICITLY_ALLOWED_SKILL_TOOLS, y as parseSkillFile } from "./index-
|
|
1
|
+
import { A as SkillSource, D as SkillConfig, O as SkillDiagnostic, c as DeactivationReason, d as createSkillActivationState, j as SkillsConfig, k as SkillResource, l as SkillActivationState, o as ActivationVia, s as ActiveSkill, u as SkillActivationStateOptions } from "./agent-CMAklak7.js";
|
|
2
|
+
import { S as installAllowedToolsGate, _ as inferSource, a as SkillValidationResult, b as buildCatalog, c as parseAllowedToolPattern, d as validateSkillName, f as resolveSkills, g as getDefaultScanPaths, h as discoverSkills, i as SkillValidationIssue, l as validateResourcePath, m as SourcedScanPath, n as writeSkillToDisk, o as isToolAllowedByUnion, p as interpolateShellCommands, r as writeSkillsToDisk, s as matchesAllowedTool, t as defineSkill, u as validateSkillForWrite, v as parseFrontmatter, x as IMPLICITLY_ALLOWED_SKILL_TOOLS, y as parseSkillFile } from "./index-CF5QwBiz.js";
|
|
3
3
|
export { ActivationVia, ActiveSkill, DeactivationReason, IMPLICITLY_ALLOWED_SKILL_TOOLS, SkillActivationState, SkillActivationStateOptions, SkillConfig, SkillDiagnostic, SkillResource, SkillSource, SkillValidationIssue, SkillValidationResult, SkillsConfig, SourcedScanPath, buildCatalog, createSkillActivationState, defineSkill, discoverSkills, getDefaultScanPaths, inferSource, installAllowedToolsGate, interpolateShellCommands, isToolAllowedByUnion, matchesAllowedTool, parseAllowedToolPattern, parseFrontmatter, parseSkillFile, resolveSkills, validateResourcePath, validateSkillForWrite, validateSkillName, writeSkillToDisk, writeSkillsToDisk };
|
package/dist/skills.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as validateResourcePath, a as discoverSkills, b as createSkillActivationState, c as parseFrontmatter, f as IMPLICITLY_ALLOWED_SKILL_TOOLS, g as parseAllowedToolPattern, h as matchesAllowedTool, i as writeSkillsToDisk, l as parseSkillFile, m as isToolAllowedByUnion, n as resolveSkills, o as getDefaultScanPaths, p as installAllowedToolsGate, r as writeSkillToDisk, s as inferSource, t as interpolateShellCommands, u as buildCatalog, v as validateSkillForWrite, y as validateSkillName } from "./interpolate-
|
|
1
|
+
import { _ as validateResourcePath, a as discoverSkills, b as createSkillActivationState, c as parseFrontmatter, f as IMPLICITLY_ALLOWED_SKILL_TOOLS, g as parseAllowedToolPattern, h as matchesAllowedTool, i as writeSkillsToDisk, l as parseSkillFile, m as isToolAllowedByUnion, n as resolveSkills, o as getDefaultScanPaths, p as installAllowedToolsGate, r as writeSkillToDisk, s as inferSource, t as interpolateShellCommands, u as buildCatalog, v as validateSkillForWrite, y as validateSkillName } from "./interpolate-Cvjy8gpk.js";
|
|
2
2
|
//#region src/skills/index.ts
|
|
3
3
|
/**
|
|
4
4
|
* Define an inline skill configuration.
|