@sema-agent/core 7.16.0 → 7.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +209 -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-engine/engine.js +2 -1
- package/dist/core/memory-engine/layout.d.ts +18 -6
- package/dist/core/memory-engine/layout.js +40 -21
- 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/physical-path.d.ts +37 -0
- package/dist/core/physical-path.js +30 -0
- 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 +11 -10
- 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 +52 -0
- package/dist/core/runner/prepare-artifact.js +63 -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-question-face.js +2 -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 +12 -11
- 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 +5 -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 +11 -12
- 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/skills-directory.js +4 -3
- package/dist/core/spec-contract.js +5 -4
- 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 +32 -28
- package/dist/core/task-registry-shared.js +4 -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 +6 -1
- 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 +66 -22
- package/dist/core/tool-policy.js +31 -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 +8 -5
- 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 +20 -14
- package/dist/index.js +16 -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-script-store.js +9 -25
- 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 +237 -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/cc/task-list-store.js +2 -10
- package/dist/stores/file/checkpoint-store.js +1 -5
- package/dist/stores/file/file-history-store.js +2 -2
- package/dist/stores/file/fs-atomic.d.ts +15 -18
- package/dist/stores/file/fs-atomic.js +4 -14
- package/dist/stores/file/mailbox-store.d.ts +7 -11
- package/dist/stores/file/mailbox-store.js +4 -11
- 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 +297 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +90 -8
- package/dist/tools/fs/bash-readonly-classifier.js +294 -70
- package/dist/tools/fs/fs-bash.d.ts +14 -10
- package/dist/tools/fs/fs-bash.js +50 -33
- 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 +119 -39
|
@@ -138,18 +138,24 @@ export interface EngineNotice {
|
|
|
138
138
|
* `onNotice` sink per value (console arm once per process) instead of lying in wait; where the
|
|
139
139
|
* value IS in force the prepare refuses with the same code as `TaskResult.errorCode` (one
|
|
140
140
|
* fact, one code, two loudness dialects); `detail: { raw }`.
|
|
141
|
-
* - `"config.
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
141
|
+
* - `"config.artifact_host_invalid"` — `RunnerDeps.artifactHost` is wired but its `capabilities()`
|
|
142
|
+
* declaration fails one of the seat's invariants (see `ArtifactHostCapabilities`), or its `onMount`
|
|
143
|
+
* threw: the `Artifact` tool is not mounted for the task and this is the loud half (an absent seat
|
|
144
|
+
* is silent; a broken one must not look the same). Once per prepared leg;
|
|
145
|
+
* `detail: { problem, sessionId, runId }`.
|
|
146
|
+
* - `"config.durable_gate_unavailable"` — a durable-approval MANDATE is in force on a leg with NO
|
|
147
|
+
* checkpoint store: the per-principal `forceDurableGate` entitlement, or the task's own
|
|
148
|
+
* `durableApproval` opt-in with no live approver seat (`durableMandateSourceOf`, the predicate the
|
|
149
|
+
* leg's children inherit). A mandate with no park facility cannot be honored: no ask of the leg can suspend, so each one resolves on the LIVE
|
|
145
150
|
* chain — a live approver / question face answers in-stream, and with none the ask is denied
|
|
146
151
|
* fail-closed — and NO durable approval record is written. The ask routing itself is not
|
|
147
152
|
* changed by the notice (a store-less leg never could park); the notice is the loud half of a
|
|
148
153
|
* posture that used to degrade silently, disclosed before the first ask of the leg. Audience
|
|
149
154
|
* `"user"` (the person whose asks will not be recorded is entitled to that; the operator hears
|
|
150
155
|
* it through the sink as always). Once per prepared task leg — a resume or a delegated child
|
|
151
|
-
* is its own leg and says so again; `detail: { sessionId, runId, principal?, cause,
|
|
152
|
-
* liveQuestionFace }` — `
|
|
156
|
+
* is its own leg and says so again; `detail: { sessionId, runId, principal?, cause, source,
|
|
157
|
+
* liveApprover, liveQuestionFace }` — `source` is `"force_durable_gate"` or `"durable_approval"`
|
|
158
|
+
* (which mandate is unhonored; the recovery differs); `cause` is `"no_deployment_store"` (the deployment wired none) or
|
|
153
159
|
* `"task_store_disabled"` (`TaskSpec.checkpointStore: "disabled"`, the per-run off switch, on a leg
|
|
154
160
|
* whose deployment may well have a store: the fix is the task's); `liveApprover` /
|
|
155
161
|
* `liveQuestionFace`: whether a live seat will EFFECTIVELY answer the leg's permission asks /
|
|
@@ -195,10 +201,12 @@ export interface EngineNotice {
|
|
|
195
201
|
* `detail: { cause: "user_halt", sessionId, runId, taskId? }` — no `inputId` and no
|
|
196
202
|
* `actorId`, because no text entered the model and the verb carries no caller identity. A
|
|
197
203
|
* consumer keying `detail.inputId` off this row must treat it as ABSENT on this lane.
|
|
198
|
-
* Fence tension
|
|
199
|
-
* raced
|
|
200
|
-
* still collect to `suspended` (park wins, row redeemable,
|
|
201
|
-
*
|
|
204
|
+
* Fence tension: this notice asserts THE CUT only — when the cut
|
|
205
|
+
* raced an approval park whose commit was already in flight, the gate's durable leg may
|
|
206
|
+
* still collect to `suspended` (park wins, row redeemable, the park's abort owns the
|
|
207
|
+
* ending, no `haltedByUser`, plus a `task.halt_unconsumed`); the notice stands beside that
|
|
208
|
+
* terminal without contradiction, because a seat really was cut — `haltedByUser` answers
|
|
209
|
+
* the other question (was the halt CONSUMED), see that seat.
|
|
202
210
|
* In both lanes: one notice per REAL cut (a `now` that found nothing in flight or whose frame
|
|
203
211
|
* already rode the imminent boundary, and a halt with nothing in flight, announce nothing — no
|
|
204
212
|
* false interrupt claims); `runId` (#499) is the INVOCATION that was cut (the other two ids are
|
|
@@ -209,17 +217,21 @@ export interface EngineNotice {
|
|
|
209
217
|
* implemented, so the unhonored-knob disclosure it carried has no referent; consumers must
|
|
210
218
|
* judge ladder support by VERSION, never by that code's absence.
|
|
211
219
|
*
|
|
212
|
-
* - `"task.halt_unconsumed"`
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
220
|
+
* - `"task.halt_unconsumed"` — the user's stop verb ({@link TaskStream.halt}) was ANSWERED (not
|
|
221
|
+
* refused) but the run's ending was not its doing: the run's own ending stands and the result will
|
|
222
|
+
* NOT carry `haltedByUser` for it (the seat signs consumption, never acceptance). TWO mint points,
|
|
223
|
+
* one fact, one spelling, disjoint: the VERB arm — the halt arrived while the run's ending was already
|
|
224
|
+
* owned by its own abort (nothing cut, nothing stopped; once per such verb call, and only while no halt
|
|
225
|
+
* has been ACCEPTED on the run — after one was, the settle arm alone speaks for the run);
|
|
226
|
+
* the SETTLE arm — the halt was ACCEPTED (it may even have cut a live seat: `{turnCut:true}` +
|
|
227
|
+
* `task.turn_interrupted`) but the run ended for its own reason before the machinery consumed it —
|
|
228
|
+
* no interrupt settlement took the halt's cut as its cause and no boundary consult stopped the loop
|
|
229
|
+
* for it (a brain minting its own `aborted` final beside a late halt, a natural completion the halt
|
|
230
|
+
* arrived too late to prevent, an approval park whose commit won the race and whose abort owned the
|
|
231
|
+
* ending); once per such run, at the result's settlement. Either way the notice is the halt's trace
|
|
232
|
+
* beside the verb receipt. `detail: { sessionId, runId, taskId? }` — `runId` is the run whose ending
|
|
233
|
+
* the halt failed to claim. Audience `"user"`. A REFUSED halt (typed `steering.not_running` throw)
|
|
234
|
+
* mints nothing: the refusal itself is the loud answer.
|
|
223
235
|
*
|
|
224
236
|
* - `"task.late_approval"` (design/384 slice 1) — a synchronous ask approval was NOT CONSUMED:
|
|
225
237
|
* a run or turn interrupt released the wait (the `resolveAsk` race arm detached the approver's
|
|
@@ -514,17 +526,19 @@ export interface EngineNotice {
|
|
|
514
526
|
* access scope of the read that found the gap — from the continuation read faces (SendMessage preflight /
|
|
515
527
|
* AgentTranscript's durable leg); the per-call honest refusals are unchanged, and the declared
|
|
516
528
|
* tier is NOT auto-downgraded (declaration-制 — observation reports, it never re-adjudicates).
|
|
517
|
-
* - `"delegation.ask_unresolvable"`
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
* answered `unavailable`
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
529
|
+
* - `"delegation.ask_unresolvable"` — an `ask` reached its FINAL deny with nobody having judged it:
|
|
530
|
+
* the call's approval could not be RESOLVED. `detail.cause` (closed set `ASK_UNRESOLVABLE_CAUSES`):
|
|
531
|
+
* `approver_unavailable` — the approver consulted (the run's own seat, or an ancestor's frozen seat
|
|
532
|
+
* on a delegated child) answered `unavailable` and no park took the call (`tool_end.gate.settlement
|
|
533
|
+
* .kind:"approver_unavailable"`); `no_approver_wired` — no approver / question face is wired, or the
|
|
534
|
+
* seat is the deny posture (the headless auto-deny, `settlement.kind:"no_approver"`);
|
|
535
|
+
* `mandate_unreconstructible` — an inherited durable mandate binds the ask and this leg has no park
|
|
536
|
+
* facility (an in-fold deny, no settlement on the frame). The deny itself is unchanged (the
|
|
537
|
+
* fail-closed sentence as the tool result); this is its person-facing half, minted at the gate's ONE
|
|
538
|
+
* deny exit, once per tool call, by `ask-unresolvable-notice.ts`. `detail: { sessionId, toolName,
|
|
539
|
+
* toolCallId, cause, parkLaneExisted }` — `parkLaneExisted` says whether a park lane was armed and
|
|
540
|
+
* declined/failed (`true`) or none existed (`false`, the usual shape). Audience `"user"`: the refused
|
|
541
|
+
* work is the session user's own; the operator hears it through the sink like every code.
|
|
528
542
|
*
|
|
529
543
|
* - `"memory.consolidation_recommended"` (design/339 §2.2/§6.2) — the engine-minted per-scope
|
|
530
544
|
* session count crossed the consolidation thresholds (time gate open ∧ enough distinct
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The whole-number env grammar, with one deliberate narrowing against the reference harness: no
|
|
3
|
+
* `parseInt` prefix leg, so `"30s"` is a REJECTION rather than `30` — "a typo silently becomes a budget" is
|
|
4
|
+
* the class of defect this chokepoint exists to close, and the prefix leg is survivable upstream only
|
|
5
|
+
* because its clamp floor catches the result. What IS accepted is what an operator plausibly means: plain
|
|
6
|
+
* digits (an explicit sign included), scientific notation, and digit grouping — the last one because this
|
|
7
|
+
* repository writes its own constants that way (`100_000_000`), and copying a source literal into the
|
|
8
|
+
* environment must not silently become `100`. Hex, octal and every other spelling are rejected.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseWholeNumber(raw: string): number | undefined;
|
|
11
|
+
/** What a knob is a budget OF — the unit word the invalid-value announcement names. */
|
|
12
|
+
export type EnvBudgetUnit = "characters" | "tokens" | "milliseconds";
|
|
13
|
+
/** One knob's contract: its name, its unit, the value in force when it is absent or invalid (`undefined`
|
|
14
|
+
* = "a downstream built-in default stands", the SDK-timeout shape), and the range it is held to. */
|
|
15
|
+
export interface EnvBudgetSpec<D extends number | undefined> {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly unit: EnvBudgetUnit;
|
|
18
|
+
readonly default: D;
|
|
19
|
+
/** Inclusive floor; a smaller value is raised to it, loudly. */
|
|
20
|
+
readonly min?: number;
|
|
21
|
+
/** Inclusive ceiling; a larger value is capped to it, loudly. */
|
|
22
|
+
readonly max?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Resolve one knob from its raw environment string (see the module note for the four states).
|
|
26
|
+
*
|
|
27
|
+
* @contract env_budget.single_resolver — every process-level numeric env knob resolves through this
|
|
28
|
+
* function: one grammar, one range rule, one loud outlet. A knob resolved elsewhere is a fork of this
|
|
29
|
+
* contract, not a knob.
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveEnvBudget<D extends number | undefined>(rawEnv: string | undefined, spec: EnvBudgetSpec<D>): number | D;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { announceEnvKnob } from "./env-knob-announce.js";
|
|
2
|
+
const ENV_SCIENTIFIC_RE = /^[+-]?(\d+(\.\d*)?|\.\d+)[eE][+-]?\d+$/;
|
|
3
|
+
const ENV_GROUPED_RE = /^[+-]?\d{1,3}([_,\u00A0\u202F ])\d{3}(?:\1\d{3})*$/;
|
|
4
|
+
const ENV_GROUP_SEPARATORS_RE = /[_,\u00A0\u202F ]/g;
|
|
5
|
+
const ENV_NUMERIC_MAX_LEN = 32;
|
|
6
|
+
export function parseWholeNumber(raw) {
|
|
7
|
+
if (/^[+-]?\d+$/.test(raw)) {
|
|
8
|
+
const n = Number(raw);
|
|
9
|
+
return Number.isSafeInteger(n) ? n : undefined;
|
|
10
|
+
}
|
|
11
|
+
if (raw.length > ENV_NUMERIC_MAX_LEN)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (ENV_SCIENTIFIC_RE.test(raw)) {
|
|
14
|
+
const n = Number(raw);
|
|
15
|
+
return Number.isSafeInteger(n) ? n : undefined;
|
|
16
|
+
}
|
|
17
|
+
if (ENV_GROUPED_RE.test(raw))
|
|
18
|
+
return parseInt(raw.replace(ENV_GROUP_SEPARATORS_RE, ""), 10);
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
export function resolveEnvBudget(rawEnv, spec) {
|
|
22
|
+
if (rawEnv === undefined || rawEnv.trim() === "")
|
|
23
|
+
return spec.default;
|
|
24
|
+
const raw = rawEnv.trim();
|
|
25
|
+
const n = parseWholeNumber(raw);
|
|
26
|
+
if (n === undefined || n <= 0) {
|
|
27
|
+
announceEnvKnob(`${spec.name}=${rawEnv} was ignored — it is not a positive whole number of ${spec.unit}. Using ${spec.default === undefined ? "the built-in default" : spec.default} instead.`);
|
|
28
|
+
return spec.default;
|
|
29
|
+
}
|
|
30
|
+
if (spec.max !== undefined && n > spec.max) {
|
|
31
|
+
announceEnvKnob(`${spec.name}=${rawEnv} Capped from ${n} to ${spec.max}.`);
|
|
32
|
+
return spec.max;
|
|
33
|
+
}
|
|
34
|
+
if (spec.min !== undefined && n < spec.min) {
|
|
35
|
+
announceEnvKnob(`${spec.name}=${rawEnv} Raised from ${n} to ${spec.min}.`);
|
|
36
|
+
return spec.min;
|
|
37
|
+
}
|
|
38
|
+
return n;
|
|
39
|
+
}
|
|
@@ -35,18 +35,18 @@ export declare class RollingTailBuffer {
|
|
|
35
35
|
text: string;
|
|
36
36
|
droppedBytes: number;
|
|
37
37
|
};
|
|
38
|
-
/**
|
|
38
|
+
/** The retained window's RAW bytes — cursor slicing must happen in the byte domain
|
|
39
39
|
* (re-encoding a lossily-decoded string inflates every invalid byte to a 3-byte U+FFFD and lands
|
|
40
40
|
* byte cursors on the wrong offsets). */
|
|
41
41
|
bytes(): Buffer;
|
|
42
|
-
/**
|
|
42
|
+
/** Head-eviction count ALONE — the retention window is [headDroppedBytes, pushed-total)
|
|
43
43
|
* in the accepted-byte domain; source skips must not shift this origin. */
|
|
44
44
|
headDroppedBytes(): number;
|
|
45
|
-
/**
|
|
45
|
+
/** Source-skip count alone (see {@link recordSkippedBytes}) — poll disclosure reports
|
|
46
46
|
* the per-poll increment of this figure instead of silently absorbing it into the window origin. */
|
|
47
47
|
sourceSkippedBytes(): number;
|
|
48
48
|
}
|
|
49
|
-
/**
|
|
49
|
+
/** Per-stream cursor state for incremental (poll-style) consumption of a
|
|
50
50
|
* {@link RollingTailBuffer}. All counters live in the ACCEPTED-byte domain (bytes actually pushed);
|
|
51
51
|
* source skips are tracked separately for disclosure. Module-level export (not on the package
|
|
52
52
|
* surface) so the slice arithmetic is unit-pinnable without an 8MB integration harness. */
|
|
@@ -65,7 +65,7 @@ export interface StreamCursorState {
|
|
|
65
65
|
/** Fresh cursor state over a new tail. */
|
|
66
66
|
export declare function newStreamCursorState(tail?: RollingTailBuffer): StreamCursorState;
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
68
|
+
* Slice the undelivered increment out of a rolling tail — in the BYTE domain, decoded
|
|
69
69
|
* incrementally. Replaces the former decode→re-encode→byte-offset-subarray shape, which mixed two
|
|
70
70
|
* domains: byte cursors landed inside a re-encoded string whose length differs from the raw stream as
|
|
71
71
|
* soon as any invalid-UTF-8 byte sits in the window (each inflates to a 3-byte U+FFFD), making every
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { isAbsolutePathForm, isWinFormPath } from "../tools/fs/safety.js";
|
|
2
|
+
import { isAbsoluteForFamily, isAbsolutePathForm, isWinFormPath, pathFamilyOf } from "../tools/fs/safety.js";
|
|
3
3
|
import { fileHistoryBoundariesToKeep, resolveFileHistoryRetention } from "./file-history-retention.js";
|
|
4
4
|
const normSlash = (p) => p.replace(/\\/g, "/");
|
|
5
5
|
const stripTrailSep = (p) => (isWinFormPath(p) ? p.replace(/[\\/]+$/, "") : p.replace(/\/+$/, ""));
|
|
@@ -21,9 +21,6 @@ function joinUnder(root, rel) {
|
|
|
21
21
|
function isUnsafeRelPathLike(relN) {
|
|
22
22
|
return isAbsolutePathForm(relN) || /^[A-Za-z]:/.test(relN) || relN.split("/").includes("..");
|
|
23
23
|
}
|
|
24
|
-
function isAbsoluteForFamily(root, p) {
|
|
25
|
-
return isWinFormPath(root) ? isAbsolutePathForm(p) : p.startsWith("/");
|
|
26
|
-
}
|
|
27
24
|
function parentDirOf(absPath) {
|
|
28
25
|
const win = isWinFormPath(absPath);
|
|
29
26
|
const norm = win ? normSlash(absPath) : absPath;
|
|
@@ -47,7 +44,7 @@ export function trackKeyOf(root, absPath) {
|
|
|
47
44
|
return absPath;
|
|
48
45
|
const win = isWinFormPath(root);
|
|
49
46
|
const relN = win ? normSlash(rel) : rel;
|
|
50
|
-
if (win ? isUnsafeRelPathLike(relN) : isAbsoluteForFamily(
|
|
47
|
+
if (win ? isUnsafeRelPathLike(relN) : isAbsoluteForFamily("posix", relN) || relN.split("/").includes(".."))
|
|
51
48
|
return absPath;
|
|
52
49
|
return rel;
|
|
53
50
|
}
|
|
@@ -62,7 +59,7 @@ export function resolveTrackKey(root, key, minted) {
|
|
|
62
59
|
}
|
|
63
60
|
}
|
|
64
61
|
const keyN = win ? normSlash(key) : key;
|
|
65
|
-
if (isAbsoluteForFamily(root, keyN) || (win && /^[A-Za-z]:/.test(keyN)))
|
|
62
|
+
if (isAbsoluteForFamily(pathFamilyOf({ root }), keyN) || (win && /^[A-Za-z]:/.test(keyN)))
|
|
66
63
|
return { ok: true, abs: key };
|
|
67
64
|
if (win ? isUnsafeRelPathLike(keyN) : keyN.split("/").includes(".."))
|
|
68
65
|
return { ok: false, reason: `unsafe recorded key escapes root: ${key}` };
|
|
@@ -11,7 +11,7 @@ export interface FsWriteGatePolicyOptions {
|
|
|
11
11
|
/** The task's real execution env (the one the hands run against) — canonicalization must see the
|
|
12
12
|
* actual filesystem (a remote/E2B task resolves in its container). */
|
|
13
13
|
env: ExecutionEnv;
|
|
14
|
-
/** The FALLBACK base a relative path resolves against (omit = paths resolve as given).
|
|
14
|
+
/** The FALLBACK base a relative path resolves against (omit = paths resolve as given). The
|
|
15
15
|
* call's TARGET prefers the live tracked cwd when the engine stamps one ({@link ToolCallRequest.cwd});
|
|
16
16
|
* the `acceptDirs`/`exemptDirs` entries below always resolve against THIS value. */
|
|
17
17
|
rootPath?: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canonicalizeTarget, writeTargetPath } from "../tools/fs/safety.js";
|
|
1
|
+
import { canonicalizeTarget, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
|
|
2
2
|
import { isWithin } from "./runner/session-rule-policy.js";
|
|
3
3
|
import { declaredPathTargetOf, isProtectedWrite } from "./tool-registry.js";
|
|
4
4
|
import { pathTargetBaseOf } from "./effective-path-target.js";
|
|
@@ -27,7 +27,7 @@ export function createFsWriteGatePolicy(opts) {
|
|
|
27
27
|
if (path === undefined) {
|
|
28
28
|
return ask(`write tool "${req.toolName}" requires approval: the call has no resolvable path target to confine${declared === undefined ? " (the tool declares no path target)" : ""}${advisory}`);
|
|
29
29
|
}
|
|
30
|
-
const canon = await canonicalizeTarget(env, path, signal, pathTargetBaseOf(declared, { root: rootPath, cwd: req.cwd }));
|
|
30
|
+
const canon = await canonicalizeTarget(env, path, signal, pathFamilyOf({ root: rootPath, cwd: req.cwd ?? env.cwd }), pathTargetBaseOf(declared, { root: rootPath, cwd: req.cwd }));
|
|
31
31
|
if (!canon.ok) {
|
|
32
32
|
return ask(`write to "${path}" requires approval: its real target could not be resolved (${canon.message})${advisory}`);
|
|
33
33
|
}
|
|
@@ -35,7 +35,7 @@ export function createFsWriteGatePolicy(opts) {
|
|
|
35
35
|
if (!dirs)
|
|
36
36
|
continue;
|
|
37
37
|
for (const raw of dirs) {
|
|
38
|
-
const dir = await canonicalizeTarget(env, raw, signal, rootPath);
|
|
38
|
+
const dir = await canonicalizeTarget(env, raw, signal, pathFamilyOf({ root: rootPath ?? env.cwd }), rootPath);
|
|
39
39
|
if (dir.ok && isWithin(dir.key, canon.key))
|
|
40
40
|
return { action: "allow" };
|
|
41
41
|
}
|
package/dist/core/gate-lanes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { attestClassifierDenyCause, classifierDenyCauseOf, decisionText, describeThrown } from "./tool-policy.js";
|
|
2
|
-
import { askOriginOf, classifierMayAnswer, persistedRuleMandateOf, probeBoundaryMandate } from "./ask-origin.js";
|
|
2
|
+
import { askOriginOf, classifierMayAnswer, persistedRuleMandateOf, probeBoundaryMandate, safetyAxisOf } from "./ask-origin.js";
|
|
3
3
|
import { CLASSIFIER_PARSE_FAILURE_DENY_MESSAGE, classifierUnavailableDenyMessage, denialLimitFallbackMessage, unarmedWindow } from "./auto-mode.js";
|
|
4
4
|
import { inlineUntrusted } from "./untrusted-text.js";
|
|
5
5
|
import { isRuleBehavior } from "./permission-rule-model.js";
|
|
@@ -439,9 +439,14 @@ export async function runGateLanes(pass) {
|
|
|
439
439
|
};
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
442
|
+
const safety = safetyAxisOf({
|
|
443
|
+
egress: input.egress,
|
|
444
|
+
shellGated: input.shellGated,
|
|
445
|
+
irreversibility: input.irreversibility,
|
|
446
|
+
probeMandated: pass.decision.action === "ask" && pass.decision.probeMandated === true,
|
|
447
|
+
probeUnanswered: pass.probeUnanswered === true,
|
|
448
|
+
tightened: pass.tightenedBy,
|
|
449
|
+
});
|
|
445
450
|
const realApprovalOf = (d) => {
|
|
446
451
|
if (d.action !== "ask" || d.requiresRealApproval !== true)
|
|
447
452
|
return undefined;
|
|
@@ -521,16 +526,6 @@ export async function runGateLanes(pass) {
|
|
|
521
526
|
return { suspend: suspended, preToolContext: pass.preToolContext };
|
|
522
527
|
}
|
|
523
528
|
}
|
|
524
|
-
const noteUnresolvable = (r) => {
|
|
525
|
-
if (r.action !== "deny" || r.approverUnavailable !== true)
|
|
526
|
-
return;
|
|
527
|
-
try {
|
|
528
|
-
input.onAskUnresolvable?.({ toolName, toolCallId, settlementKind: "approver_unavailable", parkLaneExisted: suspendAsk !== undefined });
|
|
529
|
-
}
|
|
530
|
-
catch {
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
noteUnresolvable(resolved);
|
|
534
529
|
if (askBeforeResolve.action === "ask" && askBeforeResolve.denialLimitFallback !== undefined) {
|
|
535
530
|
if (pass.decision.action === "allow") {
|
|
536
531
|
input.autoMode?.denialTracking?.recordAllow();
|
|
@@ -666,7 +661,6 @@ export async function runGateLanes(pass) {
|
|
|
666
661
|
const editAskReq = { toolName, args: editArgs, toolCallId, ...(pass.req.face !== undefined ? { face: pass.req.face } : {}) };
|
|
667
662
|
const rr = await (callSignal !== undefined ? resolveAsk(editAskDecision, editAskReq, callSignal) : resolveAsk(editAskDecision, editAskReq));
|
|
668
663
|
ledger.fromResolved(rr, editAskDecision, toolName);
|
|
669
|
-
noteUnresolvable(rr);
|
|
670
664
|
if (rr.action !== "allow") {
|
|
671
665
|
editDenied = rr;
|
|
672
666
|
pass.deniedBy = "ask_resolution";
|
|
@@ -104,7 +104,7 @@ export type NoticeAudience = "user" | "operator";
|
|
|
104
104
|
* src/ for notice mint shapes and names any code that is minted but unregistered, or registered but
|
|
105
105
|
* no longer minted.
|
|
106
106
|
*/
|
|
107
|
-
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "route.fallback_to_primary", "route.base_url_changed_key_unchanged", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "tool_result.offload_put_failed"];
|
|
107
|
+
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.tool_model_gate_removed", "config.tool_model_gate_unknown_class", "config.tool_model_gate_env_invalid", "config.artifact_host_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "workflow.governance_key_stripped", "workflow.agent_option_ignored", "memory.session_polluted", "memory.harvest_quarantined", "memory.delegation_static_mark_waived", "memory.content_class_declared", "memory.hold_opened", "memory.hold_released", "memory.hold_disposed", "memory.consolidation_recommended", "memory.consolidation_committed", "memory.consolidation_conflict", "memory.consolidation_incomplete", "memory.consolidation_refused", "memory.consolidation_withheld", "route.fallback_to_primary", "route.base_url_changed_key_unchanged", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "tool_result.offload_put_failed"];
|
|
108
108
|
/** A code this engine mints (see {@link ENGINE_NOTICE_CODES}). NOT the type of
|
|
109
109
|
* `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
|
|
110
110
|
* sink is a supported shape, and narrowing that field would break it. */
|
|
@@ -105,6 +105,7 @@ export const ENGINE_NOTICE_CODES = [
|
|
|
105
105
|
"config.tool_model_gate_removed",
|
|
106
106
|
"config.tool_model_gate_unknown_class",
|
|
107
107
|
"config.tool_model_gate_env_invalid",
|
|
108
|
+
"config.artifact_host_invalid",
|
|
108
109
|
"config.tool_card_undeclared",
|
|
109
110
|
"config.tool_face_undeclared",
|
|
110
111
|
"config.tool_face_invalid",
|
|
@@ -177,6 +178,7 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
177
178
|
"config.tool_model_gate_removed": "operator",
|
|
178
179
|
"config.tool_model_gate_unknown_class": "operator",
|
|
179
180
|
"config.tool_model_gate_env_invalid": "operator",
|
|
181
|
+
"config.artifact_host_invalid": "operator",
|
|
180
182
|
"config.tool_card_undeclared": "operator",
|
|
181
183
|
"config.tool_face_undeclared": "operator",
|
|
182
184
|
"config.tool_face_invalid": "operator",
|
|
@@ -46,7 +46,7 @@ export interface TrackEditRequest {
|
|
|
46
46
|
export type TrackEditResult = {
|
|
47
47
|
ok: true;
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
49
|
+
* The compensating handle for the ordering the topology cannot avoid: the pre-image
|
|
50
50
|
* capture runs BEFORE the write, so a write that then FAILS (lost create race, ENOSPC,
|
|
51
51
|
* EACCES, read-only mount) would leave a first-touch record for an edit that never happened —
|
|
52
52
|
* and an `existed-not` record makes an untouched path a rewind DELETE target, removing bytes
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -533,7 +533,7 @@ export interface StopHookContext {
|
|
|
533
533
|
/** Consecutive blocks so far in this run (resets when a stop() call lets the run proceed). */
|
|
534
534
|
consecutiveBlocks: number;
|
|
535
535
|
/**
|
|
536
|
-
*
|
|
536
|
+
* A view of the conversation this hook is being asked to judge.
|
|
537
537
|
*
|
|
538
538
|
* Until now a Stop hook received two counters and nothing else. That is enough for a hook that asks
|
|
539
539
|
* "have I blocked too often?", and structurally insufficient for the entire class of hooks that ask
|
|
@@ -1195,13 +1195,13 @@ export interface ToolGateInput {
|
|
|
1195
1195
|
* decline/pre-commit-failure paths keep their existing `undefined` fallbacks (the gate then keeps the
|
|
1196
1196
|
* fail-closed deny / typed refusal the caller carries). */
|
|
1197
1197
|
liveFaceUnavailable?: boolean,
|
|
1198
|
-
/**
|
|
1198
|
+
/** Present ⇒ the surviving ask carries `requiresRealApproval` and the mint must escalate to
|
|
1199
1199
|
* the non-budgetable `irreversible_ask` kind carrying this bit (a budget resolver must never
|
|
1200
1200
|
* auto-approve what only judgment may clear). `origin` records whether the bit came from an org
|
|
1201
1201
|
* ASK rule, from the org-unavailable tighten (whose resume semantics differ — see
|
|
1202
1202
|
* {@link import("./checkpoint-store.js").RealApprovalGateBit}), or from a policy/hook. */
|
|
1203
1203
|
realApproval?: import("./checkpoint-store.js").RealApprovalGateBit,
|
|
1204
|
-
/**
|
|
1204
|
+
/** The matched-but-outranked persisted rule (the surviving ask's `persistedRuleShadowed`)
|
|
1205
1205
|
* — threaded so the park mint's risk descriptor carries the disclosure on the durable route. */
|
|
1206
1206
|
shadowedRule?: string,
|
|
1207
1207
|
/** The surviving ask's `decisionReason` — threaded so the park mint's rule-suggestion door can
|
|
@@ -1209,11 +1209,11 @@ export interface ToolGateInput {
|
|
|
1209
1209
|
* decision (a hook-raised ask is one no persisted rule may clear, so its parked card must not
|
|
1210
1210
|
* offer to mint one). */
|
|
1211
1211
|
askDecisionReason?: DecisionReason,
|
|
1212
|
-
/**
|
|
1212
|
+
/** The reversibility probe's cause for the tighten (the surviving ask's `probeReason`)
|
|
1213
1213
|
* — threaded so the park mint's risk descriptor carries on the durable route the same account of
|
|
1214
1214
|
* WHY the call was gated that the synchronous card gets. Already neutralized + capped at the gate. */
|
|
1215
1215
|
probeReason?: string,
|
|
1216
|
-
/**
|
|
1216
|
+
/** The STRUCTURED cause (the surviving ask's `probeCause`) — same durable-route errand
|
|
1217
1217
|
* as `probeReason`, already validated at the gate. */
|
|
1218
1218
|
probeCause?: import("./checkpoint-store.js").ProbeCause,
|
|
1219
1219
|
/** design/375 §5.2②: the surviving ask's engine-stamped `segmentCoverage` — threaded so the park
|
|
@@ -1229,7 +1229,7 @@ export interface ToolGateInput {
|
|
|
1229
1229
|
* logical neighbour would silently re-bind three existing call sites' arguments with no type
|
|
1230
1230
|
* error to catch it. */
|
|
1231
1231
|
matchedAskRule?: string,
|
|
1232
|
-
/**
|
|
1232
|
+
/** The surviving ask's engine-stamped `probeMandated` — threaded for the SAME errand as
|
|
1233
1233
|
* `askDecisionReason` and `matchedAskRule`: the park mint's rule-suggestion door must know that
|
|
1234
1234
|
* this ask is one no persisted rule may clear, or the parked card offers a "stop asking me this"
|
|
1235
1235
|
* rule the lane would then refuse to honor. APPENDED at the tail for the reason its neighbour
|
|
@@ -1308,12 +1308,12 @@ export interface ToolGateInput {
|
|
|
1308
1308
|
* claim) or a durable suspend (the ask path). runToolGate still try/catches defensively: an observer
|
|
1309
1309
|
* must never alter the deny outcome.
|
|
1310
1310
|
*
|
|
1311
|
-
*
|
|
1311
|
+
* ALSO fired for the engine's fail-closed interception of a THROWING PreToolUse hook, with
|
|
1312
1312
|
* `source:"hook"` — the exclusion is the hook's own deny DECISION, not an engine decision.
|
|
1313
1313
|
*/
|
|
1314
1314
|
permissionDenied?: (payload: PermissionDeniedPayload) => void | Promise<void>;
|
|
1315
1315
|
/**
|
|
1316
|
-
*
|
|
1316
|
+
* Trace sink for a PreToolUse hook that THREW — the deployment-facing half of the crash
|
|
1317
1317
|
* (`onError(phase:"hook")`, wired by the caller). The model-facing half is the fail-closed block reason;
|
|
1318
1318
|
* this one carries the exception object itself to whoever runs the deployment, because a crashing hook is
|
|
1319
1319
|
* a bug someone has to fix and the model-facing summary is bounded/sanitized. Never affects the outcome
|
|
@@ -1346,7 +1346,7 @@ export interface ToolGateInput {
|
|
|
1346
1346
|
*/
|
|
1347
1347
|
peerMessage?: boolean;
|
|
1348
1348
|
/**
|
|
1349
|
-
*
|
|
1349
|
+
* The write-protection judge — ENGINE-BUILT from
|
|
1350
1350
|
* the deployment's table seat by `createWriteProtectionCheck` (write-protect.ts), never a
|
|
1351
1351
|
* deployment callback (the deployment authors table ROWS, which are validated loudly at compile;
|
|
1352
1352
|
* the judge itself is pure and synchronous, so it is trusted here like the `egress` mark). Judges
|
|
@@ -1381,7 +1381,7 @@ export interface ToolGateInput {
|
|
|
1381
1381
|
autoMode?: {
|
|
1382
1382
|
decider: import("./auto-mode.js").AutoModeDecider;
|
|
1383
1383
|
/**
|
|
1384
|
-
*
|
|
1384
|
+
* The per-run DENIAL-LIMIT tracker beside the decider (CC 2.1.250 `FO`/`Wie`; same owner,
|
|
1385
1385
|
* same lifetime). Absent ⇒ the pre-#548 chain byte for byte: every block is a deny, without bound.
|
|
1386
1386
|
* Present ⇒ a block first counts, then judges; the block that reaches a bound becomes the fallback
|
|
1387
1387
|
* ask (`requiresRealApproval: true` + `denialLimitFallback`), and a classifier allow or a person's
|
|
@@ -1391,7 +1391,7 @@ export interface ToolGateInput {
|
|
|
1391
1391
|
denialTracking?: AutoModeDenialTracker;
|
|
1392
1392
|
};
|
|
1393
1393
|
/**
|
|
1394
|
-
*
|
|
1394
|
+
* The HEADLESS arm of the denial-limit fallback: the fallback ask was resolved by a deny no
|
|
1395
1395
|
* person made (no approver wired; a blanket `"allow"` refused as no approver; an approver that
|
|
1396
1396
|
* reported unavailable with no park to take it). The fallback had nowhere to go, so the caller — which
|
|
1397
1397
|
* owns the run — stops it (`TaskResult.errorCode = "classifier.denial_limit"`, CC's "too many
|
|
@@ -1407,17 +1407,18 @@ export interface ToolGateInput {
|
|
|
1407
1407
|
fallback: import("./auto-mode.js").DenialLimitFallback;
|
|
1408
1408
|
}) => void;
|
|
1409
1409
|
/**
|
|
1410
|
-
*
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1410
|
+
* The deny exit nobody judged: the call's approval could not be RESOLVED (`cause` — the approver
|
|
1411
|
+
* answered `unavailable`, no approver is wired, or an inherited durable mandate has no park facility on
|
|
1412
|
+
* this leg) and no durable park took the call, so the fail-closed deny STANDS. Fired once per such call
|
|
1413
|
+
* from the gate's single deny exit, after every park re-route has had its attempt, with the facts the
|
|
1414
|
+
* `delegation.ask_unresolvable` notice is composed from (`ask-unresolvable-notice.ts`);
|
|
1414
1415
|
* `parkLaneExisted` = a park lane was armed for this run. Observe-only: a throwing seat never alters
|
|
1415
1416
|
* the deny. Absent ⇒ the deny stands silently (a host driving the gate directly, no notice sink).
|
|
1416
1417
|
*/
|
|
1417
1418
|
onAskUnresolvable?: (info: {
|
|
1418
1419
|
toolName: string;
|
|
1419
1420
|
toolCallId: string;
|
|
1420
|
-
|
|
1421
|
+
cause: import("./ask-unresolvable-notice.js").AskUnresolvableCause;
|
|
1421
1422
|
parkLaneExisted: boolean;
|
|
1422
1423
|
}) => void;
|
|
1423
1424
|
/**
|
|
@@ -1513,7 +1514,7 @@ export interface ToolGateInput {
|
|
|
1513
1514
|
}) => void;
|
|
1514
1515
|
};
|
|
1515
1516
|
/**
|
|
1516
|
-
*
|
|
1517
|
+
* Observation sink for a shell call the READ-ONLY reader cleared (`readOnlyShellVerdict`, the
|
|
1517
1518
|
* allow layer's second member beside the persisted allow rule: consulted for the shell tool only, after
|
|
1518
1519
|
* the person's allow rules, under the SAME consumption predicate — never over a mandated ask, a hook's
|
|
1519
1520
|
* ask, an explicit ask rule, governance or a marked call). The allow-side disclosure of "why did this run
|
|
@@ -67,7 +67,7 @@ export declare class TransportLspSession implements LspSession {
|
|
|
67
67
|
* it REPORTS failure instead (`{ok:false}`) — an unreadable file no longer looks like a clean sync to the
|
|
68
68
|
* caller. Concurrent callers share one in-flight sync, each keeping its OWN cancellation (HRD-LSP-12). */
|
|
69
69
|
private syncOne;
|
|
70
|
-
/**
|
|
70
|
+
/** Structural fallback classification for errors carrying no {@link lspFailureOf} brand (a
|
|
71
71
|
* third-party transport, e.g. the TOB WS lane) — abort signal and transport liveness are the only
|
|
72
72
|
* transport-neutral facts available; everything else is a server fault. Cannot produce
|
|
73
73
|
* "unsupported_operation" until that transport brands its JSON-RPC error codes (cross-repo slip). */
|
package/dist/core/lsp.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface LspSymbolInfo {
|
|
|
50
50
|
positionUnknown?: boolean;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* WHY a "none" result was "none". Absent `reason` = a GENUINE empty answer (the server
|
|
54
54
|
* answered null/[] — today's behavior byte-for-byte, and what an external manager returning bare
|
|
55
55
|
* `{kind:"none"}` keeps getting). The failure reasons exist because one literal "No results." used to cover
|
|
56
56
|
* both real emptiness and every transport-level fault — a findReferences that TIMED OUT read as "no
|
package/dist/core/lsp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { uriToPath } from "./lsp-protocol.js";
|
|
2
|
-
import { foldRedundantPathSegments,
|
|
2
|
+
import { foldRedundantPathSegments, isAbsoluteForFamily, joinForFamily, pathFamilyOf } from "../tools/fs/safety.js";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import { defineTool, errorResult } from "./tools.js";
|
|
5
5
|
import { toolFace } from "./tool-catalog-entries.js";
|
|
@@ -69,10 +69,11 @@ export function settleOnAbort(job, signal, onAbort) {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
export function resolveLspPath(filePath, cwd) {
|
|
72
|
-
const
|
|
73
|
-
|
|
72
|
+
const family = cwd === undefined ? undefined : pathFamilyOf({ root: cwd });
|
|
73
|
+
const isAbsForm = family !== undefined && isAbsoluteForFamily(family, filePath);
|
|
74
|
+
if (isAbsForm || !cwd || family === undefined)
|
|
74
75
|
return foldRedundantPathSegments(filePath);
|
|
75
|
-
return foldRedundantPathSegments(
|
|
76
|
+
return foldRedundantPathSegments(joinForFamily(family, cwd, filePath));
|
|
76
77
|
}
|
|
77
78
|
export const MAX_LSP_FILE_BYTES = 10 * 1000 * 1000;
|
|
78
79
|
function fmt(result, ctx) {
|
package/dist/core/mcp.d.ts
CHANGED
|
@@ -168,7 +168,7 @@ export interface MaterializedMcp {
|
|
|
168
168
|
* A consumer (service) surfacing it as session status must understand that. `toolNames` are the namespaced
|
|
169
169
|
* (`<server>__<tool>`) names exposed.
|
|
170
170
|
*
|
|
171
|
-
*
|
|
171
|
+
* READ THIS AS A SNAPSHOT, NOT A HEARTBEAT. `status` is the connect-time verdict and is never
|
|
172
172
|
* rewritten: a server that dies mid-task still reads `connected` here, because that IS what happened at
|
|
173
173
|
* materialize. The one live bit is {@link McpServerStatus.transportClosed} (set if and when core observes
|
|
174
174
|
* the transport go away — see its own note for the reach). For an authoritative answer about a server's
|
|
@@ -231,7 +231,7 @@ export interface McpRefreshResult {
|
|
|
231
231
|
*/
|
|
232
232
|
error?: string;
|
|
233
233
|
/**
|
|
234
|
-
*
|
|
234
|
+
* Present when this entry's `tools/list` walk did NOT reach the end of pagination. `tools` is
|
|
235
235
|
* then a MERGE: everything the partial listing did return, plus the previously-mounted entries whose
|
|
236
236
|
* absence it cannot prove (so the consumer's replace-the-whole-prefix swap withdraws nothing, and
|
|
237
237
|
* `removed: []` describes a roster that really lost nothing). Absent on a complete walk and on every
|
|
@@ -267,7 +267,7 @@ export interface McpServerStatus {
|
|
|
267
267
|
delivered?: McpDelivered;
|
|
268
268
|
httpStatus?: number;
|
|
269
269
|
/**
|
|
270
|
-
*
|
|
270
|
+
* Set to `true` once core OBSERVES this server's transport close during the task; absent
|
|
271
271
|
* otherwise (never `false`: absence means "no close was observed", which is not the same as "alive").
|
|
272
272
|
* It mirrors exactly the flag core's own fail-fast path keys on, so `transportClosed === true` also
|
|
273
273
|
* means every further call to this server's tools/resources is refused without dialing.
|
|
@@ -376,7 +376,7 @@ export declare function mcpStartupTimeoutMs(): number | undefined;
|
|
|
376
376
|
* (see {@link MCP_SPEC_ERROR_CODE_NAMES}). */
|
|
377
377
|
export declare function describeMcpSpecErrorCode(code: unknown): string | undefined;
|
|
378
378
|
/**
|
|
379
|
-
*
|
|
379
|
+
* The SDK stamps `MCP error <code>: ` onto an McpError's message IN ITS CONSTRUCTOR, and it
|
|
380
380
|
* does so at BOTH ends of the wire: a server built on an MCP SDK throws an McpError, its already-stamped
|
|
381
381
|
* `.message` goes out as the JSON-RPC `error.message`, and the client constructs its own McpError over
|
|
382
382
|
* that string — so the failure reaches the model as `MCP error -32602: MCP error -32602: …`. Collapse a
|
|
@@ -582,7 +582,7 @@ export declare function classifyDirReadInvalidParams(message: string): "not_foun
|
|
|
582
582
|
*/
|
|
583
583
|
export declare function listEntryFingerprint(entry: unknown): string | undefined;
|
|
584
584
|
/**
|
|
585
|
-
*
|
|
585
|
+
* Walk a paginated MCP list method to the END (`{cursor?}` → `{items, nextCursor?}`), bounded.
|
|
586
586
|
*
|
|
587
587
|
* Every spec-standard MCP list method is paginated the same way: absence of `nextCursor` means "that
|
|
588
588
|
* was the last page". A single request therefore returns the WHOLE list only for servers that choose
|