zidane 6.5.0 → 6.5.2
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/{acp-B-AOovHz.js → acp-565X8nVP.js} +6 -7
- package/dist/{acp-B-AOovHz.js.map → acp-565X8nVP.js.map} +1 -1
- package/dist/acp-cli.js +6 -6
- package/dist/acp.d.ts +1 -1
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-SQ6rxfpT.js → agent-CvO7UOxv.js} +1427 -610
- package/dist/agent-CvO7UOxv.js.map +1 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -2
- package/dist/{anthropic-D3lkw5mv.js → anthropic-Dov2mC5e.js} +3 -3
- package/dist/{anthropic-D3lkw5mv.js.map → anthropic-Dov2mC5e.js.map} +1 -1
- package/dist/{auth-BzYbbdN_.js → auth-B8ZuBcKC.js} +5 -5
- package/dist/{auth-BzYbbdN_.js.map → auth-B8ZuBcKC.js.map} +1 -1
- package/dist/chat/pure.d.ts +4 -4
- package/dist/chat/pure.js +3 -3
- package/dist/chat.d.ts +114 -5
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +6 -6
- package/dist/context-segments-1mcqSCM3.js +494 -0
- package/dist/context-segments-1mcqSCM3.js.map +1 -0
- package/dist/{context-transport-CDBatvQc.js → context-transport-ClvadrlH.js} +3 -3
- package/dist/{context-transport-CDBatvQc.js.map → context-transport-ClvadrlH.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +3 -3
- package/dist/{index-CWBSrqI6.d.ts → index-DfERTrql.d.ts} +178 -6
- package/dist/index-DfERTrql.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9 -10
- package/dist/index.js.map +1 -1
- package/dist/{login-XZDS4K2o.js → login-Cl6ML2XN.js} +2 -2
- package/dist/{login-XZDS4K2o.js.map → login-Cl6ML2XN.js.map} +1 -1
- package/dist/{mcp-D27rP47I.js → mcp-DdntU47g.js} +3 -3
- package/dist/{mcp-D27rP47I.js.map → mcp-DdntU47g.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-C01kKGj0.js → messages-C_H9soi5.js} +2 -3
- package/dist/{messages-C01kKGj0.js.map → messages-C_H9soi5.js.map} +1 -1
- package/dist/{openai-compat-B3c_hzwf.js → openai-compat-CnHeo4Lz.js} +216 -3
- package/dist/openai-compat-CnHeo4Lz.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +2 -2
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-z1vU0b2Q.js → providers-B6mFe7OD.js} +3 -3
- package/dist/{providers-z1vU0b2Q.js.map → providers-B6mFe7OD.js.map} +1 -1
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +4 -4
- package/dist/restate.d.ts +178 -2
- package/dist/restate.d.ts.map +1 -1
- package/dist/restate.js +458 -4
- package/dist/restate.js.map +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-cr9VdoEO.js → session-VpLrNaFQ.js} +2 -2
- package/dist/{session-cr9VdoEO.js.map → session-VpLrNaFQ.js.map} +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/session.js +2 -2
- package/dist/skills.d.ts +1 -1
- package/dist/{stdio-O-m9WVkE.js → stdio-BQeCMmuK.js} +2 -2
- package/dist/{stdio-O-m9WVkE.js.map → stdio-BQeCMmuK.js.map} +1 -1
- package/dist/{stdio-loader-BSGQmG4L.js → stdio-loader-BN1l1ZAG.js} +2 -2
- package/dist/{stdio-loader-BSGQmG4L.js.map → stdio-loader-BN1l1ZAG.js.map} +1 -1
- package/dist/{tool-formatters-gFqQbzPY.d.ts → tool-formatters-B2gBbKaS.d.ts} +2 -2
- package/dist/{tool-formatters-gFqQbzPY.d.ts.map → tool-formatters-B2gBbKaS.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -2
- package/dist/{transcript-anchors-qCs3wZpu.d.ts → transcript-anchors-CeIxhKP7.d.ts} +2 -2
- package/dist/{transcript-anchors-qCs3wZpu.d.ts.map → transcript-anchors-CeIxhKP7.d.ts.map} +1 -1
- package/dist/{transcript-anchors-D4IxJifV.js → transcript-anchors-CmFKUw8M.js} +139 -19
- package/dist/transcript-anchors-CmFKUw8M.js.map +1 -0
- package/dist/tui.d.ts +74 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +819 -83
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-M9-jUyDd.d.ts → turn-operations-Dyv3Z-Ff.d.ts} +3 -3
- package/dist/{turn-operations-M9-jUyDd.d.ts.map → turn-operations-Dyv3Z-Ff.d.ts.map} +1 -1
- package/dist/{turn-operations-CAvi8NYt.js → turn-operations-iGu5J08j.js} +2 -2
- package/dist/{turn-operations-CAvi8NYt.js.map → turn-operations-iGu5J08j.js.map} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/{xai-BPwMQSuF.js → xai-Du6Wfnlb.js} +2 -2
- package/dist/{xai-BPwMQSuF.js.map → xai-Du6Wfnlb.js.map} +1 -1
- package/docs/CHAT.md +42 -1
- package/docs/INTERACTIONS.md +39 -0
- package/docs/RESTATE.md +102 -3
- package/docs/TUI.md +28 -2
- package/package.json +1 -1
- package/dist/agent-SQ6rxfpT.js.map +0 -1
- package/dist/aliasing-lVFbm81n.js +0 -217
- package/dist/aliasing-lVFbm81n.js.map +0 -1
- package/dist/context-breakdown-Dzo25N45.js +0 -155
- package/dist/context-breakdown-Dzo25N45.js.map +0 -1
- package/dist/edit-utils-EGosADZq.js +0 -234
- package/dist/edit-utils-EGosADZq.js.map +0 -1
- package/dist/index-CWBSrqI6.d.ts.map +0 -1
- package/dist/openai-compat-B3c_hzwf.js.map +0 -1
- package/dist/tool-state-DTs-3ZHH.js +0 -653
- package/dist/tool-state-DTs-3ZHH.js.map +0 -1
- package/dist/transcript-anchors-D4IxJifV.js.map +0 -1
|
@@ -1,653 +0,0 @@
|
|
|
1
|
-
import { o as toCanonicalName } from "./aliasing-lVFbm81n.js";
|
|
2
|
-
import { l as toolResultToText } from "./types-BIarq1qE.js";
|
|
3
|
-
import { i as styleReplacementForVia, n as resolveOldString } from "./edit-utils-EGosADZq.js";
|
|
4
|
-
import { resolve } from "node:path";
|
|
5
|
-
import { Buffer } from "node:buffer";
|
|
6
|
-
//#region src/tools/read-state.ts
|
|
7
|
-
const STATE = /* @__PURE__ */ new WeakMap();
|
|
8
|
-
/**
|
|
9
|
-
* Get or lazily create the per-session read-state map. Returns `undefined`
|
|
10
|
-
* when no session is provided.
|
|
11
|
-
*
|
|
12
|
-
* Most tool callers should prefer {@link resolveReadStateMap}, which
|
|
13
|
-
* additionally honors an explicit `ctx.readState` (the path
|
|
14
|
-
* `spawn`'s `shareReadState: true` uses to forward the parent's map
|
|
15
|
-
* into a sessionless child). Use this helper directly only when the
|
|
16
|
-
* Session-keyed map is exactly what you want and you don't need to
|
|
17
|
-
* accept an override.
|
|
18
|
-
*/
|
|
19
|
-
function getReadState(session) {
|
|
20
|
-
if (!session) return void 0;
|
|
21
|
-
let map = STATE.get(session);
|
|
22
|
-
if (!map) {
|
|
23
|
-
map = /* @__PURE__ */ new Map();
|
|
24
|
-
STATE.set(session, map);
|
|
25
|
-
}
|
|
26
|
-
return map;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Resolve the active read-state map from a tool context. An explicit
|
|
30
|
-
* `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to
|
|
31
|
-
* forward the parent's map into a sessionless child); otherwise the
|
|
32
|
-
* usual `Session`-keyed lookup applies.
|
|
33
|
-
*
|
|
34
|
-
* Tools should call this helper instead of `getReadState(ctx.session)`
|
|
35
|
-
* directly so the `shareReadState` plumbing is honored uniformly.
|
|
36
|
-
*/
|
|
37
|
-
function resolveReadStateMap(ctx) {
|
|
38
|
-
return ctx.readState ?? getReadState(ctx.session);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Mark a read-state entry as elided from the on-wire history WITHOUT
|
|
42
|
-
* un-registering it.
|
|
43
|
-
*
|
|
44
|
-
* Used by the loop's `elideStaleReads` pass. The entry must survive —
|
|
45
|
-
* deleting it made the `requireReadBeforeEdit` gate reject the very next
|
|
46
|
-
* edit on a file the model had demonstrably read and just edited (the
|
|
47
|
-
* gate's failure mode then defeats the gate: models degrade to raw shell
|
|
48
|
-
* edits that bypass the guard entirely). The `elided` flag instead
|
|
49
|
-
* defeats only the dedup stub: the next identical re-read returns the
|
|
50
|
-
* full body again, and `rememberRead` clears the flag.
|
|
51
|
-
*
|
|
52
|
-
* No-op when the path has no entry in the resolved map(s).
|
|
53
|
-
*/
|
|
54
|
-
function markReadStateElided(ctx, cwd, path) {
|
|
55
|
-
const key = readStateKey(cwd, path);
|
|
56
|
-
for (const map of [ctx.readState, ctx.session ? STATE.get(ctx.session) : void 0]) {
|
|
57
|
-
const entry = map?.get(key);
|
|
58
|
-
if (entry && entry.elided !== true) map.set(key, {
|
|
59
|
-
...entry,
|
|
60
|
-
elided: true
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Record one `requireReadBeforeEdit` drift refusal against `entry` and
|
|
66
|
-
* return the consecutive count (1 for the first). The caller composes the
|
|
67
|
-
* refusal message with {@link staleRefusalHint}.
|
|
68
|
-
*/
|
|
69
|
-
function noteStaleRejection(map, key, entry) {
|
|
70
|
-
const count = (entry.staleRejections ?? 0) + 1;
|
|
71
|
-
map.set(key, {
|
|
72
|
-
...entry,
|
|
73
|
-
staleRejections: count
|
|
74
|
-
});
|
|
75
|
-
return count;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Escalating suffix for a drift refusal. Empty on the first refusal — the
|
|
79
|
-
* base "re-read the file" advice is correct and sufficient. From the second
|
|
80
|
-
* consecutive refusal on, the model has demonstrably re-read and been
|
|
81
|
-
* refused again, so repeating the same advice reads as a broken tool;
|
|
82
|
-
* name the real hypothesis (an external writer) instead.
|
|
83
|
-
*/
|
|
84
|
-
function staleRefusalHint(count) {
|
|
85
|
-
if (count < 2) return "";
|
|
86
|
-
return ` This is refusal #${count} since your last read — a process outside this session (editor auto-save, formatter, codegen watcher) may be rewriting the file between your reads.`;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Canonical read-state key for a `(cwd, path)` pair.
|
|
90
|
-
*
|
|
91
|
-
* `ctx.execution.readFile(handle, path)` resolves the model-provided
|
|
92
|
-
* path against `handle.cwd` before touching disk — so `src/App.tsx`,
|
|
93
|
-
* `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**
|
|
94
|
-
* bytes. The read-state map MUST mirror that resolution: without it,
|
|
95
|
-
* a model that reads `src/App.tsx` and then edits `./src/App.tsx`
|
|
96
|
-
* trips the `requireReadBeforeEdit` gate for a file it demonstrably
|
|
97
|
-
* already saw (the gate's "has not been read in this session" message
|
|
98
|
-
* fires on a stale key).
|
|
99
|
-
*
|
|
100
|
-
* `node:path`'s `resolve(cwd, path)` short-circuits when `path` is
|
|
101
|
-
* already absolute, so absolute and relative shapes converge on the
|
|
102
|
-
* same canonical form. We don't `realpath()` symlinks — the file IS
|
|
103
|
-
* the path the model addressed, not the realpath behind it; the host's
|
|
104
|
-
* execution context decides how to dereference.
|
|
105
|
-
*/
|
|
106
|
-
function readStateKey(cwd, path) {
|
|
107
|
-
return resolve(cwd, path);
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* 64-bit content hash: two independent 32-bit FNV-1a-style lanes (different
|
|
111
|
-
* offset bases + multipliers) concatenated as 16 hex chars. Fast,
|
|
112
|
-
* non-cryptographic — but wide enough that the `requireReadBeforeEdit`
|
|
113
|
-
* drift gate, which silently corrupts a file on a false "unchanged"
|
|
114
|
-
* verdict, isn't betting on a 32-bit birthday bound (~1 in 4 billion per
|
|
115
|
-
* pair; uncomfortably reachable over many files × many sessions). Entries
|
|
116
|
-
* live only in per-session in-memory maps, so widening needs no
|
|
117
|
-
* stored-format migration. Still cheaper than allocating a Buffer and
|
|
118
|
-
* pulling in `crypto`.
|
|
119
|
-
*/
|
|
120
|
-
function hashContent(text) {
|
|
121
|
-
let h1 = 2166136261;
|
|
122
|
-
let h2 = 2128831031;
|
|
123
|
-
for (let i = 0; i < text.length; i++) {
|
|
124
|
-
const c = text.charCodeAt(i);
|
|
125
|
-
h1 = Math.imul(h1 ^ c, 16777619) >>> 0;
|
|
126
|
-
h2 = Math.imul(h2 ^ c, 2246822507) >>> 0;
|
|
127
|
-
}
|
|
128
|
-
return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
|
|
129
|
-
}
|
|
130
|
-
const READ_FILE_DEFAULT_OFFSET = 1;
|
|
131
|
-
const READ_FILE_DEFAULT_LIMIT = 2e3;
|
|
132
|
-
const READ_FILE_DEFAULT_MAX_BYTES = 262144;
|
|
133
|
-
const READ_FILE_FOOTER_RE = /\n\n…(?:read lines|truncated)/;
|
|
134
|
-
const READ_FILE_LINE_RE = /^\d{1,9}\t(.*)$/;
|
|
135
|
-
/**
|
|
136
|
-
* Rebuild the per-session read-state map from durable session turns.
|
|
137
|
-
*
|
|
138
|
-
* Durable replay runtimes may return recorded tool results without re-running
|
|
139
|
-
* the tool body, so in-memory side effects like `readState.set(...)` can be
|
|
140
|
-
* missing even though the transcript contains the successful `read_file`.
|
|
141
|
-
* This helper is intentionally opt-in: live hosts keep the normal side-effect
|
|
142
|
-
* behavior unless they call it themselves.
|
|
143
|
-
*
|
|
144
|
-
* Hydration only fills missing keys. Live in-memory state may already reflect a
|
|
145
|
-
* newer read or edit from the current run, and durable history cannot prove it
|
|
146
|
-
* is fresher. Relative paths are resolved against the current execution cwd,
|
|
147
|
-
* matching replay hosts that run sessions from a stable workspace.
|
|
148
|
-
*/
|
|
149
|
-
function hydrateReadStateFromSession(session, cwd, options = {}) {
|
|
150
|
-
if (!session) return 0;
|
|
151
|
-
const readState = getReadState(session);
|
|
152
|
-
if (!readState) return 0;
|
|
153
|
-
const normalized = normalizeHydrateReadStateOptions(options);
|
|
154
|
-
const pending = /* @__PURE__ */ new Map();
|
|
155
|
-
const known = /* @__PURE__ */ new Map();
|
|
156
|
-
const preexistingKeys = new Set(readState.keys());
|
|
157
|
-
let hydrated = 0;
|
|
158
|
-
for (const turn of session.turns) for (const block of turn.content) {
|
|
159
|
-
if (block.type === "tool_call") {
|
|
160
|
-
const canonicalName = canonicalToolCallName(block.name, normalized);
|
|
161
|
-
if (canonicalName === normalized.readFileToolName || canonicalName === normalized.writeFileToolName || canonicalName === normalized.editToolName || canonicalName === normalized.multiEditToolName) pending.set(block.id, {
|
|
162
|
-
name: canonicalName,
|
|
163
|
-
input: block.input
|
|
164
|
-
});
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (block.type !== "tool_result") continue;
|
|
168
|
-
const call = pending.get(block.callId);
|
|
169
|
-
if (!call) continue;
|
|
170
|
-
pending.delete(block.callId);
|
|
171
|
-
if (block.isError === true) continue;
|
|
172
|
-
const next = stateFromToolResult(call, toolResultToText(block.output), turn, cwd, normalized, known);
|
|
173
|
-
if (!next) continue;
|
|
174
|
-
if (!preexistingKeys.has(next.key)) {
|
|
175
|
-
readState.set(next.key, next.file.entry);
|
|
176
|
-
hydrated += 1;
|
|
177
|
-
}
|
|
178
|
-
known.set(next.key, next.file);
|
|
179
|
-
}
|
|
180
|
-
return hydrated;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Async replay hydrator for durable hosts that can cheaply read the current
|
|
184
|
-
* full file through their execution context. Transcript-only reconstruction is
|
|
185
|
-
* still preferred for full reads; partial/truncated read_file entries fall back
|
|
186
|
-
* to `readFileForHash` and hash those full current bytes.
|
|
187
|
-
*/
|
|
188
|
-
async function hydrateReadStateFromSessionAsync(session, cwd, options = {}) {
|
|
189
|
-
if (!session) return 0;
|
|
190
|
-
const readState = getReadState(session);
|
|
191
|
-
if (!readState) return 0;
|
|
192
|
-
const normalized = normalizeHydrateReadStateOptions(options);
|
|
193
|
-
const pending = /* @__PURE__ */ new Map();
|
|
194
|
-
const known = /* @__PURE__ */ new Map();
|
|
195
|
-
const preexistingKeys = new Set(readState.keys());
|
|
196
|
-
let hydrated = 0;
|
|
197
|
-
for (const turn of session.turns) for (const block of turn.content) {
|
|
198
|
-
if (block.type === "tool_call") {
|
|
199
|
-
const canonicalName = canonicalToolCallName(block.name, normalized);
|
|
200
|
-
if (canonicalName === normalized.readFileToolName || canonicalName === normalized.writeFileToolName || canonicalName === normalized.editToolName || canonicalName === normalized.multiEditToolName) pending.set(block.id, {
|
|
201
|
-
name: canonicalName,
|
|
202
|
-
input: block.input
|
|
203
|
-
});
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
if (block.type !== "tool_result") continue;
|
|
207
|
-
const call = pending.get(block.callId);
|
|
208
|
-
if (!call) continue;
|
|
209
|
-
pending.delete(block.callId);
|
|
210
|
-
if (block.isError === true) continue;
|
|
211
|
-
const next = await stateFromToolResultAsync(call, toolResultToText(block.output), turn, cwd, normalized, known);
|
|
212
|
-
if (!next) continue;
|
|
213
|
-
if (!preexistingKeys.has(next.key)) {
|
|
214
|
-
readState.set(next.key, next.file.entry);
|
|
215
|
-
hydrated += 1;
|
|
216
|
-
}
|
|
217
|
-
known.set(next.key, next.file);
|
|
218
|
-
}
|
|
219
|
-
return hydrated;
|
|
220
|
-
}
|
|
221
|
-
function normalizeHydrateReadStateOptions(options) {
|
|
222
|
-
return {
|
|
223
|
-
readFileToolName: options.readFileToolName ?? "read_file",
|
|
224
|
-
writeFileToolName: options.writeFileToolName ?? "write_file",
|
|
225
|
-
editToolName: options.editToolName ?? "edit",
|
|
226
|
-
multiEditToolName: options.multiEditToolName ?? "multi_edit",
|
|
227
|
-
defaultLineNumbers: options.defaultLineNumbers ?? true,
|
|
228
|
-
...options.readFileForHash ? { readFileForHash: options.readFileForHash } : {},
|
|
229
|
-
...options.aliasMaps ? { aliasMaps: options.aliasMaps } : {}
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Resolve a transcript tool-call name to its canonical form for matching.
|
|
234
|
-
* Honors `aliasMaps` (wire → canonical) when present; otherwise the name is
|
|
235
|
-
* already canonical (zidane persists canonical names) and passes through.
|
|
236
|
-
*/
|
|
237
|
-
function canonicalToolCallName(name, options) {
|
|
238
|
-
return options.aliasMaps ? toCanonicalName(name, options.aliasMaps) : name;
|
|
239
|
-
}
|
|
240
|
-
function stateFromToolResult(call, output, resultTurn, cwd, options, known) {
|
|
241
|
-
if (call.name === options.readFileToolName) return readFileStateFromResult(call.input, output, resultTurn, cwd, options.defaultLineNumbers);
|
|
242
|
-
if (call.name === options.writeFileToolName) return writeFileStateFromResult(call.input, output, resultTurn, cwd);
|
|
243
|
-
if (call.name === options.editToolName) return editStateFromResult(call.input, output, resultTurn, cwd, known);
|
|
244
|
-
if (call.name === options.multiEditToolName) return multiEditStateFromResult(call.input, output, resultTurn, cwd, known);
|
|
245
|
-
}
|
|
246
|
-
async function stateFromToolResultAsync(call, output, resultTurn, cwd, options, known) {
|
|
247
|
-
if (call.name === options.readFileToolName) return await readFileStateFromResultAsync(call.input, output, resultTurn, cwd, {
|
|
248
|
-
defaultLineNumbers: options.defaultLineNumbers,
|
|
249
|
-
readFileForHash: options.readFileForHash
|
|
250
|
-
});
|
|
251
|
-
return stateFromToolResult(call, output, resultTurn, cwd, options, known);
|
|
252
|
-
}
|
|
253
|
-
function readFileStateFromResult(input, output, resultTurn, cwd, defaultLineNumbers) {
|
|
254
|
-
const path = typeof input.path === "string" ? input.path : void 0;
|
|
255
|
-
if (!path || !isSuccessfulFullReadOutput(output)) return void 0;
|
|
256
|
-
const lineNumbers = typeof input.lineNumbers === "boolean" ? input.lineNumbers : defaultLineNumbers;
|
|
257
|
-
const content = lineNumbers ? stripReadFileLineNumbers(output) : output;
|
|
258
|
-
if (content === void 0) return void 0;
|
|
259
|
-
const entry = {
|
|
260
|
-
contentHash: hashContent(content),
|
|
261
|
-
offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),
|
|
262
|
-
limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),
|
|
263
|
-
maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),
|
|
264
|
-
lineNumbers,
|
|
265
|
-
mtimeMs: resultTurn.createdAt
|
|
266
|
-
};
|
|
267
|
-
return {
|
|
268
|
-
key: readStateKey(cwd, path),
|
|
269
|
-
file: {
|
|
270
|
-
content,
|
|
271
|
-
entry
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
async function readFileStateFromResultAsync(input, output, resultTurn, cwd, options) {
|
|
276
|
-
const reconstructed = readFileStateFromResult(input, output, resultTurn, cwd, options.defaultLineNumbers);
|
|
277
|
-
if (reconstructed) return reconstructed;
|
|
278
|
-
const path = typeof input.path === "string" ? input.path : void 0;
|
|
279
|
-
if (!path || !options.readFileForHash || !isSuccessfulReadOutput(output)) return void 0;
|
|
280
|
-
let content;
|
|
281
|
-
try {
|
|
282
|
-
content = await options.readFileForHash(path);
|
|
283
|
-
} catch {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
if (typeof content !== "string") return void 0;
|
|
287
|
-
const lineNumbers = typeof input.lineNumbers === "boolean" ? input.lineNumbers : options.defaultLineNumbers;
|
|
288
|
-
const entry = {
|
|
289
|
-
contentHash: hashContent(content),
|
|
290
|
-
offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),
|
|
291
|
-
limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),
|
|
292
|
-
maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),
|
|
293
|
-
lineNumbers,
|
|
294
|
-
mtimeMs: resultTurn.createdAt
|
|
295
|
-
};
|
|
296
|
-
return {
|
|
297
|
-
key: readStateKey(cwd, path),
|
|
298
|
-
file: {
|
|
299
|
-
content,
|
|
300
|
-
entry
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
function writeFileStateFromResult(input, output, resultTurn, cwd) {
|
|
305
|
-
const path = typeof input.path === "string" ? input.path : void 0;
|
|
306
|
-
const content = typeof input.content === "string" ? input.content : void 0;
|
|
307
|
-
if (!path || content === void 0) return void 0;
|
|
308
|
-
if (!output.startsWith(`Created ${path} (`) && !output.startsWith(`Updated ${path} (`)) return void 0;
|
|
309
|
-
return {
|
|
310
|
-
key: readStateKey(cwd, path),
|
|
311
|
-
file: {
|
|
312
|
-
content,
|
|
313
|
-
entry: {
|
|
314
|
-
contentHash: hashContent(content),
|
|
315
|
-
offset: 0,
|
|
316
|
-
limit: Number.POSITIVE_INFINITY,
|
|
317
|
-
maxBytes: Number.POSITIVE_INFINITY,
|
|
318
|
-
mtimeMs: resultTurn.createdAt,
|
|
319
|
-
elided: true
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
function editStateFromResult(input, output, resultTurn, cwd, known) {
|
|
325
|
-
const path = typeof input.path === "string" ? input.path : void 0;
|
|
326
|
-
const oldString = typeof input.old_string === "string" ? input.old_string : void 0;
|
|
327
|
-
const newString = typeof input.new_string === "string" ? input.new_string : void 0;
|
|
328
|
-
if (!path || oldString === void 0 || newString === void 0) return void 0;
|
|
329
|
-
if (!output.startsWith(`Edited ${path}: replaced `)) return void 0;
|
|
330
|
-
const key = readStateKey(cwd, path);
|
|
331
|
-
const prior = known.get(key);
|
|
332
|
-
if (!prior) return void 0;
|
|
333
|
-
const nextContent = applyEdit(prior.content, oldString, newString, input.replace_all === true);
|
|
334
|
-
if (nextContent === void 0) return void 0;
|
|
335
|
-
return {
|
|
336
|
-
key,
|
|
337
|
-
file: {
|
|
338
|
-
content: nextContent,
|
|
339
|
-
entry: {
|
|
340
|
-
...prior.entry,
|
|
341
|
-
contentHash: hashContent(nextContent),
|
|
342
|
-
mtimeMs: resultTurn.createdAt,
|
|
343
|
-
elided: true
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
function multiEditStateFromResult(input, output, resultTurn, cwd, known) {
|
|
349
|
-
const path = typeof input.path === "string" ? input.path : void 0;
|
|
350
|
-
const edits = Array.isArray(input.edits) ? input.edits : void 0;
|
|
351
|
-
if (!path || !edits) return void 0;
|
|
352
|
-
if (!output.startsWith(`Edited ${path}: applied `)) return void 0;
|
|
353
|
-
const key = readStateKey(cwd, path);
|
|
354
|
-
const prior = known.get(key);
|
|
355
|
-
if (!prior) return void 0;
|
|
356
|
-
const applied = parseMultiEditAppliedSteps(output, edits.length);
|
|
357
|
-
let current = prior.content;
|
|
358
|
-
for (let i = 0; i < edits.length; i++) {
|
|
359
|
-
if (!applied.has(i)) continue;
|
|
360
|
-
const step = edits[i];
|
|
361
|
-
if (typeof step.old_string !== "string" || typeof step.new_string !== "string") return void 0;
|
|
362
|
-
const next = applyEdit(current, step.old_string, step.new_string, step.replace_all === true);
|
|
363
|
-
if (next === void 0) return void 0;
|
|
364
|
-
current = next;
|
|
365
|
-
}
|
|
366
|
-
return {
|
|
367
|
-
key,
|
|
368
|
-
file: {
|
|
369
|
-
content: current,
|
|
370
|
-
entry: {
|
|
371
|
-
...prior.entry,
|
|
372
|
-
contentHash: hashContent(current),
|
|
373
|
-
mtimeMs: resultTurn.createdAt,
|
|
374
|
-
elided: true
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
function applyEdit(content, oldString, newString, replaceAll) {
|
|
380
|
-
const match = resolveOldString(content, oldString);
|
|
381
|
-
if (!match) return void 0;
|
|
382
|
-
if (match.occurrences > 1 && !replaceAll) return void 0;
|
|
383
|
-
const styledReplacement = styleReplacementForVia(newString, match.via, match.actual);
|
|
384
|
-
return replaceAll ? content.split(match.actual).join(styledReplacement) : content.replace(match.actual, styledReplacement);
|
|
385
|
-
}
|
|
386
|
-
function parseMultiEditAppliedSteps(output, editCount) {
|
|
387
|
-
const annotationMatch = /<edit-outcomes>\n([\s\S]*?)\n<\/edit-outcomes>/.exec(output);
|
|
388
|
-
if (!annotationMatch) return new Set(Array.from({ length: editCount }, (_, i) => i));
|
|
389
|
-
const applied = /* @__PURE__ */ new Set();
|
|
390
|
-
for (const line of annotationMatch[1].split("\n")) {
|
|
391
|
-
const match = /^#(\d+) applied$/.exec(line.trim());
|
|
392
|
-
if (!match) continue;
|
|
393
|
-
const index = Number(match[1]) - 1;
|
|
394
|
-
if (index >= 0 && index < editCount) applied.add(index);
|
|
395
|
-
}
|
|
396
|
-
return applied;
|
|
397
|
-
}
|
|
398
|
-
function isSuccessfulFullReadOutput(output) {
|
|
399
|
-
if (!isSuccessfulReadOutput(output) || READ_FILE_FOOTER_RE.test(output)) return false;
|
|
400
|
-
return true;
|
|
401
|
-
}
|
|
402
|
-
function isSuccessfulReadOutput(output) {
|
|
403
|
-
if (output.length === 0) return true;
|
|
404
|
-
if (output.startsWith("File not found:") || output.startsWith("[binary file:") || output.startsWith("Image:") || output.startsWith("Document:") || output.startsWith("[image too large to inline:") || output.startsWith("[document:") || output.startsWith("[document too large to attach:") || output.startsWith("Image read failed:") || output.startsWith("Document read failed:") || output.includes(" unchanged since the previous read in this session ")) return false;
|
|
405
|
-
return true;
|
|
406
|
-
}
|
|
407
|
-
function stripReadFileLineNumbers(output) {
|
|
408
|
-
const lines = output.split("\n");
|
|
409
|
-
const stripped = [];
|
|
410
|
-
for (const line of lines) {
|
|
411
|
-
const match = READ_FILE_LINE_RE.exec(line);
|
|
412
|
-
if (!match) return void 0;
|
|
413
|
-
stripped.push(match[1]);
|
|
414
|
-
}
|
|
415
|
-
return stripped.join("\n");
|
|
416
|
-
}
|
|
417
|
-
function normalizeReadInteger(value, fallback) {
|
|
418
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return fallback;
|
|
419
|
-
return Math.floor(value);
|
|
420
|
-
}
|
|
421
|
-
const TOOL_DEDUP_STATE = /* @__PURE__ */ new WeakMap();
|
|
422
|
-
/**
|
|
423
|
-
* Get or lazily create the per-session tool-dedup map. Returns `undefined`
|
|
424
|
-
* when no session is provided — middleware should treat that as "no dedup".
|
|
425
|
-
*/
|
|
426
|
-
function getToolDedupState(session) {
|
|
427
|
-
if (!session) return void 0;
|
|
428
|
-
let map = TOOL_DEDUP_STATE.get(session);
|
|
429
|
-
if (!map) {
|
|
430
|
-
map = /* @__PURE__ */ new Map();
|
|
431
|
-
TOOL_DEDUP_STATE.set(session, map);
|
|
432
|
-
}
|
|
433
|
-
return map;
|
|
434
|
-
}
|
|
435
|
-
//#endregion
|
|
436
|
-
//#region src/tool-state.ts
|
|
437
|
-
/**
|
|
438
|
-
* Durable key/value state for tools.
|
|
439
|
-
*
|
|
440
|
-
* A tool reaches this through {@link ToolContext.state} — a plain,
|
|
441
|
-
* SYNCHRONOUS map view. Synchronous is not a convenience choice: under a
|
|
442
|
-
* durable-execution runtime the tool body runs inside a journaled action
|
|
443
|
-
* (Restate's `ctx.run`), and a durable read (`ctx.get`) is illegal in
|
|
444
|
-
* there. So the flow is split into three phases the runtime can journal
|
|
445
|
-
* independently:
|
|
446
|
-
*
|
|
447
|
-
* 1. **hydrate** — load the snapshot ONCE per agent, outside any
|
|
448
|
-
* journaled action, before the first tool dispatch.
|
|
449
|
-
* 2. **stage** — the tool mutates its per-call view. Mutations are
|
|
450
|
-
* recorded as an ordered {@link ToolStateOp} list, NOT written
|
|
451
|
-
* through: they're plain data, so a durable adapter can journal them
|
|
452
|
-
* alongside the tool result.
|
|
453
|
-
* 3. **commit** — the loop applies the staged ops through
|
|
454
|
-
* {@link ToolStateStore.commit} after the call settles, outside the
|
|
455
|
-
* journaled action.
|
|
456
|
-
*
|
|
457
|
-
* The invariant that makes this replay-safe:
|
|
458
|
-
*
|
|
459
|
-
* > A staged mutation is committed only if it came back OUT of a journal
|
|
460
|
-
* > entry.
|
|
461
|
-
*
|
|
462
|
-
* A durable adapter (see `restateTool`) reads {@link StagedToolState.pending}
|
|
463
|
-
* from inside its journaled closure, ships the ops out as part of the
|
|
464
|
-
* journaled value, and calls {@link StagedToolState.restore} outside it. On
|
|
465
|
-
* replay the tool body never runs, so nothing is staged live — `restore`
|
|
466
|
-
* re-seeds the exact same ops from the journal and the commit re-issues an
|
|
467
|
-
* identical write. Paths that produced no journal entry (a fatal error, a
|
|
468
|
-
* control abort, a dead attempt) call {@link StagedToolState.clear} so live
|
|
469
|
-
* mutations from an attempt nobody recorded can never reach the store.
|
|
470
|
-
*
|
|
471
|
-
* The store seam is deliberately runtime-agnostic: `zidane/restate` ships
|
|
472
|
-
* `restateToolStateStore`, but a Redis / Postgres / file-backed
|
|
473
|
-
* implementation is the same two methods. With no store configured the
|
|
474
|
-
* controller is in-memory only — `ctx.state` still works everywhere
|
|
475
|
-
* (local CLI, tests, subagents), it just doesn't outlive the process.
|
|
476
|
-
*/
|
|
477
|
-
/**
|
|
478
|
-
* Default per-value size budget: 64 KiB of JSON.
|
|
479
|
-
*
|
|
480
|
-
* Deliberately far below any backend's hard ceiling (Restate's journal-entry
|
|
481
|
-
* limit is 32 MiB) because this is a design guard, not a protocol limit.
|
|
482
|
-
* Durable tool state is for flags, ids, counters, and cursors; a value that
|
|
483
|
-
* doesn't fit in 64 KiB is a payload, and payloads belong in the tool result
|
|
484
|
-
* or in storage the tool owns. Raise it on the store if you disagree — but
|
|
485
|
-
* know that everything here is rewritten on every commit.
|
|
486
|
-
*/
|
|
487
|
-
const DEFAULT_MAX_VALUE_BYTES = 64 * 1024;
|
|
488
|
-
/** Brand so {@link stagedToolState} can recognise views this module made. */
|
|
489
|
-
const STAGED = Symbol("zidane.toolState.staged");
|
|
490
|
-
/**
|
|
491
|
-
* Resolve the staging handle behind a view. Returns `undefined` for a
|
|
492
|
-
* missing view or a foreign implementation (a host that supplied its own
|
|
493
|
-
* `ToolStateView`), which lets adapters degrade to "no durable staging"
|
|
494
|
-
* instead of throwing.
|
|
495
|
-
*/
|
|
496
|
-
function stagedToolState(view) {
|
|
497
|
-
if (!view) return void 0;
|
|
498
|
-
return view[STAGED] ?? void 0;
|
|
499
|
-
}
|
|
500
|
-
function createToolStateController(store) {
|
|
501
|
-
const snapshot = /* @__PURE__ */ new Map();
|
|
502
|
-
const views = /* @__PURE__ */ new Map();
|
|
503
|
-
const maxValueBytes = store?.maxValueBytes ?? 65536;
|
|
504
|
-
let hydrated;
|
|
505
|
-
const hydrate = () => {
|
|
506
|
-
if (hydrated) return hydrated;
|
|
507
|
-
hydrated = (async () => {
|
|
508
|
-
if (!store) return;
|
|
509
|
-
const loaded = await store.load();
|
|
510
|
-
for (const [key, value] of Object.entries(loaded ?? {})) if (!snapshot.has(key)) snapshot.set(key, value);
|
|
511
|
-
})().catch((err) => {
|
|
512
|
-
hydrated = void 0;
|
|
513
|
-
throw err;
|
|
514
|
-
});
|
|
515
|
-
return hydrated;
|
|
516
|
-
};
|
|
517
|
-
const begin = (callId) => {
|
|
518
|
-
const existing = views.get(callId);
|
|
519
|
-
if (existing) return existing;
|
|
520
|
-
const view = createStagingView(snapshot, maxValueBytes);
|
|
521
|
-
views.set(callId, view);
|
|
522
|
-
return view;
|
|
523
|
-
};
|
|
524
|
-
const commit = async (callId) => {
|
|
525
|
-
const view = views.get(callId);
|
|
526
|
-
if (!view) return;
|
|
527
|
-
views.delete(callId);
|
|
528
|
-
const ops = view[STAGED].pending();
|
|
529
|
-
if (ops.length === 0) return;
|
|
530
|
-
await store?.commit(ops);
|
|
531
|
-
applyOps(snapshot, ops);
|
|
532
|
-
};
|
|
533
|
-
return {
|
|
534
|
-
hydrate,
|
|
535
|
-
begin,
|
|
536
|
-
commit,
|
|
537
|
-
discard: (callId) => {
|
|
538
|
-
views.delete(callId);
|
|
539
|
-
},
|
|
540
|
-
pending: (callId) => views.get(callId)?.[STAGED].pending() ?? [],
|
|
541
|
-
snapshot: () => Object.fromEntries(snapshot)
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Build a view over `snapshot` that records writes instead of applying
|
|
546
|
-
* them. Reads layer staged ops on top of the snapshot so a tool sees its
|
|
547
|
-
* own writes back — including deletions, which read as absent.
|
|
548
|
-
*/
|
|
549
|
-
function createStagingView(snapshot, maxValueBytes) {
|
|
550
|
-
let ops = [];
|
|
551
|
-
const latest = /* @__PURE__ */ new Map();
|
|
552
|
-
const stage = (op) => {
|
|
553
|
-
ops.push(op);
|
|
554
|
-
latest.set(op.key, op);
|
|
555
|
-
};
|
|
556
|
-
const resolve = (key) => {
|
|
557
|
-
const staged = latest.get(key);
|
|
558
|
-
if (staged) return staged.delete === true ? { present: false } : {
|
|
559
|
-
present: true,
|
|
560
|
-
value: staged.value
|
|
561
|
-
};
|
|
562
|
-
return snapshot.has(key) ? {
|
|
563
|
-
present: true,
|
|
564
|
-
value: snapshot.get(key)
|
|
565
|
-
} : { present: false };
|
|
566
|
-
};
|
|
567
|
-
const view = {
|
|
568
|
-
get: (key) => {
|
|
569
|
-
const resolved = resolve(key);
|
|
570
|
-
return resolved.present ? resolved.value : void 0;
|
|
571
|
-
},
|
|
572
|
-
set: (key, value) => {
|
|
573
|
-
assertStorable(key, value, maxValueBytes);
|
|
574
|
-
if (value === void 0) stage({
|
|
575
|
-
key,
|
|
576
|
-
delete: true
|
|
577
|
-
});
|
|
578
|
-
else stage({
|
|
579
|
-
key,
|
|
580
|
-
value
|
|
581
|
-
});
|
|
582
|
-
},
|
|
583
|
-
delete: (key) => {
|
|
584
|
-
stage({
|
|
585
|
-
key,
|
|
586
|
-
delete: true
|
|
587
|
-
});
|
|
588
|
-
},
|
|
589
|
-
has: (key) => resolve(key).present,
|
|
590
|
-
keys: () => {
|
|
591
|
-
const keys = new Set(snapshot.keys());
|
|
592
|
-
for (const [key, op] of latest) if (op.delete === true) keys.delete(key);
|
|
593
|
-
else keys.add(key);
|
|
594
|
-
return [...keys].sort();
|
|
595
|
-
},
|
|
596
|
-
[STAGED]: {
|
|
597
|
-
pending: () => [...ops],
|
|
598
|
-
restore: (restored) => {
|
|
599
|
-
ops = [...restored];
|
|
600
|
-
latest.clear();
|
|
601
|
-
for (const op of ops) latest.set(op.key, op);
|
|
602
|
-
},
|
|
603
|
-
clear: () => {
|
|
604
|
-
ops = [];
|
|
605
|
-
latest.clear();
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
Object.defineProperty(view, STAGED, {
|
|
610
|
-
value: view[STAGED],
|
|
611
|
-
enumerable: false
|
|
612
|
-
});
|
|
613
|
-
return view;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Reject a value that can't survive the trip to the store, at the moment the
|
|
617
|
-
* tool stages it.
|
|
618
|
-
*
|
|
619
|
-
* Scalars are skipped: they're bounded, and paying a `JSON.stringify` per
|
|
620
|
-
* `set` on a hot tool would be a real cost for no signal. Strings and objects
|
|
621
|
-
* are measured in UTF-8 bytes, which is what both the journal and the store
|
|
622
|
-
* actually spend.
|
|
623
|
-
*/
|
|
624
|
-
function assertStorable(key, value, maxValueBytes) {
|
|
625
|
-
if (value === void 0 || value === null || typeof value === "boolean" || typeof value === "number") return;
|
|
626
|
-
if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") throw new TypeError(`tool state value for "${key}" is a ${typeof value}, which cannot cross a durable store — use a JSON-serializable value.`);
|
|
627
|
-
let json;
|
|
628
|
-
try {
|
|
629
|
-
json = typeof value === "string" ? value : JSON.stringify(value) ?? "";
|
|
630
|
-
} catch (err) {
|
|
631
|
-
throw new TypeError(`tool state value for "${key}" is not JSON-serializable (${err instanceof Error ? err.message : String(err)}) — durable tool state crosses a journal, so cycles and exotic values can't be stored.`);
|
|
632
|
-
}
|
|
633
|
-
const bytes = Buffer.byteLength(json, "utf8");
|
|
634
|
-
if (bytes > maxValueBytes) throw new RangeError(`tool state value for "${key}" is ${formatBytes(bytes)}, over the ${formatBytes(maxValueBytes)} limit. Durable tool state is for flags, ids, counters, and cursors — it is rewritten on every commit and crosses a journal entry. Keep the payload in the tool result or in storage the tool owns, and store a reference here.`);
|
|
635
|
-
}
|
|
636
|
-
function formatBytes(bytes) {
|
|
637
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
638
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
639
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
640
|
-
}
|
|
641
|
-
/** Apply ops to a snapshot map in order — last write per key wins. */
|
|
642
|
-
function applyOps(snapshot, ops) {
|
|
643
|
-
for (const op of ops) if (op.delete === true) snapshot.delete(op.key);
|
|
644
|
-
else snapshot.set(op.key, op.value);
|
|
645
|
-
}
|
|
646
|
-
/** Runtime guard for a journaled op list — adapters validate before restoring. */
|
|
647
|
-
function isToolStateOps(value) {
|
|
648
|
-
return Array.isArray(value) && value.every((op) => typeof op === "object" && op !== null && typeof op.key === "string" && (op.delete === true || "value" in op));
|
|
649
|
-
}
|
|
650
|
-
//#endregion
|
|
651
|
-
export { getReadState as a, hydrateReadStateFromSession as c, noteStaleRejection as d, readStateKey as f, stagedToolState as i, hydrateReadStateFromSessionAsync as l, staleRefusalHint as m, createToolStateController as n, getToolDedupState as o, resolveReadStateMap as p, isToolStateOps as r, hashContent as s, DEFAULT_MAX_VALUE_BYTES as t, markReadStateElided as u };
|
|
652
|
-
|
|
653
|
-
//# sourceMappingURL=tool-state-DTs-3ZHH.js.map
|