zidane 6.4.4 → 6.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/acp-cli.js +7 -6
- package/dist/acp-cli.js.map +1 -1
- package/dist/{acp-BLO58K3F.js → acp-o-LQAjFw.js} +4 -5
- package/dist/{acp-BLO58K3F.js.map → acp-o-LQAjFw.js.map} +1 -1
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-8sUadp5M.js → agent-BVNkC1h4.js} +886 -113
- package/dist/agent-BVNkC1h4.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 +3 -3
- package/dist/chat/pure.js +2 -2
- package/dist/chat.d.ts +13 -44
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +8 -7
- package/dist/chat.js.map +1 -1
- package/dist/{edit-utils-EGosADZq.js → context-breakdown-BLF10tOO.js} +154 -2
- package/dist/context-breakdown-BLF10tOO.js.map +1 -0
- package/dist/{context-transport-D29WvHcm.js → context-transport-D-daWOmp.js} +3 -3
- package/dist/{context-transport-D29WvHcm.js.map → context-transport-D-daWOmp.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 +2 -2
- package/dist/extensions.js +2 -2
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +3 -3
- package/dist/{index-C-Nd2ZbV.d.ts → index-CWBSrqI6.d.ts} +676 -3
- package/dist/index-CWBSrqI6.d.ts.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +13 -13
- package/dist/{login-CMVVnAHS.js → login-CR3fDHXh.js} +3 -3
- package/dist/{login-CMVVnAHS.js.map → login-CR3fDHXh.js.map} +1 -1
- package/dist/{mcp-3GXUBwur.js → mcp-C6IkXNOS.js} +87 -6
- package/dist/mcp-C6IkXNOS.js.map +1 -0
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +3 -2
- 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/origin-pinned-fetch-CqG8VZIq.js +159 -0
- package/dist/origin-pinned-fetch-CqG8VZIq.js.map +1 -0
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/plugins-2PMTVcp-.js +1174 -0
- package/dist/plugins-2PMTVcp-.js.map +1 -0
- package/dist/plugins.d.ts +2 -0
- package/dist/plugins.js +2 -0
- 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 +1 -1
- 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 +463 -7
- 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/{resolve-CZJxds85.js → skills-Cp9vnBwd.js} +1430 -1006
- package/dist/skills-Cp9vnBwd.js.map +1 -0
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +1 -2
- package/dist/{stdio-BvL8HGYW.js → stdio-DzUUkkhS.js} +2 -2
- package/dist/{stdio-BvL8HGYW.js.map → stdio-DzUUkkhS.js.map} +1 -1
- package/dist/{stdio-loader-CjfoqBle.js → stdio-loader-BILyUGmi.js} +2 -2
- package/dist/{stdio-loader-CjfoqBle.js.map → stdio-loader-BILyUGmi.js.map} +1 -1
- package/dist/{tool-formatters-DPQjso_C.d.ts → tool-formatters-gFqQbzPY.d.ts} +2 -2
- package/dist/{tool-formatters-DPQjso_C.d.ts.map → tool-formatters-gFqQbzPY.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-CgDzYin1.js → transcript-anchors-DEIr0tJC.js} +44 -7
- package/dist/transcript-anchors-DEIr0tJC.js.map +1 -0
- package/dist/{transcript-anchors-BCUpbNxc.d.ts → transcript-anchors-qCs3wZpu.d.ts} +98 -92
- package/dist/transcript-anchors-qCs3wZpu.d.ts.map +1 -0
- package/dist/tui.d.ts +38 -4
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +843 -109
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-CAvi8NYt.js → turn-operations-C3IijXMW.js} +2 -2
- package/dist/{turn-operations-CAvi8NYt.js.map → turn-operations-C3IijXMW.js.map} +1 -1
- package/dist/{turn-operations-Bu2Hkinw.d.ts → turn-operations-M9-jUyDd.d.ts} +3 -3
- package/dist/{turn-operations-Bu2Hkinw.d.ts.map → turn-operations-M9-jUyDd.d.ts.map} +1 -1
- package/dist/types-BIarq1qE.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/PLUGINS.md +340 -0
- package/docs/RESTATE.md +168 -3
- package/docs/SKILL.md +24 -8
- package/package.json +6 -1
- package/dist/agent-8sUadp5M.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.map +0 -1
- package/dist/index-C-Nd2ZbV.d.ts.map +0 -1
- package/dist/mcp-3GXUBwur.js.map +0 -1
- package/dist/openai-compat-B3c_hzwf.js.map +0 -1
- package/dist/resolve-CZJxds85.js.map +0 -1
- package/dist/skills-Dd6XCi4s.js +0 -420
- package/dist/skills-Dd6XCi4s.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-BCUpbNxc.d.ts.map +0 -1
- package/dist/transcript-anchors-CgDzYin1.js.map +0 -1
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as rewriteMessagesToWire, H as toWireName, I as augmentMcpDoubleUnderscoreAliases, L as buildAliasMaps, M as buildPromptMessage, N as canonicalizePrompt, R as buildSanitizedWireAliases, V as toCanonicalName, z as rewriteContentToCanonical } from "./openai-compat-CnHeo4Lz.js";
|
|
2
2
|
import { a as isWireTurn, c as toolOutputByteLength, i as documentBlockMarker, l as toolResultToText, o as sanitizeToolResultMetadata, s as toolOutputBudgetByteLength, t as DEFAULT_AGENT_CLOCK } from "./types-BIarq1qE.js";
|
|
3
|
-
import {
|
|
4
|
-
import { a as getReadState, d as noteStaleRejection, f as readStateKey, l as hydrateReadStateFromSessionAsync, m as staleRefusalHint, n as createToolStateController, o as getToolDedupState, p as resolveReadStateMap, s as hashContent, u as markReadStateElided } from "./tool-state-DTs-3ZHH.js";
|
|
3
|
+
import { a as styleReplacementForVia, i as stripLineNumberPrefixes, n as describeVia, r as resolveOldString, t as buildContextBreakdown } from "./context-breakdown-BLF10tOO.js";
|
|
5
4
|
import { n as estimateTokens, r as utf8ByteLength } from "./utils-ngQzYzZD.js";
|
|
6
|
-
import { t as buildContextBreakdown } from "./context-breakdown-Dzo25N45.js";
|
|
7
5
|
import { a as formatDuration, c as previewLine, o as formatTaskStatus, s as formatTaskSummary } from "./format-BF745pLL.js";
|
|
8
6
|
import { i as parseGitLsFiles, r as GIT_LS_FILES_COMMAND } from "./glob-DluQFSw1.js";
|
|
9
7
|
import { i as isOutputExceededMessage } from "./output-cap-C9zukcCj.js";
|
|
10
8
|
import { r as createProcessContext, t as resolveDetachedTasksCapability } from "./contexts-DWVt7MvF.js";
|
|
11
9
|
import { n as shellQuote, t as alwaysQuote } from "./shell-quote-BmnhZmdM.js";
|
|
12
10
|
import { a as AgentProviderError, d as isAbortLikeError, h as toTypedError, i as AgentLengthResumeExhaustedError, n as AgentBudgetExceededError, s as AgentToolPairingError, t as AgentAbortedError, u as errorMessage } from "./errors-BwBxu3zD.js";
|
|
13
|
-
import {
|
|
11
|
+
import { A as createSkillActivationState, D as escapeXml, E as buildCatalog, F as validateResourcePathReal, R as projectUserPaths, _ as lastLine, a as interpolateShellCommands, b as resolveSubdirRoot, g as fetchInstallSource, h as defaultWhich, k as installAllowedToolsGate, m as defaultExec, n as resolveSkills, o as stripShellInterpolations, p as InstallSourceError, v as parseInstallSource, y as resolveNpmVersion } from "./skills-Cp9vnBwd.js";
|
|
14
12
|
import { n as writeFileAtomicAsync, t as writeFileAtomic } from "./atomic-write-B7zr1ddF.js";
|
|
13
|
+
import { p as resolveStorageDirs, r as loadAgentPlugin } from "./plugins-2PMTVcp-.js";
|
|
15
14
|
import { r as createLogger, t as consoleSink } from "./logger-DBX9uYOw.js";
|
|
16
15
|
import { n as reconcileImageMediaType } from "./media-sniff-UKijEaiQ.js";
|
|
17
|
-
import {
|
|
18
|
-
import { M as buildPromptMessage, N as canonicalizePrompt } from "./openai-compat-B3c_hzwf.js";
|
|
16
|
+
import { f as wrapDiscoveredMcpTools, i as deriveMcpToolMeta, n as connectMcpServers, s as nonOwningMcpView } from "./mcp-C6IkXNOS.js";
|
|
19
17
|
import { appendStaticSection, renderSystemForWire } from "./prompt.js";
|
|
20
|
-
import { a as detectTurnInterruption, c as ensureToolResultPairing, l as filterUnresolvedToolUses, m as resolveContentRefsInMessages, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, p as remintDuplicateToolCallIds, s as ensureEndsWithUserMessage } from "./messages-
|
|
21
|
-
import { a as getExtensionSessionState, c as setExtensionSessionState, l as withExtensionSession, n as currentExtensionSession, r as deleteExtensionSessionState, s as resumeChainRunIds } from "./session-
|
|
18
|
+
import { a as detectTurnInterruption, c as ensureToolResultPairing, l as filterUnresolvedToolUses, m as resolveContentRefsInMessages, n as SYNTHETIC_TOOL_RESULT_PLACEHOLDER, p as remintDuplicateToolCallIds, s as ensureEndsWithUserMessage } from "./messages-C_H9soi5.js";
|
|
19
|
+
import { a as getExtensionSessionState, c as setExtensionSessionState, l as withExtensionSession, n as currentExtensionSession, r as deleteExtensionSessionState, s as resumeChainRunIds } from "./session-VpLrNaFQ.js";
|
|
22
20
|
import { n as flattenTurns, r as formatTokenUsage, t as effectiveInputFromTurn } from "./stats-DAKBEKjc.js";
|
|
23
21
|
import { a as withInactivityTimeout, i as settleWithinTimeout, n as linkAbortSignal, o as withTimeout$1, r as resolveTimeoutMs, t as OperationTimeoutError } from "./timeout-BDetbuN6.js";
|
|
24
22
|
import { createRequire } from "node:module";
|
|
@@ -30,6 +28,436 @@ import { copyFile, mkdir, readdir, rm, stat, unlink } from "node:fs/promises";
|
|
|
30
28
|
import { pathToFileURL } from "node:url";
|
|
31
29
|
import { homedir, tmpdir } from "node:os";
|
|
32
30
|
import { createHash } from "node:crypto";
|
|
31
|
+
//#region src/tools/read-state.ts
|
|
32
|
+
const STATE = /* @__PURE__ */ new WeakMap();
|
|
33
|
+
/**
|
|
34
|
+
* Get or lazily create the per-session read-state map. Returns `undefined`
|
|
35
|
+
* when no session is provided.
|
|
36
|
+
*
|
|
37
|
+
* Most tool callers should prefer {@link resolveReadStateMap}, which
|
|
38
|
+
* additionally honors an explicit `ctx.readState` (the path
|
|
39
|
+
* `spawn`'s `shareReadState: true` uses to forward the parent's map
|
|
40
|
+
* into a sessionless child). Use this helper directly only when the
|
|
41
|
+
* Session-keyed map is exactly what you want and you don't need to
|
|
42
|
+
* accept an override.
|
|
43
|
+
*/
|
|
44
|
+
function getReadState(session) {
|
|
45
|
+
if (!session) return void 0;
|
|
46
|
+
let map = STATE.get(session);
|
|
47
|
+
if (!map) {
|
|
48
|
+
map = /* @__PURE__ */ new Map();
|
|
49
|
+
STATE.set(session, map);
|
|
50
|
+
}
|
|
51
|
+
return map;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the active read-state map from a tool context. An explicit
|
|
55
|
+
* `ctx.readState` wins (used by `spawn`'s `shareReadState` opt-in to
|
|
56
|
+
* forward the parent's map into a sessionless child); otherwise the
|
|
57
|
+
* usual `Session`-keyed lookup applies.
|
|
58
|
+
*
|
|
59
|
+
* Tools should call this helper instead of `getReadState(ctx.session)`
|
|
60
|
+
* directly so the `shareReadState` plumbing is honored uniformly.
|
|
61
|
+
*/
|
|
62
|
+
function resolveReadStateMap(ctx) {
|
|
63
|
+
return ctx.readState ?? getReadState(ctx.session);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Mark a read-state entry as elided from the on-wire history WITHOUT
|
|
67
|
+
* un-registering it.
|
|
68
|
+
*
|
|
69
|
+
* Used by the loop's `elideStaleReads` pass. The entry must survive —
|
|
70
|
+
* deleting it made the `requireReadBeforeEdit` gate reject the very next
|
|
71
|
+
* edit on a file the model had demonstrably read and just edited (the
|
|
72
|
+
* gate's failure mode then defeats the gate: models degrade to raw shell
|
|
73
|
+
* edits that bypass the guard entirely). The `elided` flag instead
|
|
74
|
+
* defeats only the dedup stub: the next identical re-read returns the
|
|
75
|
+
* full body again, and `rememberRead` clears the flag.
|
|
76
|
+
*
|
|
77
|
+
* No-op when the path has no entry in the resolved map(s).
|
|
78
|
+
*/
|
|
79
|
+
function markReadStateElided(ctx, cwd, path) {
|
|
80
|
+
const key = readStateKey(cwd, path);
|
|
81
|
+
for (const map of [ctx.readState, ctx.session ? STATE.get(ctx.session) : void 0]) {
|
|
82
|
+
const entry = map?.get(key);
|
|
83
|
+
if (entry && entry.elided !== true) map.set(key, {
|
|
84
|
+
...entry,
|
|
85
|
+
elided: true
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Record one `requireReadBeforeEdit` drift refusal against `entry` and
|
|
91
|
+
* return the consecutive count (1 for the first). The caller composes the
|
|
92
|
+
* refusal message with {@link staleRefusalHint}.
|
|
93
|
+
*/
|
|
94
|
+
function noteStaleRejection(map, key, entry) {
|
|
95
|
+
const count = (entry.staleRejections ?? 0) + 1;
|
|
96
|
+
map.set(key, {
|
|
97
|
+
...entry,
|
|
98
|
+
staleRejections: count
|
|
99
|
+
});
|
|
100
|
+
return count;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Escalating suffix for a drift refusal. Empty on the first refusal — the
|
|
104
|
+
* base "re-read the file" advice is correct and sufficient. From the second
|
|
105
|
+
* consecutive refusal on, the model has demonstrably re-read and been
|
|
106
|
+
* refused again, so repeating the same advice reads as a broken tool;
|
|
107
|
+
* name the real hypothesis (an external writer) instead.
|
|
108
|
+
*/
|
|
109
|
+
function staleRefusalHint(count) {
|
|
110
|
+
if (count < 2) return "";
|
|
111
|
+
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.`;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Canonical read-state key for a `(cwd, path)` pair.
|
|
115
|
+
*
|
|
116
|
+
* `ctx.execution.readFile(handle, path)` resolves the model-provided
|
|
117
|
+
* path against `handle.cwd` before touching disk — so `src/App.tsx`,
|
|
118
|
+
* `./src/App.tsx`, and `/abs/cwd/src/App.tsx` all read the **same**
|
|
119
|
+
* bytes. The read-state map MUST mirror that resolution: without it,
|
|
120
|
+
* a model that reads `src/App.tsx` and then edits `./src/App.tsx`
|
|
121
|
+
* trips the `requireReadBeforeEdit` gate for a file it demonstrably
|
|
122
|
+
* already saw (the gate's "has not been read in this session" message
|
|
123
|
+
* fires on a stale key).
|
|
124
|
+
*
|
|
125
|
+
* `node:path`'s `resolve(cwd, path)` short-circuits when `path` is
|
|
126
|
+
* already absolute, so absolute and relative shapes converge on the
|
|
127
|
+
* same canonical form. We don't `realpath()` symlinks — the file IS
|
|
128
|
+
* the path the model addressed, not the realpath behind it; the host's
|
|
129
|
+
* execution context decides how to dereference.
|
|
130
|
+
*/
|
|
131
|
+
function readStateKey(cwd, path) {
|
|
132
|
+
return resolve(cwd, path);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 64-bit content hash: two independent 32-bit FNV-1a-style lanes (different
|
|
136
|
+
* offset bases + multipliers) concatenated as 16 hex chars. Fast,
|
|
137
|
+
* non-cryptographic — but wide enough that the `requireReadBeforeEdit`
|
|
138
|
+
* drift gate, which silently corrupts a file on a false "unchanged"
|
|
139
|
+
* verdict, isn't betting on a 32-bit birthday bound (~1 in 4 billion per
|
|
140
|
+
* pair; uncomfortably reachable over many files × many sessions). Entries
|
|
141
|
+
* live only in per-session in-memory maps, so widening needs no
|
|
142
|
+
* stored-format migration. Still cheaper than allocating a Buffer and
|
|
143
|
+
* pulling in `crypto`.
|
|
144
|
+
*/
|
|
145
|
+
function hashContent(text) {
|
|
146
|
+
let h1 = 2166136261;
|
|
147
|
+
let h2 = 2128831031;
|
|
148
|
+
for (let i = 0; i < text.length; i++) {
|
|
149
|
+
const c = text.charCodeAt(i);
|
|
150
|
+
h1 = Math.imul(h1 ^ c, 16777619) >>> 0;
|
|
151
|
+
h2 = Math.imul(h2 ^ c, 2246822507) >>> 0;
|
|
152
|
+
}
|
|
153
|
+
return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
|
|
154
|
+
}
|
|
155
|
+
const READ_FILE_DEFAULT_OFFSET = 1;
|
|
156
|
+
const READ_FILE_DEFAULT_LIMIT = 2e3;
|
|
157
|
+
const READ_FILE_DEFAULT_MAX_BYTES = 262144;
|
|
158
|
+
const READ_FILE_FOOTER_RE = /\n\n…(?:read lines|truncated)/;
|
|
159
|
+
const READ_FILE_LINE_RE = /^\d{1,9}\t(.*)$/;
|
|
160
|
+
/**
|
|
161
|
+
* Rebuild the per-session read-state map from durable session turns.
|
|
162
|
+
*
|
|
163
|
+
* Durable replay runtimes may return recorded tool results without re-running
|
|
164
|
+
* the tool body, so in-memory side effects like `readState.set(...)` can be
|
|
165
|
+
* missing even though the transcript contains the successful `read_file`.
|
|
166
|
+
* This helper is intentionally opt-in: live hosts keep the normal side-effect
|
|
167
|
+
* behavior unless they call it themselves.
|
|
168
|
+
*
|
|
169
|
+
* Hydration only fills missing keys. Live in-memory state may already reflect a
|
|
170
|
+
* newer read or edit from the current run, and durable history cannot prove it
|
|
171
|
+
* is fresher. Relative paths are resolved against the current execution cwd,
|
|
172
|
+
* matching replay hosts that run sessions from a stable workspace.
|
|
173
|
+
*/
|
|
174
|
+
function hydrateReadStateFromSession(session, cwd, options = {}) {
|
|
175
|
+
if (!session) return 0;
|
|
176
|
+
const readState = getReadState(session);
|
|
177
|
+
if (!readState) return 0;
|
|
178
|
+
const normalized = normalizeHydrateReadStateOptions(options);
|
|
179
|
+
const pending = /* @__PURE__ */ new Map();
|
|
180
|
+
const known = /* @__PURE__ */ new Map();
|
|
181
|
+
const preexistingKeys = new Set(readState.keys());
|
|
182
|
+
let hydrated = 0;
|
|
183
|
+
for (const turn of session.turns) for (const block of turn.content) {
|
|
184
|
+
if (block.type === "tool_call") {
|
|
185
|
+
const canonicalName = canonicalToolCallName(block.name, normalized);
|
|
186
|
+
if (canonicalName === normalized.readFileToolName || canonicalName === normalized.writeFileToolName || canonicalName === normalized.editToolName || canonicalName === normalized.multiEditToolName) pending.set(block.id, {
|
|
187
|
+
name: canonicalName,
|
|
188
|
+
input: block.input
|
|
189
|
+
});
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (block.type !== "tool_result") continue;
|
|
193
|
+
const call = pending.get(block.callId);
|
|
194
|
+
if (!call) continue;
|
|
195
|
+
pending.delete(block.callId);
|
|
196
|
+
if (block.isError === true) continue;
|
|
197
|
+
const next = stateFromToolResult(call, toolResultToText(block.output), turn, cwd, normalized, known);
|
|
198
|
+
if (!next) continue;
|
|
199
|
+
if (!preexistingKeys.has(next.key)) {
|
|
200
|
+
readState.set(next.key, next.file.entry);
|
|
201
|
+
hydrated += 1;
|
|
202
|
+
}
|
|
203
|
+
known.set(next.key, next.file);
|
|
204
|
+
}
|
|
205
|
+
return hydrated;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Async replay hydrator for durable hosts that can cheaply read the current
|
|
209
|
+
* full file through their execution context. Transcript-only reconstruction is
|
|
210
|
+
* still preferred for full reads; partial/truncated read_file entries fall back
|
|
211
|
+
* to `readFileForHash` and hash those full current bytes.
|
|
212
|
+
*/
|
|
213
|
+
async function hydrateReadStateFromSessionAsync(session, cwd, options = {}) {
|
|
214
|
+
if (!session) return 0;
|
|
215
|
+
const readState = getReadState(session);
|
|
216
|
+
if (!readState) return 0;
|
|
217
|
+
const normalized = normalizeHydrateReadStateOptions(options);
|
|
218
|
+
const pending = /* @__PURE__ */ new Map();
|
|
219
|
+
const known = /* @__PURE__ */ new Map();
|
|
220
|
+
const preexistingKeys = new Set(readState.keys());
|
|
221
|
+
let hydrated = 0;
|
|
222
|
+
for (const turn of session.turns) for (const block of turn.content) {
|
|
223
|
+
if (block.type === "tool_call") {
|
|
224
|
+
const canonicalName = canonicalToolCallName(block.name, normalized);
|
|
225
|
+
if (canonicalName === normalized.readFileToolName || canonicalName === normalized.writeFileToolName || canonicalName === normalized.editToolName || canonicalName === normalized.multiEditToolName) pending.set(block.id, {
|
|
226
|
+
name: canonicalName,
|
|
227
|
+
input: block.input
|
|
228
|
+
});
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (block.type !== "tool_result") continue;
|
|
232
|
+
const call = pending.get(block.callId);
|
|
233
|
+
if (!call) continue;
|
|
234
|
+
pending.delete(block.callId);
|
|
235
|
+
if (block.isError === true) continue;
|
|
236
|
+
const next = await stateFromToolResultAsync(call, toolResultToText(block.output), turn, cwd, normalized, known);
|
|
237
|
+
if (!next) continue;
|
|
238
|
+
if (!preexistingKeys.has(next.key)) {
|
|
239
|
+
readState.set(next.key, next.file.entry);
|
|
240
|
+
hydrated += 1;
|
|
241
|
+
}
|
|
242
|
+
known.set(next.key, next.file);
|
|
243
|
+
}
|
|
244
|
+
return hydrated;
|
|
245
|
+
}
|
|
246
|
+
function normalizeHydrateReadStateOptions(options) {
|
|
247
|
+
return {
|
|
248
|
+
readFileToolName: options.readFileToolName ?? "read_file",
|
|
249
|
+
writeFileToolName: options.writeFileToolName ?? "write_file",
|
|
250
|
+
editToolName: options.editToolName ?? "edit",
|
|
251
|
+
multiEditToolName: options.multiEditToolName ?? "multi_edit",
|
|
252
|
+
defaultLineNumbers: options.defaultLineNumbers ?? true,
|
|
253
|
+
...options.readFileForHash ? { readFileForHash: options.readFileForHash } : {},
|
|
254
|
+
...options.aliasMaps ? { aliasMaps: options.aliasMaps } : {}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Resolve a transcript tool-call name to its canonical form for matching.
|
|
259
|
+
* Honors `aliasMaps` (wire → canonical) when present; otherwise the name is
|
|
260
|
+
* already canonical (zidane persists canonical names) and passes through.
|
|
261
|
+
*/
|
|
262
|
+
function canonicalToolCallName(name, options) {
|
|
263
|
+
return options.aliasMaps ? toCanonicalName(name, options.aliasMaps) : name;
|
|
264
|
+
}
|
|
265
|
+
function stateFromToolResult(call, output, resultTurn, cwd, options, known) {
|
|
266
|
+
if (call.name === options.readFileToolName) return readFileStateFromResult(call.input, output, resultTurn, cwd, options.defaultLineNumbers);
|
|
267
|
+
if (call.name === options.writeFileToolName) return writeFileStateFromResult(call.input, output, resultTurn, cwd);
|
|
268
|
+
if (call.name === options.editToolName) return editStateFromResult(call.input, output, resultTurn, cwd, known);
|
|
269
|
+
if (call.name === options.multiEditToolName) return multiEditStateFromResult(call.input, output, resultTurn, cwd, known);
|
|
270
|
+
}
|
|
271
|
+
async function stateFromToolResultAsync(call, output, resultTurn, cwd, options, known) {
|
|
272
|
+
if (call.name === options.readFileToolName) return await readFileStateFromResultAsync(call.input, output, resultTurn, cwd, {
|
|
273
|
+
defaultLineNumbers: options.defaultLineNumbers,
|
|
274
|
+
readFileForHash: options.readFileForHash
|
|
275
|
+
});
|
|
276
|
+
return stateFromToolResult(call, output, resultTurn, cwd, options, known);
|
|
277
|
+
}
|
|
278
|
+
function readFileStateFromResult(input, output, resultTurn, cwd, defaultLineNumbers) {
|
|
279
|
+
const path = typeof input.path === "string" ? input.path : void 0;
|
|
280
|
+
if (!path || !isSuccessfulFullReadOutput(output)) return void 0;
|
|
281
|
+
const lineNumbers = typeof input.lineNumbers === "boolean" ? input.lineNumbers : defaultLineNumbers;
|
|
282
|
+
const content = lineNumbers ? stripReadFileLineNumbers(output) : output;
|
|
283
|
+
if (content === void 0) return void 0;
|
|
284
|
+
const entry = {
|
|
285
|
+
contentHash: hashContent(content),
|
|
286
|
+
offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),
|
|
287
|
+
limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),
|
|
288
|
+
maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),
|
|
289
|
+
lineNumbers,
|
|
290
|
+
mtimeMs: resultTurn.createdAt
|
|
291
|
+
};
|
|
292
|
+
return {
|
|
293
|
+
key: readStateKey(cwd, path),
|
|
294
|
+
file: {
|
|
295
|
+
content,
|
|
296
|
+
entry
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
async function readFileStateFromResultAsync(input, output, resultTurn, cwd, options) {
|
|
301
|
+
const reconstructed = readFileStateFromResult(input, output, resultTurn, cwd, options.defaultLineNumbers);
|
|
302
|
+
if (reconstructed) return reconstructed;
|
|
303
|
+
const path = typeof input.path === "string" ? input.path : void 0;
|
|
304
|
+
if (!path || !options.readFileForHash || !isSuccessfulReadOutput(output)) return void 0;
|
|
305
|
+
let content;
|
|
306
|
+
try {
|
|
307
|
+
content = await options.readFileForHash(path);
|
|
308
|
+
} catch {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (typeof content !== "string") return void 0;
|
|
312
|
+
const lineNumbers = typeof input.lineNumbers === "boolean" ? input.lineNumbers : options.defaultLineNumbers;
|
|
313
|
+
const entry = {
|
|
314
|
+
contentHash: hashContent(content),
|
|
315
|
+
offset: normalizeReadInteger(input.offset, READ_FILE_DEFAULT_OFFSET),
|
|
316
|
+
limit: normalizeReadInteger(input.limit, READ_FILE_DEFAULT_LIMIT),
|
|
317
|
+
maxBytes: normalizeReadInteger(input.maxBytes, READ_FILE_DEFAULT_MAX_BYTES),
|
|
318
|
+
lineNumbers,
|
|
319
|
+
mtimeMs: resultTurn.createdAt
|
|
320
|
+
};
|
|
321
|
+
return {
|
|
322
|
+
key: readStateKey(cwd, path),
|
|
323
|
+
file: {
|
|
324
|
+
content,
|
|
325
|
+
entry
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function writeFileStateFromResult(input, output, resultTurn, cwd) {
|
|
330
|
+
const path = typeof input.path === "string" ? input.path : void 0;
|
|
331
|
+
const content = typeof input.content === "string" ? input.content : void 0;
|
|
332
|
+
if (!path || content === void 0) return void 0;
|
|
333
|
+
if (!output.startsWith(`Created ${path} (`) && !output.startsWith(`Updated ${path} (`)) return void 0;
|
|
334
|
+
return {
|
|
335
|
+
key: readStateKey(cwd, path),
|
|
336
|
+
file: {
|
|
337
|
+
content,
|
|
338
|
+
entry: {
|
|
339
|
+
contentHash: hashContent(content),
|
|
340
|
+
offset: 0,
|
|
341
|
+
limit: Number.POSITIVE_INFINITY,
|
|
342
|
+
maxBytes: Number.POSITIVE_INFINITY,
|
|
343
|
+
mtimeMs: resultTurn.createdAt,
|
|
344
|
+
elided: true
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function editStateFromResult(input, output, resultTurn, cwd, known) {
|
|
350
|
+
const path = typeof input.path === "string" ? input.path : void 0;
|
|
351
|
+
const oldString = typeof input.old_string === "string" ? input.old_string : void 0;
|
|
352
|
+
const newString = typeof input.new_string === "string" ? input.new_string : void 0;
|
|
353
|
+
if (!path || oldString === void 0 || newString === void 0) return void 0;
|
|
354
|
+
if (!output.startsWith(`Edited ${path}: replaced `)) return void 0;
|
|
355
|
+
const key = readStateKey(cwd, path);
|
|
356
|
+
const prior = known.get(key);
|
|
357
|
+
if (!prior) return void 0;
|
|
358
|
+
const nextContent = applyEdit(prior.content, oldString, newString, input.replace_all === true);
|
|
359
|
+
if (nextContent === void 0) return void 0;
|
|
360
|
+
return {
|
|
361
|
+
key,
|
|
362
|
+
file: {
|
|
363
|
+
content: nextContent,
|
|
364
|
+
entry: {
|
|
365
|
+
...prior.entry,
|
|
366
|
+
contentHash: hashContent(nextContent),
|
|
367
|
+
mtimeMs: resultTurn.createdAt,
|
|
368
|
+
elided: true
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function multiEditStateFromResult(input, output, resultTurn, cwd, known) {
|
|
374
|
+
const path = typeof input.path === "string" ? input.path : void 0;
|
|
375
|
+
const edits = Array.isArray(input.edits) ? input.edits : void 0;
|
|
376
|
+
if (!path || !edits) return void 0;
|
|
377
|
+
if (!output.startsWith(`Edited ${path}: applied `)) return void 0;
|
|
378
|
+
const key = readStateKey(cwd, path);
|
|
379
|
+
const prior = known.get(key);
|
|
380
|
+
if (!prior) return void 0;
|
|
381
|
+
const applied = parseMultiEditAppliedSteps(output, edits.length);
|
|
382
|
+
let current = prior.content;
|
|
383
|
+
for (let i = 0; i < edits.length; i++) {
|
|
384
|
+
if (!applied.has(i)) continue;
|
|
385
|
+
const step = edits[i];
|
|
386
|
+
if (typeof step.old_string !== "string" || typeof step.new_string !== "string") return void 0;
|
|
387
|
+
const next = applyEdit(current, step.old_string, step.new_string, step.replace_all === true);
|
|
388
|
+
if (next === void 0) return void 0;
|
|
389
|
+
current = next;
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
key,
|
|
393
|
+
file: {
|
|
394
|
+
content: current,
|
|
395
|
+
entry: {
|
|
396
|
+
...prior.entry,
|
|
397
|
+
contentHash: hashContent(current),
|
|
398
|
+
mtimeMs: resultTurn.createdAt,
|
|
399
|
+
elided: true
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function applyEdit(content, oldString, newString, replaceAll) {
|
|
405
|
+
const match = resolveOldString(content, oldString);
|
|
406
|
+
if (!match) return void 0;
|
|
407
|
+
if (match.occurrences > 1 && !replaceAll) return void 0;
|
|
408
|
+
const styledReplacement = styleReplacementForVia(newString, match.via, match.actual);
|
|
409
|
+
return replaceAll ? content.split(match.actual).join(styledReplacement) : content.replace(match.actual, styledReplacement);
|
|
410
|
+
}
|
|
411
|
+
function parseMultiEditAppliedSteps(output, editCount) {
|
|
412
|
+
const annotationMatch = /<edit-outcomes>\n([\s\S]*?)\n<\/edit-outcomes>/.exec(output);
|
|
413
|
+
if (!annotationMatch) return new Set(Array.from({ length: editCount }, (_, i) => i));
|
|
414
|
+
const applied = /* @__PURE__ */ new Set();
|
|
415
|
+
for (const line of annotationMatch[1].split("\n")) {
|
|
416
|
+
const match = /^#(\d+) applied$/.exec(line.trim());
|
|
417
|
+
if (!match) continue;
|
|
418
|
+
const index = Number(match[1]) - 1;
|
|
419
|
+
if (index >= 0 && index < editCount) applied.add(index);
|
|
420
|
+
}
|
|
421
|
+
return applied;
|
|
422
|
+
}
|
|
423
|
+
function isSuccessfulFullReadOutput(output) {
|
|
424
|
+
if (!isSuccessfulReadOutput(output) || READ_FILE_FOOTER_RE.test(output)) return false;
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
function isSuccessfulReadOutput(output) {
|
|
428
|
+
if (output.length === 0) return true;
|
|
429
|
+
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;
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
function stripReadFileLineNumbers(output) {
|
|
433
|
+
const lines = output.split("\n");
|
|
434
|
+
const stripped = [];
|
|
435
|
+
for (const line of lines) {
|
|
436
|
+
const match = READ_FILE_LINE_RE.exec(line);
|
|
437
|
+
if (!match) return void 0;
|
|
438
|
+
stripped.push(match[1]);
|
|
439
|
+
}
|
|
440
|
+
return stripped.join("\n");
|
|
441
|
+
}
|
|
442
|
+
function normalizeReadInteger(value, fallback) {
|
|
443
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return fallback;
|
|
444
|
+
return Math.floor(value);
|
|
445
|
+
}
|
|
446
|
+
const TOOL_DEDUP_STATE = /* @__PURE__ */ new WeakMap();
|
|
447
|
+
/**
|
|
448
|
+
* Get or lazily create the per-session tool-dedup map. Returns `undefined`
|
|
449
|
+
* when no session is provided — middleware should treat that as "no dedup".
|
|
450
|
+
*/
|
|
451
|
+
function getToolDedupState(session) {
|
|
452
|
+
if (!session) return void 0;
|
|
453
|
+
let map = TOOL_DEDUP_STATE.get(session);
|
|
454
|
+
if (!map) {
|
|
455
|
+
map = /* @__PURE__ */ new Map();
|
|
456
|
+
TOOL_DEDUP_STATE.set(session, map);
|
|
457
|
+
}
|
|
458
|
+
return map;
|
|
459
|
+
}
|
|
460
|
+
//#endregion
|
|
33
461
|
//#region src/behaviors/dedup-tools.ts
|
|
34
462
|
/**
|
|
35
463
|
* Install the per-tool argument-dedup middleware on a hook bus.
|
|
@@ -1224,6 +1652,43 @@ function defineExtension(definition) {
|
|
|
1224
1652
|
return definition;
|
|
1225
1653
|
}
|
|
1226
1654
|
//#endregion
|
|
1655
|
+
//#region src/debug-log.ts
|
|
1656
|
+
/**
|
|
1657
|
+
* `ZIDANE_DEBUG`-gated diagnostic logging.
|
|
1658
|
+
*
|
|
1659
|
+
* The three subsystems that need this (TUI shell, discovery, extension
|
|
1660
|
+
* scanning) each had their own three-line copy, differing only in the scope
|
|
1661
|
+
* label and — accidentally — in sink and formatting: one used `console.error`
|
|
1662
|
+
* with a raw error object, two used `process.stderr.write` with
|
|
1663
|
+
* {@link errorMessage}. The label is a real per-module difference, so it's a
|
|
1664
|
+
* parameter; the rest was drift.
|
|
1665
|
+
*
|
|
1666
|
+
* Distinct from `./logger`, which is the structured, sink-backed `Logger`
|
|
1667
|
+
* surfaced to extensions and hosts. This is the unstructured "print it when
|
|
1668
|
+
* debugging" path: no levels, no sinks, off unless `ZIDANE_DEBUG` is set, and
|
|
1669
|
+
* always stderr so it can never corrupt stdout for a piped host.
|
|
1670
|
+
*/
|
|
1671
|
+
/**
|
|
1672
|
+
* Build a debug logger scoped to a subsystem. Emits
|
|
1673
|
+
* `[zidane/<scope>] <label>: <details…>` on stderr when `ZIDANE_DEBUG` is set,
|
|
1674
|
+
* and is a no-op otherwise.
|
|
1675
|
+
*
|
|
1676
|
+
* `details` are flattened through {@link errorMessage}, so an `Error` prints
|
|
1677
|
+
* its message rather than `[object Object]` and plain strings pass through.
|
|
1678
|
+
*
|
|
1679
|
+
* The env var is read per call rather than captured at module load so a test
|
|
1680
|
+
* (or a host toggling it at runtime) sees the change; the check is a property
|
|
1681
|
+
* read on an already-materialized object, so the cost is irrelevant next to
|
|
1682
|
+
* the I/O it guards.
|
|
1683
|
+
*/
|
|
1684
|
+
function createDebugLog(scope) {
|
|
1685
|
+
return (label, ...details) => {
|
|
1686
|
+
if (!process.env.ZIDANE_DEBUG) return;
|
|
1687
|
+
const tail = details.length > 0 ? `: ${details.map(errorMessage).join(" ")}` : "";
|
|
1688
|
+
process.stderr.write(`[zidane/${scope}] ${label}${tail}\n`);
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
//#endregion
|
|
1227
1692
|
//#region src/extensions/with-timeout.ts
|
|
1228
1693
|
/**
|
|
1229
1694
|
* Internal: race a promise against a timeout. Shared by the discovery
|
|
@@ -1246,6 +1711,11 @@ function withTimeout(promise, timeoutMs, message, onTimeout) {
|
|
|
1246
1711
|
//#endregion
|
|
1247
1712
|
//#region src/extensions/discovery.ts
|
|
1248
1713
|
/**
|
|
1714
|
+
* Scoped `ZIDANE_DEBUG` logger. A `const` rather than a hoisted function
|
|
1715
|
+
* declaration, so it must precede its call sites.
|
|
1716
|
+
*/
|
|
1717
|
+
const debugLog = createDebugLog("extensions");
|
|
1718
|
+
/**
|
|
1249
1719
|
* Entry-point filenames probed inside each `<name>/` directory, in
|
|
1250
1720
|
* preference order. Bun loads `.ts` / `.tsx` natively; `.mjs` / `.js`
|
|
1251
1721
|
* are accepted so pre-compiled extensions (or vibe-coded JS-only ones)
|
|
@@ -1657,9 +2127,6 @@ function isExtensionManifest(value) {
|
|
|
1657
2127
|
const isStringArray = (x) => Array.isArray(x) && x.every((el) => typeof el === "string");
|
|
1658
2128
|
return (v.name === void 0 || typeof v.name === "string") && (v.displayName === void 0 || typeof v.displayName === "string") && (v.description === void 0 || typeof v.description === "string") && (v.permissions === void 0 || isStringArray(v.permissions)) && (v.capabilities === void 0 || isStringArray(v.capabilities));
|
|
1659
2129
|
}
|
|
1660
|
-
function debugLog(label, err) {
|
|
1661
|
-
if (process.env.ZIDANE_DEBUG) process.stderr.write(`[zidane/extensions] ${label}: ${errorMessage(err)}\n`);
|
|
1662
|
-
}
|
|
1663
2130
|
//#endregion
|
|
1664
2131
|
//#region src/extensions/host-modules.ts
|
|
1665
2132
|
const registered = /* @__PURE__ */ new Set();
|
|
@@ -2318,86 +2785,6 @@ function stripJsonComments(input) {
|
|
|
2318
2785
|
return out;
|
|
2319
2786
|
}
|
|
2320
2787
|
//#endregion
|
|
2321
|
-
//#region src/chat/xdg.ts
|
|
2322
|
-
/**
|
|
2323
|
-
* XDG Base Directory resolution for zidane.
|
|
2324
|
-
*
|
|
2325
|
-
* The user's storage spreads across four logical slots:
|
|
2326
|
-
*
|
|
2327
|
-
* - **config** — credentials, mcp-credentials, keybindings, config.json,
|
|
2328
|
-
* mcps.json, skills (user-editable surface).
|
|
2329
|
-
* - **data** — sessions.db (long-lived chat history).
|
|
2330
|
-
* - **state** — state.json (per-machine UI bookkeeping: last provider,
|
|
2331
|
-
* last model, last agent, settings).
|
|
2332
|
-
* - **cache** — persisted tool results, background-task logs,
|
|
2333
|
-
* auto-update registry cache (regenerable, safe to wipe).
|
|
2334
|
-
*
|
|
2335
|
-
* Historically all four collapsed to `~/.zidane/`. This module adds
|
|
2336
|
-
* XDG-Base-Directory-aware defaults so a Linux user with no existing
|
|
2337
|
-
* `~/.zidane/` lands in the conventional `~/.config/zidane/`,
|
|
2338
|
-
* `~/.local/share/zidane/`, `~/.local/state/zidane/`, `~/.cache/zidane/`
|
|
2339
|
-
* trio. Existing setups (anyone with a `~/.zidane/` directory or
|
|
2340
|
-
* `ZIDANE_STORAGE_DIR` set) keep the single-dir layout unchanged.
|
|
2341
|
-
*
|
|
2342
|
-
* Resolution flow — applied in this order:
|
|
2343
|
-
*
|
|
2344
|
-
* 1. Caller passed an explicit `storageDir` (`ChatOptions.storageDir`)
|
|
2345
|
-
* OR `ZIDANE_STORAGE_DIR` is set → **legacy-explicit** single-dir
|
|
2346
|
-
* layout under `<storageDir>/<prefix>`. The four slots collapse to
|
|
2347
|
-
* the same directory.
|
|
2348
|
-
* 2. `~/<prefix>/` already exists on disk → **legacy-existing**
|
|
2349
|
-
* single-dir layout under that directory. Upgrades never silently
|
|
2350
|
-
* shift files around.
|
|
2351
|
-
* 3. Any `XDG_*_HOME` env var is set OR `process.platform === 'linux'`
|
|
2352
|
-
* → **XDG split**. Each slot lands in its own dir. Per-slot
|
|
2353
|
-
* `ZIDANE_{CONFIG,DATA,CACHE,STATE}_DIR` overrides beat the XDG
|
|
2354
|
-
* vars for surgical tweaks.
|
|
2355
|
-
* 4. Otherwise (macOS/Windows, no XDG signal, no existing dir) →
|
|
2356
|
-
* **default** single-dir layout at `~/<prefix>/`. Day-one behavior
|
|
2357
|
-
* on these platforms is unchanged.
|
|
2358
|
-
*
|
|
2359
|
-
* The `userDir` legacy alias always equals `configDir` so existing code
|
|
2360
|
-
* that still references `paths.userDir` keeps reading credentials, mcps,
|
|
2361
|
-
* etc. from the right place.
|
|
2362
|
-
*/
|
|
2363
|
-
/**
|
|
2364
|
-
* Resolve the four storage slots according to the precedence above.
|
|
2365
|
-
*
|
|
2366
|
-
* Pure aside from one filesystem probe (`existsSync(home/<prefix>)`)
|
|
2367
|
-
* — the same probe `resolveStoragePaths` would do anyway when
|
|
2368
|
-
* computing `paths.userDir`. No directories are created here; the
|
|
2369
|
-
* relevant write paths (state store, credentials writer, persist
|
|
2370
|
-
* dir) handle their own lazy `mkdirSync` already.
|
|
2371
|
-
*/
|
|
2372
|
-
function resolveStorageDirs(opts = {}) {
|
|
2373
|
-
const env = opts.env ?? process.env;
|
|
2374
|
-
const home = opts.home ?? homedir();
|
|
2375
|
-
const platform = opts.platform ?? process.platform;
|
|
2376
|
-
const rawPrefix = opts.prefix ?? ".zidane";
|
|
2377
|
-
const prefixBare = rawPrefix.replace(/^\./, "");
|
|
2378
|
-
const explicitStorageDir = opts.storageDir ?? env.ZIDANE_STORAGE_DIR;
|
|
2379
|
-
if (explicitStorageDir) return single(resolve(explicitStorageDir, rawPrefix), "legacy-explicit");
|
|
2380
|
-
const legacyHomeDir = resolve(home, rawPrefix);
|
|
2381
|
-
if (existsSync(legacyHomeDir)) return single(legacyHomeDir, "legacy-existing");
|
|
2382
|
-
if (platform === "linux" || !!env.XDG_CONFIG_HOME || !!env.XDG_DATA_HOME || !!env.XDG_CACHE_HOME || !!env.XDG_STATE_HOME || !!env.ZIDANE_CONFIG_DIR || !!env.ZIDANE_DATA_DIR || !!env.ZIDANE_STATE_DIR || !!env.ZIDANE_CACHE_DIR) return {
|
|
2383
|
-
configDir: env.ZIDANE_CONFIG_DIR ?? resolve(env.XDG_CONFIG_HOME || resolve(home, ".config"), prefixBare),
|
|
2384
|
-
dataDir: env.ZIDANE_DATA_DIR ?? resolve(env.XDG_DATA_HOME || resolve(home, ".local", "share"), prefixBare),
|
|
2385
|
-
stateDir: env.ZIDANE_STATE_DIR ?? resolve(env.XDG_STATE_HOME || resolve(home, ".local", "state"), prefixBare),
|
|
2386
|
-
cacheDir: env.ZIDANE_CACHE_DIR ?? resolve(env.XDG_CACHE_HOME || resolve(home, ".cache"), prefixBare),
|
|
2387
|
-
mode: "xdg"
|
|
2388
|
-
};
|
|
2389
|
-
return single(legacyHomeDir, "default");
|
|
2390
|
-
}
|
|
2391
|
-
function single(base, mode) {
|
|
2392
|
-
return {
|
|
2393
|
-
configDir: base,
|
|
2394
|
-
dataDir: base,
|
|
2395
|
-
stateDir: base,
|
|
2396
|
-
cacheDir: base,
|
|
2397
|
-
mode
|
|
2398
|
-
};
|
|
2399
|
-
}
|
|
2400
|
-
//#endregion
|
|
2401
2788
|
//#region src/tools/path-suggest.ts
|
|
2402
2789
|
function entryPath$2(entry) {
|
|
2403
2790
|
return typeof entry === "string" ? entry : entry.path;
|
|
@@ -5068,6 +5455,12 @@ function describeChildRuns(session) {
|
|
|
5068
5455
|
if (childRuns.length === 0) return "No child runs exist in this session.";
|
|
5069
5456
|
return childRuns.map((r, i) => `- ${r.label ?? `child-${i + 1}`} · ${r.status} · ${r.prompt.slice(0, 80)}`).join("\n");
|
|
5070
5457
|
}
|
|
5458
|
+
/**
|
|
5459
|
+
* Reserve the next `child-N` label for this session. Exported so the durable
|
|
5460
|
+
* (child-as-invocation) spawn tool in `src/restate/spawn.ts` allocates labels
|
|
5461
|
+
* from the same session-anchored counter — otherwise the two tools would number
|
|
5462
|
+
* children independently and a session that used both would collide.
|
|
5463
|
+
*/
|
|
5071
5464
|
function reserveChildLabel(session) {
|
|
5072
5465
|
let counter = sessionChildCounters.get(session);
|
|
5073
5466
|
if (counter === void 0) {
|
|
@@ -5444,6 +5837,7 @@ function buildSpawnTool(options, state) {
|
|
|
5444
5837
|
}, state);
|
|
5445
5838
|
},
|
|
5446
5839
|
isConcurrencySafe: true,
|
|
5840
|
+
runsNestedAgent: true,
|
|
5447
5841
|
spec: {
|
|
5448
5842
|
name: "spawn",
|
|
5449
5843
|
description: `Spawn a sub-agent for a self-contained task that benefits from isolation (separate context window, separate retries): for example, a deep research dive or a long codegen pass on a specific file. The sub-agent inherits your tool set (including \`spawn\` itself, depth-capped) and your system prompt unless overridden, runs independently, and returns its final response. Do NOT spawn for sequential steps you could do yourself.${modelCatalog ? " Children can run on a DIFFERENT model via the `model` field: cost-tier your delegation: mechanical, parallelizable, or high-volume search work belongs on cheaper/faster models, while synthesis and judgment stay on your own model." : ""}${options.persist ? " A stopped or failed sub-agent can be CONTINUED instead of restarted: pass `resume` with its child label (e.g. \"child-2\") and put the continuation instruction in `task`; the child picks up with its prior conversation intact." : ""}`,
|
|
@@ -5585,15 +5979,31 @@ function buildSpawnTool(options, state) {
|
|
|
5585
5979
|
...ctx.behavior !== void 0 ? { behavior: ctx.behavior } : {}
|
|
5586
5980
|
};
|
|
5587
5981
|
const sharedReadState = options.shareReadState ? resolveReadStateMap(ctx) : void 0;
|
|
5982
|
+
const journaledDeadlineMs = ctx.clock?.journaled === true && options.timeoutMs !== void 0 && options.timeoutMs > 0 ? options.timeoutMs : void 0;
|
|
5588
5983
|
const agent = createAgent({
|
|
5589
5984
|
...parentPreset,
|
|
5590
5985
|
...options.preset,
|
|
5591
5986
|
...inheritedMcp.mcpConnector !== void 0 && options.preset?.mcpServers === void 0 ? { mcpConnector: inheritedMcp.mcpConnector } : {},
|
|
5592
5987
|
provider: childProvider,
|
|
5593
5988
|
execution: ctx.execution,
|
|
5989
|
+
...ctx.clock ? { clock: ctx.clock } : {},
|
|
5990
|
+
...options.shareHandle && ctx.handle ? { handle: ctx.handle } : {},
|
|
5594
5991
|
...options.persist && ctx.session ? { session: ctx.session } : {},
|
|
5595
5992
|
...sharedReadState ? { readState: sharedReadState } : {}
|
|
5596
5993
|
});
|
|
5994
|
+
let reportedStats;
|
|
5995
|
+
agent.hooks.hook("agent:done", (stats) => {
|
|
5996
|
+
reportedStats = stats;
|
|
5997
|
+
});
|
|
5998
|
+
const nowMs = () => ctx.clock ? ctx.clock.now() : Date.now();
|
|
5999
|
+
const spawnStartedMs = await nowMs();
|
|
6000
|
+
let deadlineExceeded = false;
|
|
6001
|
+
if (journaledDeadlineMs !== void 0) agent.hooks.hook("turn:after", (turnCtx) => {
|
|
6002
|
+
if (deadlineExceeded) return;
|
|
6003
|
+
if (turnCtx.message.createdAt - spawnStartedMs < journaledDeadlineMs) return;
|
|
6004
|
+
deadlineExceeded = true;
|
|
6005
|
+
agent.abort();
|
|
6006
|
+
});
|
|
5597
6007
|
if (forwardHooks) {
|
|
5598
6008
|
const unregisterEnricher = agent.hooks.hook("tool:before", async (toolCtx) => {
|
|
5599
6009
|
if (toolCtx.name !== "write_file" && toolCtx.name !== "edit" && toolCtx.name !== "multi_edit") return;
|
|
@@ -5614,6 +6024,7 @@ function buildSpawnTool(options, state) {
|
|
|
5614
6024
|
const spawnHookCtx = {
|
|
5615
6025
|
id,
|
|
5616
6026
|
task,
|
|
6027
|
+
...ctx.callId ? { callId: ctx.callId } : {},
|
|
5617
6028
|
...resumeLabel !== void 0 ? { resumesChildId: resumeLabel } : {},
|
|
5618
6029
|
depth: childDepth,
|
|
5619
6030
|
model: announcedModel,
|
|
@@ -5631,48 +6042,54 @@ function buildSpawnTool(options, state) {
|
|
|
5631
6042
|
signal: ctx.signal,
|
|
5632
6043
|
depth: childDepth,
|
|
5633
6044
|
...options.persist && ctx.runId ? { parentRunId: ctx.runId } : {},
|
|
6045
|
+
...ctx.callId ? { parentCallId: ctx.callId } : {},
|
|
5634
6046
|
...options.persist && ctx.session ? { runLabel: id } : {},
|
|
5635
6047
|
...resumeRun ? { resumeRunId: resumeRun.id } : {},
|
|
5636
6048
|
...propagatedTracing ? { tracingContext: propagatedTracing } : {}
|
|
5637
6049
|
});
|
|
5638
6050
|
try {
|
|
5639
|
-
finalStats = await raceWithTimeout(runPromise, options.timeoutMs);
|
|
6051
|
+
finalStats = await raceWithTimeout(runPromise, journaledDeadlineMs === void 0 ? options.timeoutMs : void 0);
|
|
5640
6052
|
const treeTurns = flattenTurns(finalStats).length;
|
|
5641
6053
|
const usage = formatTokenUsage(finalStats);
|
|
5642
|
-
|
|
6054
|
+
const elapsedMs = await nowMs() - spawnStartedMs;
|
|
6055
|
+
if (deadlineExceeded) {
|
|
6056
|
+
childRunStatus = "timeout";
|
|
6057
|
+
result = `[sub-agent ${id}] Timed out after ${journaledDeadlineMs}ms`;
|
|
6058
|
+
} else if (ctx.signal.aborted) {
|
|
5643
6059
|
childRunStatus = "aborted";
|
|
5644
|
-
result = [`[sub-agent ${id}] Aborted after ${treeTurns} turns (${
|
|
6060
|
+
result = [`[sub-agent ${id}] Aborted after ${treeTurns} turns (${elapsedMs}ms)`, `Tokens: ${usage}`].join("\n");
|
|
5645
6061
|
} else {
|
|
5646
6062
|
const response = extractText(agent.turns.at(-1));
|
|
5647
6063
|
responseText = response ?? "";
|
|
5648
6064
|
result = [
|
|
5649
|
-
`[sub-agent ${id}] Completed in ${treeTurns} turns (${
|
|
6065
|
+
`[sub-agent ${id}] Completed in ${treeTurns} turns (${elapsedMs}ms)`,
|
|
5650
6066
|
`Tokens: ${usage}`,
|
|
5651
6067
|
"",
|
|
5652
6068
|
response || "(no text response)"
|
|
5653
6069
|
].join("\n");
|
|
5654
6070
|
}
|
|
5655
6071
|
} catch (err) {
|
|
5656
|
-
|
|
6072
|
+
const timedOutAfterMs = err instanceof SpawnTimeoutError ? err.timeoutMs : deadlineExceeded ? journaledDeadlineMs : void 0;
|
|
6073
|
+
if (timedOutAfterMs !== void 0) {
|
|
5657
6074
|
childRunStatus = "timeout";
|
|
5658
6075
|
agent.abort();
|
|
5659
6076
|
try {
|
|
5660
6077
|
finalStats = await runPromise;
|
|
5661
6078
|
} catch {
|
|
5662
|
-
finalStats = {
|
|
6079
|
+
finalStats = reportedStats ?? {
|
|
5663
6080
|
totalIn: 0,
|
|
5664
6081
|
totalOut: 0,
|
|
5665
6082
|
totalCacheRead: 0,
|
|
5666
6083
|
totalCacheCreation: 0,
|
|
5667
6084
|
turns: 0,
|
|
5668
|
-
elapsed:
|
|
6085
|
+
elapsed: timedOutAfterMs
|
|
5669
6086
|
};
|
|
5670
6087
|
}
|
|
5671
|
-
result = `[sub-agent ${id}] Timed out after ${
|
|
6088
|
+
result = `[sub-agent ${id}] Timed out after ${timedOutAfterMs}ms`;
|
|
5672
6089
|
} else {
|
|
5673
6090
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
5674
6091
|
childRunStatus = "error";
|
|
5675
|
-
finalStats = {
|
|
6092
|
+
finalStats = reportedStats ?? {
|
|
5676
6093
|
totalIn: 0,
|
|
5677
6094
|
totalOut: 0,
|
|
5678
6095
|
totalCacheRead: 0,
|
|
@@ -5684,6 +6101,7 @@ function buildSpawnTool(options, state) {
|
|
|
5684
6101
|
await ctx.hooks.callHook("spawn:error", {
|
|
5685
6102
|
id,
|
|
5686
6103
|
task,
|
|
6104
|
+
...ctx.callId ? { callId: ctx.callId } : {},
|
|
5687
6105
|
depth: childDepth,
|
|
5688
6106
|
model: announcedModel,
|
|
5689
6107
|
provider: childProvider.name,
|
|
@@ -5692,7 +6110,7 @@ function buildSpawnTool(options, state) {
|
|
|
5692
6110
|
}
|
|
5693
6111
|
} finally {
|
|
5694
6112
|
const childHandle = agent.handle;
|
|
5695
|
-
if (childHandle && ctx.execution.reassignBackgroundTasks) try {
|
|
6113
|
+
if (childHandle && childHandle.id !== ctx.handle.id && ctx.execution.reassignBackgroundTasks) try {
|
|
5696
6114
|
const reassigned = await ctx.execution.reassignBackgroundTasks(childHandle, ctx.handle, (info) => {
|
|
5697
6115
|
Promise.resolve(ctx.hooks.callHook("background:exit", info)).catch(() => {});
|
|
5698
6116
|
});
|
|
@@ -5726,6 +6144,7 @@ function buildSpawnTool(options, state) {
|
|
|
5726
6144
|
const childRunStats = {
|
|
5727
6145
|
id,
|
|
5728
6146
|
task,
|
|
6147
|
+
...ctx.callId ? { callId: ctx.callId } : {},
|
|
5729
6148
|
stats: finalStats,
|
|
5730
6149
|
depth: childDepth,
|
|
5731
6150
|
status: childRunStatus,
|
|
@@ -5736,6 +6155,7 @@ function buildSpawnTool(options, state) {
|
|
|
5736
6155
|
if (destroyError) await ctx.hooks.callHook("spawn:error", {
|
|
5737
6156
|
id,
|
|
5738
6157
|
task,
|
|
6158
|
+
...ctx.callId ? { callId: ctx.callId } : {},
|
|
5739
6159
|
depth: childDepth,
|
|
5740
6160
|
model: announcedModel,
|
|
5741
6161
|
provider: childProvider.name,
|
|
@@ -6262,6 +6682,58 @@ function warnUnsafeKey(logger, method, key) {
|
|
|
6262
6682
|
logger.warn(`${method}: refused unsafe key`, { key });
|
|
6263
6683
|
}
|
|
6264
6684
|
/**
|
|
6685
|
+
* Resolve a path inside one extension's storage root for the given scope.
|
|
6686
|
+
*
|
|
6687
|
+
* A module-level function (rather than a closure inside `makeContext`) so
|
|
6688
|
+
* `ctx.loadAgentPlugin` can derive its `${PLUGIN_DATA}` parent from the same
|
|
6689
|
+
* rules without depending on declaration order within the context builder.
|
|
6690
|
+
*/
|
|
6691
|
+
function extensionStoragePath(paths, extensionName, scope, ...segments) {
|
|
6692
|
+
const root = resolve(scope === "cache" ? paths.cacheDir : scope === "data" ? paths.dataDir : scope === "project" || scope === "session" ? paths.projectRoot ?? paths.dataDir : paths.configDir ?? paths.dataDir, "extensions", extensionName);
|
|
6693
|
+
const target = resolve(root, ...segments);
|
|
6694
|
+
if (target !== root && !target.startsWith(root + sep)) return root;
|
|
6695
|
+
return target;
|
|
6696
|
+
}
|
|
6697
|
+
/**
|
|
6698
|
+
* Stamp the contributing extension onto a skill.
|
|
6699
|
+
*
|
|
6700
|
+
* `SkillConfig.source` carries the scope (project / user / inline) and is
|
|
6701
|
+
* defaulted here for extensions that don't set it. The extension NAME goes in
|
|
6702
|
+
* `metadata` under the `zidane.` prefix the Agent Skills spec reserves for
|
|
6703
|
+
* client-specific keys, so the skill stays spec-conformant while a host UI can
|
|
6704
|
+
* still say which extension shipped it.
|
|
6705
|
+
*/
|
|
6706
|
+
function withProvenance(skill, source, extensionName) {
|
|
6707
|
+
return {
|
|
6708
|
+
...skill,
|
|
6709
|
+
source: skill.source ?? source,
|
|
6710
|
+
metadata: {
|
|
6711
|
+
...skill.metadata,
|
|
6712
|
+
"zidane.extension": extensionName
|
|
6713
|
+
}
|
|
6714
|
+
};
|
|
6715
|
+
}
|
|
6716
|
+
/**
|
|
6717
|
+
* Rejected-plugin placeholder for `ctx.loadAgentPlugin` calls that never
|
|
6718
|
+
* reach the loader (missing `dir`, or a late call after `setup` resolved).
|
|
6719
|
+
* Returning a `LoadedPlugin` with `ok: false` rather than throwing keeps the
|
|
6720
|
+
* method's contract uniform — callers branch on `ok`, never on exceptions.
|
|
6721
|
+
*/
|
|
6722
|
+
function emptyPluginHandle(dir) {
|
|
6723
|
+
return {
|
|
6724
|
+
ok: false,
|
|
6725
|
+
dir,
|
|
6726
|
+
root: dir,
|
|
6727
|
+
skills: [],
|
|
6728
|
+
mcpServers: [],
|
|
6729
|
+
components: {
|
|
6730
|
+
skills: "unsupported",
|
|
6731
|
+
mcp: "unsupported"
|
|
6732
|
+
},
|
|
6733
|
+
diagnostics: []
|
|
6734
|
+
};
|
|
6735
|
+
}
|
|
6736
|
+
/**
|
|
6265
6737
|
* Build the merged registry. `loaded` typically comes from
|
|
6266
6738
|
* `discoverExtensions()` plus any inline ones via `inlineLoadedExtension`.
|
|
6267
6739
|
*
|
|
@@ -6307,6 +6779,7 @@ async function buildExtensionRegistry(loaded, options = {}) {
|
|
|
6307
6779
|
hooks: {},
|
|
6308
6780
|
mcpServers: [],
|
|
6309
6781
|
skills: [],
|
|
6782
|
+
plugins: [],
|
|
6310
6783
|
systemFragments: [],
|
|
6311
6784
|
contextBreakdownSections: [],
|
|
6312
6785
|
contextPanelSections: [],
|
|
@@ -6467,6 +6940,7 @@ function captureContributions(perExtSinks) {
|
|
|
6467
6940
|
hooks: perExtSinks.hooks,
|
|
6468
6941
|
mcpServers: perExtSinks.mcpServers,
|
|
6469
6942
|
skills: perExtSinks.skills,
|
|
6943
|
+
plugins: perExtSinks.plugins,
|
|
6470
6944
|
systemFragments: perExtSinks.systemFragments,
|
|
6471
6945
|
contextBreakdownSections: perExtSinks.contextBreakdownSections,
|
|
6472
6946
|
contextPanelSections: perExtSinks.contextPanelSections,
|
|
@@ -6544,6 +7018,7 @@ function mergeContributions(active, inactive = [], options = {}) {
|
|
|
6544
7018
|
const hooks = {};
|
|
6545
7019
|
const mcpServers = [];
|
|
6546
7020
|
const skills = [];
|
|
7021
|
+
const plugins = [];
|
|
6547
7022
|
const systemFragments = [];
|
|
6548
7023
|
const contextBreakdownSections = [];
|
|
6549
7024
|
const contextPanelSections = [];
|
|
@@ -6586,6 +7061,7 @@ function mergeContributions(active, inactive = [], options = {}) {
|
|
|
6586
7061
|
}
|
|
6587
7062
|
mcpServers.push(...c.mcpServers);
|
|
6588
7063
|
skills.push(...c.skills);
|
|
7064
|
+
plugins.push(...c.plugins);
|
|
6589
7065
|
systemFragments.push(...c.systemFragments);
|
|
6590
7066
|
contextBreakdownSections.push(...c.contextBreakdownSections);
|
|
6591
7067
|
contextPanelSections.push(...c.contextPanelSections);
|
|
@@ -6659,6 +7135,7 @@ function mergeContributions(active, inactive = [], options = {}) {
|
|
|
6659
7135
|
hooks,
|
|
6660
7136
|
mcpServers,
|
|
6661
7137
|
skills,
|
|
7138
|
+
plugins,
|
|
6662
7139
|
systemFragments,
|
|
6663
7140
|
contextBreakdownSections,
|
|
6664
7141
|
contextPanelSections,
|
|
@@ -6777,6 +7254,46 @@ function filterExtensionRegistry(registry, enabled) {
|
|
|
6777
7254
|
const filteredOut = registry.loaded.filter((ext) => !allow.has(ext.name));
|
|
6778
7255
|
return mergeContributions(filteredActive, [...registry.inactive, ...filteredOut]);
|
|
6779
7256
|
}
|
|
7257
|
+
/**
|
|
7258
|
+
* Drop the contributions of Agent Plugins the user has disabled.
|
|
7259
|
+
*
|
|
7260
|
+
* `filterExtensionRegistry` keys on EXTENSION names, so it can't express
|
|
7261
|
+
* "this extension runs, but the plugin it loaded does not". Plugin skills and
|
|
7262
|
+
* MCP servers are fanned into the loading extension's sinks by
|
|
7263
|
+
* `ctx.loadAgentPlugin`, which means without this pass `enabledPlugins` would
|
|
7264
|
+
* render a checkbox that changes nothing for extension-loaded plugins.
|
|
7265
|
+
*
|
|
7266
|
+
* Skills are matched by `metadata['zidane.plugin']` (stamped by the loader);
|
|
7267
|
+
* MCP servers carry no such marker, so they're matched by identity against
|
|
7268
|
+
* the server list on each {@link LoadedPluginRecord}.
|
|
7269
|
+
*
|
|
7270
|
+
* Returns the input registry unchanged when nothing is disabled, so callers
|
|
7271
|
+
* can compare identity to skip recomposing.
|
|
7272
|
+
*/
|
|
7273
|
+
function filterRegistryPlugins(registry, enabledPlugins, shadowed) {
|
|
7274
|
+
if (registry.plugins.length === 0) return registry;
|
|
7275
|
+
const allow = enabledPlugins === void 0 ? null : new Set(enabledPlugins);
|
|
7276
|
+
const dropped = registry.plugins.filter((record) => allow !== null && !allow.has(record.name) || shadowed?.has(record.name) === true);
|
|
7277
|
+
if (dropped.length === 0) return registry;
|
|
7278
|
+
const droppedNames = new Set(dropped.map((record) => record.name));
|
|
7279
|
+
const droppedServers = new Set(dropped.flatMap((record) => record.plugin.mcpServers));
|
|
7280
|
+
const prune = (ext) => {
|
|
7281
|
+
const contributions = ext.contributions;
|
|
7282
|
+
if (!contributions || contributions.plugins.length === 0) return ext;
|
|
7283
|
+
return {
|
|
7284
|
+
...ext,
|
|
7285
|
+
contributions: {
|
|
7286
|
+
...contributions,
|
|
7287
|
+
skills: contributions.skills.filter((skill) => {
|
|
7288
|
+
const owner = skill.metadata?.["zidane.plugin"];
|
|
7289
|
+
return owner === void 0 || !droppedNames.has(owner);
|
|
7290
|
+
}),
|
|
7291
|
+
mcpServers: contributions.mcpServers.filter((server) => !droppedServers.has(server))
|
|
7292
|
+
}
|
|
7293
|
+
};
|
|
7294
|
+
};
|
|
7295
|
+
return mergeContributions(registry.loaded.map(prune), registry.inactive);
|
|
7296
|
+
}
|
|
6780
7297
|
/** Dispose resources owned by an extension registry. Idempotent per registry. */
|
|
6781
7298
|
async function disposeExtensionRegistry(registry) {
|
|
6782
7299
|
await registry.dispose();
|
|
@@ -6845,10 +7362,50 @@ function makeContext(opts) {
|
|
|
6845
7362
|
logger.warn("registerSkill: missing name");
|
|
6846
7363
|
return;
|
|
6847
7364
|
}
|
|
6848
|
-
sinks.skills.push(
|
|
6849
|
-
|
|
6850
|
-
|
|
7365
|
+
sinks.skills.push(withProvenance(skill, source, definition.name));
|
|
7366
|
+
};
|
|
7367
|
+
const loadPlugin = async (spec) => {
|
|
7368
|
+
const options = typeof spec === "string" ? { dir: spec } : spec;
|
|
7369
|
+
const requested = options?.dir;
|
|
7370
|
+
if (typeof requested !== "string" || requested.length === 0) {
|
|
7371
|
+
logger.warn("loadAgentPlugin: missing dir");
|
|
7372
|
+
return emptyPluginHandle("");
|
|
7373
|
+
}
|
|
7374
|
+
if (!guardOpen("loadAgentPlugin")) return emptyPluginHandle(requested);
|
|
7375
|
+
const target = isAbsolute(requested) ? requested : resolve(dir ?? paths.cwd, requested);
|
|
7376
|
+
const loaded = await loadAgentPlugin(target, {
|
|
7377
|
+
source,
|
|
7378
|
+
dataRoot: extensionStoragePath(paths, definition.name, "data", "plugin-data"),
|
|
7379
|
+
...options.skills !== void 0 ? { skills: options.skills } : {},
|
|
7380
|
+
...options.mcp !== void 0 ? { mcp: options.mcp } : {},
|
|
7381
|
+
...options.namePrefix !== void 0 ? { namePrefix: options.namePrefix } : {}
|
|
7382
|
+
});
|
|
7383
|
+
for (const diagnostic of loaded.diagnostics) {
|
|
7384
|
+
const detail = {
|
|
7385
|
+
plugin: loaded.manifest?.name ?? target,
|
|
7386
|
+
code: diagnostic.code,
|
|
7387
|
+
...diagnostic.path ? { path: diagnostic.path } : {}
|
|
7388
|
+
};
|
|
7389
|
+
if (diagnostic.severity === "error") logger.error(`loadAgentPlugin: ${diagnostic.message}`, detail);
|
|
7390
|
+
else logger.warn(`loadAgentPlugin: ${diagnostic.message}`, detail);
|
|
7391
|
+
}
|
|
7392
|
+
if (!guardOpen("loadAgentPlugin (completed after setup resolved)")) return loaded;
|
|
7393
|
+
if (loaded.ok) {
|
|
7394
|
+
for (const skill of loaded.skills) sinks.skills.push(withProvenance(skill, source, definition.name));
|
|
7395
|
+
for (const config of loaded.mcpServers) sinks.mcpServers.push(config);
|
|
7396
|
+
}
|
|
7397
|
+
sinks.plugins.push({
|
|
7398
|
+
name: loaded.manifest?.name ?? basename(target),
|
|
7399
|
+
extensionName: definition.name,
|
|
7400
|
+
dir: target,
|
|
7401
|
+
plugin: loaded
|
|
7402
|
+
});
|
|
7403
|
+
logger.debug("loadAgentPlugin: registered", {
|
|
7404
|
+
plugin: loaded.manifest?.name,
|
|
7405
|
+
skills: loaded.skills.length,
|
|
7406
|
+
mcpServers: loaded.mcpServers.length
|
|
6851
7407
|
});
|
|
7408
|
+
return loaded;
|
|
6852
7409
|
};
|
|
6853
7410
|
const registerCompletionProvider = (provider) => {
|
|
6854
7411
|
if (!guardOpen("registerCompletionProvider")) return;
|
|
@@ -7231,12 +7788,7 @@ function makeContext(opts) {
|
|
|
7231
7788
|
const persisted = readSettings?.()?.extensionConfig?.[definition.name]?.[key];
|
|
7232
7789
|
return resolveExtensionConfigValue(item, persisted);
|
|
7233
7790
|
} };
|
|
7234
|
-
const storageSurface = { path: (scope, ...segments) =>
|
|
7235
|
-
const root = resolve(scope === "cache" ? paths.cacheDir : scope === "data" ? paths.dataDir : scope === "project" || scope === "session" ? paths.projectRoot ?? paths.dataDir : paths.configDir ?? paths.dataDir, "extensions", definition.name);
|
|
7236
|
-
const target = resolve(root, ...segments);
|
|
7237
|
-
if (target !== root && !target.startsWith(root + sep)) return root;
|
|
7238
|
-
return target;
|
|
7239
|
-
} };
|
|
7791
|
+
const storageSurface = { path: (scope, ...segments) => extensionStoragePath(paths, definition.name, scope, ...segments) };
|
|
7240
7792
|
const sessionSurface = {
|
|
7241
7793
|
get: (key) => getExtensionSessionState(currentExtensionSession(), definition.name, key),
|
|
7242
7794
|
set: (key, value) => setExtensionSessionState(currentExtensionSession(), definition.name, key, value),
|
|
@@ -7347,6 +7899,7 @@ function makeContext(opts) {
|
|
|
7347
7899
|
registerHook,
|
|
7348
7900
|
registerMcpServer,
|
|
7349
7901
|
registerSkill,
|
|
7902
|
+
loadAgentPlugin: loadPlugin,
|
|
7350
7903
|
registerCompletionProvider,
|
|
7351
7904
|
registerInlineAutocompleteProvider,
|
|
7352
7905
|
registerCommand,
|
|
@@ -7997,6 +8550,7 @@ const EMPTY_EXTENSION_REGISTRY = Object.freeze({
|
|
|
7997
8550
|
hooks: Object.freeze({}),
|
|
7998
8551
|
mcpServers: Object.freeze([]),
|
|
7999
8552
|
skills: Object.freeze([]),
|
|
8553
|
+
plugins: Object.freeze([]),
|
|
8000
8554
|
systemFragments: Object.freeze([]),
|
|
8001
8555
|
contextBreakdownSections: Object.freeze([]),
|
|
8002
8556
|
contextPanelSections: Object.freeze([]),
|
|
@@ -12511,6 +13065,7 @@ function buildToolHookBase(ctx, turnId, callId, name, displayName, input) {
|
|
|
12511
13065
|
input,
|
|
12512
13066
|
...ctx.runId !== void 0 ? { runId: ctx.runId } : {},
|
|
12513
13067
|
...ctx.parentRunId !== void 0 ? { parentRunId: ctx.parentRunId } : {},
|
|
13068
|
+
...ctx.parentCallId !== void 0 ? { parentCallId: ctx.parentCallId } : {},
|
|
12514
13069
|
...typeof ctx.depth === "number" ? { depth: ctx.depth } : {}
|
|
12515
13070
|
};
|
|
12516
13071
|
}
|
|
@@ -12722,6 +13277,7 @@ async function runSingleToolDispatch(ctx, call, turnId, fixed) {
|
|
|
12722
13277
|
callId,
|
|
12723
13278
|
runId: ctx.runId,
|
|
12724
13279
|
...ctx.parentRunId !== void 0 ? { parentRunId: ctx.parentRunId } : {},
|
|
13280
|
+
...ctx.parentCallId !== void 0 ? { parentCallId: ctx.parentCallId } : {},
|
|
12725
13281
|
...ctx.session ? { session: ctx.session } : {},
|
|
12726
13282
|
...ctx.readState ? { readState: ctx.readState } : {},
|
|
12727
13283
|
...ctx.toolState ? { state: ctx.toolState.begin(callId) } : {},
|
|
@@ -13322,6 +13878,7 @@ async function startDurableToolExecution(ctx, call, turnId, index) {
|
|
|
13322
13878
|
callId,
|
|
13323
13879
|
runId: ctx.runId,
|
|
13324
13880
|
...ctx.parentRunId !== void 0 ? { parentRunId: ctx.parentRunId } : {},
|
|
13881
|
+
...ctx.parentCallId !== void 0 ? { parentCallId: ctx.parentCallId } : {},
|
|
13325
13882
|
...ctx.session ? { session: ctx.session } : {},
|
|
13326
13883
|
...ctx.readState ? { readState: ctx.readState } : {},
|
|
13327
13884
|
...ctx.toolState ? { state: ctx.toolState.begin(callId) } : {},
|
|
@@ -13703,6 +14260,221 @@ function createToolCancelRegistry(options = {}) {
|
|
|
13703
14260
|
};
|
|
13704
14261
|
}
|
|
13705
14262
|
//#endregion
|
|
14263
|
+
//#region src/tool-state.ts
|
|
14264
|
+
/**
|
|
14265
|
+
* Durable key/value state for tools.
|
|
14266
|
+
*
|
|
14267
|
+
* A tool reaches this through {@link ToolContext.state} — a plain,
|
|
14268
|
+
* SYNCHRONOUS map view. Synchronous is not a convenience choice: under a
|
|
14269
|
+
* durable-execution runtime the tool body runs inside a journaled action
|
|
14270
|
+
* (Restate's `ctx.run`), and a durable read (`ctx.get`) is illegal in
|
|
14271
|
+
* there. So the flow is split into three phases the runtime can journal
|
|
14272
|
+
* independently:
|
|
14273
|
+
*
|
|
14274
|
+
* 1. **hydrate** — load the snapshot ONCE per agent, outside any
|
|
14275
|
+
* journaled action, before the first tool dispatch.
|
|
14276
|
+
* 2. **stage** — the tool mutates its per-call view. Mutations are
|
|
14277
|
+
* recorded as an ordered {@link ToolStateOp} list, NOT written
|
|
14278
|
+
* through: they're plain data, so a durable adapter can journal them
|
|
14279
|
+
* alongside the tool result.
|
|
14280
|
+
* 3. **commit** — the loop applies the staged ops through
|
|
14281
|
+
* {@link ToolStateStore.commit} after the call settles, outside the
|
|
14282
|
+
* journaled action.
|
|
14283
|
+
*
|
|
14284
|
+
* The invariant that makes this replay-safe:
|
|
14285
|
+
*
|
|
14286
|
+
* > A staged mutation is committed only if it came back OUT of a journal
|
|
14287
|
+
* > entry.
|
|
14288
|
+
*
|
|
14289
|
+
* A durable adapter (see `restateTool`) reads {@link StagedToolState.pending}
|
|
14290
|
+
* from inside its journaled closure, ships the ops out as part of the
|
|
14291
|
+
* journaled value, and calls {@link StagedToolState.restore} outside it. On
|
|
14292
|
+
* replay the tool body never runs, so nothing is staged live — `restore`
|
|
14293
|
+
* re-seeds the exact same ops from the journal and the commit re-issues an
|
|
14294
|
+
* identical write. Paths that produced no journal entry (a fatal error, a
|
|
14295
|
+
* control abort, a dead attempt) call {@link StagedToolState.clear} so live
|
|
14296
|
+
* mutations from an attempt nobody recorded can never reach the store.
|
|
14297
|
+
*
|
|
14298
|
+
* The store seam is deliberately runtime-agnostic: `zidane/restate` ships
|
|
14299
|
+
* `restateToolStateStore`, but a Redis / Postgres / file-backed
|
|
14300
|
+
* implementation is the same two methods. With no store configured the
|
|
14301
|
+
* controller is in-memory only — `ctx.state` still works everywhere
|
|
14302
|
+
* (local CLI, tests, subagents), it just doesn't outlive the process.
|
|
14303
|
+
*/
|
|
14304
|
+
/**
|
|
14305
|
+
* Default per-value size budget: 64 KiB of JSON.
|
|
14306
|
+
*
|
|
14307
|
+
* Deliberately far below any backend's hard ceiling (Restate's journal-entry
|
|
14308
|
+
* limit is 32 MiB) because this is a design guard, not a protocol limit.
|
|
14309
|
+
* Durable tool state is for flags, ids, counters, and cursors; a value that
|
|
14310
|
+
* doesn't fit in 64 KiB is a payload, and payloads belong in the tool result
|
|
14311
|
+
* or in storage the tool owns. Raise it on the store if you disagree — but
|
|
14312
|
+
* know that everything here is rewritten on every commit.
|
|
14313
|
+
*/
|
|
14314
|
+
const DEFAULT_MAX_VALUE_BYTES = 64 * 1024;
|
|
14315
|
+
/** Brand so {@link stagedToolState} can recognise views this module made. */
|
|
14316
|
+
const STAGED = Symbol("zidane.toolState.staged");
|
|
14317
|
+
/**
|
|
14318
|
+
* Resolve the staging handle behind a view. Returns `undefined` for a
|
|
14319
|
+
* missing view or a foreign implementation (a host that supplied its own
|
|
14320
|
+
* `ToolStateView`), which lets adapters degrade to "no durable staging"
|
|
14321
|
+
* instead of throwing.
|
|
14322
|
+
*/
|
|
14323
|
+
function stagedToolState(view) {
|
|
14324
|
+
if (!view) return void 0;
|
|
14325
|
+
return view[STAGED] ?? void 0;
|
|
14326
|
+
}
|
|
14327
|
+
function createToolStateController(store) {
|
|
14328
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
14329
|
+
const views = /* @__PURE__ */ new Map();
|
|
14330
|
+
const maxValueBytes = store?.maxValueBytes ?? 65536;
|
|
14331
|
+
let hydrated;
|
|
14332
|
+
const hydrate = () => {
|
|
14333
|
+
if (hydrated) return hydrated;
|
|
14334
|
+
hydrated = (async () => {
|
|
14335
|
+
if (!store) return;
|
|
14336
|
+
const loaded = await store.load();
|
|
14337
|
+
for (const [key, value] of Object.entries(loaded ?? {})) if (!snapshot.has(key)) snapshot.set(key, value);
|
|
14338
|
+
})().catch((err) => {
|
|
14339
|
+
hydrated = void 0;
|
|
14340
|
+
throw err;
|
|
14341
|
+
});
|
|
14342
|
+
return hydrated;
|
|
14343
|
+
};
|
|
14344
|
+
const begin = (callId) => {
|
|
14345
|
+
const existing = views.get(callId);
|
|
14346
|
+
if (existing) return existing;
|
|
14347
|
+
const view = createStagingView(snapshot, maxValueBytes);
|
|
14348
|
+
views.set(callId, view);
|
|
14349
|
+
return view;
|
|
14350
|
+
};
|
|
14351
|
+
const commit = async (callId) => {
|
|
14352
|
+
const view = views.get(callId);
|
|
14353
|
+
if (!view) return;
|
|
14354
|
+
views.delete(callId);
|
|
14355
|
+
const ops = view[STAGED].pending();
|
|
14356
|
+
if (ops.length === 0) return;
|
|
14357
|
+
await store?.commit(ops);
|
|
14358
|
+
applyOps(snapshot, ops);
|
|
14359
|
+
};
|
|
14360
|
+
return {
|
|
14361
|
+
hydrate,
|
|
14362
|
+
begin,
|
|
14363
|
+
commit,
|
|
14364
|
+
discard: (callId) => {
|
|
14365
|
+
views.delete(callId);
|
|
14366
|
+
},
|
|
14367
|
+
pending: (callId) => views.get(callId)?.[STAGED].pending() ?? [],
|
|
14368
|
+
snapshot: () => Object.fromEntries(snapshot)
|
|
14369
|
+
};
|
|
14370
|
+
}
|
|
14371
|
+
/**
|
|
14372
|
+
* Build a view over `snapshot` that records writes instead of applying
|
|
14373
|
+
* them. Reads layer staged ops on top of the snapshot so a tool sees its
|
|
14374
|
+
* own writes back — including deletions, which read as absent.
|
|
14375
|
+
*/
|
|
14376
|
+
function createStagingView(snapshot, maxValueBytes) {
|
|
14377
|
+
let ops = [];
|
|
14378
|
+
const latest = /* @__PURE__ */ new Map();
|
|
14379
|
+
const stage = (op) => {
|
|
14380
|
+
ops.push(op);
|
|
14381
|
+
latest.set(op.key, op);
|
|
14382
|
+
};
|
|
14383
|
+
const resolve = (key) => {
|
|
14384
|
+
const staged = latest.get(key);
|
|
14385
|
+
if (staged) return staged.delete === true ? { present: false } : {
|
|
14386
|
+
present: true,
|
|
14387
|
+
value: staged.value
|
|
14388
|
+
};
|
|
14389
|
+
return snapshot.has(key) ? {
|
|
14390
|
+
present: true,
|
|
14391
|
+
value: snapshot.get(key)
|
|
14392
|
+
} : { present: false };
|
|
14393
|
+
};
|
|
14394
|
+
const view = {
|
|
14395
|
+
get: (key) => {
|
|
14396
|
+
const resolved = resolve(key);
|
|
14397
|
+
return resolved.present ? resolved.value : void 0;
|
|
14398
|
+
},
|
|
14399
|
+
set: (key, value) => {
|
|
14400
|
+
assertStorable(key, value, maxValueBytes);
|
|
14401
|
+
if (value === void 0) stage({
|
|
14402
|
+
key,
|
|
14403
|
+
delete: true
|
|
14404
|
+
});
|
|
14405
|
+
else stage({
|
|
14406
|
+
key,
|
|
14407
|
+
value
|
|
14408
|
+
});
|
|
14409
|
+
},
|
|
14410
|
+
delete: (key) => {
|
|
14411
|
+
stage({
|
|
14412
|
+
key,
|
|
14413
|
+
delete: true
|
|
14414
|
+
});
|
|
14415
|
+
},
|
|
14416
|
+
has: (key) => resolve(key).present,
|
|
14417
|
+
keys: () => {
|
|
14418
|
+
const keys = new Set(snapshot.keys());
|
|
14419
|
+
for (const [key, op] of latest) if (op.delete === true) keys.delete(key);
|
|
14420
|
+
else keys.add(key);
|
|
14421
|
+
return [...keys].sort();
|
|
14422
|
+
},
|
|
14423
|
+
[STAGED]: {
|
|
14424
|
+
pending: () => [...ops],
|
|
14425
|
+
restore: (restored) => {
|
|
14426
|
+
ops = [...restored];
|
|
14427
|
+
latest.clear();
|
|
14428
|
+
for (const op of ops) latest.set(op.key, op);
|
|
14429
|
+
},
|
|
14430
|
+
clear: () => {
|
|
14431
|
+
ops = [];
|
|
14432
|
+
latest.clear();
|
|
14433
|
+
}
|
|
14434
|
+
}
|
|
14435
|
+
};
|
|
14436
|
+
Object.defineProperty(view, STAGED, {
|
|
14437
|
+
value: view[STAGED],
|
|
14438
|
+
enumerable: false
|
|
14439
|
+
});
|
|
14440
|
+
return view;
|
|
14441
|
+
}
|
|
14442
|
+
/**
|
|
14443
|
+
* Reject a value that can't survive the trip to the store, at the moment the
|
|
14444
|
+
* tool stages it.
|
|
14445
|
+
*
|
|
14446
|
+
* Scalars are skipped: they're bounded, and paying a `JSON.stringify` per
|
|
14447
|
+
* `set` on a hot tool would be a real cost for no signal. Strings and objects
|
|
14448
|
+
* are measured in UTF-8 bytes, which is what both the journal and the store
|
|
14449
|
+
* actually spend.
|
|
14450
|
+
*/
|
|
14451
|
+
function assertStorable(key, value, maxValueBytes) {
|
|
14452
|
+
if (value === void 0 || value === null || typeof value === "boolean" || typeof value === "number") return;
|
|
14453
|
+
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.`);
|
|
14454
|
+
let json;
|
|
14455
|
+
try {
|
|
14456
|
+
json = typeof value === "string" ? value : JSON.stringify(value) ?? "";
|
|
14457
|
+
} catch (err) {
|
|
14458
|
+
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.`);
|
|
14459
|
+
}
|
|
14460
|
+
const bytes = Buffer.byteLength(json, "utf8");
|
|
14461
|
+
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.`);
|
|
14462
|
+
}
|
|
14463
|
+
function formatBytes(bytes) {
|
|
14464
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
14465
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
14466
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
14467
|
+
}
|
|
14468
|
+
/** Apply ops to a snapshot map in order — last write per key wins. */
|
|
14469
|
+
function applyOps(snapshot, ops) {
|
|
14470
|
+
for (const op of ops) if (op.delete === true) snapshot.delete(op.key);
|
|
14471
|
+
else snapshot.set(op.key, op.value);
|
|
14472
|
+
}
|
|
14473
|
+
/** Runtime guard for a journaled op list — adapters validate before restoring. */
|
|
14474
|
+
function isToolStateOps(value) {
|
|
14475
|
+
return Array.isArray(value) && value.every((op) => typeof op === "object" && op !== null && typeof op.key === "string" && (op.delete === true || "value" in op));
|
|
14476
|
+
}
|
|
14477
|
+
//#endregion
|
|
13706
14478
|
//#region src/agent.ts
|
|
13707
14479
|
/**
|
|
13708
14480
|
* Single builder for a {@link ContextAssembly}, shared by the per-run snapshot
|
|
@@ -15406,6 +16178,7 @@ function createAgent(rawOptions) {
|
|
|
15406
16178
|
...agentReadState ? { readState: agentReadState } : {},
|
|
15407
16179
|
toolState: toolStateController,
|
|
15408
16180
|
...options.parentRunId ? { parentRunId: options.parentRunId } : {},
|
|
16181
|
+
...options.parentCallId ? { parentCallId: options.parentCallId } : {},
|
|
15409
16182
|
depth: runDepth,
|
|
15410
16183
|
thinkingBudget,
|
|
15411
16184
|
...taskBudget !== void 0 ? { taskBudget } : {},
|
|
@@ -16024,6 +16797,6 @@ function createAgent(rawOptions) {
|
|
|
16024
16797
|
};
|
|
16025
16798
|
}
|
|
16026
16799
|
//#endregion
|
|
16027
|
-
export {
|
|
16800
|
+
export { installExtensionFromSpec as $, groupBindings as $t, selectFilesFromSession as A, defaultRepeatGuardNormalize as An, createSkillsReadTool as At, buildUpToCompactPrompt as B, matchModelEntries as Bt, resolveLegacySessionDir as C, resolveExtensionConfigValue as Cn, waitTask as Ct, resolveTasksDir as D, OUTPUT_RESERVE_TOKENS as Dn, reserveChildLabel as Dt, resolveSessionDir as E, AUTO_COMPACT_MIN_GROWTH_FRACTION as En, isSpawnTool as Et, TRAILER as F, hashContent as Fn, readFile$1 as Ft, summaryToTurn as G, edit as Gt, anchorPreviewFor as H, createInteractionTool as Ht, buildCompactPrompt as I, hydrateReadStateFromSession as In, looksBinary as It, CompactPromptTooLongError as J, KEYBINDING_DEFS as Jt, truncateHeadForPtlRetry as K, validateToolArgs as Kt, buildFromCompactPrompt as L, hydrateReadStateFromSessionAsync as Ln, multiEdit as Lt, compactConversation as M, normalizeShellCommand as Mn, createShellTool as Mt, BASE_INSTRUCTIONS as N, stableStringify as Nn, shell as Nt, buildPostCompactAttachments as O, effectiveContextWindow as On, createSkillsUseTool as Ot, NO_TOOLS_PREAMBLE as P, getReadState as Pn, tailTruncate as Pt, fetchExtensionSource as Q, formatBindingForDisplay as Qt, buildFullCompactPrompt as R, readStateKey as Rn, createModelSearchTool as Rt, resolveLegacyPersistDir as S, resolveExtensionConfig as Sn, WAIT_TASK_TIMED_OUT_PREFIX as St, resolvePersistDir as T, toWireConfigSchema as Tn, createSpawnTool as Tt, sliceForCompaction as U, grep as Ut, ANCHOR_PREVIEW_MAX_CHARS as V, listFiles as Vt, stripImagesFromTurns as W, glob as Wt, createExtensionPromptBridge as X, KEYBINDING_KEY_COL_WIDTH as Xt, EMPTY_EXTENSION_REGISTRY as Y, KEYBINDING_DEF_BY_ACTION as Yt, ExtensionInstallError as Z, ensureKeybindingsFile as Zt, buildPersistedStub as _, defineExtension as _n, composePresets as _t, stagedToolState as a, readKeybindingsRaw as an, createExtensionHud as at, migrateSessionArtifactDirs as b, isValidExtensionConfigItem as bn, basic_default as bt, INTERRUPT_MESSAGE_FOR_TOOL_USE as c, registerExtensionHostModules as cn, buildExtensionRegistry as ct, TOOL_USE_SKIPPED_MESSAGE as d, discoverExtensions as dn, filterExtensionRegistry as dt, keybindingsPath as en, listExtensionCandidates as et, applyCompactSummaryCutoff as f, inlineLoadedExtension as fn, filterRegistryPlugins as ft, PERSISTENCE_PREVIEW_BYTES as g, createDebugLog as gn, createWorkspaceSurface as gt, PERSISTED_STUB_PREFIX as h, scanExtensionDirectories as hn, createExtensionHostRuntimeBridge as ht, isToolStateOps as i, readKeybindings as in, updateInstalledExtension as it, selectRecentFiles as j, defaultRepeatGuardTracked as jn, shellKill as jt, selectFilesFromReadState as k, shouldAutoCompact as kn, createSkillsRunScriptTool as kt, SHELL_CASCADE_CANCEL_MESSAGE as l, ENTRY_CANDIDATES as ln, composeExtensionPreset as lt, renderStaleReadNotice as m, parseExtensionScanPathEnv as mn, partitionExtensionCatalog as mt, DEFAULT_MAX_VALUE_BYTES as n, mergeKeybindings as nn, readExtensionInstallProvenance as nt, normalizeProviders as o, resolveExtensionKeybindings as on, discoverAndBuildExtensionRegistry as ot, collectNewlyStaleReadPaths as p, isExtensionDefinition as pn, mergeContributions as pt, CompactInvalidInputError as q, DEFAULT_KEYBINDINGS as qt, createToolStateController as r, parseBindingSpec as rn, removeInstalledExtension as rt, resolveModelTargets as s, stripJsonComments as sn, buildExtensionPreset as st, createAgent as t, matchesBinding as tn, parseExtensionInstallSource as tt, TOOL_USE_CANCELLED_MESSAGE as u, defaultExtensionScanPaths as un, disposeExtensionRegistry as ut, cleanupPersistedSession as v, clampExtensionConfigNumber as vn, definePreset as vt, resolveMcpWarningsDir as w, sanitizeExtensionConfigActionPatch as wn, createToolSearchTool as wt, rehomePersistedStubs as x, isValidExtensionConfigValue as xn, writeFile$1 as xt, maybePersistToolResult as y, coerceExtensionConfigInput as yn, basicTools as yt, buildTailCompactPrompt as z, resolveReadStateMap as zn, formatModelLine as zt };
|
|
16028
16801
|
|
|
16029
|
-
//# sourceMappingURL=agent-
|
|
16802
|
+
//# sourceMappingURL=agent-BVNkC1h4.js.map
|