@sema-agent/core 7.16.0 → 7.17.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 +173 -0
- package/dist/agents/builtin-agents.d.ts +3 -3
- package/dist/agents/cross-session-envelope.d.ts +0 -1
- package/dist/agents/cross-session-envelope.js +0 -1
- package/dist/agents/peer-notices.d.ts +0 -9
- package/dist/agents/peer-notices.js +0 -63
- package/dist/agents/subagent.js +7 -15
- package/dist/brain/request-params.d.ts +2 -2
- package/dist/config/defaults.d.ts +0 -7
- package/dist/config/defaults.js +0 -2
- package/dist/core/artifact-host.d.ts +218 -0
- package/dist/core/artifact-host.js +92 -0
- package/dist/core/ask-origin.d.ts +29 -3
- package/dist/core/ask-origin.js +17 -9
- package/dist/core/ask-question.d.ts +25 -0
- package/dist/core/ask-question.js +4 -0
- package/dist/core/ask-unresolvable-notice.d.ts +30 -21
- package/dist/core/ask-unresolvable-notice.js +16 -8
- package/dist/core/auto-compaction.d.ts +10 -10
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/background-agent-store.d.ts +5 -5
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-execution-record.d.ts +0 -3
- package/dist/core/checkpoint-execution-record.js +0 -4
- package/dist/core/checkpoint-store.d.ts +71 -99
- package/dist/core/checkpoint-store.js +9 -16
- package/dist/core/context-edit.d.ts +36 -104
- package/dist/core/context-edit.js +37 -60
- package/dist/core/effective-path-target.d.ts +68 -8
- package/dist/core/effective-path-target.js +42 -30
- package/dist/core/engine-notice.d.ts +46 -32
- package/dist/core/env-budget.d.ts +31 -0
- package/dist/core/env-budget.js +39 -0
- package/dist/core/exec-output-tail.d.ts +5 -5
- package/dist/core/file-history-store.js +3 -6
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.js +3 -3
- package/dist/core/gate-lanes.js +9 -15
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +18 -17
- package/dist/core/lsp-session.d.ts +1 -1
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/lsp.js +5 -4
- package/dist/core/mcp.d.ts +5 -5
- package/dist/core/mcp.js +4 -48
- package/dist/core/memory-recall.d.ts +0 -8
- package/dist/core/memory.d.ts +1 -1
- package/dist/core/oracle-isolation.js +2 -2
- package/dist/core/parent-spec-seat.d.ts +66 -0
- package/dist/core/parent-spec-seat.js +47 -0
- package/dist/core/permission-rule-model.d.ts +11 -6
- package/dist/core/permission-rule-model.js +53 -37
- package/dist/core/permission-rules.js +8 -3
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +19 -0
- package/dist/core/retired-keys.js +15 -0
- package/dist/core/runner/active-skill-scope.js +3 -3
- package/dist/core/runner/assemble-result.d.ts +15 -13
- package/dist/core/runner/assemble-result.js +5 -4
- package/dist/core/runner/compaction-call-options.d.ts +25 -89
- package/dist/core/runner/contracts.d.ts +10 -9
- package/dist/core/runner/gate-exit.js +15 -1
- package/dist/core/runner/halt-attribution.d.ts +35 -0
- package/dist/core/runner/halt-attribution.js +16 -0
- package/dist/core/runner/memory-consolidation.d.ts +0 -2
- package/dist/core/runner/memory-consolidation.js +0 -1
- package/dist/core/runner/prepare-artifact.d.ts +53 -0
- package/dist/core/runner/prepare-artifact.js +77 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
- package/dist/core/runner/prepare-config-doors.d.ts +3 -3
- package/dist/core/runner/prepare-config-doors.js +5 -2
- package/dist/core/runner/prepare-context-lane.d.ts +2 -2
- package/dist/core/runner/prepare-context-lane.js +2 -13
- package/dist/core/runner/prepare-file-history.d.ts +1 -1
- package/dist/core/runner/prepare-file-history.js +3 -52
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -7
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +5 -2
- package/dist/core/runner/prepare-inherited-gate.js +7 -8
- package/dist/core/runner/prepare-memory.d.ts +4 -6
- package/dist/core/runner/prepare-memory.js +4 -2
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -3
- package/dist/core/runner/prepare-policy-chain.js +15 -30
- package/dist/core/runner/prepare-prompt-assembly.js +1 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
- package/dist/core/runner/prepare-suspend-saga.js +0 -2
- package/dist/core/runner/prepare-task.js +10 -9
- package/dist/core/runner/prepare-wiring-manifest.js +9 -7
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-internals-and-config.js +4 -0
- package/dist/core/runner/retired-runner-deps.d.ts +3 -0
- package/dist/core/runner/retired-runner-deps.js +7 -0
- package/dist/core/runner/run-harness-handlers.js +2 -3
- package/dist/core/runner/run-settle-and-teardown.d.ts +1 -1
- package/dist/core/runner/run-settle-and-teardown.js +3 -1
- package/dist/core/runner/session-file-state-replay.js +3 -3
- package/dist/core/runner/session-rule-policy.js +3 -4
- package/dist/core/runner/stream-halt-verbs.js +1 -1
- package/dist/core/runner/stream-steer-verb.js +1 -4
- package/dist/core/runner/workspace-path.js +3 -0
- package/dist/core/runner-deps.d.ts +30 -49
- package/dist/core/safe-notify.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +4 -4
- package/dist/core/session.d.ts +2 -2
- package/dist/core/skill-tool-specifier.d.ts +1 -1
- package/dist/core/skills-directory.d.ts +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/strategy-store.d.ts +8 -11
- package/dist/core/task-event.d.ts +20 -17
- package/dist/core/task-limits.d.ts +1 -1
- package/dist/core/task-notification.d.ts +5 -5
- package/dist/core/task-registry-agent.d.ts +5 -5
- package/dist/core/task-registry-monitor.d.ts +2 -2
- package/dist/core/task-registry-shared.d.ts +27 -27
- package/dist/core/task-registry-shared.js +3 -13
- package/dist/core/task-registry.d.ts +8 -8
- package/dist/core/task-result.d.ts +37 -37
- package/dist/core/task-spec.d.ts +5 -5
- package/dist/core/task-stream.d.ts +16 -11
- package/dist/core/tool-catalog-entries.d.ts +1 -0
- package/dist/core/tool-catalog-entries.js +5 -0
- package/dist/core/tool-conformance.d.ts +0 -2
- package/dist/core/tool-conformance.js +1 -3
- package/dist/core/tool-errors.d.ts +5 -0
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +51 -22
- package/dist/core/tool-policy.js +28 -5
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +11 -11
- package/dist/core/tool-spec.d.ts +34 -38
- package/dist/core/tools.d.ts +2 -2
- package/dist/core/trace.d.ts +12 -15
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +4 -3
- package/dist/core/workflow-journal-store.d.ts +11 -11
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
- package/dist/engine/execution-env/node-execution-env.js +4 -2
- package/dist/engine/harness/agent-harness.d.ts +47 -4
- package/dist/engine/harness/agent-harness.js +52 -9
- package/dist/engine/loop/agent-loop.js +15 -8
- package/dist/engine/loop/types.d.ts +5 -0
- package/dist/index.d.ts +19 -14
- package/dist/index.js +15 -10
- package/dist/internal/harness.d.ts +1 -0
- package/dist/orchestration/run-spec.d.ts +3 -0
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +12 -6
- package/dist/orchestration/run-workflow-tool.js +23 -5
- package/dist/orchestration/workflow-primitives.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +1 -4
- package/dist/orchestration/workflow-types.d.ts +34 -4
- package/dist/orchestration/workflow-types.js +2 -0
- package/dist/orchestration/workflow.d.ts +39 -7
- package/dist/orchestration/workflow.js +234 -113
- package/dist/prompt-assembly/assemble.d.ts +2 -2
- package/dist/prompt-assembly/assemble.js +9 -9
- package/dist/prompt-assembly/composer.d.ts +6 -7
- package/dist/prompt-assembly/composer.js +10 -10
- package/dist/prompt-assembly/epoch.js +1 -1
- package/dist/prompt-assembly/event-registry.d.ts +0 -21
- package/dist/prompt-assembly/packs/sema-default.js +30 -30
- package/dist/prompt-assembly/types.d.ts +14 -14
- package/dist/prompts/coordinator.d.ts +0 -13
- package/dist/prompts/coordinator.js +0 -3
- package/dist/stores/file/checkpoint-store.js +1 -5
- package/dist/stores/file/file-history-store.js +2 -2
- package/dist/stores/file/permission-rule-store.d.ts +4 -35
- package/dist/stores/file/permission-rule-store.js +4 -148
- package/dist/tools/artifact/artifact-text.d.ts +62 -0
- package/dist/tools/artifact/artifact-text.js +68 -0
- package/dist/tools/artifact/artifact-tool.d.ts +52 -0
- package/dist/tools/artifact/artifact-tool.js +351 -0
- package/dist/tools/artifact/local-stub.d.ts +26 -0
- package/dist/tools/artifact/local-stub.js +296 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +74 -8
- package/dist/tools/fs/bash-readonly-classifier.js +171 -60
- package/dist/tools/fs/fs-bash.d.ts +14 -10
- package/dist/tools/fs/fs-bash.js +19 -27
- package/dist/tools/fs/fs-search-tools.js +5 -2
- package/dist/tools/fs/fs-shared.d.ts +2 -0
- package/dist/tools/fs/fs-shared.js +3 -11
- package/dist/tools/fs/index.d.ts +4 -0
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/safety.d.ts +117 -1
- package/dist/tools/fs/safety.js +131 -31
- package/dist/tools/fs/search.js +8 -6
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +115 -39
|
@@ -453,9 +453,6 @@ export function validatePendingSteer(steer) {
|
|
|
453
453
|
if (inputId !== undefined && (inputId === "" || inputId.length > MAX_STEER_INPUT_ID_CHARS)) {
|
|
454
454
|
throw new CheckpointError("steering.invalid_content", `steering inputId must be a non-empty string of at most ${MAX_STEER_INPUT_ID_CHARS} characters`);
|
|
455
455
|
}
|
|
456
|
-
if (inputId === LEGACY_PENDING_STEER_INPUT_ID) {
|
|
457
|
-
throw new CheckpointError("steering.invalid_content", `steering inputId "${LEGACY_PENDING_STEER_INPUT_ID}" is reserved for a pre-queue parked steer and cannot be supplied by a caller`);
|
|
458
|
-
}
|
|
459
456
|
if (sanitizeUntrustedText(text) !== text) {
|
|
460
457
|
throw new CheckpointError("steering.invalid_content", "steering text must not contain a system-reminder break-out tag");
|
|
461
458
|
}
|
|
@@ -512,14 +509,11 @@ function captureActorAssertion(actor) {
|
|
|
512
509
|
return { id, hostAsserted, ...(issuer !== undefined ? { issuer } : {}) };
|
|
513
510
|
}
|
|
514
511
|
export function readPendingSteerQueue(state) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
return [...legacy, ...queued];
|
|
521
|
-
}
|
|
522
|
-
export const LEGACY_PENDING_STEER_INPUT_ID = "legacy-single-seat";
|
|
512
|
+
if (state.pendingSteer !== undefined) {
|
|
513
|
+
throw new CheckpointError("checkpoint.unsupported_version", "this checkpoint carries a parked steer in the retired pre-queue single seat (`state.pendingSteer`), which this engine does not read — decide the parked instruction on the host's surface and, if it still applies, park it again with setPendingSteer", { reason: "seat_retired" });
|
|
514
|
+
}
|
|
515
|
+
return [...(state.pendingSteerQueue ?? [])].sort((a, b) => a.seq - b.seq);
|
|
516
|
+
}
|
|
523
517
|
function samePendingSteerPayload(a, b) {
|
|
524
518
|
return (a.text === b.text &&
|
|
525
519
|
a.trusted === b.trusted &&
|
|
@@ -530,23 +524,22 @@ function samePendingSteerPayload(a, b) {
|
|
|
530
524
|
}
|
|
531
525
|
export function appendPendingSteer(state, entry) {
|
|
532
526
|
const current = readPendingSteerQueue(state);
|
|
533
|
-
const existingQueue = [...(state.pendingSteerQueue ?? [])].sort((a, b) => a.seq - b.seq);
|
|
534
527
|
const collision = current.find((e) => e.inputId === entry.inputId);
|
|
535
528
|
if (collision !== undefined) {
|
|
536
529
|
if (!samePendingSteerPayload(collision, entry)) {
|
|
537
530
|
throw new CheckpointError("steering.duplicate_input_id", `a different steering instruction is already parked under inputId "${entry.inputId}" — ` +
|
|
538
531
|
`re-issue this one with a fresh inputId (an identical payload would have been an idempotent retry)`);
|
|
539
532
|
}
|
|
540
|
-
return
|
|
533
|
+
return current;
|
|
541
534
|
}
|
|
542
535
|
const nextSeq = current.reduce((max, e) => Math.max(max, e.seq), 0) + 1;
|
|
543
|
-
const next = [...
|
|
544
|
-
const totalEntries = next.length
|
|
536
|
+
const next = [...current, { ...entry, seq: nextSeq }];
|
|
537
|
+
const totalEntries = next.length;
|
|
545
538
|
if (totalEntries > MAX_PENDING_STEER_ENTRIES) {
|
|
546
539
|
throw new CheckpointError("steering.queue_full", `the parked steering queue already holds ${totalEntries - 1} entries (max ${MAX_PENDING_STEER_ENTRIES}) — ` +
|
|
547
540
|
`deliver or remove a parked steer before adding another`);
|
|
548
541
|
}
|
|
549
|
-
const bytes = Buffer.byteLength(JSON.stringify({
|
|
542
|
+
const bytes = Buffer.byteLength(JSON.stringify({ pendingSteerQueue: next }), "utf8");
|
|
550
543
|
if (bytes > PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES) {
|
|
551
544
|
throw new CheckpointError("steering.queue_full", `the parked steering queue would serialize to ${bytes} bytes (max ${PENDING_STEER_QUEUE_BYTE_BUDGET_BYTES}) — ` +
|
|
552
545
|
`deliver or remove a parked steer before adding another`);
|
|
@@ -49,24 +49,14 @@ export declare const COMPACTION_TRIGGER_BUFFER_TOKENS = 13000;
|
|
|
49
49
|
*/
|
|
50
50
|
export declare function contextEditFrontier(window: number): number;
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* The `RunnerDeps.microCompact.machine` vocabulary — ONE clearing machine, switchable:
|
|
53
53
|
*
|
|
54
|
-
* - `"cc"` (default
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* - `"
|
|
59
|
-
*
|
|
60
|
-
* legacy marker bytes. In the request pipeline the opt-out also keeps the pre-374 BACKSTOP
|
|
61
|
-
* ORDER (guard trim as the ordinary second line) — see the guard-chain note in prepare-task's
|
|
62
|
-
* context hook.
|
|
63
|
-
*
|
|
64
|
-
* The pre-flip P-form warning (design/374 r2-B⑨/C-5: "cc" + the OLD message-dropping guard-trim
|
|
65
|
-
* backstop = the 20k gate in front of the only reduction) is RESOLVED, not merely re-worded: the
|
|
66
|
-
* slice-3 fallback re-ordering shipped in the same version as this default (blocking-point machine
|
|
67
|
-
* re-run when the frontier pass is off → in-turn forced compaction behind the adopt seam → trim
|
|
68
|
-
* demoted to the disaster-only last resort), so the default configuration never hands an
|
|
69
|
-
* under-20k refusal straight to a message-dropping trim.
|
|
54
|
+
* - `"cc"` (default): the CC 2.1.223 `yId` machine — keep 5, a ≥20000-token minimum-savings gate
|
|
55
|
+
* (below it the pass is a byte-identical no-op), ONE deep clear of everything beyond the keep
|
|
56
|
+
* window, the window base counts already-cleared/offloaded occurrences (they hold their keep
|
|
57
|
+
* slots), and CC's plain marker bytes.
|
|
58
|
+
* - `"off"`: no proactive frontier clearing; the machine gets its one shot at the blocking point
|
|
59
|
+
* of the guard chain instead (trace trigger `"blocking"`).
|
|
70
60
|
*
|
|
71
61
|
* WHOLE-MACHINE CROSS-CHECK against the 2.1.250 corpus (design/374 slice 4 — the "the knob number
|
|
72
62
|
* was copied, the machine was not" disease is checked by naming the machine's parts, not the one
|
|
@@ -93,39 +83,28 @@ export declare function contextEditFrontier(window: number): number;
|
|
|
93
83
|
* keeps a PROACTIVE frontier trigger CC's client does not have, anchored to Anthropic's
|
|
94
84
|
* platform context-management policy rather than to CC-CLI — registered divergence D-1, and
|
|
95
85
|
* re-stated at {@link clearStaleToolResults}'s ANCHOR paragraph.
|
|
86
|
+
*
|
|
87
|
+
* A closed set (`docs/CLOSED-SETS.md`): the knob door quotes it, so a word outside it is refused by
|
|
88
|
+
* name rather than folded to the default.
|
|
96
89
|
*/
|
|
97
|
-
export
|
|
90
|
+
export declare const MICRO_COMPACT_MACHINES: readonly ["off", "cc"];
|
|
91
|
+
export type MicroCompactMachine = (typeof MICRO_COMPACT_MACHINES)[number];
|
|
98
92
|
/**
|
|
99
|
-
* How many most-recent CANDIDATE tool results keep their content when the stale-result pass runs
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* the frontier and the message-dropping trim, so widening it disables the whole defense in
|
|
108
|
-
* exactly the terminal-batch shape that needs it. This is why the slice-3 "keep default 3→5"
|
|
109
|
-
* flip rides the MACHINE default, not this constant: painting 5 onto the opt-out machine would
|
|
110
|
-
* recreate the probed failure inside the escape hatch.
|
|
111
|
-
* - CC machine (the default since slice 3): 5 (2.1.223 `uAp`; 2.1.250 `var k = 5` at the call site
|
|
112
|
-
* cli250.js:58375, and 2.1.245 carries the same value — zero drift across 245→250; same value
|
|
113
|
-
* again on CC 88's `timeBasedMCConfig`, i.e. stable across all three corpus generations, and it
|
|
114
|
-
* is a hard-coded constant upstream, not a setting). The #384 adjudication adopted 5
|
|
115
|
-
* CONDITIONAL on the whole machine coming with it (20k gate + deep clear + re-ordered
|
|
116
|
-
* backstops); the cc machine carries that condition, and slice 3's backstop re-ordering
|
|
117
|
-
* completed it — 5 is now the effective default keep, and the 200k probe above is answered by
|
|
118
|
-
* the machine (§3.5: the gate correctly refuses the no-win clear and the in-turn forced
|
|
119
|
-
* compaction owns the request instead of a message-dropping trim).
|
|
93
|
+
* How many most-recent CANDIDATE tool results keep their content when the stale-result pass runs:
|
|
94
|
+
* 5 (CC 2.1.223 `uAp`; 2.1.250 `var k = 5` at the call site cli250.js:58375, and 2.1.245 carries
|
|
95
|
+
* the same value — zero drift across 245→250; same value again on CC 88's `timeBasedMCConfig`, i.e.
|
|
96
|
+
* stable across all three corpus generations, and it is a hard-coded constant upstream, not a
|
|
97
|
+
* setting). The #384 adjudication adopted 5 CONDITIONAL on the whole machine coming with it (20k
|
|
98
|
+
* gate + deep clear + re-ordered backstops); the machine carries that condition (§3.5: the gate
|
|
99
|
+
* refuses a no-win clear and the in-turn forced compaction owns the request instead of a
|
|
100
|
+
* message-dropping trim).
|
|
120
101
|
*/
|
|
121
|
-
export declare const DEFAULT_KEEP_RECENT_TOOL_RESULTS = 3;
|
|
122
102
|
export declare const CC_DEFAULT_KEEP_RECENT_TOOL_RESULTS = 5;
|
|
123
103
|
/** design/374 §3.1 — CC's minimum-savings gate (`vUs` :397773; 2.1.250 `Mnn` cli250.js:41714, same
|
|
124
104
|
* value): the cc machine refuses to clear at
|
|
125
105
|
* all unless the structural estimate of what WOULD be cleared reaches this many tokens. Rationale
|
|
126
106
|
* (independent of "CC does it"): every clear breaks the provider prefix cache once, so a clear that
|
|
127
|
-
* saves less than the re-prefill it causes is a net loss; the gate prices that trade.
|
|
128
|
-
* machine has no such gate (1 token over budget clears) — that difference is machine-defining. */
|
|
107
|
+
* saves less than the re-prefill it causes is a net loss; the gate prices that trade. */
|
|
129
108
|
export declare const MIN_CLEAR_SAVINGS_TOKENS = 20000;
|
|
130
109
|
/** Minimum kept results — CC `EUs` :397710 `Math.max(1, keepRecent)`. See
|
|
131
110
|
* {@link ContextEditOptions.keepRecentToolResults} for why both degenerate ends are unusable. */
|
|
@@ -133,26 +112,10 @@ export declare const MIN_KEEP_RECENT_TOOL_RESULTS = 1;
|
|
|
133
112
|
export interface ContextEditOptions {
|
|
134
113
|
/** Start clearing once estimated context tokens exceed this. */
|
|
135
114
|
budgetTokens: number;
|
|
136
|
-
/**
|
|
137
|
-
* design/374 — which clearing machine runs; see {@link ContextEditMachine}. Default `"cc"`
|
|
138
|
-
* (the slice-3 default flip); pass `"legacy"` explicitly for the pre-374 machine, byte-identical.
|
|
139
|
-
*/
|
|
140
|
-
machine?: ContextEditMachine;
|
|
141
|
-
/**
|
|
142
|
-
* Mixed-configuration protection (design/374 r2-Md⑪ family): make the LEGACY machine recognize
|
|
143
|
-
* the CC marker forms as already-cleared, so a ledger-replayed CC marker is never re-cleared
|
|
144
|
-
* into a legacy marker (destroying its ref/media disclosure). Wired by the pipeline whenever a
|
|
145
|
-
* cc-minting knob is on (machine "cc" — where it is implied — or the rejection-recovery arm over
|
|
146
|
-
* a legacy frontier machine). Default false — which since the slice-3 flip is the posture of the
|
|
147
|
-
* explicit OPT-OUT machine with MC-R also off (C15; the shipped default machine is "cc", where
|
|
148
|
-
* this option is moot): in that pure opt-out, tool output that merely SPELLS the CC marker is
|
|
149
|
-
* untrusted bytes and stays clearable exactly as pre-374.
|
|
150
|
-
*/
|
|
151
|
-
recognizeCcMarkers?: boolean;
|
|
152
115
|
/**
|
|
153
116
|
* Fired AT MOST ONCE per pass, only when the pass cleared at least one result: the cleared
|
|
154
|
-
* indexes (into the returned array) and the pass's savings estimate (
|
|
155
|
-
* minimum-savings estimate it gated on
|
|
117
|
+
* indexes (into the returned array) and the pass's savings estimate (the structural
|
|
118
|
+
* minimum-savings estimate it gated on). The
|
|
156
119
|
* request-projection ledger (design/374 §3.2.6) records cleared occurrences through this seat —
|
|
157
120
|
* observation only, a throwing callback is swallowed and never fails the request build.
|
|
158
121
|
*/
|
|
@@ -164,9 +127,7 @@ export interface ContextEditOptions {
|
|
|
164
127
|
}) => void;
|
|
165
128
|
/**
|
|
166
129
|
* Always keep the content of this many most-recent CANDIDATE tool results (candidates = results
|
|
167
|
-
* from {@link compactableTools}). Default
|
|
168
|
-
* {@link DEFAULT_KEEP_RECENT_TOOL_RESULTS} = 3 (legacy) / {@link CC_DEFAULT_KEEP_RECENT_TOOL_RESULTS}
|
|
169
|
-
* = 5 (cc); see those constants for why the two machines carry different numbers.
|
|
130
|
+
* from {@link compactableTools}). Default {@link CC_DEFAULT_KEEP_RECENT_TOOL_RESULTS} = 5.
|
|
170
131
|
*
|
|
171
132
|
* Floored at 1, which IS CC's rule (`EUs` `Math.max(1, keepRecent)`), whose own source note gives
|
|
172
133
|
* the reason: 0 leaves the model with zero working tool context, and in CC's `slice(-0)` spelling
|
|
@@ -190,20 +151,16 @@ export interface ContextEditOptions {
|
|
|
190
151
|
persist: (toolCallId: string, fullText: string) => string;
|
|
191
152
|
};
|
|
192
153
|
/**
|
|
193
|
-
* design/123 D3 (= fable-M5) — ANCHORED
|
|
194
|
-
* charsPerToken).tokens` for the SAME message array. When set, the trigger gate
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* the anchor usage was billed with that content included and does not shrink on a request-view
|
|
200
|
-
* edit (the clear only pays off on the NEXT request, after a fresh anchor lands), so the defense
|
|
201
|
-
* keeps clearing deeper until the anchored total is met or nothing clearable remains.
|
|
202
|
-
* Absent ⇒ legacy structural gate + accounting (byte-identical behavior).
|
|
154
|
+
* design/123 D3 (= fable-M5) — ANCHORED trigger: `estimateContextTokens(messages,
|
|
155
|
+
* charsPerToken).tokens` for the SAME message array. When set, the trigger gate reads this
|
|
156
|
+
* usage-anchored value instead of the pure structural sum (which underestimates code by ~25% and
|
|
157
|
+
* CJK by 60–75% — the defense would self-certify and pass over-window requests). The minimum-
|
|
158
|
+
* savings gate stays CC's structural form either way (design/374 D-5). Absent ⇒ the structural
|
|
159
|
+
* sum triggers.
|
|
203
160
|
*/
|
|
204
161
|
anchoredTotalTokens?: number;
|
|
205
162
|
/** design/123 D2 — structural coefficient (`Model.charsPerToken`); default 3 (design/374 slice 4;
|
|
206
|
-
* it was 4 before). Note this does NOT reach the
|
|
163
|
+
* it was 4 before). Note this does NOT reach the ≥20k gate, which is CC-exactly
|
|
207
164
|
* hard-coded to 4 — see {@link structuralClearSavings}. */
|
|
208
165
|
charsPerToken?: number;
|
|
209
166
|
/**
|
|
@@ -215,13 +172,11 @@ export interface ContextEditOptions {
|
|
|
215
172
|
compactableTools?: ReadonlySet<string>;
|
|
216
173
|
/**
|
|
217
174
|
* design/374 (RB-212 durable half): the PRE-CAP counterpart array of `messages`, position-aligned
|
|
218
|
-
* (the pipeline's aggregate caps replace in place, never drop). When present, the
|
|
175
|
+
* (the pipeline's aggregate caps replace in place, never drop). When present, the machine
|
|
219
176
|
* composes each clear's marker + offload text from `clearSource[idx]` — the same occurrence
|
|
220
177
|
* bytes the cleared-projection ledger repaints the marker onto at the next session rebuild — so
|
|
221
178
|
* the durable marker discloses the occurrence's REAL media and the page-back ref resolves to its
|
|
222
|
-
* real text rather than to a cap-stripped copy.
|
|
223
|
-
* request-local and correctly describe the projection they were handed — default bytes do not
|
|
224
|
-
* move). An entry that is not a tool result, or that carries a different `toolCallId` than the
|
|
179
|
+
* real text rather than to a cap-stripped copy. An entry that is not a tool result, or that carries a different `toolCallId` than the
|
|
225
180
|
* target, is ignored per index (composition falls back to the projection copy — a misaligned
|
|
226
181
|
* source must never mis-attribute content).
|
|
227
182
|
*/
|
|
@@ -240,28 +195,6 @@ export interface ContextEditOptions {
|
|
|
240
195
|
* excluded one layer later, at `convertToLlm`, by its own three-key identity (`isSyntheticApiErrorMessage`).
|
|
241
196
|
*/
|
|
242
197
|
export declare function dropEmptyFailureAssistants(messages: AgentMessage[]): AgentMessage[];
|
|
243
|
-
/**
|
|
244
|
-
* Context-editing — the lightest, safest reduction: when context exceeds the budget, replace the
|
|
245
|
-
* CONTENT of older tool-result messages with a short marker, keeping the turn structure
|
|
246
|
-
* (tool_call/tool_result pairing) intact; the most recent `keepRecentToolResults` CANDIDATE
|
|
247
|
-
* results keep their content. Request-only and non-destructive: returns a new array, the durable
|
|
248
|
-
* session keeps the full results (the request-projection ledger, not this function, is what makes
|
|
249
|
-
* a clear DECISION durable across requests — see `replayClearedProjection`).
|
|
250
|
-
*
|
|
251
|
-
* ANCHOR (design/374 片4 re-anchoring): this proactive frontier hook is the client-side form of
|
|
252
|
-
* Anthropic's PLATFORM context-management policy (`clear_tool_uses_20250919` — same
|
|
253
|
-
* trigger/keep/clear-at-least construction), NOT a CC-CLI parity claim: CC 2.1.x's client
|
|
254
|
-
* keep-recent clear lives exclusively on its `context_hint` REJECTION leg (default-gated off), a
|
|
255
|
-
* machine whose sema form is the MC-R arm (`planRejectionClears` + the runner's recovery chain).
|
|
256
|
-
* The per-mechanism CC anchors that DO hold (the COMPACTABLE_TOOLS set, the candidate-scoped keep
|
|
257
|
-
* window, the floor, and the whole `machine: "cc"` clearing form) are cited at their sites.
|
|
258
|
-
* (An earlier note here cited an unverified "~29% improvement" figure — removed: no first-party
|
|
259
|
-
* source in the corpus; the machine's case rests on the measured mechanics, not that number.)
|
|
260
|
-
*
|
|
261
|
-
* Clear ORDER is machine-dependent: legacy clears oldest-first and stops at the budget; the cc
|
|
262
|
-
* machine deep-clears everything beyond the keep window behind the ≥20k gate (see
|
|
263
|
-
* {@link ContextEditMachine}).
|
|
264
|
-
*/
|
|
265
198
|
export declare function clearStaleToolResults(messages: AgentMessage[], opts: ContextEditOptions): AgentMessage[];
|
|
266
199
|
/** design/374 slice 1b (X5) — the resolved trigger-side window and whether the clamp repaired it. */
|
|
267
200
|
export interface TriggerWindowResolution {
|
|
@@ -368,11 +301,10 @@ export type RejectionClearPlan = {
|
|
|
368
301
|
};
|
|
369
302
|
/**
|
|
370
303
|
* design/374 §3.2 (slice 2) — plan the MC-R rejection clear over the REJECTED projection: the same
|
|
371
|
-
*
|
|
304
|
+
* machine (keep 5 default, window base counts cleared occurrences, five-form exclusion, Sby
|
|
372
305
|
* savings, ≥20k gate) with the TRIGGER replaced by the rejection itself — the provider already said
|
|
373
|
-
* the input does not fit, so there is no budget coordinate to compare against (
|
|
374
|
-
*
|
|
375
|
-
* incremental form is defined by "stop when the budget is met" and has no meaning here).
|
|
306
|
+
* the input does not fit, so there is no budget coordinate to compare against (an enabled MC-R runs
|
|
307
|
+
* even with the frontier machine `"off"`).
|
|
376
308
|
* `keyOf` threads occurrence identity (the ledger is the ONLY way a rejection-arm clear survives
|
|
377
309
|
* the session rebuild — §1.2's adoption chain discards the in-memory view); a candidate the index
|
|
378
310
|
* cannot key is skipped from BOTH the clear set and the savings (never promise savings the ledger
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { DEFAULT_CHARS_PER_TOKEN,
|
|
2
|
+
import { DEFAULT_CHARS_PER_TOKEN, estimateTokens } from "../internal/harness.js";
|
|
3
3
|
import { isToolResult } from "./message-utils.js";
|
|
4
4
|
import { PERSISTED_OUTPUT_PREFIX, offloadPagebackHint } from "./tool-result-store.js";
|
|
5
|
+
import { assertNoRetiredKeys } from "./retired-keys.js";
|
|
5
6
|
import { compactableToolNames } from "./tool-registry.js";
|
|
6
|
-
const CLEARED_MARKER = "[tool result cleared to save context]";
|
|
7
7
|
const CC_CLEARED_MARKER = "[Old tool result content cleared]";
|
|
8
|
-
const LEGACY_CLEARED_EXTENDED_PREFIX = "[tool result cleared to save context —";
|
|
9
8
|
const CC_CLEARED_EXTENDED_PREFIX = "[Old tool result content cleared —";
|
|
10
9
|
const refNote = (ref) => offloadPagebackHint(ref, "cleared");
|
|
11
10
|
const mediaNote = (blocks) => {
|
|
@@ -22,10 +21,9 @@ const mediaNote = (blocks) => {
|
|
|
22
21
|
const breakdown = [...byType].map(([t, c]) => `${c} ${t}`).join(", ");
|
|
23
22
|
return `${n} attachments (${breakdown}) no longer visible after this clear`;
|
|
24
23
|
};
|
|
25
|
-
const clearedMarker = (notes
|
|
24
|
+
const clearedMarker = (notes) => {
|
|
26
25
|
const present = notes.filter((n) => n !== undefined);
|
|
27
|
-
|
|
28
|
-
return present.length > 0 ? `${extendedPrefix} ${present.join("; ")}]` : plain;
|
|
26
|
+
return present.length > 0 ? `${CC_CLEARED_EXTENDED_PREFIX} ${present.join("; ")}]` : CC_CLEARED_MARKER;
|
|
29
27
|
};
|
|
30
28
|
export const EDIT_FRACTION = 0.7;
|
|
31
29
|
export const CONTEXT_OUTPUT_RESERVE_TOKENS = 20000;
|
|
@@ -33,7 +31,7 @@ export const COMPACTION_TRIGGER_BUFFER_TOKENS = 13000;
|
|
|
33
31
|
export function contextEditFrontier(window) {
|
|
34
32
|
return Math.max(window - (CONTEXT_OUTPUT_RESERVE_TOKENS + COMPACTION_TRIGGER_BUFFER_TOKENS), Math.floor(window * EDIT_FRACTION));
|
|
35
33
|
}
|
|
36
|
-
export const
|
|
34
|
+
export const MICRO_COMPACT_MACHINES = ["off", "cc"];
|
|
37
35
|
export const CC_DEFAULT_KEEP_RECENT_TOOL_RESULTS = 5;
|
|
38
36
|
export const MIN_CLEAR_SAVINGS_TOKENS = 20000;
|
|
39
37
|
const MEDIA_BLOCK_SAVINGS_TOKENS = 2000;
|
|
@@ -55,16 +53,9 @@ function soleMarkerText(m) {
|
|
|
55
53
|
const text = content[0].text;
|
|
56
54
|
return typeof text === "string" ? text : undefined;
|
|
57
55
|
}
|
|
58
|
-
function isCleared(m
|
|
56
|
+
function isCleared(m) {
|
|
59
57
|
const text = soleMarkerText(m);
|
|
60
|
-
if (text ===
|
|
61
|
-
return false;
|
|
62
|
-
if (text === CLEARED_MARKER || text.startsWith(LEGACY_CLEARED_EXTENDED_PREFIX))
|
|
63
|
-
return true;
|
|
64
|
-
return recognizeCcForms && (text === CC_CLEARED_MARKER || text.startsWith(CC_CLEARED_EXTENDED_PREFIX));
|
|
65
|
-
}
|
|
66
|
-
function isCcCleared(m) {
|
|
67
|
-
if (isCleared(m, true))
|
|
58
|
+
if (text !== undefined && (text === CC_CLEARED_MARKER || text.startsWith(CC_CLEARED_EXTENDED_PREFIX)))
|
|
68
59
|
return true;
|
|
69
60
|
const content = m.content;
|
|
70
61
|
if (!Array.isArray(content) || content.length === 0)
|
|
@@ -97,7 +88,7 @@ function structuralClearSavings(target) {
|
|
|
97
88
|
}
|
|
98
89
|
return saved;
|
|
99
90
|
}
|
|
100
|
-
function clearOneResult(target, offload
|
|
91
|
+
function clearOneResult(target, offload) {
|
|
101
92
|
const rawContent = Array.isArray(target.content) ? target.content : [];
|
|
102
93
|
let ref;
|
|
103
94
|
if (offload) {
|
|
@@ -115,7 +106,7 @@ function clearOneResult(target, offload, machine) {
|
|
|
115
106
|
}
|
|
116
107
|
}
|
|
117
108
|
const mediaBlocks = rawContent.filter((c) => c?.type !== "text");
|
|
118
|
-
const marker = clearedMarker([ref, mediaBlocks.length > 0 ? mediaNote(mediaBlocks) : undefined]
|
|
109
|
+
const marker = clearedMarker([ref, mediaBlocks.length > 0 ? mediaNote(mediaBlocks) : undefined]);
|
|
119
110
|
return { ...target, content: [{ type: "text", text: marker }] };
|
|
120
111
|
}
|
|
121
112
|
function reportCleared(onCleared, clears, tokensSavedEstimate) {
|
|
@@ -127,18 +118,34 @@ function reportCleared(onCleared, clears, tokensSavedEstimate) {
|
|
|
127
118
|
catch {
|
|
128
119
|
}
|
|
129
120
|
}
|
|
121
|
+
const CONTEXT_EDIT_OPTION_KEYS = new Set([
|
|
122
|
+
"budgetTokens",
|
|
123
|
+
"onCleared",
|
|
124
|
+
"keepRecentToolResults",
|
|
125
|
+
"offload",
|
|
126
|
+
"anchoredTotalTokens",
|
|
127
|
+
"charsPerToken",
|
|
128
|
+
"compactableTools",
|
|
129
|
+
"clearSource",
|
|
130
|
+
]);
|
|
131
|
+
const RETIRED_CONTEXT_EDIT_OPTION_KEYS = {
|
|
132
|
+
machine: 'the pass runs one machine (the CC form); the frontier on/off switch is `RunnerDeps.microCompact.machine: "off" | "cc"`',
|
|
133
|
+
recognizeCcMarkers: "the cleared-marker forms are always recognized",
|
|
134
|
+
};
|
|
130
135
|
export function clearStaleToolResults(messages, opts) {
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
136
|
+
assertNoRetiredKeys(opts, RETIRED_CONTEXT_EDIT_OPTION_KEYS, "clearStaleToolResults option");
|
|
137
|
+
for (const key of Object.keys(opts)) {
|
|
138
|
+
if (CONTEXT_EDIT_OPTION_KEYS.has(key) || Reflect.get(opts, key) === undefined)
|
|
139
|
+
continue;
|
|
140
|
+
const replacement = RETIRED_CONTEXT_EDIT_OPTION_KEYS[key];
|
|
141
|
+
throw new TypeError(`clearStaleToolResults: option "${key}" is not one this pass reads` +
|
|
142
|
+
(replacement !== undefined ? ` — it is retired: ${replacement}` : "") +
|
|
143
|
+
`. Legal keys: ${[...CONTEXT_EDIT_OPTION_KEYS].join(", ")}. Refused rather than ignored: a silently dropped option reads to the caller as in force.`);
|
|
135
144
|
}
|
|
136
|
-
const
|
|
145
|
+
const cpt = opts.charsPerToken ?? DEFAULT_CHARS_PER_TOKEN;
|
|
137
146
|
let total;
|
|
138
|
-
|
|
139
|
-
if (anchored) {
|
|
147
|
+
if (opts.anchoredTotalTokens !== undefined) {
|
|
140
148
|
total = opts.anchoredTotalTokens;
|
|
141
|
-
anchorIdx = estimateContextTokens(messages, cpt).lastUsageIndex ?? -1;
|
|
142
149
|
}
|
|
143
150
|
else {
|
|
144
151
|
total = 0;
|
|
@@ -149,41 +156,11 @@ export function clearStaleToolResults(messages, opts) {
|
|
|
149
156
|
if (total <= opts.budgetTokens) {
|
|
150
157
|
return messages;
|
|
151
158
|
}
|
|
152
|
-
if (machine === "cc") {
|
|
153
|
-
return clearStaleToolResultsCc(messages, opts);
|
|
154
|
-
}
|
|
155
|
-
const keep = resolveKeepRecentToolResults(opts.keepRecentToolResults, DEFAULT_KEEP_RECENT_TOOL_RESULTS);
|
|
156
|
-
const compactable = opts.compactableTools ?? COMPACTABLE_TOOLS;
|
|
157
|
-
const toolResultCandidates = messages.flatMap((m, i) => isToolResult(m) && !isCleared(m, opts.recognizeCcMarkers === true) && compactable.has(m.toolName) ? [{ idx: i, target: m }] : []);
|
|
158
|
-
const clearable = toolResultCandidates.slice(0, Math.max(0, toolResultCandidates.length - keep));
|
|
159
|
-
if (clearable.length === 0) {
|
|
160
|
-
return messages;
|
|
161
|
-
}
|
|
162
|
-
const out = messages.slice();
|
|
163
|
-
let current = total;
|
|
164
|
-
const clears = [];
|
|
165
|
-
let savedEstimate = 0;
|
|
166
|
-
for (const { idx, target } of clearable) {
|
|
167
|
-
if (current <= opts.budgetTokens) {
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
const before = estimateTokens(target, cpt);
|
|
171
|
-
const cleared = clearOneResult(target, opts.offload, "legacy");
|
|
172
|
-
out[idx] = cleared;
|
|
173
|
-
clears.push({ index: idx });
|
|
174
|
-
const delta = before - estimateTokens(cleared, cpt);
|
|
175
|
-
savedEstimate += delta;
|
|
176
|
-
current -= idx > anchorIdx ? delta : 0;
|
|
177
|
-
}
|
|
178
|
-
reportCleared(opts.onCleared, clears, savedEstimate);
|
|
179
|
-
return out;
|
|
180
|
-
}
|
|
181
|
-
function clearStaleToolResultsCc(messages, opts) {
|
|
182
159
|
const keep = resolveKeepRecentToolResults(opts.keepRecentToolResults, CC_DEFAULT_KEEP_RECENT_TOOL_RESULTS);
|
|
183
160
|
const compactable = opts.compactableTools ?? COMPACTABLE_TOOLS;
|
|
184
161
|
const candidates = messages.flatMap((m, i) => isToolResult(m) && compactable.has(m.toolName) ? [{ idx: i, target: m }] : []);
|
|
185
162
|
const beyondKeep = candidates.slice(0, Math.max(0, candidates.length - keep));
|
|
186
|
-
const clearable = beyondKeep.filter(({ target }) => !
|
|
163
|
+
const clearable = beyondKeep.filter(({ target }) => !isCleared(target));
|
|
187
164
|
if (clearable.length === 0) {
|
|
188
165
|
return messages;
|
|
189
166
|
}
|
|
@@ -196,7 +173,7 @@ function clearStaleToolResultsCc(messages, opts) {
|
|
|
196
173
|
for (const { idx, target } of clearable) {
|
|
197
174
|
const src = opts.clearSource?.[idx];
|
|
198
175
|
const source = src !== undefined && isToolResult(src) && src.toolCallId === target.toolCallId ? src : target;
|
|
199
|
-
out[idx] = clearOneResult(source, opts.offload
|
|
176
|
+
out[idx] = clearOneResult(source, opts.offload);
|
|
200
177
|
clears.push({ index: idx });
|
|
201
178
|
}
|
|
202
179
|
reportCleared(opts.onCleared, clears, tokensSaved);
|
|
@@ -248,7 +225,7 @@ export function planRejectionClears(messages, opts) {
|
|
|
248
225
|
const candidates = messages.flatMap((m) => (isToolResult(m) && compactable.has(m.toolName) ? [m] : []));
|
|
249
226
|
const beyondKeep = candidates.slice(0, Math.max(0, candidates.length - keep));
|
|
250
227
|
const clearable = beyondKeep.flatMap((m) => {
|
|
251
|
-
if (
|
|
228
|
+
if (isCleared(m))
|
|
252
229
|
return [];
|
|
253
230
|
const at = opts.keyOf(m);
|
|
254
231
|
return at === undefined ? [] : [{ m, at }];
|
|
@@ -260,7 +237,7 @@ export function planRejectionClears(messages, opts) {
|
|
|
260
237
|
return { declined: "below_min_savings" };
|
|
261
238
|
const cleared = clearable.map(({ m, at }) => {
|
|
262
239
|
const source = at.occurrence !== undefined && isToolResult(at.occurrence) ? at.occurrence : m;
|
|
263
|
-
const clearedMsg = clearOneResult(source, opts.offload
|
|
240
|
+
const clearedMsg = clearOneResult(source, opts.offload);
|
|
264
241
|
return { key: at.key, groupCount: at.groupCount, marker: clearedMsg.content[0]?.text ?? CC_CLEARED_MARKER, fp: at.fp };
|
|
265
242
|
});
|
|
266
243
|
return { cleared, tokensSavedEstimate };
|
|
@@ -1,7 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE resolution of a call's EFFECTIVE PATH TARGET — the absolute path a path-targeting tool will
|
|
3
|
+
* actually open — derived from the tool's OWN declaration ({@link ToolCallPathTarget}'s `base` / `absent` /
|
|
4
|
+
* `patternParam`) rather than guessed, separately, by every fence.
|
|
5
|
+
*
|
|
6
|
+
* ## Why this module exists
|
|
7
|
+
*
|
|
8
|
+
* The permission-rule lanes resolved a call's path with one hard-coded rule (`liveCwd ?? root` for a
|
|
9
|
+
* relative spelling, "no verdict" for an absent slot) while each tool resolved its own target its own way.
|
|
10
|
+
* The three shapes that rule got wrong were all silent — a rule that names the very directory the tool
|
|
11
|
+
* reads simply never fires:
|
|
12
|
+
*
|
|
13
|
+
* · Grep/Glob with NO `path` search the whole ROOT; the lanes read the absent slot as "no path", so a
|
|
14
|
+
* deny on the root reached nothing;
|
|
15
|
+
* · Glob with an ABSOLUTE `pattern` and no `path` searches the base the pattern carries
|
|
16
|
+
* (`/proj/secret/**` ⇒ `/proj/secret`); the lanes never looked at `pattern`;
|
|
17
|
+
* · Grep/Glob resolve a RELATIVE `path` against the ROOT (their describes say so, and both tools hand
|
|
18
|
+
* `rootCanonical` to the resolver), while the lanes resolved it against the live `cd` — so after a
|
|
19
|
+
* `cd` into a sibling directory the lane judged a path the tool would never open.
|
|
20
|
+
*
|
|
21
|
+
* The fix is not a fourth arm in the lanes: it is that "which base does this tool resolve against, and what
|
|
22
|
+
* does it target when the slot is empty" is a FACT OF THE TOOL, so the tool declares it once and both
|
|
23
|
+
* sides read the declaration. {@link pathTargetBaseOf} is the shared SELECTION (which base), and each side
|
|
24
|
+
* then applies its own identity to the selected base — lexical-normal here (the rule family's one path
|
|
25
|
+
* identity, zero IO), canonical (symlink-resolving, IO) inside the tool.
|
|
26
|
+
*
|
|
27
|
+
* ## What it does NOT decide
|
|
28
|
+
*
|
|
29
|
+
* Nothing here judges anything: it answers "which absolute path is this call about". Containment, the
|
|
30
|
+
* sensitive-path deny face and the rule precedence walk are unchanged and still run where they ran.
|
|
31
|
+
* `undefined` means "this call has no path this reading can locate" — the documented fail-toward-asking
|
|
32
|
+
* posture of both lanes (no refusal on the tightening side, no grant on the allow side).
|
|
33
|
+
*
|
|
34
|
+
* RepoMap shares the search tools' root-relative `path` semantics but declares NO path target at all (no
|
|
35
|
+
* rule family reaches it, and the rule grammar is derived from the catalog's path targets), so it has no
|
|
36
|
+
* declaration to read; the day it declares one, it reads the same selector as everything else.
|
|
37
|
+
*/
|
|
38
|
+
import { type PathFamily, type PathIdentity, type Win32NamespaceRefusal } from "../tools/fs/safety.js";
|
|
1
39
|
import type { ToolCallPathTarget } from "./tool-face.js";
|
|
2
40
|
import { type ToolCallLike } from "./tool-registry.js";
|
|
3
41
|
/** The bases of ONE CALL a target resolves against: the task root, and the live tracked working directory
|
|
4
|
-
* (absent when no tracker moved or the caller keeps none). Facts of the call, never of the tool.
|
|
42
|
+
* (absent when no tracker moved or the caller keeps none). Facts of the call, never of the tool. The
|
|
43
|
+
* tree's FAMILY is read from them once per call ({@link pathFamilyOf}). */
|
|
5
44
|
export interface ToolPathBases {
|
|
6
45
|
readonly root?: string | undefined;
|
|
7
46
|
readonly cwd?: string | undefined;
|
|
@@ -17,22 +56,43 @@ export declare function pathTargetBaseOf(pt: Pick<ToolCallPathTarget, "base"> |
|
|
|
17
56
|
* design/116 批④ #8 (CC 2.1.198 RWp, bundle :332987) — decompose an ABSOLUTE glob pattern into a base
|
|
18
57
|
* directory + relative pattern: cut at the last separator before the first glob metachar (`* ? [ {`); a
|
|
19
58
|
* pattern with no metachar splits dirname/basename. `undefined` for a relative pattern (the caller keeps it
|
|
20
|
-
* as-is) or when no usable base can be derived
|
|
59
|
+
* as-is) or when no usable base can be derived; a namespace refusal ({@link win32NamespaceScreen}, run
|
|
60
|
+
* BEFORE the metachar scan so `\\?\` is a prefix, never a `?` wildcard) is answered by its word.
|
|
61
|
+
* Absoluteness and the separator set are the FAMILY's: on a POSIX tree `C:\\x\\*` is a relative pattern.
|
|
21
62
|
*
|
|
22
63
|
* Lives HERE rather than beside the search engine because two readers need the identical cut: Glob's own
|
|
23
64
|
* scope resolution and the path fences that must know which directory an absolute pattern really searches.
|
|
24
65
|
*/
|
|
25
|
-
export declare function splitAbsoluteGlobPattern(pattern: string): {
|
|
66
|
+
export declare function splitAbsoluteGlobPattern(pattern: string, family: PathFamily | undefined): {
|
|
26
67
|
baseDir: string;
|
|
27
68
|
relativePattern: string;
|
|
69
|
+
} | {
|
|
70
|
+
refused: Win32NamespaceRefusal;
|
|
71
|
+
} | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* The reading of a call's path slot under ONE declaration: `path` = the absolute target's identity; `unreadable`
|
|
74
|
+
* = the slot spells a target but the base it needs is not a usable base of this tree (fed to TIGHTENING rows
|
|
75
|
+
* only, as the ask a person clears — never to the allow arm); `refused` = a namespace refusal of the
|
|
76
|
+
* spelling itself (a rule speaks about no such spelling: no verdict, no ask — the fs throat delivers the
|
|
77
|
+
* refusal with the same word); `undefined` = no target in this reading.
|
|
78
|
+
*/
|
|
79
|
+
export type PathTargetReading = {
|
|
80
|
+
readonly kind: "path";
|
|
81
|
+
readonly identity: PathIdentity;
|
|
82
|
+
} | {
|
|
83
|
+
readonly kind: "unreadable";
|
|
84
|
+
readonly reason: string;
|
|
85
|
+
} | {
|
|
86
|
+
readonly kind: "refused";
|
|
87
|
+
readonly reason: Win32NamespaceRefusal;
|
|
28
88
|
} | undefined;
|
|
29
89
|
/**
|
|
30
|
-
* The absolute
|
|
31
|
-
*
|
|
32
|
-
*
|
|
90
|
+
* The absolute target this call is about under ONE reading of its path slot, as a {@link PathTargetReading}.
|
|
91
|
+
* `pt` is the reading (design/388 B6/B17: the PROTECTIVE target for a tightening fence, the DECLARED target
|
|
92
|
+
* for a grant) — this function never picks between the two.
|
|
33
93
|
*
|
|
34
94
|
* The order is the tool's own:
|
|
35
|
-
* ① a spelling in the slot wins —
|
|
95
|
+
* ① a spelling in the slot wins — fully qualified as spelled, otherwise against {@link pathTargetBaseOf}'s base;
|
|
36
96
|
* ② otherwise, when the declaration says an absent slot means `none`, the call is about no path;
|
|
37
97
|
* ③ otherwise a `patternParam`'s ABSOLUTE pattern supplies its own base directory;
|
|
38
98
|
* ④ otherwise the base itself (the tool searches all of it).
|
|
@@ -40,4 +100,4 @@ export declare function splitAbsoluteGlobPattern(pattern: string): {
|
|
|
40
100
|
* A base the deployment did not supply yields `undefined`: a relative spelling with nothing to resolve
|
|
41
101
|
* against names no file, and claiming it names `/<spelling>` was a fabrication no fence should judge.
|
|
42
102
|
*/
|
|
43
|
-
export declare function effectivePathTargetOf(call: ToolCallLike, pt: ToolCallPathTarget | undefined, bases: ToolPathBases):
|
|
103
|
+
export declare function effectivePathTargetOf(call: ToolCallLike, pt: ToolCallPathTarget | undefined, bases: ToolPathBases): PathTargetReading;
|