@sema-agent/core 7.14.0 → 7.16.0
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 +59 -0
- package/dist/core/ask-origin.d.ts +2 -2
- package/dist/core/checkpoint-store.d.ts +14 -15
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +4 -3
- package/dist/core/memory-engine/delegation-settlement.d.ts +37 -12
- package/dist/core/memory-engine/delegation-settlement.js +128 -39
- package/dist/core/memory-engine/engine.d.ts +9 -8
- package/dist/core/memory-engine/engine.js +115 -120
- package/dist/core/memory-engine/file-backend.d.ts +17 -5
- package/dist/core/memory-engine/file-backend.js +78 -34
- package/dist/core/memory-engine/index.d.ts +2 -2
- package/dist/core/memory-engine/index.js +2 -2
- package/dist/core/memory-engine/layout.d.ts +152 -54
- package/dist/core/memory-engine/layout.js +441 -104
- package/dist/core/memory-engine/types.d.ts +8 -3
- package/dist/core/memory-engine/types.js +5 -0
- package/dist/core/runner/abort-race.d.ts +3 -3
- package/dist/core/runner/active-skill-scope.d.ts +2 -2
- package/dist/core/runner/assemble-result.d.ts +29 -29
- package/dist/core/runner/checkpoint-scope.d.ts +4 -4
- package/dist/core/runner/clock-and-limits.d.ts +3 -3
- package/dist/core/runner/compaction-call-options.d.ts +11 -11
- package/dist/core/runner/compaction-knobs.d.ts +4 -4
- package/dist/core/runner/compaction-seams.d.ts +5 -5
- package/dist/core/runner/contracts.d.ts +224 -226
- package/dist/core/runner/decide-continuation.d.ts +6 -6
- package/dist/core/runner/denial-limit-arms.d.ts +8 -8
- package/dist/core/runner/derived-route-fallback.d.ts +2 -2
- package/dist/core/runner/gate-exit.d.ts +6 -6
- package/dist/core/runner/git-status-frame.d.ts +7 -7
- package/dist/core/runner/inherited-ask-grants.d.ts +6 -6
- package/dist/core/runner/memory-capture-optout.d.ts +1 -1
- package/dist/core/runner/memory-consolidation-driver.d.ts +4 -4
- package/dist/core/runner/memory-consolidation.d.ts +6 -6
- package/dist/core/runner/park-commit.d.ts +6 -6
- package/dist/core/runner/permission-rule-lanes.d.ts +22 -20
- package/dist/core/runner/permission-rule-lanes.js +1 -1
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +4 -4
- package/dist/core/runner/prepare-ask-lane.d.ts +3 -3
- package/dist/core/runner/prepare-boundary-parks.d.ts +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +5 -5
- package/dist/core/runner/prepare-config-doors.d.ts +21 -21
- package/dist/core/runner/prepare-context-lane.d.ts +6 -6
- package/dist/core/runner/prepare-defer-classify.d.ts +3 -3
- package/dist/core/runner/prepare-defer-classify.js +12 -10
- package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
- package/dist/core/runner/prepare-file-history.d.ts +2 -2
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -9
- package/dist/core/runner/prepare-gate-stations.js +2 -2
- package/dist/core/runner/prepare-hands-readface.d.ts +41 -41
- package/dist/core/runner/prepare-hands-readface.js +27 -29
- package/dist/core/runner/prepare-inherited-gate.d.ts +6 -6
- package/dist/core/runner/prepare-listings.d.ts +3 -3
- package/dist/core/runner/prepare-lsp.d.ts +3 -3
- package/dist/core/runner/prepare-memory-engine-session.d.ts +5 -5
- package/dist/core/runner/prepare-memory.js +6 -7
- package/dist/core/runner/prepare-offload-wrappers.d.ts +2 -2
- package/dist/core/runner/prepare-park-ask.d.ts +11 -12
- package/dist/core/runner/prepare-park-ask.js +3 -3
- package/dist/core/runner/prepare-policy-chain.d.ts +6 -6
- package/dist/core/runner/prepare-policy-chain.js +2 -2
- package/dist/core/runner/prepare-prompt-inputs.d.ts +5 -5
- package/dist/core/runner/prepare-protocol-tools.d.ts +1 -1
- package/dist/core/runner/prepare-question-face.d.ts +4 -4
- package/dist/core/runner/prepare-run-refs.d.ts +11 -11
- package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
- package/dist/core/runner/prepare-suspend-saga.d.ts +4 -4
- package/dist/core/runner/prepare-task.d.ts +12 -12
- package/dist/core/runner/prepare-task.js +5 -7
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +2 -3
- package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -4
- package/dist/core/runner/prepare-workspace-restore.d.ts +1 -1
- package/dist/core/runner/remote-env-retry.d.ts +3 -3
- package/dist/core/runner/resume-admission.d.ts +1 -1
- package/dist/core/runner/resume-claim.d.ts +1 -1
- package/dist/core/runner/resume-internals-and-config.d.ts +1 -1
- package/dist/core/runner/resume-policy-outcome.d.ts +1 -1
- package/dist/core/runner/resume-preflight.d.ts +1 -1
- package/dist/core/runner/resume-review-outcome.d.ts +1 -1
- package/dist/core/runner/run-brain-sinks.d.ts +1 -1
- package/dist/core/runner/run-clock-and-content.d.ts +4 -4
- package/dist/core/runner/run-compaction-machinery.d.ts +3 -3
- package/dist/core/runner/run-git-lane.d.ts +1 -1
- package/dist/core/runner/run-harness-handlers.d.ts +2 -2
- package/dist/core/runner/run-identity-wiring.d.ts +4 -4
- package/dist/core/runner/run-leg.d.ts +3 -3
- package/dist/core/runner/run-notification-lane.d.ts +2 -2
- package/dist/core/runner/run-reasoning-seat.d.ts +2 -2
- package/dist/core/runner/run-settle-and-teardown.d.ts +4 -4
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-terminal-adoption.d.ts +5 -5
- package/dist/core/runner/runtask.d.ts +14 -14
- package/dist/core/runner/session-file-state-replay.d.ts +3 -3
- package/dist/core/runner/session-rule-policy.d.ts +2 -2
- package/dist/core/runner/steer-admission.d.ts +2 -2
- package/dist/core/runner/stream-halt-verbs.d.ts +1 -1
- package/dist/core/runner/stream-reap.d.ts +1 -1
- package/dist/core/runner/synthetic-tools.d.ts +8 -8
- package/dist/core/runner/teardown-bounded.d.ts +2 -2
- package/dist/core/runner/tool-defer-gate.d.ts +86 -0
- package/dist/core/runner/tool-defer-gate.js +57 -0
- package/dist/core/runner/tool-disclosure.d.ts +33 -69
- package/dist/core/runner/tool-disclosure.js +0 -43
- package/dist/core/runner/tool-end-body.d.ts +3 -3
- package/dist/core/runner/tool-face-overlay.d.ts +2 -2
- package/dist/core/runner/tool-output-projection.d.ts +2 -2
- package/dist/core/runner/turn-attachments.d.ts +53 -53
- package/dist/core/runner/usage-accounting.d.ts +4 -4
- package/dist/core/runner/workspace-path.d.ts +1 -1
- package/dist/core/runner-deps.d.ts +16 -9
- package/dist/core/task-spec.d.ts +8 -2
- package/dist/core/tool-spec.d.ts +7 -4
- package/dist/core/types.d.ts +1 -1
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/tools/fs/bash-readonly-classifier.d.ts +40 -40
- package/dist/tools/fs/encoding.d.ts +31 -40
- package/dist/tools/fs/fs-bash.d.ts +76 -35
- package/dist/tools/fs/fs-bash.js +86 -29
- package/dist/tools/fs/fs-pdf.d.ts +8 -8
- package/dist/tools/fs/fs-search-tools.d.ts +4 -4
- package/dist/tools/fs/fs-shared.d.ts +144 -158
- package/dist/tools/fs/fs-write.d.ts +8 -8
- package/dist/tools/fs/gh-rate-limit.d.ts +13 -14
- package/dist/tools/fs/index.d.ts +74 -78
- package/dist/tools/fs/notebook.d.ts +24 -25
- package/dist/tools/fs/pdf.d.ts +3 -3
- package/dist/tools/fs/read-deny.d.ts +22 -22
- package/dist/tools/fs/read-face.d.ts +21 -24
- package/dist/tools/fs/repo-map.d.ts +8 -8
- package/dist/tools/fs/safety.d.ts +116 -129
- package/dist/tools/fs/search.d.ts +63 -71
- package/package.json +5 -2
- package/test/export-surface.snapshot.json +9 -1
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DEFAULT_CHARS_PER_TOKEN } from "../../internal/harness.js";
|
|
2
|
+
export const DEFER_THRESHOLD_FRACTION = 0.1;
|
|
3
|
+
const CHARS_PER_TOKEN = DEFAULT_CHARS_PER_TOKEN;
|
|
4
|
+
function inlinedChars(t) {
|
|
5
|
+
let schema = "";
|
|
6
|
+
try {
|
|
7
|
+
schema = JSON.stringify(t.parameters) ?? "";
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
schema = "";
|
|
11
|
+
}
|
|
12
|
+
return t.name.length + t.description.length + schema.length;
|
|
13
|
+
}
|
|
14
|
+
export const DEFER_MODE_INVALID_CODE = "config.defer_mode_invalid";
|
|
15
|
+
export function deferralGate(opts) {
|
|
16
|
+
if (opts.deferMode !== undefined && opts.deferMode !== "auto") {
|
|
17
|
+
const e = new Error(`RunnerDeps.deferMode: invalid value ${JSON.stringify(opts.deferMode)} — expected "auto" or omitted; a bad value refuses loudly, nothing falls back to a default.`);
|
|
18
|
+
e.code = DEFER_MODE_INVALID_CODE;
|
|
19
|
+
throw e;
|
|
20
|
+
}
|
|
21
|
+
const pinned = new Set(opts.alwaysLoadNames ?? []);
|
|
22
|
+
for (const s of opts.specs) {
|
|
23
|
+
if (s.alwaysLoad === true)
|
|
24
|
+
pinned.add(s.name);
|
|
25
|
+
}
|
|
26
|
+
const explicit = new Set();
|
|
27
|
+
for (const name of opts.callerDeferNames ?? [])
|
|
28
|
+
if (!pinned.has(name))
|
|
29
|
+
explicit.add(name);
|
|
30
|
+
const gated = new Set();
|
|
31
|
+
const select = (name) => {
|
|
32
|
+
if (!pinned.has(name) && !explicit.has(name))
|
|
33
|
+
gated.add(name);
|
|
34
|
+
};
|
|
35
|
+
for (const s of opts.specs)
|
|
36
|
+
if (s.defer === true || opts.deferMode === "auto")
|
|
37
|
+
select(s.name);
|
|
38
|
+
for (const name of opts.protocolToolNames)
|
|
39
|
+
select(name);
|
|
40
|
+
for (const name of opts.builtinDeferNames ?? [])
|
|
41
|
+
select(name);
|
|
42
|
+
let gatedChars = 0;
|
|
43
|
+
for (const t of opts.fullTools)
|
|
44
|
+
if (gated.has(t.name))
|
|
45
|
+
gatedChars += inlinedChars(t);
|
|
46
|
+
const windowTokens = Math.max(0, opts.model.contextTokens ?? opts.model.contextWindow ?? 0);
|
|
47
|
+
const thresholdChars = Math.floor(windowTokens * DEFER_THRESHOLD_FRACTION) * CHARS_PER_TOKEN;
|
|
48
|
+
return { explicit, gated, gatedChars, thresholdChars, open: gatedChars >= thresholdChars };
|
|
49
|
+
}
|
|
50
|
+
export function classifyDeferred(opts) {
|
|
51
|
+
const gate = deferralGate(opts);
|
|
52
|
+
const deferred = new Set(gate.explicit);
|
|
53
|
+
if (gate.open)
|
|
54
|
+
for (const n of gate.gated)
|
|
55
|
+
deferred.add(n);
|
|
56
|
+
return deferred;
|
|
57
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { type TSchema } from "typebox";
|
|
2
2
|
import type { AgentMessage, AgentTool, AgentToolResult, AgentToolUpdateCallback, ToolExecutionMode } from "../../internal/harness-types.js";
|
|
3
|
-
import type { Model } from "../../internal/llm.js";
|
|
4
|
-
import type { ToolSpec } from "../types.js";
|
|
5
|
-
import type { ToolFingerprintInput } from "../cache-break-detector.js";
|
|
6
3
|
/**
|
|
7
|
-
* Deferred-tool dynamic disclosure
|
|
4
|
+
* Deferred-tool dynamic disclosure. A deployment with hundreds of tools / several MCP
|
|
8
5
|
* servers can't inline every full JSON Schema into every request — turn-1 cache-creation tokens
|
|
9
6
|
* explode and any tool-description drift breaks the prefix cache. Instead, deferred tools ship as
|
|
10
7
|
* lightweight placeholders ({name, one-line hint, empty params}); a resident `tool_search` lets the
|
|
@@ -13,13 +10,13 @@ import type { ToolFingerprintInput } from "../cache-break-detector.js";
|
|
|
13
10
|
* announces them via the search's own result content (tail of the log; the cache prefix is untouched).
|
|
14
11
|
*
|
|
15
12
|
* This module is pure tool/registry logic: prepare-task owns the harness wiring and supplies a
|
|
16
|
-
* `rematerialize` callback (which calls `harness.setTools` + refreshes the
|
|
13
|
+
* `rematerialize` callback (which calls `harness.setTools` + refreshes the fingerprint).
|
|
17
14
|
*/
|
|
18
15
|
export declare const TOOL_SEARCH_NAME = "ToolSearch";
|
|
19
|
-
/** Default keyword-search result cap (
|
|
16
|
+
/** Default keyword-search result cap (CC ToolSearchTool.ts:28-32 `max_results` default 5). */
|
|
20
17
|
export declare const TOOL_SEARCH_DEFAULT_MAX_RESULTS = 5;
|
|
21
18
|
/**
|
|
22
|
-
*
|
|
19
|
+
* How many times in a row ONE placeholder may answer the IDENTICAL invalid-arguments
|
|
23
20
|
* shape before the lane declares the loop unproductive and stops re-teaching. Three, because the
|
|
24
21
|
* corrective round is worth paying for twice (the first rejection hands over the schema; a second
|
|
25
22
|
* covers a model that needed the restatement) and a third identical failure is evidence that the
|
|
@@ -46,49 +43,16 @@ export interface DeferredToolInfo {
|
|
|
46
43
|
/** First line of a description, trimmed to a single short hint for a placeholder / announcement. */
|
|
47
44
|
export declare function deferHint(description: string, max?: number): string;
|
|
48
45
|
/**
|
|
49
|
-
* Neutralize a tool name before it appears in ANY model-facing text
|
|
46
|
+
* Neutralize a tool name before it appears in ANY model-facing text. A tool name is
|
|
50
47
|
* externally influenced — an untrusted MCP server can return one containing newlines (to inject a fake
|
|
51
48
|
* `<system-reminder>`/instruction line), backticks/quotes (to corrupt the JSON or markdown the model is
|
|
52
49
|
* told to emit), or angle brackets (to break a delimiter). Strip control chars, CR/LF, `` ` ``, `"`, `<`, `>`.
|
|
53
50
|
* For a normal name (`[A-Za-z0-9_.-]`, incl. MCP `server__tool`) this is a no-op.
|
|
54
51
|
*
|
|
55
52
|
* MUST wrap a tool name at EVERY site where it reaches the model — placeholder text, the search tool's
|
|
56
|
-
* announcement, anywhere. A bare interpolation is a prompt-injection hole
|
|
53
|
+
* announcement, anywhere. A bare interpolation is a prompt-injection hole.
|
|
57
54
|
*/
|
|
58
55
|
export declare function safeName(name: string): string;
|
|
59
|
-
/**
|
|
60
|
-
* Decide which tool names are deferred. THIS CONTRACT IS THE SINGLE AUTHORITY on the default-mode
|
|
61
|
-
* source list (TaskSpec/RunnerDeps JSDoc points here), so every arm the body implements is named:
|
|
62
|
-
* deterministic by default = explicit `ToolSpec.defer === true`, plus every REMOTE-PROTOCOL tool name
|
|
63
|
-
* (MCP servers AND A2A peers — most numerous / most drift-prone), plus `deferNames`, i.e. the
|
|
64
|
-
* already-mounted wire names from `TaskSpec.deferTools` and from BUILT-INS that declare their own
|
|
65
|
-
* `defer` (a built-in ToolSpec never reaches `specs`, so its declared deferral has to arrive here as a
|
|
66
|
-
* mounted name — this is why the shared-memory pair defaults to deferred). With `deferMode: "auto"`, ADDITIONALLY
|
|
67
|
-
* defer the remaining user tools when their combined inlined schema would exceed ~10% of the model's
|
|
68
|
-
* context window — an explicit opt-in (council minor #6), because an implicit threshold makes the same
|
|
69
|
-
* TaskSpec yield different tools[] across models/contexts. Explicit `defer` always wins — except
|
|
70
|
-
* against an alwaysLoad pin (RB-400-a), which exempts a name from every deferral source.
|
|
71
|
-
*/
|
|
72
|
-
export declare function classifyDeferred(opts: {
|
|
73
|
-
specs: ReadonlyArray<ToolSpec>;
|
|
74
|
-
/** Every namespaced tool a remote protocol materialized for this task (MCP servers AND A2A peers —
|
|
75
|
-
* the caller unions them). The constant-defer arm is about the SHAPE of a remote roster (many tools,
|
|
76
|
-
* schemas the deployment does not control, drift between tasks), which is the same for every protocol
|
|
77
|
-
* in the table: naming one protocol here left the next one inlined by omission, with nobody deciding
|
|
78
|
-
* that. Only an alwaysLoad pin exempts a name. */
|
|
79
|
-
protocolToolNames: ReadonlyArray<string>;
|
|
80
|
-
fullTools: ReadonlyArray<ToolFingerprintInput>;
|
|
81
|
-
deferMode?: "auto";
|
|
82
|
-
model: Model;
|
|
83
|
-
/** TaskSpec.deferTools — defer ALREADY-MOUNTED tools by wire name (built-ins included;
|
|
84
|
-
* `ToolSpec.defer` only reaches caller specs). Caller pre-filters to mounted names. */
|
|
85
|
-
deferNames?: ReadonlyArray<string>;
|
|
86
|
-
/** RB-400-a — inline-pin names (`TaskSpec.alwaysLoadTools` + the MCP tools whose server declared
|
|
87
|
-
* `_meta["anthropic/alwaysLoad"]`, caller-collected; A2A has no such per-skill declaration, so the
|
|
88
|
-
* task-level list is a peer's only inline-keep channel): exempt from EVERY deferral source below.
|
|
89
|
-
* `ToolSpec.alwaysLoad` rides the specs directly and needs no entry here. */
|
|
90
|
-
alwaysLoadNames?: ReadonlyArray<string>;
|
|
91
|
-
}): Set<string>;
|
|
92
56
|
/** Build the registry the search ranks over (deferred tools only — non-deferred are already visible). */
|
|
93
57
|
export declare function buildDeferredRegistry(deferred: ReadonlySet<string>, tools: ReadonlyArray<{
|
|
94
58
|
name: string;
|
|
@@ -96,7 +60,7 @@ export declare function buildDeferredRegistry(deferred: ReadonlySet<string>, too
|
|
|
96
60
|
aliases?: readonly string[];
|
|
97
61
|
}>): Map<string, DeferredToolInfo>;
|
|
98
62
|
/**
|
|
99
|
-
*
|
|
63
|
+
* The direct-call lane a placeholder may carry. The upstream defer gate is SHAPE
|
|
100
64
|
* validation, not activation state: a call whose arguments validate against the REAL tool's
|
|
101
65
|
* declared schema runs the real tool directly (activation is only about whether the model has
|
|
102
66
|
* been SHOWN the schema, never an execution gate). Governance cannot be laundered through this
|
|
@@ -106,7 +70,7 @@ export declare function buildDeferredRegistry(deferred: ReadonlySet<string>, too
|
|
|
106
70
|
*/
|
|
107
71
|
export interface PlaceholderDirectCall {
|
|
108
72
|
/**
|
|
109
|
-
*
|
|
73
|
+
* Resolve the real tool AT CALL TIME, never from a prepare-time snapshot. The mounted
|
|
110
74
|
* roster is a LIVE array: an MCP refresh splices a server's name domain out and pushes freshly
|
|
111
75
|
* materialized tool objects back in, so a Map captured when the placeholder was built would hand
|
|
112
76
|
* this lane the WITHDRAWN object (stale schema shape gate, stale transport client) long after the
|
|
@@ -115,19 +79,19 @@ export interface PlaceholderDirectCall {
|
|
|
115
79
|
*/
|
|
116
80
|
resolveReal: () => PlaceholderDirectTarget | undefined;
|
|
117
81
|
/**
|
|
118
|
-
*
|
|
82
|
+
* The scheduling declaration this placeholder carries while it stands in for the real
|
|
119
83
|
* tool. Mirrors the REAL tool's own `executionMode` (see the mirror rationale in
|
|
120
84
|
* {@link createPlaceholderTool}); absent ⇒ the engine's fail-closed default, exactly as it would
|
|
121
85
|
* be for the real tool once activated.
|
|
122
86
|
*/
|
|
123
87
|
executionMode?: ToolExecutionMode;
|
|
124
88
|
/**
|
|
125
|
-
*
|
|
89
|
+
* Does THIS name sit on the static face right now, i.e. will it never be swapped for
|
|
126
90
|
* the real tool's declaration? The lane's model-facing rejection text reads it to say where the
|
|
127
91
|
* schema it hands back actually lives (this result only, versus this result AND every later
|
|
128
92
|
* request's tools block). Absent ⇒ the swap posture.
|
|
129
93
|
*
|
|
130
|
-
|
|
94
|
+
* A FUNCTION, evaluated per call, for the same reason `resolveReal` is: the answer is not
|
|
131
95
|
* a prepare-time constant. The run-wide strategy is, but the per-tool exemption on top of it reads
|
|
132
96
|
* the LIVE roster — an MCP refresh can replace a tool with a declaration that no longer fits the
|
|
133
97
|
* result-text carrier (or one that now does), and a boolean captured when the placeholder was built
|
|
@@ -137,9 +101,9 @@ export interface PlaceholderDirectCall {
|
|
|
137
101
|
/**
|
|
138
102
|
* Activation side effects (active-set add + rematerialize + boundary delta announcement).
|
|
139
103
|
* Idempotent; awaited BEFORE the invoke so the next boundary already shows the full schema.
|
|
140
|
-
*
|
|
104
|
+
* MUST be atomic: a thrown rematerialize has to leave the active set as it found it.
|
|
141
105
|
*
|
|
142
|
-
*
|
|
106
|
+
* Returns the announcement text this activation has to CARRY, or `undefined` when it has
|
|
143
107
|
* none (already active, or nothing rides on this name). The ToolSearch lane announces on its own
|
|
144
108
|
* result content; a direct call makes no ToolSearch call at all, so a face whose only carrier is
|
|
145
109
|
* that result — today the agent-type roster of a deferred delegation tool — had no way to reach
|
|
@@ -154,13 +118,13 @@ export interface PlaceholderDirectTarget {
|
|
|
154
118
|
/** The REAL tool's declared parameter schema — the shape gate for a direct call. */
|
|
155
119
|
parameters: TSchema;
|
|
156
120
|
/** Invoke the real tool; the result passes through VERBATIM (structured details intact).
|
|
157
|
-
*
|
|
121
|
+
* `onUpdate` is part of the contract: the engine hands every execute a progress sink,
|
|
158
122
|
* and dropping it here silently mutes streaming progress (MCP `notifications/progress`) for any
|
|
159
123
|
* tool reached through this lane, while the SAME tool streams normally once activated. */
|
|
160
124
|
invoke: (toolCallId: string, params: unknown, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<unknown>) => Promise<AgentToolResult<unknown>>;
|
|
161
125
|
}
|
|
162
126
|
/**
|
|
163
|
-
*
|
|
127
|
+
* Can the STATIC face carry this declaration at all? Static's only in-context schema
|
|
164
128
|
* carrier is result TEXT, so a declaration that cannot be serialized, or that the model-facing error
|
|
165
129
|
* bound would truncate into non-JSON, has no carrier there. Such a tool is exempted per-tool from the
|
|
166
130
|
* static face (it materializes into the tools block on activation, the swap way) rather than being
|
|
@@ -169,20 +133,20 @@ export interface PlaceholderDirectTarget {
|
|
|
169
133
|
*/
|
|
170
134
|
export declare function staticSchemaRenderable(schema: TSchema | undefined): boolean;
|
|
171
135
|
/** A placeholder AgentTool for a deferred tool: visible name + hint, empty params. Without `direct`,
|
|
172
|
-
* every call is the teaching rejection; with it, a schema-valid call runs the real tool
|
|
136
|
+
* every call is the teaching rejection; with it, a schema-valid call runs the real tool. */
|
|
173
137
|
export declare function createPlaceholderTool(info: DeferredToolInfo, direct?: PlaceholderDirectCall): AgentTool;
|
|
174
138
|
/** Keyword score of a query against a deferred tool: name hits weigh more than description/hint hits. */
|
|
175
139
|
export declare function scoreToolMatch(query: string, info: DeferredToolInfo): number;
|
|
176
|
-
/** The argument shapes `tool_search` accepts (
|
|
177
|
-
* `max_results`).
|
|
178
|
-
* durable-replay compat was already sacrificed by
|
|
140
|
+
/** The argument shapes `tool_search` accepts (CC-canonical): `query` (+ optional
|
|
141
|
+
* `max_results`). The pre-keyword-search `select` ARRAY arm is retired —
|
|
142
|
+
* durable-replay compat was already sacrificed by a prior ruling (replay resolution included);
|
|
179
143
|
* the CC-canonical `select:A,B,C` string prefix inside `query` is the live selection form. */
|
|
180
144
|
export interface ToolSearchArgs {
|
|
181
145
|
query?: string;
|
|
182
146
|
max_results?: number;
|
|
183
147
|
}
|
|
184
148
|
/**
|
|
185
|
-
* Resolve a tool_search request to deferred-tool names (
|
|
149
|
+
* Resolve a tool_search request to deferred-tool names (CC ToolSearchTool.ts form).
|
|
186
150
|
* Query forms, checked in order:
|
|
187
151
|
* ① `select:A,B,C` prefix (case-insensitive prefix, CC ToolSearchTool.ts:363) — comma-split EXACT
|
|
188
152
|
* name activation against the registry's names; misses are reported in `missing` (never silently
|
|
@@ -190,7 +154,7 @@ export interface ToolSearchArgs {
|
|
|
190
154
|
* ② bare tool name (case-insensitive equality, CC:199-204) — selects that tool directly; handles
|
|
191
155
|
* models sending a name instead of the select: prefix (seen from subagents/post-compaction).
|
|
192
156
|
* ③ keyword search — scored matches capped at `max_results` (CC default 5).
|
|
193
|
-
*
|
|
157
|
+
* The pre-keyword-search `select` ARRAY arm is retired (see {@link ToolSearchArgs}) — a stale
|
|
194
158
|
* replayed array argument now resolves like an empty query (nothing matched, honestly).
|
|
195
159
|
*/
|
|
196
160
|
export declare function resolveToolSearchDetailed(args: ToolSearchArgs, registry: ReadonlyMap<string, DeferredToolInfo>): {
|
|
@@ -201,16 +165,16 @@ export declare function resolveToolSearchDetailed(args: ToolSearchArgs, registry
|
|
|
201
165
|
* {@link extractDiscoveredToolNames} (and kept as the stable exported signature). */
|
|
202
166
|
export declare function resolveToolSearch(args: ToolSearchArgs, registry: ReadonlyMap<string, DeferredToolInfo>): string[];
|
|
203
167
|
/**
|
|
204
|
-
* Stateless recompute of the activated-tool set from the message log
|
|
168
|
+
* Stateless recompute of the activated-tool set from the message log. The in-process
|
|
205
169
|
* `active` Set is the primary source within one process, but a RESUMED session (new process) starts with
|
|
206
170
|
* an empty Set — without this, previously-activated tools would revert to placeholders. Two carriers,
|
|
207
171
|
* both deterministic and both replayed here:
|
|
208
172
|
*
|
|
209
173
|
* ① every prior `tool_search` call's arguments, re-resolved through `resolveToolSearch` — unless that
|
|
210
|
-
* call's OWN result carries the error flag (
|
|
174
|
+
* call's OWN result carries the error flag (a denied/blocked search never reached execute
|
|
211
175
|
* and staged nothing; a call with no result at all keeps the historic credit);
|
|
212
|
-
* ②
|
|
213
|
-
*
|
|
176
|
+
* ② A SUCCESSFUL call on a deferred name, which is an activation in its own right: the
|
|
177
|
+
* direct lane runs `activate()` BEFORE it invokes the real tool, so a result that came back
|
|
214
178
|
* without an error flag can only have come from a call that passed the shape gate and activated.
|
|
215
179
|
* No new carrier had to be minted for this: the toolCall part and its result are already in the
|
|
216
180
|
* transcript. Before this arm, a direct activation left NO trace at all — the next leg reverted the
|
|
@@ -223,14 +187,14 @@ export declare function resolveToolSearch(args: ToolSearchArgs, registry: Readon
|
|
|
223
187
|
* denial or hook block, an abort, invalid arguments — is flagged as an error, and none of them activated
|
|
224
188
|
* anything. It is an UNDER-approximation in one direction only: an activation whose real tool then failed
|
|
225
189
|
* (or threw) is not counted, so that name reverts to a placeholder and the model re-activates on demand —
|
|
226
|
-
* self-healing, exactly like the
|
|
190
|
+
* self-healing, exactly like the keyword-cap note below, never a widening. The shape-INVALID
|
|
227
191
|
* direct call (ruled 2026-08-03) activates too and is flagged as an error, so it lands in that same
|
|
228
192
|
* under-approximated class by construction: within the process the in-memory `active` set carries it, and
|
|
229
193
|
* a replay that loses it costs the corrected call — which succeeds, and is itself carrier ②. The one way to
|
|
230
194
|
* self-declare a positive is a deployment `tool_result` hook that rewrites a rejection's error flag to false,
|
|
231
195
|
* which is the deployment editing its own transcript.
|
|
232
196
|
*
|
|
233
|
-
*
|
|
197
|
+
* Pre-116 checkpoints whose KEYWORD queries activated up to 25 tools re-derive at
|
|
234
198
|
* the new default cap (5) — the extra tools revert to placeholders, which is self-healing (the model
|
|
235
199
|
* re-activates on demand), never a widening. Exact-name and `select:`-prefix activations replay unchanged.
|
|
236
200
|
*
|
|
@@ -244,14 +208,14 @@ export declare function extractDiscoveredToolNames(messages: ReadonlyArray<Agent
|
|
|
244
208
|
/**
|
|
245
209
|
* Build the resident `tool_search` tool. On a search that NEWLY activates deferred tools it (1) adds them
|
|
246
210
|
* to the monotonic `active` Set, (2) calls `rematerialize` (prepare-task swaps placeholders→full schema
|
|
247
|
-
* via `harness.setTools` and refreshes the
|
|
211
|
+
* via `harness.setTools` and refreshes the fingerprint), and (3) returns the "now available"
|
|
248
212
|
* announcement as its OWN result content — the delta lands at the tail of the log, never in the cache prefix.
|
|
249
213
|
*/
|
|
250
214
|
export declare function createToolSearchTool(opts: {
|
|
251
215
|
registry: ReadonlyMap<string, DeferredToolInfo>;
|
|
252
216
|
active: Set<string>;
|
|
253
217
|
rematerialize: (active: ReadonlySet<string>) => Promise<void>;
|
|
254
|
-
/**
|
|
218
|
+
/** Listing ride-along on the deferred-tool channel: when an activation newly
|
|
255
219
|
* materializes a listing-bearing tool (the deferred delegation tool whose description points at an
|
|
256
220
|
* agent-types listing that the deferred leg's boundary producer never ships), the caller returns
|
|
257
221
|
* the listing text here and it rides THIS search's own result content — tail of the log, cache
|
|
@@ -262,12 +226,12 @@ export declare function createToolSearchTool(opts: {
|
|
|
262
226
|
* "Workflow is already available — call it" instead of a generic not-found that reads as absence
|
|
263
227
|
* (observed: the model treated the miss as unavailability and only recovered by guessing). */
|
|
264
228
|
mountedNames?: () => ReadonlySet<string>;
|
|
265
|
-
/**
|
|
229
|
+
/** Whether the placeholders on this task carry the direct-call lane
|
|
266
230
|
* (`TaskSpec.deferSelfResolve` not disabled). Selects which of the two teaching postures below is
|
|
267
231
|
* true for this task; defaults to the direct-lane posture, matching the spec field's own default. */
|
|
268
232
|
directCallEnabled?: boolean;
|
|
269
|
-
/**
|
|
270
|
-
* private one. The `active` set and `rematerialize` are shared with the
|
|
233
|
+
/** Run this tool's activation critical section on the CALLER's serializer instead of a
|
|
234
|
+
* private one. The `active` set and `rematerialize` are shared with the direct-call lane,
|
|
271
235
|
* which activates from a placeholder's own execute; both lanes must queue on ONE section or a
|
|
272
236
|
* concurrent batch lets the direct lane stage a name while this section is still pending (and
|
|
273
237
|
* vice versa, so a rollback here cannot be observed as "already active" there). Absent ⇒ a private
|
|
@@ -283,7 +247,7 @@ export declare function createToolSearchTool(opts: {
|
|
|
283
247
|
*/
|
|
284
248
|
isMounted?: (name: string) => boolean;
|
|
285
249
|
serializeActivation?: <T>(section: () => Promise<T>) => Promise<T>;
|
|
286
|
-
/**
|
|
250
|
+
/** Resolve a deferred name's REAL declared
|
|
287
251
|
* parameter schema, read from the live roster at call time. Presence switches this face to the
|
|
288
252
|
* static posture: activation never swaps the placeholder in the tools block, so the result text
|
|
289
253
|
* BELOW is the schema's only in-context carrier — each matched tool's schema is inlined into the
|
|
@@ -2,11 +2,8 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { Value } from "typebox/value";
|
|
3
3
|
import { defineTool, errorResult } from "../tools.js";
|
|
4
4
|
import { formatZodValidationError, truncateError } from "../tool-errors.js";
|
|
5
|
-
import { DEFAULT_CHARS_PER_TOKEN } from "../../internal/harness.js";
|
|
6
5
|
import { toolFace } from "../tool-catalog-entries.js";
|
|
7
6
|
export const TOOL_SEARCH_NAME = "ToolSearch";
|
|
8
|
-
const DEFER_AUTO_FRACTION = 0.1;
|
|
9
|
-
const CHARS_PER_TOKEN = DEFAULT_CHARS_PER_TOKEN;
|
|
10
7
|
export const TOOL_SEARCH_DEFAULT_MAX_RESULTS = 5;
|
|
11
8
|
const MAX_QUERY_RESULTS = 25;
|
|
12
9
|
export const DEFERRED_NO_PROGRESS_LIMIT = 3;
|
|
@@ -19,46 +16,6 @@ export function deferHint(description, max = 120) {
|
|
|
19
16
|
export function safeName(name) {
|
|
20
17
|
return name.replace(/[\x00-\x1f<>"`]/g, "");
|
|
21
18
|
}
|
|
22
|
-
function inlinedChars(t) {
|
|
23
|
-
let schema = "";
|
|
24
|
-
try {
|
|
25
|
-
schema = JSON.stringify(t.parameters) ?? "";
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
schema = "";
|
|
29
|
-
}
|
|
30
|
-
return t.description.length + schema.length;
|
|
31
|
-
}
|
|
32
|
-
export function classifyDeferred(opts) {
|
|
33
|
-
const pinned = new Set(opts.alwaysLoadNames ?? []);
|
|
34
|
-
for (const s of opts.specs) {
|
|
35
|
-
if (s.alwaysLoad === true)
|
|
36
|
-
pinned.add(s.name);
|
|
37
|
-
}
|
|
38
|
-
const deferred = new Set();
|
|
39
|
-
for (const s of opts.specs) {
|
|
40
|
-
if (s.defer === true && !pinned.has(s.name))
|
|
41
|
-
deferred.add(s.name);
|
|
42
|
-
}
|
|
43
|
-
for (const name of opts.protocolToolNames)
|
|
44
|
-
if (!pinned.has(name))
|
|
45
|
-
deferred.add(name);
|
|
46
|
-
for (const name of opts.deferNames ?? [])
|
|
47
|
-
if (!pinned.has(name))
|
|
48
|
-
deferred.add(name);
|
|
49
|
-
if (opts.deferMode === "auto") {
|
|
50
|
-
const callerNames = new Set(opts.specs.map((s) => s.name));
|
|
51
|
-
const candidates = opts.fullTools.filter((t) => !deferred.has(t.name) && !pinned.has(t.name) && callerNames.has(t.name));
|
|
52
|
-
const inlineFace = opts.fullTools.filter((t) => !deferred.has(t.name));
|
|
53
|
-
const total = inlineFace.reduce((n, t) => n + inlinedChars(t), 0);
|
|
54
|
-
const window = (opts.model.contextTokens ?? opts.model.contextWindow ?? 0) * CHARS_PER_TOKEN;
|
|
55
|
-
if (window > 0 && total > DEFER_AUTO_FRACTION * window) {
|
|
56
|
-
for (const t of candidates)
|
|
57
|
-
deferred.add(t.name);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return deferred;
|
|
61
|
-
}
|
|
62
19
|
export function buildDeferredRegistry(deferred, tools) {
|
|
63
20
|
const reg = new Map();
|
|
64
21
|
for (const t of tools) {
|
|
@@ -17,13 +17,13 @@ import { type McpDelivered } from "../mcp-failure.js";
|
|
|
17
17
|
import type { RecoveredOrphan } from "../session-reconcile.js";
|
|
18
18
|
import { type StructuredProjector } from "./tool-output-projection.js";
|
|
19
19
|
import type { WriteFamily } from "./turn-attachments.js";
|
|
20
|
-
/**
|
|
20
|
+
/** Derive the thin `response` digest from a harness tool result: model-facing text
|
|
21
21
|
* content only (never `details` — the H4 thin-projection discipline), capped with a truncation note. */
|
|
22
22
|
export declare function batchResponseDigest(result: unknown): string | undefined;
|
|
23
23
|
/** The `tool_end` body fields projected from a harness tool result — output/truncated/totalChars via
|
|
24
24
|
* {@link toolOutputFrom} and the CC card via the LEG's projector (`Prepared.structuredProjector`: the engine card set
|
|
25
25
|
* ∪ the caller rows' declared `renderHints.resultCards`, an undeclared type dropped and announced once per leg —
|
|
26
|
-
*
|
|
26
|
+
* handed in because the once-per-leg state lives on the leg. Single construction point for BOTH
|
|
27
27
|
* the live loop's frames and the resumed batch's frames (`resolvePendingCall` + the deferred-sibling
|
|
28
28
|
* close): the resumed frames used to carry only `isError`, so a client rendering tool output from frames
|
|
29
29
|
* showed an empty body for every durable-approved call. Same projection = same source as the transcript.
|
|
@@ -72,7 +72,7 @@ export declare function reconciledToolEndBody(orphan: Pick<RecoveredOrphan, "tex
|
|
|
72
72
|
/** task #51 F2/F3 — the CANONICAL write-tool → reminder-window mapping (CC qFm :480253-480254
|
|
73
73
|
* `a.name === bD || a.name === hF` = TaskCreate/TaskUpdate; BFm :480217-480219 = TodoWrite). Live
|
|
74
74
|
* deployment-alias resolution happens at the call sites (run-local `writeFamilyOf` over the mounted
|
|
75
|
-
* roster); retired-name normalization is gone
|
|
75
|
+
* roster); retired-name normalization is gone. */
|
|
76
76
|
export declare function writeFamilyOfCanonical(name: string): WriteFamily | undefined;
|
|
77
77
|
/** A `toolResult` transcript message (text content). Used by the resume engine to close pending calls. */
|
|
78
78
|
export declare function toolResultMsg(toolCallId: string, toolName: string, text: string, isError: boolean): AgentMessage;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The MCP `toolFaces` OVERLAY: a deployment's per-tool face declaration for a
|
|
3
3
|
* protocol tool that has no `ToolSpec` of its own (`McpServerSpec.toolFaces`, keyed by the server's RAW
|
|
4
4
|
* tool name like `allowTools` / `toolAxes`). Validated against the MATERIALIZED schema at every listing
|
|
5
5
|
* (the first mount and every RefreshMcpTools): `pathTarget.param` must be a top-level property; an
|
|
@@ -16,7 +16,7 @@ import type { AgentTool } from "../../internal/harness-types.js";
|
|
|
16
16
|
import type { McpServerSpec } from "../mcp-server-spec.js";
|
|
17
17
|
import { type ToolApprovalCard } from "../tool-face.js";
|
|
18
18
|
import { type EngineNotice } from "../engine-notice.js";
|
|
19
|
-
/** The keys each specialised approval card needs on the call (
|
|
19
|
+
/** The keys each specialised approval card needs on the call (the SHELL owns the
|
|
20
20
|
* card's full schema; this is the pre-filter the overlay is validated against). */
|
|
21
21
|
export declare const APPROVAL_CARD_REQUIRED_KEYS: Readonly<Record<ToolApprovalCard, readonly string[]>>;
|
|
22
22
|
export type McpToolFace = NonNullable<McpServerSpec["toolFaces"]>[string];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The `tool_end` output/structured projection, verbatim from
|
|
3
3
|
* `runtask.ts`'s runLocked R4 phase. Pure functions over the harness `AgentToolResult`: zero closure
|
|
4
4
|
* state, zero runtask coupling — the ONLY module-level input is the size cap below. Event-sequence
|
|
5
5
|
* snapshot pin (recorded before/after the move, byte-equal) guards the projection semantics.
|
|
@@ -14,7 +14,7 @@ export declare const toolOutputFrom: (result: unknown) => {
|
|
|
14
14
|
* as a parameter — there is no module-level card set to fall back to. */
|
|
15
15
|
export type StructuredProjector = (result: unknown) => unknown;
|
|
16
16
|
/**
|
|
17
|
-
* L4's run-time half
|
|
17
|
+
* L4's run-time half: a per-leg projector — the engine card set plus the leg's declared
|
|
18
18
|
* caller cards — that DROPS an undeclared `details.type` and announces it ONCE per leg
|
|
19
19
|
* (`config.tool_card_undeclared`, audience operator). With no options it is the engine set alone, unannounced —
|
|
20
20
|
* the shape a caller outside a leg (a test, a fixture) uses.
|