@code-yeongyu/senpi 2026.9.4-2 → 2026.9.4-3
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/CHANGELOG.md +23 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +31 -41
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/credential-pool/state-store.d.ts +1 -0
- package/dist/core/credential-pool/state-store.d.ts.map +1 -1
- package/dist/core/credential-pool/state-store.js +22 -2
- package/dist/core/credential-pool/state-store.js.map +1 -1
- package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/store.js +8 -20
- package/dist/core/extensions/builtin/goal/store.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.d.ts.map +1 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.js +3 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.js.map +1 -1
- package/dist/core/extensions/builtin/imagegen/auth.d.ts.map +1 -1
- package/dist/core/extensions/builtin/imagegen/auth.js +10 -2
- package/dist/core/extensions/builtin/imagegen/auth.js.map +1 -1
- package/dist/core/extensions/builtin/loop/store.d.ts +8 -6
- package/dist/core/extensions/builtin/loop/store.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop/store.js +54 -88
- package/dist/core/extensions/builtin/loop/store.js.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.d.ts +2 -0
- package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +181 -3
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.d.ts +10 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.js +21 -0
- package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts +24 -0
- package/dist/core/extensions/builtin/terminal/manifest-lease.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/manifest-lease.js +105 -0
- package/dist/core/extensions/builtin/terminal/manifest-lease.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -2
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +24 -2
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/restore.d.ts +43 -0
- package/dist/core/extensions/builtin/terminal/restore.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/restore.js +149 -0
- package/dist/core/extensions/builtin/terminal/restore.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts +110 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.js +158 -0
- package/dist/core/extensions/builtin/terminal/terminal-manifest.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/bash-input.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-input.js +3 -2
- package/dist/core/extensions/builtin/terminal/tools/bash-input.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js +5 -4
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-resize.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-resize.js +3 -2
- package/dist/core/extensions/builtin/terminal/tools/bash-resize.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/context.js +10 -0
- package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -4
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +58 -14
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
- package/dist/core/extensions/builtin/tool-search/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-search/index.js +1 -1
- package/dist/core/extensions/builtin/tool-search/index.js.map +1 -1
- package/dist/core/extensions/builtin/tool-search/native-search.d.ts +9 -4
- package/dist/core/extensions/builtin/tool-search/native-search.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tool-search/native-search.js +11 -6
- package/dist/core/extensions/builtin/tool-search/native-search.js.map +1 -1
- package/dist/core/extensions/builtin/tool-search/native-support.d.ts +13 -0
- package/dist/core/extensions/builtin/tool-search/native-support.d.ts.map +1 -0
- package/dist/core/extensions/builtin/tool-search/native-support.js +45 -0
- package/dist/core/extensions/builtin/tool-search/native-support.js.map +1 -0
- package/dist/core/lockfile-policy.d.ts +11 -0
- package/dist/core/lockfile-policy.d.ts.map +1 -1
- package/dist/core/lockfile-policy.js +19 -0
- package/dist/core/lockfile-policy.js.map +1 -1
- package/dist/core/session-sidecar-store.d.ts +62 -0
- package/dist/core/session-sidecar-store.d.ts.map +1 -0
- package/dist/core/session-sidecar-store.js +149 -0
- package/dist/core/session-sidecar-store.js.map +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +11 -15
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/modes/app-server/daemon/process.d.ts +33 -3
- package/dist/modes/app-server/daemon/process.d.ts.map +1 -1
- package/dist/modes/app-server/daemon/process.js +57 -6
- package/dist/modes/app-server/daemon/process.js.map +1 -1
- package/dist/modes/app-server/daemon.js +9 -1
- package/dist/modes/app-server/daemon.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts +2 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +77 -18
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +14 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +5 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -3,7 +3,7 @@ import { Type } from "typebox";
|
|
|
3
3
|
import { APPROVED_MONITOR_PARENT } from "../monitor-permission.js";
|
|
4
4
|
import { MonitorRegistry } from "../monitor-registry.js";
|
|
5
5
|
import { DEFAULT_COLS, DEFAULT_ROWS, TERMINAL_MONITOR_TOOL } from "../shared.js";
|
|
6
|
-
import { errorResult, textResult } from "./context.js";
|
|
6
|
+
import { errorResult, resolveTerminalId, textResult, } from "./context.js";
|
|
7
7
|
import { renderMonitorCall } from "./render.js";
|
|
8
8
|
import { spawnCommandSession } from "./spawn.js";
|
|
9
9
|
export const DEFAULT_MONITOR_TIMEOUT_MS = 300_000;
|
|
@@ -40,7 +40,7 @@ export const monitorSchema = Type.Object({
|
|
|
40
40
|
description: "Watcher deadline in milliseconds (default 300000; ignored by persistent monitors).",
|
|
41
41
|
})),
|
|
42
42
|
persistent: Type.Optional(Type.Boolean({ description: "Keep watching until the command exits or kill_bash stops its bash_id." })),
|
|
43
|
-
bash_id: Type.Optional(Type.String({ description: "Rearm: paused monitor bash_id to resume; omit
|
|
43
|
+
bash_id: Type.Optional(Type.String({ description: "Rearm: paused monitor id (mon_ or bash_id) to resume; omit for all paused." })),
|
|
44
44
|
});
|
|
45
45
|
function isFileCreateInput(input) {
|
|
46
46
|
return (typeof input.description === "string" &&
|
|
@@ -84,10 +84,43 @@ async function createMonitor(ctx, registry, input, execCtx) {
|
|
|
84
84
|
...(input.persistent ? {} : { timeoutMs: resolveTimeoutMs(input.timeout_ms) }),
|
|
85
85
|
});
|
|
86
86
|
ctx.onMonitorRearmed?.(id);
|
|
87
|
-
registry.register({ id, description: input.description, runtime, filter });
|
|
88
|
-
|
|
87
|
+
const monitorId = registry.register({ id, description: input.description, runtime, filter });
|
|
88
|
+
ctx.manager.bindMonitorId(monitorId, id);
|
|
89
|
+
// The tool call site is the only place the branch inputs (command, persistent, filter)
|
|
90
|
+
// live; hand the captured spec to the session's manifest writer for durable recording.
|
|
91
|
+
handMonitorSpec(manifestSessionKey(ctx), {
|
|
92
|
+
monitorId,
|
|
93
|
+
spec: {
|
|
94
|
+
kind: "command",
|
|
95
|
+
description: input.description,
|
|
96
|
+
command: input.command,
|
|
97
|
+
filter: input.filter,
|
|
98
|
+
cwd: execCtx?.cwd,
|
|
99
|
+
persistent: input.persistent === true,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
return textResult(`Monitor started with ID: ${monitorId}`, {
|
|
103
|
+
details: { monitor_id: monitorId, bash_id: id, monitor: true },
|
|
104
|
+
});
|
|
89
105
|
}
|
|
90
106
|
/** Build the PTY-backed monitor tool. Monitor handles share TerminalManager's bash_N namespace. */
|
|
107
|
+
const manifestWriters = new Map();
|
|
108
|
+
/** Bind the session's manifest writer so monitor tool calls can hand it specs captured at the call site. */
|
|
109
|
+
export function bindTerminalManifestWriter(sessionId, writer) {
|
|
110
|
+
manifestWriters.set(sessionId, writer);
|
|
111
|
+
}
|
|
112
|
+
export function unbindTerminalManifestWriter(sessionId) {
|
|
113
|
+
manifestWriters.delete(sessionId);
|
|
114
|
+
}
|
|
115
|
+
/** The durability session key for a tool context: the agent session id, when the context carries one. */
|
|
116
|
+
function manifestSessionKey(ctx) {
|
|
117
|
+
return ctx.getSessionContext?.()?.sessionManager?.getSessionId?.();
|
|
118
|
+
}
|
|
119
|
+
/** Hand a spec captured at the monitor tool call site to the session's bound writer, if any. */
|
|
120
|
+
function handMonitorSpec(sessionKey, registration) {
|
|
121
|
+
const writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);
|
|
122
|
+
void writer?.recordRegister(registration);
|
|
123
|
+
}
|
|
91
124
|
export function createMonitorTool(ctx) {
|
|
92
125
|
let fallbackRegistry;
|
|
93
126
|
const getRegistry = () => {
|
|
@@ -113,8 +146,7 @@ export function createMonitorTool(ctx) {
|
|
|
113
146
|
async execute(_toolCallId, input, _signal, _onUpdate, execCtx) {
|
|
114
147
|
const registry = getRegistry();
|
|
115
148
|
if (input.action === "rearm") {
|
|
116
|
-
|
|
117
|
-
if (bashId === undefined || bashId.length === 0) {
|
|
149
|
+
if (input.bash_id === undefined || input.bash_id.length === 0) {
|
|
118
150
|
const resumed = registry.resume();
|
|
119
151
|
if (resumed.length === 0)
|
|
120
152
|
return textResult("No paused monitors to re-arm.");
|
|
@@ -124,6 +156,7 @@ export function createMonitorTool(ctx) {
|
|
|
124
156
|
? `Re-armed ${resumed.length} paused monitor(s) (${total} line(s) dropped while muted).`
|
|
125
157
|
: `Re-armed ${resumed.length} paused monitor(s).`);
|
|
126
158
|
}
|
|
159
|
+
const bashId = resolveTerminalId(ctx.manager, input.bash_id);
|
|
127
160
|
const dropped = registry.mutedDropped(bashId);
|
|
128
161
|
const outcome = registry.rearm(bashId);
|
|
129
162
|
if (outcome === "not_found")
|
|
@@ -145,20 +178,31 @@ export function createMonitorTool(ctx) {
|
|
|
145
178
|
if (!ctx.monitorRegistry)
|
|
146
179
|
return errorResult("Native file monitors require a lifecycle-owned monitor registry.");
|
|
147
180
|
try {
|
|
148
|
-
const
|
|
181
|
+
const approvedParent = input[APPROVED_MONITOR_PARENT];
|
|
182
|
+
const { id, monitorId } = await ctx.monitorRegistry.registerFile({
|
|
149
183
|
description: input.description,
|
|
150
184
|
path: input.path,
|
|
151
185
|
event: input.event ?? "create",
|
|
152
186
|
timeoutMs: resolveTimeoutMs(input.timeout_ms),
|
|
153
187
|
cwd: execCtx?.cwd ?? ctx.cwd,
|
|
154
|
-
...(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
188
|
+
...(approvedParent !== undefined ? { approvedParent } : {}),
|
|
189
|
+
});
|
|
190
|
+
ctx.manager.bindMonitorId(monitorId, id);
|
|
191
|
+
// Same spec capture as the command branch: durability inputs live only here.
|
|
192
|
+
handMonitorSpec(manifestSessionKey(ctx), {
|
|
193
|
+
monitorId,
|
|
194
|
+
spec: {
|
|
195
|
+
kind: "file",
|
|
196
|
+
description: input.description,
|
|
197
|
+
path: input.path,
|
|
198
|
+
event: input.event ?? "create",
|
|
199
|
+
timeoutMs: resolveTimeoutMs(input.timeout_ms),
|
|
200
|
+
cwd: execCtx?.cwd ?? ctx.cwd,
|
|
201
|
+
...(approvedParent !== undefined ? { approvedParent } : {}),
|
|
202
|
+
},
|
|
159
203
|
});
|
|
160
|
-
return textResult(`Monitor started with ID: ${
|
|
161
|
-
details: {
|
|
204
|
+
return textResult(`Monitor started with ID: ${monitorId}`, {
|
|
205
|
+
details: { monitor_id: monitorId, bash_id: id, monitor: true },
|
|
162
206
|
});
|
|
163
207
|
}
|
|
164
208
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor.ts"],"names":[],"mappings":"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","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport { APPROVED_MONITOR_PARENT } from \"../monitor-permission.ts\";\nimport { MonitorRegistry } from \"../monitor-registry.ts\";\nimport { DEFAULT_COLS, DEFAULT_ROWS, TERMINAL_MONITOR_TOOL } from \"../shared.ts\";\nimport { errorResult, type TerminalToolContext, type TerminalToolResult, textResult } from \"./context.ts\";\nimport { renderMonitorCall } from \"./render.ts\";\nimport { spawnCommandSession } from \"./spawn.ts\";\n\nexport const DEFAULT_MONITOR_TIMEOUT_MS = 300_000;\nexport const MAX_MONITOR_TIMEOUT_MS = 3_600_000;\n\n/**\n * One flat object schema, no top-level union: several provider payload paths\n * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from\n * top-level `properties` only, so a root anyOf would reach the model as an\n * empty schema. Branch requirements are enforced at runtime in `execute`.\n */\nexport const monitorSchema = Type.Object({\n\taction: Type.Optional(\n\t\tStringEnum([\"create\", \"rearm\"] as const, {\n\t\t\tdescription: \"Defaults to create. rearm resumes a monitor paused by the wake budget.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: 200,\n\t\t\tdescription: \"Create (required): specific label shown with every event, e.g. 'errors in deploy.log'.\",\n\t\t}),\n\t),\n\tcommand: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Create, command branch (XOR path): shell command to run and watch in a PTY-backed monitor session.\",\n\t\t}),\n\t),\n\tpath: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t\"Create, file branch (XOR command): one regular file to watch natively, whose parent directory must already exist; takes no filter and no persistent.\",\n\t\t}),\n\t),\n\tevent: Type.Optional(\n\t\tStringEnum([\"create\", \"modify\"] as const, {\n\t\t\tdescription: \"File branch only: which file event fires the watch (defaults to create).\",\n\t\t}),\n\t),\n\tfilter: Type.Optional(\n\t\tType.String({ description: \"Only PTY output lines matching this regex become monitor events.\" }),\n\t),\n\ttimeout_ms: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 1,\n\t\t\tmaximum: MAX_MONITOR_TIMEOUT_MS,\n\t\t\tdescription: \"Watcher deadline in milliseconds (default 300000; ignored by persistent monitors).\",\n\t\t}),\n\t),\n\tpersistent: Type.Optional(\n\t\tType.Boolean({ description: \"Keep watching until the command exits or kill_bash stops its bash_id.\" }),\n\t),\n\tbash_id: Type.Optional(\n\t\tType.String({ description: \"Rearm: paused monitor bash_id to resume; omit to resume all paused monitors.\" }),\n\t),\n});\nexport type MonitorInput = Static<typeof monitorSchema>;\n\ntype MonitorCreateInput = MonitorInput & { description: string; command: string };\ntype FileMonitorCreateInput = MonitorInput & { description: string; path: string };\n\nfunction isFileCreateInput(input: MonitorInput): input is FileMonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.path === \"string\" &&\n\t\tinput.path.length > 0\n\t);\n}\n\nfunction isCreateInput(input: MonitorInput): input is MonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.command === \"string\" &&\n\t\tinput.command.length > 0\n\t);\n}\n\nfunction resolveDimension(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 1) return fallback;\n\treturn Math.trunc(value);\n}\n\nfunction resolveTimeoutMs(value: number | undefined): number {\n\tconst timeout = value ?? DEFAULT_MONITOR_TIMEOUT_MS;\n\treturn Math.min(Math.max(Math.trunc(timeout), 1), MAX_MONITOR_TIMEOUT_MS);\n}\n\nfunction compileFilter(filter: string | undefined): RegExp | undefined {\n\tif (filter === undefined) return undefined;\n\treturn new RegExp(filter);\n}\n\nasync function createMonitor(\n\tctx: TerminalToolContext,\n\tregistry: MonitorRegistry,\n\tinput: MonitorCreateInput,\n\texecCtx: { cwd?: string } | undefined,\n): Promise<TerminalToolResult> {\n\tlet filter: RegExp | undefined;\n\ttry {\n\t\tfilter = compileFilter(input.filter);\n\t} catch {\n\t\treturn errorResult(`Invalid monitor filter regex: ${input.filter}`);\n\t}\n\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols: resolveDimension(undefined, ctx.defaultCols || DEFAULT_COLS),\n\t\trows: resolveDimension(undefined, ctx.defaultRows || DEFAULT_ROWS),\n\t\tcwd: execCtx?.cwd,\n\t\t...(input.persistent ? {} : { timeoutMs: resolveTimeoutMs(input.timeout_ms) }),\n\t});\n\tctx.onMonitorRearmed?.(id);\n\tregistry.register({ id, description: input.description, runtime, filter });\n\treturn textResult(`Monitor started with ID: ${id}`, { details: { bash_id: id, monitor: true } });\n}\n\n/** Build the PTY-backed monitor tool. Monitor handles share TerminalManager's bash_N namespace. */\nexport function createMonitorTool(ctx: TerminalToolContext) {\n\tlet fallbackRegistry: MonitorRegistry | undefined;\n\tconst getRegistry = (): MonitorRegistry => {\n\t\tconst sessionRegistry = ctx.monitorRegistry;\n\t\tif (sessionRegistry) return sessionRegistry;\n\t\tfallbackRegistry ??= new MonitorRegistry((event) => ctx.onMonitorEvent?.(event));\n\t\treturn fallbackRegistry;\n\t};\n\treturn {\n\t\tname: TERMINAL_MONITOR_TOOL,\n\t\tlabel: \"monitor\",\n\t\tdescription:\n\t\t\t\"Subscribe to a change instead of polling. Pass command XOR path, never both: command watches a PTY session, where newline-terminated output lines (stderr merged) that match filter arrive as injected events while you keep working and command exit always delivers a summary event; path natively watches one file and fires once: create (the default) fires only when the file appears after registration, so watch a file that already exists with event modify. The path branch takes no filter and no persistent. Identical consecutive line-only update batches are deduped, so a watcher reprinting unchanged status does not re-wake the session. Returns a bash_id immediately; peek with bash_output, stop with kill_bash.\",\n\t\tpromptSnippet:\n\t\t\t\"Subscribe to a command's output or a file's create/modify event as injected events instead of polling\",\n\t\tpromptGuidelines: [\n\t\t\t\"Waiting on observable state (CI checks, builds, log patterns, deploys, a file landing) means a monitor, never a foreground sleep/poll loop.\",\n\t\t\t'Waiting for one file to appear or change is the path branch: `monitor({ description, path, event? })` beats wrapping `test -f` in a shell poll loop; a file that already exists needs `event: \"modify\"`, since `create` only fires on appearance, and registration needs the parent directory to exist already — when the run creates that directory too, use the `command` branch instead.',\n\t\t\t\"Shape the command for the events you need: one-shot gate = `until <cond>; do sleep 1; done; printf 'READY\\\\n'` with filter ^READY$; stream = `tail -n 0 -F <log> | grep --line-buffered <pat>` with persistent: true, then kill_bash.\",\n\t\t\t\"Sleep loops belong INSIDE the monitor command, never in your turn: about to sleep, re-poll bash_output, or foreground-block on a long command means register a monitor and keep working.\",\n\t\t],\n\t\tparameters: monitorSchema,\n\t\trenderCall: renderMonitorCall,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tinput: MonitorInput,\n\t\t\t_signal?: AbortSignal,\n\t\t\t_onUpdate?: undefined,\n\t\t\texecCtx?: { cwd?: string },\n\t\t): Promise<TerminalToolResult> {\n\t\t\tconst registry = getRegistry();\n\t\t\tif (input.action === \"rearm\") {\n\t\t\t\tconst bashId = input.bash_id;\n\t\t\t\tif (bashId === undefined || bashId.length === 0) {\n\t\t\t\t\tconst resumed = registry.resume();\n\t\t\t\t\tif (resumed.length === 0) return textResult(\"No paused monitors to re-arm.\");\n\t\t\t\t\tctx.onMonitorsResumed?.(resumed.map((monitor) => monitor.id));\n\t\t\t\t\tconst total = resumed.reduce((sum, monitor) => sum + monitor.mutedDropped, 0);\n\t\t\t\t\treturn textResult(\n\t\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t\t? `Re-armed ${resumed.length} paused monitor(s) (${total} line(s) dropped while muted).`\n\t\t\t\t\t\t\t: `Re-armed ${resumed.length} paused monitor(s).`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst dropped = registry.mutedDropped(bashId);\n\t\t\t\tconst outcome = registry.rearm(bashId);\n\t\t\t\tif (outcome === \"not_found\") return errorResult(`No active monitor found with id: ${bashId}`);\n\t\t\t\tif (outcome === \"not_paused\") return textResult(`Monitor ${bashId} is not paused; no action taken.`);\n\t\t\t\tctx.onMonitorRearmed?.(bashId);\n\t\t\t\treturn textResult(\n\t\t\t\t\tdropped > 0\n\t\t\t\t\t\t? `Monitor ${bashId} re-armed (${dropped} line(s) dropped while muted).`\n\t\t\t\t\t\t: `Monitor ${bashId} re-armed.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst fileInput = isFileCreateInput(input);\n\t\t\tconst commandInput = isCreateInput(input);\n\t\t\tif (fileInput && commandInput) return errorResult(\"monitor accepts either command or path, not both.\");\n\t\t\tif (fileInput) {\n\t\t\t\tif (input.filter !== undefined || input.persistent)\n\t\t\t\t\treturn errorResult(\"Native file monitors do not support filter or persistent.\");\n\t\t\t\tif (!ctx.monitorRegistry)\n\t\t\t\t\treturn errorResult(\"Native file monitors require a lifecycle-owned monitor registry.\");\n\t\t\t\ttry {\n\t\t\t\t\tconst id = await ctx.monitorRegistry.registerFile({\n\t\t\t\t\t\tdescription: input.description,\n\t\t\t\t\t\tpath: input.path,\n\t\t\t\t\t\tevent: input.event ?? \"create\",\n\t\t\t\t\t\ttimeoutMs: resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\tcwd: execCtx?.cwd ?? ctx.cwd,\n\t\t\t\t\t\t...((input as Record<string | symbol, unknown>)[APPROVED_MONITOR_PARENT] !== undefined\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tapprovedParent: (input as Record<string | symbol, unknown>)[\n\t\t\t\t\t\t\t\t\t\tAPPROVED_MONITOR_PARENT\n\t\t\t\t\t\t\t\t\t] as string,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t});\n\t\t\t\t\treturn textResult(`Monitor started with ID: ${id}`, {\n\t\t\t\t\t\tdetails: { bash_id: id, watch_id: id, monitor: true },\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn errorResult(error instanceof Error ? error.message : String(error));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!commandInput) return errorResult(\"monitor requires description and command or path to start a watcher.\");\n\t\t\treturn createMonitor(ctx, registry, input, execCtx);\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"monitor.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/monitor.ts"],"names":[],"mappings":"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","sourcesContent":["import { StringEnum } from \"@earendil-works/pi-ai\";\nimport { type Static, Type } from \"typebox\";\nimport { APPROVED_MONITOR_PARENT } from \"../monitor-permission.ts\";\nimport { MonitorRegistry } from \"../monitor-registry.ts\";\nimport { DEFAULT_COLS, DEFAULT_ROWS, TERMINAL_MONITOR_TOOL } from \"../shared.ts\";\nimport type { MonitorRegistration, TerminalManifestWriter } from \"../terminal-manifest.ts\";\nimport {\n\terrorResult,\n\tresolveTerminalId,\n\ttype TerminalToolContext,\n\ttype TerminalToolResult,\n\ttextResult,\n} from \"./context.ts\";\nimport { renderMonitorCall } from \"./render.ts\";\nimport { spawnCommandSession } from \"./spawn.ts\";\n\nexport const DEFAULT_MONITOR_TIMEOUT_MS = 300_000;\nexport const MAX_MONITOR_TIMEOUT_MS = 3_600_000;\n\n/**\n * One flat object schema, no top-level union: several provider payload paths\n * (e.g. Anthropic's legacy input_schema conversion) rebuild tool schemas from\n * top-level `properties` only, so a root anyOf would reach the model as an\n * empty schema. Branch requirements are enforced at runtime in `execute`.\n */\nexport const monitorSchema = Type.Object({\n\taction: Type.Optional(\n\t\tStringEnum([\"create\", \"rearm\"] as const, {\n\t\t\tdescription: \"Defaults to create. rearm resumes a monitor paused by the wake budget.\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tmaxLength: 200,\n\t\t\tdescription: \"Create (required): specific label shown with every event, e.g. 'errors in deploy.log'.\",\n\t\t}),\n\t),\n\tcommand: Type.Optional(\n\t\tType.String({\n\t\t\tdescription:\n\t\t\t\t\"Create, command branch (XOR path): shell command to run and watch in a PTY-backed monitor session.\",\n\t\t}),\n\t),\n\tpath: Type.Optional(\n\t\tType.String({\n\t\t\tminLength: 1,\n\t\t\tdescription:\n\t\t\t\t\"Create, file branch (XOR command): one regular file to watch natively, whose parent directory must already exist; takes no filter and no persistent.\",\n\t\t}),\n\t),\n\tevent: Type.Optional(\n\t\tStringEnum([\"create\", \"modify\"] as const, {\n\t\t\tdescription: \"File branch only: which file event fires the watch (defaults to create).\",\n\t\t}),\n\t),\n\tfilter: Type.Optional(\n\t\tType.String({ description: \"Only PTY output lines matching this regex become monitor events.\" }),\n\t),\n\ttimeout_ms: Type.Optional(\n\t\tType.Number({\n\t\t\tminimum: 1,\n\t\t\tmaximum: MAX_MONITOR_TIMEOUT_MS,\n\t\t\tdescription: \"Watcher deadline in milliseconds (default 300000; ignored by persistent monitors).\",\n\t\t}),\n\t),\n\tpersistent: Type.Optional(\n\t\tType.Boolean({ description: \"Keep watching until the command exits or kill_bash stops its bash_id.\" }),\n\t),\n\tbash_id: Type.Optional(\n\t\tType.String({ description: \"Rearm: paused monitor id (mon_ or bash_id) to resume; omit for all paused.\" }),\n\t),\n});\nexport type MonitorInput = Static<typeof monitorSchema>;\n\ntype MonitorCreateInput = MonitorInput & { description: string; command: string };\ntype FileMonitorCreateInput = MonitorInput & { description: string; path: string };\n\nfunction isFileCreateInput(input: MonitorInput): input is FileMonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.path === \"string\" &&\n\t\tinput.path.length > 0\n\t);\n}\n\nfunction isCreateInput(input: MonitorInput): input is MonitorCreateInput {\n\treturn (\n\t\ttypeof input.description === \"string\" &&\n\t\tinput.description.length > 0 &&\n\t\ttypeof input.command === \"string\" &&\n\t\tinput.command.length > 0\n\t);\n}\n\nfunction resolveDimension(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 1) return fallback;\n\treturn Math.trunc(value);\n}\n\nfunction resolveTimeoutMs(value: number | undefined): number {\n\tconst timeout = value ?? DEFAULT_MONITOR_TIMEOUT_MS;\n\treturn Math.min(Math.max(Math.trunc(timeout), 1), MAX_MONITOR_TIMEOUT_MS);\n}\n\nfunction compileFilter(filter: string | undefined): RegExp | undefined {\n\tif (filter === undefined) return undefined;\n\treturn new RegExp(filter);\n}\n\nasync function createMonitor(\n\tctx: TerminalToolContext,\n\tregistry: MonitorRegistry,\n\tinput: MonitorCreateInput,\n\texecCtx: { cwd?: string } | undefined,\n): Promise<TerminalToolResult> {\n\tlet filter: RegExp | undefined;\n\ttry {\n\t\tfilter = compileFilter(input.filter);\n\t} catch {\n\t\treturn errorResult(`Invalid monitor filter regex: ${input.filter}`);\n\t}\n\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols: resolveDimension(undefined, ctx.defaultCols || DEFAULT_COLS),\n\t\trows: resolveDimension(undefined, ctx.defaultRows || DEFAULT_ROWS),\n\t\tcwd: execCtx?.cwd,\n\t\t...(input.persistent ? {} : { timeoutMs: resolveTimeoutMs(input.timeout_ms) }),\n\t});\n\tctx.onMonitorRearmed?.(id);\n\tconst monitorId = registry.register({ id, description: input.description, runtime, filter });\n\tctx.manager.bindMonitorId(monitorId, id);\n\t// The tool call site is the only place the branch inputs (command, persistent, filter)\n\t// live; hand the captured spec to the session's manifest writer for durable recording.\n\thandMonitorSpec(manifestSessionKey(ctx), {\n\t\tmonitorId,\n\t\tspec: {\n\t\t\tkind: \"command\",\n\t\t\tdescription: input.description,\n\t\t\tcommand: input.command,\n\t\t\tfilter: input.filter,\n\t\t\tcwd: execCtx?.cwd,\n\t\t\tpersistent: input.persistent === true,\n\t\t},\n\t});\n\treturn textResult(`Monitor started with ID: ${monitorId}`, {\n\t\tdetails: { monitor_id: monitorId, bash_id: id, monitor: true },\n\t});\n}\n\n/** Build the PTY-backed monitor tool. Monitor handles share TerminalManager's bash_N namespace. */\nconst manifestWriters = new Map<string, TerminalManifestWriter>();\n\n/** Bind the session's manifest writer so monitor tool calls can hand it specs captured at the call site. */\nexport function bindTerminalManifestWriter(sessionId: string, writer: TerminalManifestWriter): void {\n\tmanifestWriters.set(sessionId, writer);\n}\n\nexport function unbindTerminalManifestWriter(sessionId: string): void {\n\tmanifestWriters.delete(sessionId);\n}\n\n/** The durability session key for a tool context: the agent session id, when the context carries one. */\nfunction manifestSessionKey(ctx: TerminalToolContext): string | undefined {\n\treturn ctx.getSessionContext?.()?.sessionManager?.getSessionId?.();\n}\n\n/** Hand a spec captured at the monitor tool call site to the session's bound writer, if any. */\nfunction handMonitorSpec(sessionKey: string | undefined, registration: MonitorRegistration): void {\n\tconst writer = sessionKey === undefined ? undefined : manifestWriters.get(sessionKey);\n\tvoid writer?.recordRegister(registration);\n}\n\nexport function createMonitorTool(ctx: TerminalToolContext) {\n\tlet fallbackRegistry: MonitorRegistry | undefined;\n\tconst getRegistry = (): MonitorRegistry => {\n\t\tconst sessionRegistry = ctx.monitorRegistry;\n\t\tif (sessionRegistry) return sessionRegistry;\n\t\tfallbackRegistry ??= new MonitorRegistry((event) => ctx.onMonitorEvent?.(event));\n\t\treturn fallbackRegistry;\n\t};\n\treturn {\n\t\tname: TERMINAL_MONITOR_TOOL,\n\t\tlabel: \"monitor\",\n\t\tdescription:\n\t\t\t\"Subscribe to a change instead of polling. Pass command XOR path, never both: command watches a PTY session, where newline-terminated output lines (stderr merged) that match filter arrive as injected events while you keep working and command exit always delivers a summary event; path natively watches one file and fires once: create (the default) fires only when the file appears after registration, so watch a file that already exists with event modify. The path branch takes no filter and no persistent. Identical consecutive line-only update batches are deduped, so a watcher reprinting unchanged status does not re-wake the session. Returns a bash_id immediately; peek with bash_output, stop with kill_bash.\",\n\t\tpromptSnippet:\n\t\t\t\"Subscribe to a command's output or a file's create/modify event as injected events instead of polling\",\n\t\tpromptGuidelines: [\n\t\t\t\"Waiting on observable state (CI checks, builds, log patterns, deploys, a file landing) means a monitor, never a foreground sleep/poll loop.\",\n\t\t\t'Waiting for one file to appear or change is the path branch: `monitor({ description, path, event? })` beats wrapping `test -f` in a shell poll loop; a file that already exists needs `event: \"modify\"`, since `create` only fires on appearance, and registration needs the parent directory to exist already — when the run creates that directory too, use the `command` branch instead.',\n\t\t\t\"Shape the command for the events you need: one-shot gate = `until <cond>; do sleep 1; done; printf 'READY\\\\n'` with filter ^READY$; stream = `tail -n 0 -F <log> | grep --line-buffered <pat>` with persistent: true, then kill_bash.\",\n\t\t\t\"Sleep loops belong INSIDE the monitor command, never in your turn: about to sleep, re-poll bash_output, or foreground-block on a long command means register a monitor and keep working.\",\n\t\t],\n\t\tparameters: monitorSchema,\n\t\trenderCall: renderMonitorCall,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tinput: MonitorInput,\n\t\t\t_signal?: AbortSignal,\n\t\t\t_onUpdate?: undefined,\n\t\t\texecCtx?: { cwd?: string },\n\t\t): Promise<TerminalToolResult> {\n\t\t\tconst registry = getRegistry();\n\t\t\tif (input.action === \"rearm\") {\n\t\t\t\tif (input.bash_id === undefined || input.bash_id.length === 0) {\n\t\t\t\t\tconst resumed = registry.resume();\n\t\t\t\t\tif (resumed.length === 0) return textResult(\"No paused monitors to re-arm.\");\n\t\t\t\t\tctx.onMonitorsResumed?.(resumed.map((monitor) => monitor.id));\n\t\t\t\t\tconst total = resumed.reduce((sum, monitor) => sum + monitor.mutedDropped, 0);\n\t\t\t\t\treturn textResult(\n\t\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t\t? `Re-armed ${resumed.length} paused monitor(s) (${total} line(s) dropped while muted).`\n\t\t\t\t\t\t\t: `Re-armed ${resumed.length} paused monitor(s).`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst bashId = resolveTerminalId(ctx.manager, input.bash_id);\n\t\t\t\tconst dropped = registry.mutedDropped(bashId);\n\t\t\t\tconst outcome = registry.rearm(bashId);\n\t\t\t\tif (outcome === \"not_found\") return errorResult(`No active monitor found with id: ${bashId}`);\n\t\t\t\tif (outcome === \"not_paused\") return textResult(`Monitor ${bashId} is not paused; no action taken.`);\n\t\t\t\tctx.onMonitorRearmed?.(bashId);\n\t\t\t\treturn textResult(\n\t\t\t\t\tdropped > 0\n\t\t\t\t\t\t? `Monitor ${bashId} re-armed (${dropped} line(s) dropped while muted).`\n\t\t\t\t\t\t: `Monitor ${bashId} re-armed.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst fileInput = isFileCreateInput(input);\n\t\t\tconst commandInput = isCreateInput(input);\n\t\t\tif (fileInput && commandInput) return errorResult(\"monitor accepts either command or path, not both.\");\n\t\t\tif (fileInput) {\n\t\t\t\tif (input.filter !== undefined || input.persistent)\n\t\t\t\t\treturn errorResult(\"Native file monitors do not support filter or persistent.\");\n\t\t\t\tif (!ctx.monitorRegistry)\n\t\t\t\t\treturn errorResult(\"Native file monitors require a lifecycle-owned monitor registry.\");\n\t\t\t\ttry {\n\t\t\t\t\tconst approvedParent = (input as Record<string | symbol, unknown>)[APPROVED_MONITOR_PARENT] as\n\t\t\t\t\t\t| string\n\t\t\t\t\t\t| undefined;\n\t\t\t\t\tconst { id, monitorId } = await ctx.monitorRegistry.registerFile({\n\t\t\t\t\t\tdescription: input.description,\n\t\t\t\t\t\tpath: input.path,\n\t\t\t\t\t\tevent: input.event ?? \"create\",\n\t\t\t\t\t\ttimeoutMs: resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\tcwd: execCtx?.cwd ?? ctx.cwd,\n\t\t\t\t\t\t...(approvedParent !== undefined ? { approvedParent } : {}),\n\t\t\t\t\t});\n\t\t\t\t\tctx.manager.bindMonitorId(monitorId, id);\n\t\t\t\t\t// Same spec capture as the command branch: durability inputs live only here.\n\t\t\t\t\thandMonitorSpec(manifestSessionKey(ctx), {\n\t\t\t\t\t\tmonitorId,\n\t\t\t\t\t\tspec: {\n\t\t\t\t\t\t\tkind: \"file\",\n\t\t\t\t\t\t\tdescription: input.description,\n\t\t\t\t\t\t\tpath: input.path,\n\t\t\t\t\t\t\tevent: input.event ?? \"create\",\n\t\t\t\t\t\t\ttimeoutMs: resolveTimeoutMs(input.timeout_ms),\n\t\t\t\t\t\t\tcwd: execCtx?.cwd ?? ctx.cwd,\n\t\t\t\t\t\t\t...(approvedParent !== undefined ? { approvedParent } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t\treturn textResult(`Monitor started with ID: ${monitorId}`, {\n\t\t\t\t\t\tdetails: { monitor_id: monitorId, bash_id: id, monitor: true },\n\t\t\t\t\t});\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn errorResult(error instanceof Error ? error.message : String(error));\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!commandInput) return errorResult(\"monitor requires description and command or path to start a watcher.\");\n\t\t\treturn createMonitor(ctx, registry, input, execCtx);\n\t\t},\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAAwD,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGvG,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAAwD,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGvG,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CA+CtF;AAWD,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAUlF;AAED,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -41,7 +41,7 @@ export function createToolSearchExtension(service) {
|
|
|
41
41
|
},
|
|
42
42
|
searchToolName: TOOL_SEARCH_TOOL_NAME,
|
|
43
43
|
});
|
|
44
|
-
pi.on("before_provider_request", (event, ctx) => nativeAdapter.applyBeforeRequest(ctx.model
|
|
44
|
+
pi.on("before_provider_request", (event, ctx) => nativeAdapter.applyBeforeRequest(event.model ?? ctx.model, event.payload));
|
|
45
45
|
pi.on("after_provider_response", (event) => nativeAdapter.noteResponseStatus(event.status));
|
|
46
46
|
};
|
|
47
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAEhF,OAAO,EAAE,gCAAgC,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AACpG,OAAO,EAAE,oBAAoB,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvG,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAExE,MAAM,UAAU,yBAAyB,CAAC,OAA0B;IACnE,OAAO,CAAC,EAAgB,EAAQ,EAAE;QACjC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAExB,EAAE,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YACtC,OAAO,CAAC,YAAY,EAAE,CAAC;YACvB,OAAO,CAAC,yBAAyB,CAAC,GAAG,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YAC1B,OAAO,CAAC,yBAAyB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC3E,IAAI,cAAc,GAAG,KAAK,CAAC;QAC3B,OAAO,CAAC,iBAAiB,CAAC,GAAG,EAAE;YAC9B,IAAI,cAAc;gBAAE,OAAO;YAC3B,cAAc,GAAG,IAAI,CAAC;YACtB,EAAE,CAAC,YAAY,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,IAAI,gCAAgC,CAAC;YAC1D,OAAO,EAAE,GAAG,EAAE;gBACb,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,CAAC;gBAC5C,OAAO,OAAO;qBACZ,UAAU,EAAE;qBACZ,IAAI,CACJ,CAAC,GAAG,EAAE,EAAE,CACP,CAAC,GAAG,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACrD,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,4BAA4B,EAAE,CAAC,CACzD,CAAC;YACJ,CAAC;YACD,UAAU,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE;YACtC,iBAAiB,EAAE,CAAC,IAAI,EAAE,EAAE;gBAC3B,MAAM,IAAI,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;gBAC3E,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;YACxG,CAAC;YACD,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE;gBACtB,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;gBAC9E,IAAI,GAAG,EAAE,MAAM,KAAK,KAAK;oBAAE,OAAO,4BAA4B,EAAE,CAAC;gBACjE,OAAO,GAAG,EAAE,MAAM,KAAK,WAAW,IAAI,CAAC,EAAE,CAAC,cAAc,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC3E,CAAC;YACD,cAAc,EAAE,qBAAqB;SACrC,CAAC,CAAC;QACH,EAAE,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAC/C,aAAa,CAAC,kBAAkB,CAAC,KAAK,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CACzE,CAAC;QACF,EAAE,CAAC,EAAE,CAAC,yBAAyB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,kBAAkB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7F,CAAC,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB;IACxB,IAAI,CAAC;QACJ,mBAAmB,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,EAAgB;IAC3D,MAAM,OAAO,GAAG;QACf,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE;QACnC,cAAc,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,cAAc,EAAE;QACzC,cAAc,EAAE,CAAC,KAAwB,EAAE,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;KAC3E,CAAC;IACF,MAAM,YAAY,GAAG,gBAAgB,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9F,IAAI,YAAY;QAAE,8BAA8B,CAAC,OAAO,CAAC,CAAC;IAC1D,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC","sourcesContent":["import { bindToProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport type { ExtensionAPI, ExtensionFactory } from \"../../types.ts\";\nimport { AnthropicNativeToolSearchAdapter, isMcpNativeToolSearchEnabled } from \"./native-search.ts\";\nimport { getToolSearchService, installScopedToolSearchService, ToolSearchService } from \"./service.ts\";\nimport { createToolSearchTool, TOOL_SEARCH_TOOL_NAME } from \"./tool.ts\";\n\nexport function createToolSearchExtension(service: ToolSearchService): ExtensionFactory {\n\treturn (pi: ExtensionAPI): void => {\n\t\tservice.bindRuntime(pi);\n\n\t\tpi.on(\"session_start\", (_event, ctx) => {\n\t\t\tservice.beginSession();\n\t\t\tservice.maybeRehydrateFromHistory(ctx.sessionManager.getEntries());\n\t\t});\n\t\tpi.on(\"context\", (event) => {\n\t\t\tservice.maybeRehydrateFromHistory(event.messages);\n\t\t});\n\t\tpi.registerLazyToolActivator((toolName) => service.activateTool(toolName));\n\t\tlet toolRegistered = false;\n\t\tservice.bindToolRegistrar(() => {\n\t\t\tif (toolRegistered) return;\n\t\t\ttoolRegistered = true;\n\t\t\tpi.registerTool(createToolSearchTool(service));\n\t\t});\n\n\t\tconst nativeAdapter = new AnthropicNativeToolSearchAdapter({\n\t\t\tenabled: () => {\n\t\t\t\tconst active = new Set(pi.getActiveTools());\n\t\t\t\treturn service\n\t\t\t\t\t.getCatalog()\n\t\t\t\t\t.some(\n\t\t\t\t\t\t(doc) =>\n\t\t\t\t\t\t\t(doc.source === \"extension\" && !active.has(doc.name)) ||\n\t\t\t\t\t\t\t(doc.source === \"mcp\" && isMcpNativeToolSearchEnabled()),\n\t\t\t\t\t);\n\t\t\t},\n\t\t\tgetCatalog: () => service.getCatalog(),\n\t\t\tgetToolDefinition: (name) => {\n\t\t\t\tconst tool = pi.getAllTools().find((candidate) => candidate.name === name);\n\t\t\t\treturn tool === undefined ? undefined : { description: tool.description, parameters: tool.parameters };\n\t\t\t},\n\t\t\tisDeferrable: (name) => {\n\t\t\t\tconst doc = service.getCatalog().find((candidate) => candidate.name === name);\n\t\t\t\tif (doc?.source === \"mcp\") return isMcpNativeToolSearchEnabled();\n\t\t\t\treturn doc?.source === \"extension\" && !pi.getActiveTools().includes(name);\n\t\t\t},\n\t\t\tsearchToolName: TOOL_SEARCH_TOOL_NAME,\n\t\t});\n\t\tpi.on(\"before_provider_request\", (event, ctx) =>\n\t\t\tnativeAdapter.applyBeforeRequest(event.model ?? ctx.model, event.payload),\n\t\t);\n\t\tpi.on(\"after_provider_response\", (event) => nativeAdapter.noteResponseStatus(event.status));\n\t};\n}\n\nfunction hasProviderScope(): boolean {\n\ttry {\n\t\tbindToProviderScope(() => undefined);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport default function toolSearchExtension(pi: ExtensionAPI): void | Promise<void> {\n\tconst runtime = {\n\t\tgetAllTools: () => pi.getAllTools(),\n\t\tgetActiveTools: () => pi.getActiveTools(),\n\t\tsetActiveTools: (names: readonly string[]) => pi.setActiveTools([...names]),\n\t};\n\tconst sessionOwned = hasProviderScope();\n\tconst service = sessionOwned ? new ToolSearchService(runtime) : getToolSearchService(runtime);\n\tif (sessionOwned) installScopedToolSearchService(service);\n\treturn createToolSearchExtension(service)(pi);\n}\n\nexport { getToolSearchService, ToolSearchService } from \"./service.ts\";\nexport { createToolSearchTool, TOOL_SEARCH_TOOL_NAME } from \"./tool.ts\";\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ToolSearchDocument } from "./engine/document.ts";
|
|
2
|
+
import { type AnthropicToolSearchTarget } from "./native-support.ts";
|
|
2
3
|
export declare const ANTHROPIC_TOOL_SEARCH_TYPE = "tool_search_tool_bm25_20251119";
|
|
3
4
|
export declare const ANTHROPIC_TOOL_SEARCH_NAME = "tool_search_tool_bm25";
|
|
4
5
|
/** Anthropic caps a request at 10k tools; beyond that native search is invalid. */
|
|
@@ -27,11 +28,15 @@ export interface AnthropicNativeInjectionConfig {
|
|
|
27
28
|
* local search tool, never combine defer_loading with cache_control, keep at
|
|
28
29
|
* least one non-deferred tool, and skip entirely above the 10k tool cap.
|
|
29
30
|
*/
|
|
30
|
-
export declare function addAnthropicNativeToolSearch(
|
|
31
|
-
/**
|
|
31
|
+
export declare function addAnthropicNativeToolSearch(target: AnthropicToolSearchTarget, payload: unknown, config: AnthropicNativeInjectionConfig): unknown;
|
|
32
|
+
/**
|
|
33
|
+
* `tool_reference` blocks emitted inside a tool_result so the API expands the
|
|
34
|
+
* referenced tools. Anthropic names the target through `tool_name`; a block
|
|
35
|
+
* carrying `name` is rejected as a malformed content block.
|
|
36
|
+
*/
|
|
32
37
|
export declare function buildToolReferenceBlocks(names: readonly string[]): {
|
|
33
38
|
type: "tool_reference";
|
|
34
|
-
|
|
39
|
+
tool_name: string;
|
|
35
40
|
}[];
|
|
36
41
|
export interface AnthropicNativeAdapterDeps extends AnthropicNativeInjectionConfig {
|
|
37
42
|
/** Resolved provider/config gate. */
|
|
@@ -43,7 +48,7 @@ export interface AnthropicNativeAdapterDeps extends AnthropicNativeInjectionConf
|
|
|
43
48
|
export declare class AnthropicNativeToolSearchAdapter {
|
|
44
49
|
#private;
|
|
45
50
|
constructor(deps: AnthropicNativeAdapterDeps);
|
|
46
|
-
applyBeforeRequest(
|
|
51
|
+
applyBeforeRequest(target: AnthropicToolSearchTarget, payload: unknown): unknown;
|
|
47
52
|
noteResponseStatus(status: number): void;
|
|
48
53
|
get disabled(): boolean;
|
|
49
54
|
get fallbackReason(): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-search.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/native-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"native-search.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/native-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,KAAK,yBAAyB,EAAqC,MAAM,qBAAqB,CAAC;AAMxG,eAAO,MAAM,0BAA0B,mCAAmC,CAAC;AAC3E,eAAO,MAAM,0BAA0B,0BAA0B,CAAC;AAClE,mFAAmF;AACnF,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,KAAK,uBAAuB,GAAG,MAAM,OAAO,CAAC;AAK7C,gGAAgG;AAChG,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAGlF;AAED,wBAAgB,4BAA4B,IAAI,OAAO,CAEtD;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,8BAA8B;IAC9C,gFAAgF;IAChF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACrD,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,SAAS,kBAAkB,EAAE,CAAC;IAC1D,iFAAiF;IACjF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,oBAAoB,GAAG,SAAS,CAAC;CACpF;AAMD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC3C,MAAM,EAAE,yBAAyB,EACjC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,8BAA8B,GACpC,OAAO,CAYT;AAkCD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,CAElH;AAED,MAAM,WAAW,0BAA2B,SAAQ,8BAA8B;IACjF,qCAAqC;IACrC,OAAO,IAAI,OAAO,CAAC;IACnB,gEAAgE;IAChE,UAAU,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,mFAAmF;AACnF,qBAAa,gCAAgC;;gBAMhC,IAAI,EAAE,0BAA0B;IAI5C,kBAAkB,CAAC,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO;IAQhF,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQxC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAElC;CACD"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { supportsAnthropicNativeToolSearch } from "./native-support.js";
|
|
2
3
|
// Anthropic Messages supports native BM25 tool search through request payload
|
|
3
4
|
// injection alone. OpenAI native client-mode search remains intentionally out of
|
|
4
5
|
// scope because it requires a provider-layer seam.
|
|
@@ -25,8 +26,8 @@ function isRecord(value) {
|
|
|
25
26
|
* local search tool, never combine defer_loading with cache_control, keep at
|
|
26
27
|
* least one non-deferred tool, and skip entirely above the 10k tool cap.
|
|
27
28
|
*/
|
|
28
|
-
export function addAnthropicNativeToolSearch(
|
|
29
|
-
if (
|
|
29
|
+
export function addAnthropicNativeToolSearch(target, payload, config) {
|
|
30
|
+
if (!supportsAnthropicNativeToolSearch(target) || !isRecord(payload))
|
|
30
31
|
return payload;
|
|
31
32
|
const tools = Array.isArray(payload.tools) ? payload.tools : [];
|
|
32
33
|
const injected = injectInactiveCatalogTools(tools, config);
|
|
@@ -77,9 +78,13 @@ function maybeDefer(tool, config) {
|
|
|
77
78
|
return tool;
|
|
78
79
|
return { ...tool, defer_loading: true };
|
|
79
80
|
}
|
|
80
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* `tool_reference` blocks emitted inside a tool_result so the API expands the
|
|
83
|
+
* referenced tools. Anthropic names the target through `tool_name`; a block
|
|
84
|
+
* carrying `name` is rejected as a malformed content block.
|
|
85
|
+
*/
|
|
81
86
|
export function buildToolReferenceBlocks(names) {
|
|
82
|
-
return names.map((name) => ({ type: "tool_reference", name }));
|
|
87
|
+
return names.map((name) => ({ type: "tool_reference", tool_name: name }));
|
|
83
88
|
}
|
|
84
89
|
/** Session-scoped request injector with permanent 400 fallback for the session. */
|
|
85
90
|
export class AnthropicNativeToolSearchAdapter {
|
|
@@ -90,11 +95,11 @@ export class AnthropicNativeToolSearchAdapter {
|
|
|
90
95
|
constructor(deps) {
|
|
91
96
|
this.#deps = deps;
|
|
92
97
|
}
|
|
93
|
-
applyBeforeRequest(
|
|
98
|
+
applyBeforeRequest(target, payload) {
|
|
94
99
|
this.#injectedLastRequest = false;
|
|
95
100
|
if (this.#disabled || !this.#deps.enabled())
|
|
96
101
|
return payload;
|
|
97
|
-
const next = addAnthropicNativeToolSearch(
|
|
102
|
+
const next = addAnthropicNativeToolSearch(target, payload, this.#deps);
|
|
98
103
|
this.#injectedLastRequest = next !== payload;
|
|
99
104
|
return next;
|
|
100
105
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-search.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/native-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { ToolSearchDocument } from \"./engine/document.ts\";\n\n// Anthropic Messages supports native BM25 tool search through request payload\n// injection alone. OpenAI native client-mode search remains intentionally out of\n// scope because it requires a provider-layer seam.\n\nexport const ANTHROPIC_TOOL_SEARCH_TYPE = \"tool_search_tool_bm25_20251119\";\nexport const ANTHROPIC_TOOL_SEARCH_NAME = \"tool_search_tool_bm25\";\n/** Anthropic caps a request at 10k tools; beyond that native search is invalid. */\nexport const ANTHROPIC_MAX_TOOLS = 10000;\n\ntype McpNativeToolSearchGate = () => boolean;\n\nconst scopedMcpNativeToolSearchGate = new AsyncLocalStorage<McpNativeToolSearchGate>();\nlet mcpNativeToolSearchGate: McpNativeToolSearchGate = () => false;\n\n/** Bind the MCP-owned user setting without moving MCP configuration into the shared builtin. */\nexport function installMcpNativeToolSearchGate(gate: McpNativeToolSearchGate): void {\n\tmcpNativeToolSearchGate = gate;\n\tscopedMcpNativeToolSearchGate.enterWith(gate);\n}\n\nexport function isMcpNativeToolSearchEnabled(): boolean {\n\treturn (scopedMcpNativeToolSearchGate.getStore() ?? mcpNativeToolSearchGate)();\n}\n\nexport interface NativeToolDefinition {\n\treadonly description?: string;\n\treadonly parameters?: unknown;\n}\n\nexport interface AnthropicNativeInjectionConfig {\n\t/** Never defer this tool (our custom tool_search), and it is non-deferrable. */\n\treadonly searchToolName?: string;\n\t/** True only for catalog tools eligible for native deferral in this request. */\n\treadonly isDeferrable: (toolName: string) => boolean;\n\t/** Current searchable catalog. Omit when only transforming resident tools. */\n\treadonly getCatalog?: () => readonly ToolSearchDocument[];\n\t/** Resolve the winning registered definition used to inject inactive schemas. */\n\treadonly getToolDefinition?: (toolName: string) => NativeToolDefinition | undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\n/**\n * Pure payload transform. Injects eligible inactive catalog schemas, adds exactly\n * one native search tool, and enforces Anthropic's HARD RULES: never defer the\n * local search tool, never combine defer_loading with cache_control, keep at\n * least one non-deferred tool, and skip entirely above the 10k tool cap.\n */\nexport function addAnthropicNativeToolSearch(\n\tapi: string | undefined,\n\tpayload: unknown,\n\tconfig: AnthropicNativeInjectionConfig,\n): unknown {\n\tif (api !== \"anthropic-messages\" || !isRecord(payload)) return payload;\n\tconst tools = Array.isArray(payload.tools) ? payload.tools : [];\n\tconst injected = injectInactiveCatalogTools(tools, config);\n\tif (injected.length > ANTHROPIC_MAX_TOOLS) return payload;\n\tconst deferred = injected.map((tool) => maybeDefer(tool, config));\n\tconst hasSearchTool = deferred.some((tool) => isRecord(tool) && tool.type === ANTHROPIC_TOOL_SEARCH_TYPE);\n\tconst nextTools = hasSearchTool\n\t\t? deferred\n\t\t: [...deferred, { type: ANTHROPIC_TOOL_SEARCH_TYPE, name: ANTHROPIC_TOOL_SEARCH_NAME }];\n\tif (nextTools.length > ANTHROPIC_MAX_TOOLS) return payload;\n\treturn { ...payload, tools: nextTools };\n}\n\nfunction injectInactiveCatalogTools(tools: readonly unknown[], config: AnthropicNativeInjectionConfig): unknown[] {\n\tconst catalog = config.getCatalog?.() ?? [];\n\tconst residentNames = new Set(\n\t\ttools.flatMap((tool) => (isRecord(tool) && typeof tool.name === \"string\" ? [tool.name] : [])),\n\t);\n\tconst injected: unknown[] = [...tools];\n\tfor (const doc of catalog) {\n\t\tif (doc.name === config.searchToolName || residentNames.has(doc.name) || !config.isDeferrable(doc.name)) continue;\n\t\tconst definition = config.getToolDefinition?.(doc.name);\n\t\tif (definition?.parameters === undefined) continue;\n\t\tinjected.push({\n\t\t\tname: doc.name,\n\t\t\tdescription: definition.description ?? doc.description ?? doc.label,\n\t\t\tinput_schema: definition.parameters,\n\t\t\tdefer_loading: true,\n\t\t});\n\t\tresidentNames.add(doc.name);\n\t}\n\treturn injected;\n}\n\nfunction maybeDefer(tool: unknown, config: AnthropicNativeInjectionConfig): unknown {\n\tif (!isRecord(tool)) return tool;\n\tconst name = typeof tool.name === \"string\" ? tool.name : undefined;\n\tif (name === undefined) return tool;\n\tif (name === config.searchToolName) return tool;\n\tif (!config.isDeferrable(name)) return tool;\n\tif (\"cache_control\" in tool) return tool;\n\tif (tool.defer_loading === true) return tool;\n\treturn { ...tool, defer_loading: true };\n}\n\n/** `tool_reference` blocks emitted inside a tool_result so the API expands the referenced tools. */\nexport function buildToolReferenceBlocks(names: readonly string[]): { type: \"tool_reference\"; name: string }[] {\n\treturn names.map((name) => ({ type: \"tool_reference\", name }));\n}\n\nexport interface AnthropicNativeAdapterDeps extends AnthropicNativeInjectionConfig {\n\t/** Resolved provider/config gate. */\n\tenabled(): boolean;\n\t/** Invoked once when a 400 forces the local-search fallback. */\n\tonFallback?(reason: string): void;\n}\n\n/** Session-scoped request injector with permanent 400 fallback for the session. */\nexport class AnthropicNativeToolSearchAdapter {\n\t#disabled = false;\n\t#injectedLastRequest = false;\n\t#fallbackReason: string | null = null;\n\treadonly #deps: AnthropicNativeAdapterDeps;\n\n\tconstructor(deps: AnthropicNativeAdapterDeps) {\n\t\tthis.#deps = deps;\n\t}\n\n\tapplyBeforeRequest(api: string | undefined, payload: unknown): unknown {\n\t\tthis.#injectedLastRequest = false;\n\t\tif (this.#disabled || !this.#deps.enabled()) return payload;\n\t\tconst next = addAnthropicNativeToolSearch(api, payload, this.#deps);\n\t\tthis.#injectedLastRequest = next !== payload;\n\t\treturn next;\n\t}\n\n\tnoteResponseStatus(status: number): void {\n\t\tif (status !== 400 || !this.#injectedLastRequest || this.#disabled) return;\n\t\tthis.#disabled = true;\n\t\tthis.#fallbackReason =\n\t\t\t\"Anthropic returned 400 for native tool-search; disabled it and fell back to local tool_search for this session.\";\n\t\tthis.#deps.onFallback?.(this.#fallbackReason);\n\t}\n\n\tget disabled(): boolean {\n\t\treturn this.#disabled;\n\t}\n\n\tget fallbackReason(): string | null {\n\t\treturn this.#fallbackReason;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"native-search.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/native-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { ToolSearchDocument } from \"./engine/document.ts\";\nimport { type AnthropicToolSearchTarget, supportsAnthropicNativeToolSearch } from \"./native-support.ts\";\n\n// Anthropic Messages supports native BM25 tool search through request payload\n// injection alone. OpenAI native client-mode search remains intentionally out of\n// scope because it requires a provider-layer seam.\n\nexport const ANTHROPIC_TOOL_SEARCH_TYPE = \"tool_search_tool_bm25_20251119\";\nexport const ANTHROPIC_TOOL_SEARCH_NAME = \"tool_search_tool_bm25\";\n/** Anthropic caps a request at 10k tools; beyond that native search is invalid. */\nexport const ANTHROPIC_MAX_TOOLS = 10000;\n\ntype McpNativeToolSearchGate = () => boolean;\n\nconst scopedMcpNativeToolSearchGate = new AsyncLocalStorage<McpNativeToolSearchGate>();\nlet mcpNativeToolSearchGate: McpNativeToolSearchGate = () => false;\n\n/** Bind the MCP-owned user setting without moving MCP configuration into the shared builtin. */\nexport function installMcpNativeToolSearchGate(gate: McpNativeToolSearchGate): void {\n\tmcpNativeToolSearchGate = gate;\n\tscopedMcpNativeToolSearchGate.enterWith(gate);\n}\n\nexport function isMcpNativeToolSearchEnabled(): boolean {\n\treturn (scopedMcpNativeToolSearchGate.getStore() ?? mcpNativeToolSearchGate)();\n}\n\nexport interface NativeToolDefinition {\n\treadonly description?: string;\n\treadonly parameters?: unknown;\n}\n\nexport interface AnthropicNativeInjectionConfig {\n\t/** Never defer this tool (our custom tool_search), and it is non-deferrable. */\n\treadonly searchToolName?: string;\n\t/** True only for catalog tools eligible for native deferral in this request. */\n\treadonly isDeferrable: (toolName: string) => boolean;\n\t/** Current searchable catalog. Omit when only transforming resident tools. */\n\treadonly getCatalog?: () => readonly ToolSearchDocument[];\n\t/** Resolve the winning registered definition used to inject inactive schemas. */\n\treadonly getToolDefinition?: (toolName: string) => NativeToolDefinition | undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\n/**\n * Pure payload transform. Injects eligible inactive catalog schemas, adds exactly\n * one native search tool, and enforces Anthropic's HARD RULES: never defer the\n * local search tool, never combine defer_loading with cache_control, keep at\n * least one non-deferred tool, and skip entirely above the 10k tool cap.\n */\nexport function addAnthropicNativeToolSearch(\n\ttarget: AnthropicToolSearchTarget,\n\tpayload: unknown,\n\tconfig: AnthropicNativeInjectionConfig,\n): unknown {\n\tif (!supportsAnthropicNativeToolSearch(target) || !isRecord(payload)) return payload;\n\tconst tools = Array.isArray(payload.tools) ? payload.tools : [];\n\tconst injected = injectInactiveCatalogTools(tools, config);\n\tif (injected.length > ANTHROPIC_MAX_TOOLS) return payload;\n\tconst deferred = injected.map((tool) => maybeDefer(tool, config));\n\tconst hasSearchTool = deferred.some((tool) => isRecord(tool) && tool.type === ANTHROPIC_TOOL_SEARCH_TYPE);\n\tconst nextTools = hasSearchTool\n\t\t? deferred\n\t\t: [...deferred, { type: ANTHROPIC_TOOL_SEARCH_TYPE, name: ANTHROPIC_TOOL_SEARCH_NAME }];\n\tif (nextTools.length > ANTHROPIC_MAX_TOOLS) return payload;\n\treturn { ...payload, tools: nextTools };\n}\n\nfunction injectInactiveCatalogTools(tools: readonly unknown[], config: AnthropicNativeInjectionConfig): unknown[] {\n\tconst catalog = config.getCatalog?.() ?? [];\n\tconst residentNames = new Set(\n\t\ttools.flatMap((tool) => (isRecord(tool) && typeof tool.name === \"string\" ? [tool.name] : [])),\n\t);\n\tconst injected: unknown[] = [...tools];\n\tfor (const doc of catalog) {\n\t\tif (doc.name === config.searchToolName || residentNames.has(doc.name) || !config.isDeferrable(doc.name)) continue;\n\t\tconst definition = config.getToolDefinition?.(doc.name);\n\t\tif (definition?.parameters === undefined) continue;\n\t\tinjected.push({\n\t\t\tname: doc.name,\n\t\t\tdescription: definition.description ?? doc.description ?? doc.label,\n\t\t\tinput_schema: definition.parameters,\n\t\t\tdefer_loading: true,\n\t\t});\n\t\tresidentNames.add(doc.name);\n\t}\n\treturn injected;\n}\n\nfunction maybeDefer(tool: unknown, config: AnthropicNativeInjectionConfig): unknown {\n\tif (!isRecord(tool)) return tool;\n\tconst name = typeof tool.name === \"string\" ? tool.name : undefined;\n\tif (name === undefined) return tool;\n\tif (name === config.searchToolName) return tool;\n\tif (!config.isDeferrable(name)) return tool;\n\tif (\"cache_control\" in tool) return tool;\n\tif (tool.defer_loading === true) return tool;\n\treturn { ...tool, defer_loading: true };\n}\n\n/**\n * `tool_reference` blocks emitted inside a tool_result so the API expands the\n * referenced tools. Anthropic names the target through `tool_name`; a block\n * carrying `name` is rejected as a malformed content block.\n */\nexport function buildToolReferenceBlocks(names: readonly string[]): { type: \"tool_reference\"; tool_name: string }[] {\n\treturn names.map((name) => ({ type: \"tool_reference\", tool_name: name }));\n}\n\nexport interface AnthropicNativeAdapterDeps extends AnthropicNativeInjectionConfig {\n\t/** Resolved provider/config gate. */\n\tenabled(): boolean;\n\t/** Invoked once when a 400 forces the local-search fallback. */\n\tonFallback?(reason: string): void;\n}\n\n/** Session-scoped request injector with permanent 400 fallback for the session. */\nexport class AnthropicNativeToolSearchAdapter {\n\t#disabled = false;\n\t#injectedLastRequest = false;\n\t#fallbackReason: string | null = null;\n\treadonly #deps: AnthropicNativeAdapterDeps;\n\n\tconstructor(deps: AnthropicNativeAdapterDeps) {\n\t\tthis.#deps = deps;\n\t}\n\n\tapplyBeforeRequest(target: AnthropicToolSearchTarget, payload: unknown): unknown {\n\t\tthis.#injectedLastRequest = false;\n\t\tif (this.#disabled || !this.#deps.enabled()) return payload;\n\t\tconst next = addAnthropicNativeToolSearch(target, payload, this.#deps);\n\t\tthis.#injectedLastRequest = next !== payload;\n\t\treturn next;\n\t}\n\n\tnoteResponseStatus(status: number): void {\n\t\tif (status !== 400 || !this.#injectedLastRequest || this.#disabled) return;\n\t\tthis.#disabled = true;\n\t\tthis.#fallbackReason =\n\t\t\t\"Anthropic returned 400 for native tool-search; disabled it and fell back to local tool_search for this session.\";\n\t\tthis.#deps.onFallback?.(this.#fallbackReason);\n\t}\n\n\tget disabled(): boolean {\n\t\treturn this.#disabled;\n\t}\n\n\tget fallbackReason(): string | null {\n\t\treturn this.#fallbackReason;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
export type AnthropicToolSearchModel = Pick<Model<Api>, "api" | "id" | "provider"> & Partial<Pick<Model<Api>, "compat">>;
|
|
3
|
+
export type AnthropicToolSearchTarget = Api | AnthropicToolSearchModel | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Anthropic-compatible endpoints (gateways, kimi-coding, proxies) answer the
|
|
6
|
+
* same `anthropic-messages` api but reject the server tool, so the api alone is
|
|
7
|
+
* not a sufficient gate: injecting there 400s the request and disables native
|
|
8
|
+
* search for the rest of the session. A bare api string carries no model
|
|
9
|
+
* identity to gate on and stays supported, so the pure payload transform keeps
|
|
10
|
+
* its api-only contract.
|
|
11
|
+
*/
|
|
12
|
+
export declare function supportsAnthropicNativeToolSearch(target: AnthropicToolSearchTarget): boolean;
|
|
13
|
+
//# sourceMappingURL=native-support.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-support.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/native-support.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEjF,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,UAAU,CAAC,GACjF,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AACrC,MAAM,MAAM,yBAAyB,GAAG,GAAG,GAAG,wBAAwB,GAAG,SAAS,CAAC;AA8BnF;;;;;;;GAOG;AACH,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAK5F"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const CLAUDE_VERSIONED_MODEL_ID = /^claude-(?:opus|sonnet|fable)-(\d+)(?:-(\d+))?(?:-|$)/;
|
|
2
|
+
/** A trailing 8-digit group is a release date (claude-opus-4-5-20251101), not a minor version. */
|
|
3
|
+
const MODEL_ID_DATE_SUFFIX_LENGTH = 8;
|
|
4
|
+
function isSupportedModel(model) {
|
|
5
|
+
const compat = model.compat;
|
|
6
|
+
return compat?.supportsToolReferences ?? defaultSupportsToolSearch(model);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Byte-for-byte mirror of pi-ai's private
|
|
10
|
+
* `AnthropicMessagesCompat.supportsToolReferences` default; keep in lockstep
|
|
11
|
+
* with `packages/ai/src/utils/prompt-cache-ttl.ts`. Diverging would inject the
|
|
12
|
+
* server tool for a model whose deferred tools pi-ai refuses to defer, so the
|
|
13
|
+
* request would carry a search tool with nothing to find. Anthropic's table
|
|
14
|
+
* lists tool search on Opus/Sonnet 4.5+ and the Fable/Mythos line; Opus 4.1 and
|
|
15
|
+
* earlier reject the server tool and Haiku rejects `tool_reference` blocks.
|
|
16
|
+
*/
|
|
17
|
+
function defaultSupportsToolSearch(model) {
|
|
18
|
+
if (model.provider !== "anthropic" || model.id.includes("haiku"))
|
|
19
|
+
return false;
|
|
20
|
+
const version = model.id.match(CLAUDE_VERSIONED_MODEL_ID);
|
|
21
|
+
if (!version)
|
|
22
|
+
return false;
|
|
23
|
+
const major = Number(version[1]);
|
|
24
|
+
const rawMinor = version[2];
|
|
25
|
+
const minor = rawMinor !== undefined && rawMinor.length < MODEL_ID_DATE_SUFFIX_LENGTH ? Number(rawMinor) : 0;
|
|
26
|
+
return major > 4 || (major === 4 && minor >= 5);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Anthropic-compatible endpoints (gateways, kimi-coding, proxies) answer the
|
|
30
|
+
* same `anthropic-messages` api but reject the server tool, so the api alone is
|
|
31
|
+
* not a sufficient gate: injecting there 400s the request and disables native
|
|
32
|
+
* search for the rest of the session. A bare api string carries no model
|
|
33
|
+
* identity to gate on and stays supported, so the pure payload transform keeps
|
|
34
|
+
* its api-only contract.
|
|
35
|
+
*/
|
|
36
|
+
export function supportsAnthropicNativeToolSearch(target) {
|
|
37
|
+
if (target === undefined)
|
|
38
|
+
return false;
|
|
39
|
+
if (typeof target === "string")
|
|
40
|
+
return target === "anthropic-messages";
|
|
41
|
+
if (target.api !== "anthropic-messages")
|
|
42
|
+
return false;
|
|
43
|
+
return isSupportedModel(target);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=native-support.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-support.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/tool-search/native-support.ts"],"names":[],"mappings":"AAMA,MAAM,yBAAyB,GAAG,uDAAuD,CAAC;AAC1F,kGAAkG;AAClG,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAEtC,SAAS,gBAAgB,CAAC,KAA+B;IACxD,MAAM,MAAM,GAAG,KAAK,CAAC,MAA6C,CAAC;IACnE,OAAO,MAAM,EAAE,sBAAsB,IAAI,yBAAyB,CAAC,KAAK,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,yBAAyB,CAAC,KAA+B;IACjE,IAAI,KAAK,CAAC,QAAQ,KAAK,WAAW,IAAI,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/E,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC1D,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,GAAG,2BAA2B,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7G,OAAO,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iCAAiC,CAAC,MAAiC;IAClF,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACvC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,KAAK,oBAAoB,CAAC;IACvE,IAAI,MAAM,CAAC,GAAG,KAAK,oBAAoB;QAAE,OAAO,KAAK,CAAC;IACtD,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC","sourcesContent":["import type { AnthropicMessagesCompat, Api, Model } from \"@earendil-works/pi-ai\";\n\nexport type AnthropicToolSearchModel = Pick<Model<Api>, \"api\" | \"id\" | \"provider\"> &\n\tPartial<Pick<Model<Api>, \"compat\">>;\nexport type AnthropicToolSearchTarget = Api | AnthropicToolSearchModel | undefined;\n\nconst CLAUDE_VERSIONED_MODEL_ID = /^claude-(?:opus|sonnet|fable)-(\\d+)(?:-(\\d+))?(?:-|$)/;\n/** A trailing 8-digit group is a release date (claude-opus-4-5-20251101), not a minor version. */\nconst MODEL_ID_DATE_SUFFIX_LENGTH = 8;\n\nfunction isSupportedModel(model: AnthropicToolSearchModel): boolean {\n\tconst compat = model.compat as AnthropicMessagesCompat | undefined;\n\treturn compat?.supportsToolReferences ?? defaultSupportsToolSearch(model);\n}\n\n/**\n * Byte-for-byte mirror of pi-ai's private\n * `AnthropicMessagesCompat.supportsToolReferences` default; keep in lockstep\n * with `packages/ai/src/utils/prompt-cache-ttl.ts`. Diverging would inject the\n * server tool for a model whose deferred tools pi-ai refuses to defer, so the\n * request would carry a search tool with nothing to find. Anthropic's table\n * lists tool search on Opus/Sonnet 4.5+ and the Fable/Mythos line; Opus 4.1 and\n * earlier reject the server tool and Haiku rejects `tool_reference` blocks.\n */\nfunction defaultSupportsToolSearch(model: AnthropicToolSearchModel): boolean {\n\tif (model.provider !== \"anthropic\" || model.id.includes(\"haiku\")) return false;\n\tconst version = model.id.match(CLAUDE_VERSIONED_MODEL_ID);\n\tif (!version) return false;\n\tconst major = Number(version[1]);\n\tconst rawMinor = version[2];\n\tconst minor = rawMinor !== undefined && rawMinor.length < MODEL_ID_DATE_SUFFIX_LENGTH ? Number(rawMinor) : 0;\n\treturn major > 4 || (major === 4 && minor >= 5);\n}\n\n/**\n * Anthropic-compatible endpoints (gateways, kimi-coding, proxies) answer the\n * same `anthropic-messages` api but reject the server tool, so the api alone is\n * not a sufficient gate: injecting there 400s the request and disables native\n * search for the rest of the session. A bare api string carries no model\n * identity to gate on and stays supported, so the pure payload transform keeps\n * its api-only contract.\n */\nexport function supportsAnthropicNativeToolSearch(target: AnthropicToolSearchTarget): boolean {\n\tif (target === undefined) return false;\n\tif (typeof target === \"string\") return target === \"anthropic-messages\";\n\tif (target.api !== \"anthropic-messages\") return false;\n\treturn isSupportedModel(target);\n}\n"]}
|
|
@@ -11,5 +11,16 @@ export declare const FILE_STORAGE_LOCK_OPTIONS: {
|
|
|
11
11
|
readonly realpath: false;
|
|
12
12
|
readonly stale: 30000;
|
|
13
13
|
readonly update: 10000;
|
|
14
|
+
readonly retries: 0;
|
|
14
15
|
};
|
|
16
|
+
export declare const FILE_STORAGE_LOCK_RETRY_BUDGET_MS = 5500;
|
|
17
|
+
export declare const FILE_STORAGE_LOCK_RETRY_MIN_DELAY_MS = 100;
|
|
18
|
+
export declare const FILE_STORAGE_LOCK_RETRY_MAX_DELAY_MS = 1000;
|
|
19
|
+
export declare const FILE_STORAGE_SYNC_LOCK_BUDGET_MS = 1000;
|
|
20
|
+
export declare class CredentialStoreBusyError extends Error {
|
|
21
|
+
readonly path: string;
|
|
22
|
+
readonly waitedMs: number;
|
|
23
|
+
constructor(path: string, waitedMs: number, cause?: unknown);
|
|
24
|
+
}
|
|
25
|
+
export declare function isLockError(error: unknown): boolean;
|
|
15
26
|
//# sourceMappingURL=lockfile-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lockfile-policy.d.ts","sourceRoot":"","sources":["../../src/core/lockfile-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB
|
|
1
|
+
{"version":3,"file":"lockfile-policy.d.ts","sourceRoot":"","sources":["../../src/core/lockfile-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB;;;;;CAO5B,CAAC;AAEX,eAAO,MAAM,iCAAiC,OAAQ,CAAC;AACvD,eAAO,MAAM,oCAAoC,MAAM,CAAC;AACxD,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAE1D,eAAO,MAAM,gCAAgC,OAAQ,CAAC;AAEtD,qBAAa,wBAAyB,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAS3D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAInD"}
|
|
@@ -11,5 +11,24 @@ export const FILE_STORAGE_LOCK_OPTIONS = {
|
|
|
11
11
|
realpath: false,
|
|
12
12
|
stale: 30_000,
|
|
13
13
|
update: 10_000,
|
|
14
|
+
// Every acquisition passes retries: 0 and runs its own bounded wait loop below, so an
|
|
15
|
+
// AbortSignal is observed between attempts rather than after the whole budget.
|
|
16
|
+
retries: 0,
|
|
14
17
|
};
|
|
18
|
+
export const FILE_STORAGE_LOCK_RETRY_BUDGET_MS = 5_500;
|
|
19
|
+
export const FILE_STORAGE_LOCK_RETRY_MIN_DELAY_MS = 100;
|
|
20
|
+
export const FILE_STORAGE_LOCK_RETRY_MAX_DELAY_MS = 1_000;
|
|
21
|
+
// Sync callers block the TUI main thread, so keep their bounded wait short despite Atomics.wait avoiding CPU spin.
|
|
22
|
+
export const FILE_STORAGE_SYNC_LOCK_BUDGET_MS = 1_000;
|
|
23
|
+
export class CredentialStoreBusyError extends Error {
|
|
24
|
+
constructor(path, waitedMs, cause) {
|
|
25
|
+
super(`Credential store is busy: lock ${path} was held for ${waitedMs}ms. Another omo process may be refreshing credentials; close unused sessions if contention persists.`, { cause });
|
|
26
|
+
this.name = "CredentialStoreBusyError";
|
|
27
|
+
this.path = path;
|
|
28
|
+
this.waitedMs = waitedMs;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function isLockError(error) {
|
|
32
|
+
return (typeof error === "object" && error !== null && "code" in error && error.code === "ELOCKED");
|
|
33
|
+
}
|
|
15
34
|
//# sourceMappingURL=lockfile-policy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lockfile-policy.js","sourceRoot":"","sources":["../../src/core/lockfile-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"lockfile-policy.js","sourceRoot":"","sources":["../../src/core/lockfile-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,MAAM;IACd,sFAAsF;IACtF,+EAA+E;IAC/E,OAAO,EAAE,CAAC;CACD,CAAC;AAEX,MAAM,CAAC,MAAM,iCAAiC,GAAG,KAAK,CAAC;AACvD,MAAM,CAAC,MAAM,oCAAoC,GAAG,GAAG,CAAC;AACxD,MAAM,CAAC,MAAM,oCAAoC,GAAG,KAAK,CAAC;AAC1D,mHAAmH;AACnH,MAAM,CAAC,MAAM,gCAAgC,GAAG,KAAK,CAAC;AAEtD,MAAM,OAAO,wBAAyB,SAAQ,KAAK;IAIlD,YAAY,IAAY,EAAE,QAAgB,EAAE,KAAe;QAC1D,KAAK,CACJ,kCAAkC,IAAI,iBAAiB,QAAQ,sGAAsG,EACrK,EAAE,KAAK,EAAE,CACT,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;CACD;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACzC,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAK,KAA4B,CAAC,IAAI,KAAK,SAAS,CAClH,CAAC;AACH,CAAC","sourcesContent":["/**\n * Shared proper-lockfile acquisition policy for the file-backed auth and settings stores.\n *\n * proper-lockfile defaults to `stale: 10_000` and refreshes a held lock's mtime every\n * `stale / 2` ms (clamped to at least 1s). A sync contender using the default window can\n * therefore classify a live async lock (mtime refreshed every 15s at stale 30s) as stale in\n * the 10-15s gap and steal it. Every store acquires locks with this one policy so that no\n * contender can out-vote a live holder.\n */\nexport const FILE_STORAGE_LOCK_OPTIONS = {\n\trealpath: false,\n\tstale: 30_000,\n\tupdate: 10_000,\n\t// Every acquisition passes retries: 0 and runs its own bounded wait loop below, so an\n\t// AbortSignal is observed between attempts rather than after the whole budget.\n\tretries: 0,\n} as const;\n\nexport const FILE_STORAGE_LOCK_RETRY_BUDGET_MS = 5_500;\nexport const FILE_STORAGE_LOCK_RETRY_MIN_DELAY_MS = 100;\nexport const FILE_STORAGE_LOCK_RETRY_MAX_DELAY_MS = 1_000;\n// Sync callers block the TUI main thread, so keep their bounded wait short despite Atomics.wait avoiding CPU spin.\nexport const FILE_STORAGE_SYNC_LOCK_BUDGET_MS = 1_000;\n\nexport class CredentialStoreBusyError extends Error {\n\treadonly path: string;\n\treadonly waitedMs: number;\n\n\tconstructor(path: string, waitedMs: number, cause?: unknown) {\n\t\tsuper(\n\t\t\t`Credential store is busy: lock ${path} was held for ${waitedMs}ms. Another omo process may be refreshing credentials; close unused sessions if contention persists.`,\n\t\t\t{ cause },\n\t\t);\n\t\tthis.name = \"CredentialStoreBusyError\";\n\t\tthis.path = path;\n\t\tthis.waitedMs = waitedMs;\n\t}\n}\n\nexport function isLockError(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" && error !== null && \"code\" in error && (error as { code?: unknown }).code === \"ELOCKED\"\n\t);\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic atomic, versioned, per-session sidecar store.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the loop store's persistence discipline: a module-level per-file
|
|
5
|
+
* promise tail serializes read-modify-write cycles, writes go through a temp file
|
|
6
|
+
* with 0600 permissions and a rename, and parsing fails closed on version or
|
|
7
|
+
* session mismatches so a corrupt or foreign file never silently resets state.
|
|
8
|
+
*/
|
|
9
|
+
export declare class InvalidSidecarStoreError extends Error {
|
|
10
|
+
constructor(message: string, options?: {
|
|
11
|
+
cause?: unknown;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export declare class UnsupportedSidecarStoreVersionError extends Error {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
/** Location identity of one sidecar file: a directory plus the session it belongs to. */
|
|
18
|
+
export interface SidecarStoreRef {
|
|
19
|
+
baseDir: string;
|
|
20
|
+
sessionId: string;
|
|
21
|
+
}
|
|
22
|
+
/** Validates the domain fields of an already version- and session-checked payload. */
|
|
23
|
+
export type SidecarPayloadParser<T> = (raw: unknown, ref: SidecarStoreRef) => T;
|
|
24
|
+
export interface CreateSidecarStoreOptions<T> {
|
|
25
|
+
baseDir: string;
|
|
26
|
+
sessionId: string;
|
|
27
|
+
version: number;
|
|
28
|
+
tempPrefix: string;
|
|
29
|
+
parse: SidecarPayloadParser<T>;
|
|
30
|
+
}
|
|
31
|
+
export interface SidecarStore<T> {
|
|
32
|
+
filePath: string;
|
|
33
|
+
read(): Promise<T | null>;
|
|
34
|
+
write(state: T): Promise<void>;
|
|
35
|
+
mutate(fn: (current: T | null) => T | Promise<T>): Promise<T>;
|
|
36
|
+
snapshot(): T | undefined;
|
|
37
|
+
clear(): void;
|
|
38
|
+
}
|
|
39
|
+
export declare function encodedSessionId(sessionId: string): string;
|
|
40
|
+
export declare function sidecarFilePath(ref: SidecarStoreRef): string;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a per-session store bound to `baseDir/<encoded session id>.json`.
|
|
43
|
+
* Two stores for the same file share the mutation tail and snapshot cache.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createSidecarStore<T>(options: CreateSidecarStoreOptions<T>): SidecarStore<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Writes `contents` to `filePath` atomically: a hidden temp file (0600) next to the
|
|
48
|
+
* target, then a rename over it. On failure the temp file is removed; if that
|
|
49
|
+
* cleanup also fails, both errors surface as one AggregateError.
|
|
50
|
+
*/
|
|
51
|
+
export declare function writeAtomic(filePath: string, contents: string, tempPrefix: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Serializes read-modify-write cycles per key across every sidecar consumer.
|
|
54
|
+
*
|
|
55
|
+
* `key` must be the resolved absolute file path of the sidecar (the same string
|
|
56
|
+
* `createSidecarStore` uses for that file). Callers that own their own writer
|
|
57
|
+
* (goal) call this directly around that writer. Callers that want the whole
|
|
58
|
+
* store (loop) go through `createSidecarStore`, whose `mutate` already uses
|
|
59
|
+
* this tail.
|
|
60
|
+
*/
|
|
61
|
+
export declare function serializeByKey<T>(key: string, operation: () => Promise<T>): Promise<T>;
|
|
62
|
+
//# sourceMappingURL=session-sidecar-store.d.ts.map
|