@sema-agent/core 7.33.8 → 7.35.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 +462 -3
- package/dist/agents/cross-session-judge.d.ts +16 -7
- package/dist/agents/cross-session-judge.js +18 -15
- package/dist/agents/peer-session-drain.d.ts +4 -0
- package/dist/agents/peer-session-drain.js +5 -2
- package/dist/agents/repair-loop.d.ts +1 -1
- package/dist/agents/subagent.d.ts +3 -0
- package/dist/agents/subagent.js +12 -3
- package/dist/brain/anthropic.js +1 -0
- package/dist/brain/errors.d.ts +23 -1
- package/dist/brain/errors.js +19 -2
- package/dist/brain/open-responses.js +1 -0
- package/dist/brain/openai.js +1 -0
- package/dist/brain/status-sink.d.ts +7 -1
- package/dist/brain/stream-engine.d.ts +3 -0
- package/dist/brain/stream-engine.js +2 -1
- package/dist/brain/stream-shared.d.ts +3 -1
- package/dist/brain/stream-shared.js +16 -4
- package/dist/core/a2a.d.ts +3 -1
- package/dist/core/a2a.js +1 -1
- package/dist/core/ask-origin.d.ts +379 -154
- package/dist/core/ask-origin.js +224 -45
- package/dist/core/ask-question.js +2 -1
- package/dist/core/ask-unresolvable-notice.d.ts +5 -2
- package/dist/core/ask-unresolvable-notice.js +4 -3
- package/dist/core/auto-mode-arming.d.ts +17 -4
- package/dist/core/auto-mode-arming.js +31 -6
- package/dist/core/auto-mode-defaults.d.ts +16 -7
- package/dist/core/auto-mode-defaults.js +2 -1
- package/dist/core/auto-mode-directive.d.ts +18 -30
- package/dist/core/auto-mode-directive.js +9 -18
- package/dist/core/auto-mode-prompt.d.ts +62 -1
- package/dist/core/auto-mode-prompt.js +42 -4
- package/dist/core/auto-mode-rebuild.d.ts +2 -1
- package/dist/core/auto-mode-rebuild.js +1 -0
- package/dist/core/auto-mode.d.ts +32 -3
- package/dist/core/auto-mode.js +80 -41
- package/dist/core/checkpoint-store.d.ts +54 -35
- package/dist/core/checkpoint-store.js +58 -3
- package/dist/core/child-clamp-seats.js +1 -0
- package/dist/core/classifier-unavailable-arm.d.ts +57 -0
- package/dist/core/classifier-unavailable-arm.js +17 -0
- package/dist/core/effective-path-target.d.ts +12 -0
- package/dist/core/effective-path-target.js +18 -7
- package/dist/core/engine-notice.d.ts +53 -2
- package/dist/core/fs-write-gate-policy.d.ts +5 -1
- package/dist/core/fs-write-gate-policy.js +4 -1
- package/dist/core/gate-fold.js +19 -3
- package/dist/core/gate-lanes.js +116 -50
- package/dist/core/gate-outcome.d.ts +40 -7
- package/dist/core/gate-outcome.js +26 -4
- package/dist/core/governance-codes.d.ts +18 -2
- package/dist/core/governance-codes.js +23 -1
- package/dist/core/hooks.d.ts +53 -29
- package/dist/core/hooks.js +1 -3
- package/dist/core/mcp.js +1 -1
- package/dist/core/message-utils.d.ts +2 -1
- package/dist/core/message-utils.js +2 -1
- package/dist/core/mode-policy.d.ts +185 -0
- package/dist/core/mode-policy.js +184 -0
- package/dist/core/oracle-isolation.d.ts +3 -3
- package/dist/core/permission-rule-model.d.ts +20 -3
- package/dist/core/permission-rule-model.js +40 -3
- package/dist/core/permission-rule-provider.d.ts +22 -11
- package/dist/core/permission-rule-provider.js +17 -7
- package/dist/core/permission-rules.d.ts +23 -1
- package/dist/core/permission-rules.js +56 -9
- package/dist/core/persisted-rule-arms.d.ts +14 -10
- package/dist/core/persisted-rule-arms.js +13 -6
- package/dist/core/present-plan-tool.d.ts +1 -1
- package/dist/core/present-plan-tool.js +3 -3
- package/dist/core/read-only-shell.d.ts +19 -19
- package/dist/core/read-only-shell.js +38 -12
- package/dist/core/retired-keys.d.ts +9 -3
- package/dist/core/retired-keys.js +5 -1
- package/dist/core/runner/active-skill-scope.js +5 -4
- package/dist/core/runner/assemble-result.js +2 -2
- package/dist/core/runner/classifier-ladder.d.ts +43 -1
- package/dist/core/runner/classifier-ladder.js +29 -0
- package/dist/core/runner/contracts.d.ts +62 -38
- package/dist/core/runner/denial-limit-arms.d.ts +35 -7
- package/dist/core/runner/denial-limit-arms.js +15 -9
- package/dist/core/runner/env-capability-census.js +1 -1
- package/dist/core/runner/file-history-request.d.ts +26 -0
- package/dist/core/runner/file-history-request.js +90 -0
- package/dist/core/runner/gate-exit.d.ts +30 -4
- package/dist/core/runner/gate-exit.js +28 -7
- package/dist/core/runner/permission-rule-lanes.d.ts +11 -6
- package/dist/core/runner/permission-rule-lanes.js +55 -48
- package/dist/core/runner/persisted-path-rows.d.ts +64 -0
- package/dist/core/runner/persisted-path-rows.js +89 -0
- package/dist/core/runner/prepare-artifact.d.ts +5 -1
- package/dist/core/runner/prepare-artifact.js +9 -3
- package/dist/core/runner/prepare-ask-lane.d.ts +6 -4
- package/dist/core/runner/prepare-ask-lane.js +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +39 -13
- package/dist/core/runner/prepare-caps-and-workflow.js +58 -29
- package/dist/core/runner/prepare-file-history.d.ts +9 -3
- package/dist/core/runner/prepare-file-history.js +4 -90
- package/dist/core/runner/prepare-gate-stations.d.ts +37 -9
- package/dist/core/runner/prepare-gate-stations.js +46 -29
- package/dist/core/runner/prepare-hands-readface.d.ts +40 -17
- package/dist/core/runner/prepare-hands-readface.js +64 -53
- package/dist/core/runner/prepare-inherited-gate.d.ts +30 -23
- package/dist/core/runner/prepare-inherited-gate.js +45 -25
- package/dist/core/runner/prepare-park-ask.d.ts +6 -4
- package/dist/core/runner/prepare-park-ask.js +7 -5
- package/dist/core/runner/prepare-policy-chain.d.ts +25 -4
- package/dist/core/runner/prepare-policy-chain.js +203 -180
- package/dist/core/runner/prepare-protocol-tools.d.ts +28 -0
- package/dist/core/runner/prepare-protocol-tools.js +13 -3
- package/dist/core/runner/prepare-run-refs.d.ts +6 -6
- package/dist/core/runner/prepare-run-refs.js +9 -4
- package/dist/core/runner/prepare-safety-scan.d.ts +5 -0
- package/dist/core/runner/prepare-safety-scan.js +19 -3
- package/dist/core/runner/prepare-suspend-saga.d.ts +5 -5
- package/dist/core/runner/prepare-suspend-saga.js +6 -6
- package/dist/core/runner/prepare-task.js +56 -24
- package/dist/core/runner/prepare-wiring-manifest.d.ts +10 -6
- package/dist/core/runner/prepare-wiring-manifest.js +5 -3
- package/dist/core/runner/read-root-grant.d.ts +9 -0
- package/dist/core/runner/read-root-grant.js +12 -0
- package/dist/core/runner/retired-runner-deps.js +1 -0
- package/dist/core/runner/run-brain-sinks.js +3 -1
- package/dist/core/runner/run-identity-wiring.js +1 -1
- package/dist/core/runner/run-leg.js +29 -18
- package/dist/core/runner/run-notification-lane.d.ts +3 -0
- package/dist/core/runner/run-notification-lane.js +3 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
- package/dist/core/runner/run-turn-boundary.js +1 -1
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +8 -11
- package/dist/core/runner/session-mode-seats.d.ts +48 -0
- package/dist/core/runner/session-mode-seats.js +73 -0
- package/dist/core/runner/session-rule-policy.js +3 -2
- package/dist/core/runner/stream-mode-verb.d.ts +27 -0
- package/dist/core/runner/stream-mode-verb.js +19 -0
- package/dist/core/runner/turn-attachments.d.ts +3 -3
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/runner/write-protection-station.d.ts +7 -1
- package/dist/core/runner/write-protection-station.js +6 -3
- package/dist/core/runner-deps.d.ts +31 -34
- package/dist/core/runtime-caps.d.ts +12 -3
- package/dist/core/safety-axis-vocab.d.ts +2 -2
- package/dist/core/safety-axis-vocab.js +1 -1
- package/dist/core/secret-env.d.ts +30 -58
- package/dist/core/secret-env.js +149 -586
- package/dist/core/sensitive-path-policy.d.ts +4 -2
- package/dist/core/sensitive-path-policy.js +41 -31
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -1
- package/dist/core/task-event.d.ts +1 -1
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +41 -89
- package/dist/core/task-stream.d.ts +16 -0
- package/dist/core/terminal-cause.js +1 -0
- package/dist/core/tighten-task-spec.d.ts +2 -2
- package/dist/core/tighten-task-spec.js +12 -14
- package/dist/core/tool-catalog-entries.js +4 -3
- package/dist/core/tool-policy.d.ts +84 -30
- package/dist/core/tool-policy.js +76 -16
- package/dist/core/tool-registry.d.ts +38 -0
- package/dist/core/tool-registry.js +25 -1
- package/dist/core/tool-roster.d.ts +17 -2
- package/dist/core/tool-roster.js +11 -1
- package/dist/core/tool-spec.d.ts +8 -9
- package/dist/core/trace.d.ts +18 -6
- package/dist/core/types.d.ts +2 -1
- package/dist/core/wiring-manifest.d.ts +31 -5
- package/dist/core/wiring-manifest.js +28 -1
- package/dist/core/write-protect.d.ts +11 -1
- package/dist/core/write-protect.js +26 -15
- package/dist/engine/execution-env/node-execution-env.d.ts +6 -1
- package/dist/engine/execution-env/node-execution-env.js +16 -3
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.d.ts +6 -7
- package/dist/engine/llm/types.d.ts +2 -4
- package/dist/engine/loop/types.d.ts +7 -0
- package/dist/engine/lsp/node-lsp-manager.js +1 -1
- package/dist/index.d.ts +18 -8
- package/dist/index.js +15 -6
- package/dist/orchestration/workflow-governance.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +1 -1
- package/dist/orchestration/workflow-size-guideline.js +2 -2
- package/dist/stores/file/index.d.ts +5 -3
- package/dist/stores/file/session-store.d.ts +8 -2
- package/dist/stores/file/session-store.js +5 -5
- package/dist/tools/artifact/artifact-tool.d.ts +17 -0
- package/dist/tools/artifact/artifact-tool.js +16 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +28 -7
- package/dist/tools/fs/bash-readonly-classifier.js +88 -0
- package/dist/tools/fs/containment-face.d.ts +9 -3
- package/dist/tools/fs/containment-face.js +2 -0
- package/dist/tools/fs/file-read-check.d.ts +50 -0
- package/dist/tools/fs/file-read-check.js +79 -0
- package/dist/tools/fs/fs-bash.d.ts +3 -3
- package/dist/tools/fs/fs-read.d.ts +1 -0
- package/dist/tools/fs/fs-search-tools.d.ts +2 -2
- package/dist/tools/fs/fs-search-tools.js +4 -4
- package/dist/tools/fs/fs-shared.js +20 -6
- package/dist/tools/fs/fs-write.d.ts +4 -3
- package/dist/tools/fs/fs-write.js +9 -9
- package/dist/tools/fs/index.d.ts +39 -3
- package/dist/tools/fs/index.js +38 -15
- package/dist/tools/fs/read-deny-home.d.ts +93 -0
- package/dist/tools/fs/read-deny-home.js +225 -0
- package/dist/tools/fs/read-deny.d.ts +176 -34
- package/dist/tools/fs/read-deny.js +268 -61
- package/dist/tools/fs/safety.d.ts +6 -1
- package/dist/tools/fs/safety.js +18 -5
- package/dist/tools/fs/search.d.ts +3 -0
- package/dist/tools/fs/search.js +47 -9
- package/dist/tools/fs/shell-read-check.d.ts +52 -13
- package/dist/tools/fs/shell-read-check.js +199 -20
- package/dist/tools/fs/shell-read-establish.d.ts +7 -3
- package/dist/tools/fs/shell-read-establish.js +134 -21
- package/dist/tools/fs/shell-read-identity.d.ts +7 -0
- package/dist/tools/fs/shell-read-identity.js +21 -0
- package/dist/tools/fs/shell-read-judge.d.ts +92 -32
- package/dist/tools/fs/shell-read-judge.js +43 -18
- package/dist/tools/fs/shell-read-plan.d.ts +4 -0
- package/dist/tools/fs/shell-read-plan.js +26 -4
- package/dist/tools/fs/shell-read-write-targets.d.ts +73 -0
- package/dist/tools/fs/shell-read-write-targets.js +173 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +117 -11
- package/wire-changes/core@7.34.0.json +2621 -0
- package/wire-changes/core@7.35.0.json +584 -0
|
@@ -513,6 +513,7 @@ export async function runLeg(input) {
|
|
|
513
513
|
}
|
|
514
514
|
prepared.abortController.abort();
|
|
515
515
|
prepared.releaseSignal();
|
|
516
|
+
prepared.endModeCeiling();
|
|
516
517
|
strandedHumanAnswers = prepared.settleContentAskBindings();
|
|
517
518
|
try {
|
|
518
519
|
prepared.harness.recoverUndrainedEngineNotes();
|
|
@@ -633,7 +634,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
633
634
|
}
|
|
634
635
|
if (prepared.permissionRuleOrg !== undefined) {
|
|
635
636
|
const orgVerdict = prepared.permissionRuleOrg
|
|
636
|
-
.adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId })
|
|
637
|
+
.adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)) })
|
|
637
638
|
.catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
|
|
638
639
|
let orgWaitCancelled = false;
|
|
639
640
|
const org = await settleOrgVerdictWithin(orgVerdict, { status: "unavailable", disclosures: [`the org adjudication face did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms (or the task ended first)`] }, {
|
|
@@ -683,22 +684,30 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
683
684
|
}
|
|
684
685
|
if (prepared.persistedRuleLane !== undefined) {
|
|
685
686
|
const unreadable = { unreadable: true };
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
687
|
+
let laneWaitCancelled = false;
|
|
688
|
+
let laneRead;
|
|
689
|
+
try {
|
|
690
|
+
laneRead = Promise.resolve(prepared.persistedRuleLane.adjudicate({
|
|
691
|
+
toolName: pendingAction.toolName,
|
|
692
|
+
args: resolvedArgs,
|
|
693
|
+
toolCallId: pendingAction.toolCallId,
|
|
694
|
+
...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)),
|
|
695
|
+
budget: beltBudget,
|
|
696
|
+
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
697
|
+
...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
|
|
698
|
+
})).then((answer) => normalizePersistedRuleHit(answer));
|
|
699
|
+
}
|
|
700
|
+
catch {
|
|
701
|
+
laneRead = Promise.resolve(unreadable);
|
|
702
|
+
}
|
|
703
|
+
const read = await settleOrgVerdictWithin(laneRead, unreadable, { signal: prepared.abortController.signal, timeoutMs: ORG_ADJUDICATION_TIMEOUT_MS, onFallback: (cause) => void (laneWaitCancelled = cause === "aborted") });
|
|
697
704
|
const tightened = applyPersistedTightening({ action: "allow" }, read);
|
|
698
|
-
if (tightened.tightened === "deny" || tightened.tightened === "unreadable") {
|
|
699
|
-
const why =
|
|
700
|
-
?
|
|
701
|
-
:
|
|
705
|
+
if (laneWaitCancelled || tightened.tightened === "deny" || tightened.tightened === "unreadable") {
|
|
706
|
+
const why = laneWaitCancelled
|
|
707
|
+
? "the task was cancelled before your persisted permission rules could be read"
|
|
708
|
+
: tightened.tightened === "deny"
|
|
709
|
+
? `a standing deny rule of yours (${disclosedRuleSet(read.hit?.rules ?? [])}) now refuses it`
|
|
710
|
+
: `it could not be read against your standing deny/ask rules (${read.reason})`;
|
|
702
711
|
const ruleDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${why}. This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
|
|
703
712
|
await settleEnd(true, { content: ruleDenial }, vetoed("persisted_rule"));
|
|
704
713
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, ruleDenial, true));
|
|
@@ -708,7 +717,8 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
708
717
|
}
|
|
709
718
|
if (prepared.shellReadCheck !== undefined) {
|
|
710
719
|
const edited = outcome.updatedInput !== undefined && !deepJsonEqual(outcome.updatedInput, pendingAction.args);
|
|
711
|
-
const
|
|
720
|
+
const readCall = { toolName: pendingAction.toolName, ...callFaceSeat(toolCallFaceOf(prepared.toolRosterDeltas.current, pendingAction.toolName)) };
|
|
721
|
+
const judged = prepared.shellReadCheck.judge(readCall, resolvedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
|
|
712
722
|
toolCallId: pendingAction.toolCallId,
|
|
713
723
|
...(edited ? { seat: "rejudge" } : {}),
|
|
714
724
|
});
|
|
@@ -720,7 +730,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
720
730
|
}
|
|
721
731
|
catch {
|
|
722
732
|
answered = false;
|
|
723
|
-
verdict = prepared.shellReadCheck.unanswered(
|
|
733
|
+
verdict = prepared.shellReadCheck.unanswered(readCall, "the read station could not answer on the resumed leg");
|
|
724
734
|
}
|
|
725
735
|
if (!answered || verdict.disposition === "refuse" || (edited && verdict.disposition !== "vouch")) {
|
|
726
736
|
const readDenial = formatHookFeedback(`The approved tool call "${pendingAction.toolName}" was not executed: ${verdict.sentence ?? "the read boundary does not vouch for what it reads on this leg"} This approval is spent — the call has to be re-issued and approved again.`, prepared.reminderMark);
|
|
@@ -729,6 +739,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
729
739
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
730
740
|
return;
|
|
731
741
|
}
|
|
742
|
+
verdict.admit?.();
|
|
732
743
|
}
|
|
733
744
|
}
|
|
734
745
|
const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
|
|
@@ -4,6 +4,7 @@ import type { SessionStore } from "../session.js";
|
|
|
4
4
|
import { type NotificationInjectionOutcome, type PendingSessionNotifications, type SystemInjectionPriority, type TaskNotificationPayload } from "../task-notification.js";
|
|
5
5
|
import type { RunnerDeps, TaskEvent, TaskSpec } from "../types.js";
|
|
6
6
|
import type { SessionReadFileStates } from "./session-read-file-states.js";
|
|
7
|
+
import type { SessionModeSeats } from "./session-mode-seats.js";
|
|
7
8
|
import type { NotificationLaneBindings, Prepared, PrepareTaskFn, ResumeRun, RunInternals, RunnerDepsSeat, RunnerSelfSeat, TaskIdRef } from "./contracts.js";
|
|
8
9
|
export interface RunNotificationLaneInput<R> {
|
|
9
10
|
/** borrowed-readonly — the task spec: prepare's input, and the principal the peer scope is minted from. */
|
|
@@ -26,6 +27,8 @@ export interface RunNotificationLaneInput<R> {
|
|
|
26
27
|
sessions: SessionStore;
|
|
27
28
|
/** borrowed-mutable — the Runner's own per-session read-state seat, ALWAYS this Runner's (a caller-supplied one is overridden). */
|
|
28
29
|
sessionReadStates: SessionReadFileStates;
|
|
30
|
+
/** borrowed-mutable — the Runner's per-session permission-mode seats, ALWAYS this Runner's (a caller-supplied one is overridden). */
|
|
31
|
+
sessionModeSeats: SessionModeSeats;
|
|
29
32
|
/** borrowed-mutable — the Runner's per-session parked-notification store: the torn-down lane and the escalation pend here. */
|
|
30
33
|
pendingSessionNotifications: PendingSessionNotifications;
|
|
31
34
|
/** borrowed-readonly — the orchestrator's entry (a lane may not name the orchestrator; the driver hands it in), as the ONE
|
|
@@ -8,12 +8,13 @@ export function runNotificationLane(input) {
|
|
|
8
8
|
return obligations.run(() => runNotificationLaneAs(input, obligations));
|
|
9
9
|
}
|
|
10
10
|
async function runNotificationLaneAs(input, obligations) {
|
|
11
|
-
const { spec, queue, resume, internals, taskIdRef, runner, self, sessions, sessionReadStates, pendingSessionNotifications, prepareTask, next } = input;
|
|
11
|
+
const { spec, queue, resume, internals, taskIdRef, runner, self, sessions, sessionReadStates, sessionModeSeats, pendingSessionNotifications, prepareTask, next } = input;
|
|
12
12
|
const prepareResume = resume
|
|
13
13
|
? {
|
|
14
14
|
leafId: resume.cp.leafId,
|
|
15
15
|
suspendedBatch: new Set(resume.cp.pendingAction.kind === "tool_approval" ? resume.cp.pendingAction.batchToolCallIds : []),
|
|
16
16
|
seed: resume.cp.state,
|
|
17
|
+
outcome: resume.outcome,
|
|
17
18
|
priorSuspendCount: resume.cp.suspendCount,
|
|
18
19
|
priorLedger: resume.cp.resourceLedger,
|
|
19
20
|
priorHumanReview: resume.cp.humanReview,
|
|
@@ -117,6 +118,7 @@ async function runNotificationLaneAs(input, obligations) {
|
|
|
117
118
|
peerInboundChainRef,
|
|
118
119
|
detachHub,
|
|
119
120
|
sessionReadStates,
|
|
121
|
+
sessionModeSeats,
|
|
120
122
|
onTaskNotification: (notification, opts) => {
|
|
121
123
|
try {
|
|
122
124
|
upstreamTaskNotification?.(notification, opts);
|
|
@@ -6,8 +6,8 @@ import type { Prepared, ResumeRun, RunnerDepsSeat, RunState, TaskIdRef } from ".
|
|
|
6
6
|
export interface RunTelemetryAndBudgetSeatsInput {
|
|
7
7
|
/** borrowed-readonly — the task spec: limits, degrade, tracer, output retries, the resource-suspend totals. */
|
|
8
8
|
spec: TaskSpec;
|
|
9
|
-
/** borrowed-mutable — the leg's prepared seat: `liveSpendRef.get` is installed, `humanReviewRef` and
|
|
10
|
-
*
|
|
9
|
+
/** borrowed-mutable — the leg's prepared seat: `liveSpendRef.get` is installed, `humanReviewRef` is written and the
|
|
10
|
+
* mode seat steered on a resume; the model, the ledger, the session id and the usage governance are read. */
|
|
11
11
|
prepared: Prepared;
|
|
12
12
|
/** borrowed-readonly — the resume plan when this leg resumes a checkpoint (the human-review latency, the plan re-arm). */
|
|
13
13
|
resume: ResumeRun | undefined;
|
|
@@ -33,7 +33,7 @@ export function runTelemetryAndBudgetSeats(input) {
|
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
if (resume !== undefined && resume.outcome.gate === "plan_review" && resume.outcome.decision === "reject") {
|
|
36
|
-
prepared.
|
|
36
|
+
void prepared.permissionMode.steer("plan");
|
|
37
37
|
}
|
|
38
38
|
rs.telemetry.cacheFamily = cacheFamilyOf(prepared.model);
|
|
39
39
|
rs.telemetry.pricing = runner.deps.pricing?.[prepared.model.id] ?? modelCostToPricing(prepared.model.cost);
|
|
@@ -283,7 +283,7 @@ export function createTurnBoundary(input) {
|
|
|
283
283
|
cadenceTurn: rs.counters.cadenceTurns,
|
|
284
284
|
todoMounted: todoToolMounted,
|
|
285
285
|
taskListMounted: taskToolsMounted,
|
|
286
|
-
planActive: prepared.
|
|
286
|
+
planActive: prepared.permissionMode.current === "plan",
|
|
287
287
|
config: {
|
|
288
288
|
todoReminder: rs.attach.attachmentsCfg?.todoReminder === true,
|
|
289
289
|
...(rs.attach.attachmentsCfg?.todoReminderMode !== undefined ? { todoReminderMode: rs.attach.attachmentsCfg.todoReminderMode } : {}),
|
|
@@ -22,6 +22,7 @@ export declare class Runner implements RunnerSelfSeat {
|
|
|
22
22
|
* authority: a durable resume's checkpoint seed wins on its leg, and a miss (cross-process,
|
|
23
23
|
* evicted) falls back to the transcript-replay rung. See {@link SessionReadFileStates}. */
|
|
24
24
|
private readonly sessionReadStates;
|
|
25
|
+
private readonly sessionModeSeats;
|
|
25
26
|
/** task notifications born BETWEEN turns (run torn down / harness already idle), parked
|
|
26
27
|
* per session and drained into the session's next run at its first boundary. Runner-lived (outlives any
|
|
27
28
|
* single run, like the registry handles that produce into it); bounded + drop-disclosing, see
|
|
@@ -16,9 +16,10 @@ import { TtlSessionStore } from "../session-store.js";
|
|
|
16
16
|
import { errorCodeOf } from "./assemble-result.js";
|
|
17
17
|
import { effectiveDelegationFacts, prepareTask } from "./prepare-task.js";
|
|
18
18
|
import { SessionReadFileStates } from "./session-read-file-states.js";
|
|
19
|
+
import { SessionModeSeats } from "./session-mode-seats.js";
|
|
19
20
|
import { RunnerSharedToolResultStore } from "../tool-result-store.js";
|
|
20
21
|
import { PAUSE_REGISTRY } from "../pause-registry.js";
|
|
21
|
-
import {
|
|
22
|
+
import { wrapToolPolicy } from "../tool-policy.js";
|
|
22
23
|
import { PendingSessionNotifications } from "../task-notification.js";
|
|
23
24
|
import { ToolDetachHub } from "../tool-detach.js";
|
|
24
25
|
import { assertContextSettingsValue, assertContextSettingsWords, contextOverlayFor } from "../context-settings.js";
|
|
@@ -40,6 +41,7 @@ import { streamReap } from "./stream-reap.js";
|
|
|
40
41
|
import { streamSteerVerb } from "./stream-steer-verb.js";
|
|
41
42
|
import { streamLifecycleVerbs } from "./stream-lifecycle-verbs.js";
|
|
42
43
|
import { streamHaltVerbs } from "./stream-halt-verbs.js";
|
|
44
|
+
import { streamModeVerb } from "./stream-mode-verb.js";
|
|
43
45
|
import { runNotificationLane } from "./run-notification-lane.js";
|
|
44
46
|
import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
|
|
45
47
|
import { runLeg } from "./run-leg.js";
|
|
@@ -64,6 +66,7 @@ export class Runner {
|
|
|
64
66
|
sessions;
|
|
65
67
|
sessionLocks = new Map();
|
|
66
68
|
sessionReadStates = new SessionReadFileStates();
|
|
69
|
+
sessionModeSeats = new SessionModeSeats();
|
|
67
70
|
pendingSessionNotifications = new PendingSessionNotifications();
|
|
68
71
|
parentConstraintRegistry = new Map();
|
|
69
72
|
suspendedEnvReaps = new Map();
|
|
@@ -173,15 +176,7 @@ export class Runner {
|
|
|
173
176
|
return {
|
|
174
177
|
...(deps.toolPolicy !== undefined
|
|
175
178
|
? {
|
|
176
|
-
toolPolicy: (() =>
|
|
177
|
-
const sets = toolPolicyNameSets(deps.toolPolicy);
|
|
178
|
-
const wrapped = {
|
|
179
|
-
check: (req, signal) => deps.toolPolicy.check(req, signal),
|
|
180
|
-
};
|
|
181
|
-
if (sets.length > 0)
|
|
182
|
-
wrapped.nameSets = sets;
|
|
183
|
-
return wrapped;
|
|
184
|
-
})(),
|
|
179
|
+
toolPolicy: wrapToolPolicy(deps.toolPolicy, (req, signal) => deps.toolPolicy.check(req, signal)),
|
|
185
180
|
}
|
|
186
181
|
: {}),
|
|
187
182
|
...(hooks !== undefined ? { hooks } : {}),
|
|
@@ -417,6 +412,7 @@ export class Runner {
|
|
|
417
412
|
const { steer } = streamSteerVerb({ spec, internals, queue, acceptedSteerInputs, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, steeringError, runner: this.depsSeat });
|
|
418
413
|
const { notify, optOutMemoryCapture, reconnectMcpServer, compact, detach, interrupt } = streamLifecycleVerbs({ live, ready, orTimeout, steeringError, notifyRef, captureOptOutRef, mcpRedialRef, manualCompactRef, detachHub });
|
|
419
414
|
const { halt, destroy } = streamHaltVerbs({ spec, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, run, reapSuspended, runner: this.depsSeat });
|
|
415
|
+
const { setPermissionMode } = streamModeVerb({ live, ready, orTimeout, steeringError });
|
|
420
416
|
return {
|
|
421
417
|
[Symbol.asyncIterator]: () => queue[Symbol.asyncIterator](),
|
|
422
418
|
result: async () => {
|
|
@@ -430,6 +426,7 @@ export class Runner {
|
|
|
430
426
|
steer,
|
|
431
427
|
notify,
|
|
432
428
|
optOutMemoryCapture,
|
|
429
|
+
setPermissionMode,
|
|
433
430
|
reconnectMcpServer,
|
|
434
431
|
compact,
|
|
435
432
|
detach,
|
|
@@ -442,7 +439,7 @@ export class Runner {
|
|
|
442
439
|
const { spec, queue, setResult, onSuggestions, onReady, onSuspend, manualCompactRef, taskIdRef, resume, internals, notifyRef, captureOptOutRef, mcpRedialRef, entryActor, entryTracer } = input;
|
|
443
440
|
return runNotificationLane({
|
|
444
441
|
spec, queue, resume, internals, taskIdRef,
|
|
445
|
-
runner: this.depsSeat, self: this, sessions: this.sessions, sessionReadStates: this.sessionReadStates, pendingSessionNotifications: this.pendingSessionNotifications,
|
|
442
|
+
runner: this.depsSeat, self: this, sessions: this.sessions, sessionReadStates: this.sessionReadStates, sessionModeSeats: this.sessionModeSeats, pendingSessionNotifications: this.pendingSessionNotifications,
|
|
446
443
|
prepareTask,
|
|
447
444
|
next: ({ prepared, injectTaskNotification, deliveredAtTurnOpen, peerInboundChainRef, modelCatalog, notificationLane, unsubscribeTaskNotifications }) => {
|
|
448
445
|
const { runSourceTaskId, parentToolCallId, ident, emitDelegationLifecycle, loopLatch, stats, internalsNotifier, undrainedUserAtEnd, userInputLostAtEnd } = runIdentityWiring({
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Runner's per-session PERMISSION-MODE seats: the mode a session's CURRENT leg runs under, as the object a
|
|
3
|
+
* child's ceiling falls back to once the leg that spawned it has ended. A child's ceiling used to be the controller
|
|
4
|
+
* of the leg that spawned it for the child's whole life, so a session-scoped background child kept that leg's mode
|
|
5
|
+
* after the leg ended — the next leg of the same session, started in `plan`, did not reach it. The chain now carries
|
|
6
|
+
* a LEG ceiling: the spawning leg's live mode while that leg runs, and the session's seat after it. Each leg binds
|
|
7
|
+
* itself to its session's seat when its prepare commits, so the seat answers the most recently committed leg; a leg
|
|
8
|
+
* that is still running keeps answering its own children, whatever another leg of the session commits meanwhile.
|
|
9
|
+
*/
|
|
10
|
+
import type { PermissionMode } from "../ask-origin.js";
|
|
11
|
+
import type { PermissionModeReceipt } from "../mode-policy.js";
|
|
12
|
+
/** A child leg whose ceiling is a leg ceiling: its receipt is read before a ceiling move and re-judged after it. */
|
|
13
|
+
export interface ModeCeilingHolder {
|
|
14
|
+
receipt(): PermissionModeReceipt;
|
|
15
|
+
/** The ceiling's source moved; `before` is this holder's receipt under the previous source. */
|
|
16
|
+
ceilingRebound(before: PermissionModeReceipt): void;
|
|
17
|
+
}
|
|
18
|
+
/** One leg's ceiling, as the chain carries it (`InheritedGate.permissionModeCeiling`). */
|
|
19
|
+
export interface LegModeCeiling {
|
|
20
|
+
/** The spawning leg's live mode while it runs; the session's current leg's after it (`default` before any leg of
|
|
21
|
+
* the session committed — no child can read it then). */
|
|
22
|
+
readonly current: PermissionMode;
|
|
23
|
+
readonly sessionId: string;
|
|
24
|
+
/** A child leg holds the ceiling until `signal` aborts (its own leg's end): each move of the ceiling re-judges it. */
|
|
25
|
+
hold(holder: ModeCeilingHolder, signal: AbortSignal): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The seats of one Runner, keyed by session id and held WEAKLY: a seat lives while a leg ceiling holds it, and the
|
|
29
|
+
* next leg of the session finds the same object as long as any child still reads it. In-process only — a session
|
|
30
|
+
* continued on another Runner instance or in another process binds that Runner's seat.
|
|
31
|
+
*/
|
|
32
|
+
export declare class SessionModeSeats {
|
|
33
|
+
private readonly seats;
|
|
34
|
+
private readonly reaper;
|
|
35
|
+
private seatOf;
|
|
36
|
+
/**
|
|
37
|
+
* A leg's ceiling over its session's seat, the leg's commit (once its prepare succeeded: the leg becomes the
|
|
38
|
+
* session's current leg) and its end (once the leg has STOPPED — its loop settled, not merely cancelled: a cancelled
|
|
39
|
+
* leg whose tool is still pending keeps answering its own children).
|
|
40
|
+
*/
|
|
41
|
+
legCeiling(sessionId: string, leg: {
|
|
42
|
+
readonly current: PermissionMode;
|
|
43
|
+
}): {
|
|
44
|
+
ceiling: LegModeCeiling;
|
|
45
|
+
commit: () => void;
|
|
46
|
+
end: () => void;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
class Seat {
|
|
2
|
+
sessionId;
|
|
3
|
+
leg;
|
|
4
|
+
holders = new Set();
|
|
5
|
+
constructor(sessionId) {
|
|
6
|
+
this.sessionId = sessionId;
|
|
7
|
+
}
|
|
8
|
+
get current() {
|
|
9
|
+
return this.leg?.current ?? "default";
|
|
10
|
+
}
|
|
11
|
+
addHolder(holder, via, signal) {
|
|
12
|
+
if (signal.aborted)
|
|
13
|
+
return;
|
|
14
|
+
const entry = { holder, via };
|
|
15
|
+
this.holders.add(entry);
|
|
16
|
+
signal.addEventListener("abort", () => this.holders.delete(entry), { once: true });
|
|
17
|
+
}
|
|
18
|
+
moveCeiling(affected, move) {
|
|
19
|
+
const before = [...this.holders].filter((e) => affected(e.via)).map((e) => [e.holder, e.holder.receipt()]);
|
|
20
|
+
move();
|
|
21
|
+
for (const [holder, receipt] of before)
|
|
22
|
+
holder.ceilingRebound(receipt);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
class LegCeiling {
|
|
26
|
+
leg;
|
|
27
|
+
seat;
|
|
28
|
+
live = true;
|
|
29
|
+
constructor(leg, seat) {
|
|
30
|
+
this.leg = leg;
|
|
31
|
+
this.seat = seat;
|
|
32
|
+
}
|
|
33
|
+
get sessionId() {
|
|
34
|
+
return this.seat.sessionId;
|
|
35
|
+
}
|
|
36
|
+
get current() {
|
|
37
|
+
return this.live ? this.leg.current : this.seat.current;
|
|
38
|
+
}
|
|
39
|
+
hold(holder, signal) {
|
|
40
|
+
this.seat.addHolder(holder, this, signal);
|
|
41
|
+
}
|
|
42
|
+
commit() {
|
|
43
|
+
if (this.seat.leg === this.leg)
|
|
44
|
+
return;
|
|
45
|
+
this.seat.moveCeiling((via) => !via.live, () => (this.seat.leg = this.leg));
|
|
46
|
+
}
|
|
47
|
+
end() {
|
|
48
|
+
if (!this.live)
|
|
49
|
+
return;
|
|
50
|
+
this.seat.moveCeiling((via) => via === this, () => (this.live = false));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class SessionModeSeats {
|
|
54
|
+
seats = new Map();
|
|
55
|
+
reaper = new FinalizationRegistry(({ sessionId, ref }) => {
|
|
56
|
+
if (this.seats.get(sessionId) === ref)
|
|
57
|
+
this.seats.delete(sessionId);
|
|
58
|
+
});
|
|
59
|
+
seatOf(sessionId) {
|
|
60
|
+
const live = this.seats.get(sessionId)?.deref();
|
|
61
|
+
if (live !== undefined)
|
|
62
|
+
return live;
|
|
63
|
+
const seat = new Seat(sessionId);
|
|
64
|
+
const ref = new WeakRef(seat);
|
|
65
|
+
this.seats.set(sessionId, ref);
|
|
66
|
+
this.reaper.register(seat, { sessionId, ref });
|
|
67
|
+
return seat;
|
|
68
|
+
}
|
|
69
|
+
legCeiling(sessionId, leg) {
|
|
70
|
+
const ceiling = new LegCeiling(leg, this.seatOf(sessionId));
|
|
71
|
+
return { ceiling, commit: () => ceiling.commit(), end: () => ceiling.end() };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -2,6 +2,7 @@ import { canonicalizeTarget, isWinFormPath, pathFamilyOf, writeTargetPath } from
|
|
|
2
2
|
import { createCoarseCommandNamePolicy, namespacedCoveringEntries, namespacedCoveringHit } from "../tool-policy.js";
|
|
3
3
|
import { continuesLineage } from "../session-policy-store.js";
|
|
4
4
|
import { thrownText } from "../thrown-value.js";
|
|
5
|
+
import { toolNameSetHas } from "../tool-roster.js";
|
|
5
6
|
import { ruleDenialSentence } from "../rule-denial-sentence.js";
|
|
6
7
|
import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
|
|
7
8
|
import { declaredPathTargetOf, isProtectedWrite, pathConfinableWriteToolNames, skillScopeWriteToolNames } from "../tool-registry.js";
|
|
@@ -136,9 +137,9 @@ export function createSessionRulePolicy(rules, opts) {
|
|
|
136
137
|
async check(req, signal) {
|
|
137
138
|
const { toolDeny, toolAllow, toolDenyCovering, toolAllowCovering, cmdPolicy, allowDirs } = compile(await readRules(req.toolCallId, signal));
|
|
138
139
|
const toolName = req.toolName;
|
|
139
|
-
if (toolDeny
|
|
140
|
+
if (toolNameSetHas(toolDeny, req) || namespacedCoveringHit(toolDenyCovering, toolName))
|
|
140
141
|
return deny(req.toolName);
|
|
141
|
-
if (toolAllow && !toolAllow
|
|
142
|
+
if (toolAllow && !toolNameSetHas(toolAllow, req) && !namespacedCoveringHit(toolAllowCovering, toolName)) {
|
|
142
143
|
return deny(req.toolName, "it is not in the session-rule allowlist");
|
|
143
144
|
}
|
|
144
145
|
if (cmdPolicy) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The TaskStream façade's PERMISSION-MODE verb: `setPermissionMode` — the one entrance a running leg's mode changes
|
|
3
|
+
* through (a shell's mode toggle, a serving layer's `permission_mode` control frame). ONE read of the argument at the
|
|
4
|
+
* wire boundary, the steer lane's liveness gate, then the leg's live mode seat: the requested word is rewritten, the
|
|
5
|
+
* fold against the deployment's deny bits and the classifier face is re-judged (a fold is announced by the seat), a
|
|
6
|
+
* leg that became `auto` arms its classifier, and the same three-member receipt the wiring manifest carries comes
|
|
7
|
+
* back — EVERY call answers one, whether or not the effective mode moved, so a caller never has to render
|
|
8
|
+
* optimistically. The next tool call reads the new mode at the gate's arbitration point; a child leg derives its own
|
|
9
|
+
* effective mode from this seat on its next read (no forwarding frame).
|
|
10
|
+
*/
|
|
11
|
+
import type { TaskStream } from "../types.js";
|
|
12
|
+
import type { LiveHandle, TaskStreamLiveSeat } from "./contracts.js";
|
|
13
|
+
export interface StreamModeVerbInput {
|
|
14
|
+
/** borrowed-readonly — the stream's live state: `resultValue` (the finished-task refusal) and `handle` (the mode seat rides it). */
|
|
15
|
+
live: TaskStreamLiveSeat;
|
|
16
|
+
/** borrowed-readonly — the readiness promise the verb awaits before it touches the handle. */
|
|
17
|
+
ready: Promise<LiveHandle | undefined>;
|
|
18
|
+
/** borrowed-readonly — the driver's timeout race: a wedged prepare cannot hang the verb on `ready`. */
|
|
19
|
+
orTimeout: <T>(p: Promise<T>) => Promise<T | undefined>;
|
|
20
|
+
/** borrowed-readonly — the driver's refusal factory, the steer verb's own `steeringError` seat. */
|
|
21
|
+
steeringError: (msg: string, code?: string) => Error;
|
|
22
|
+
}
|
|
23
|
+
export interface StreamModeVerbResult {
|
|
24
|
+
/** The stream's `setPermissionMode` verb. */
|
|
25
|
+
setPermissionMode: TaskStream["setPermissionMode"];
|
|
26
|
+
}
|
|
27
|
+
export declare function streamModeVerb(input: StreamModeVerbInput): StreamModeVerbResult;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { isPermissionMode, PERMISSION_MODES } from "../ask-origin.js";
|
|
2
|
+
export function streamModeVerb(input) {
|
|
3
|
+
const { live, ready, orTimeout, steeringError } = input;
|
|
4
|
+
const setPermissionMode = async (mode) => {
|
|
5
|
+
const modeIn = mode;
|
|
6
|
+
if (!isPermissionMode(modeIn)) {
|
|
7
|
+
const e = new Error(`cannot set the permission mode: it must be one of ${PERMISSION_MODES.join("/")} (got ${JSON.stringify(modeIn)})`);
|
|
8
|
+
e.code = "steering.invalid_content";
|
|
9
|
+
throw e;
|
|
10
|
+
}
|
|
11
|
+
if (live.resultValue)
|
|
12
|
+
throw steeringError("the task has already finished");
|
|
13
|
+
const h = live.handle ?? (await orTimeout(ready));
|
|
14
|
+
if (!h)
|
|
15
|
+
throw steeringError("the task is not running");
|
|
16
|
+
return h.permissionMode.steer(modeIn);
|
|
17
|
+
};
|
|
18
|
+
return { setPermissionMode };
|
|
19
|
+
}
|
|
@@ -419,9 +419,9 @@ export interface AttachmentInputs {
|
|
|
419
419
|
todoMounted: boolean;
|
|
420
420
|
/** Whether the task-family tools are mounted. Both mounted ⇒ task_reminder wins (CC isTodoV2 branch). */
|
|
421
421
|
taskListMounted: boolean;
|
|
422
|
-
/**
|
|
423
|
-
* here simply stops the producer — no re-entry cycle
|
|
424
|
-
*
|
|
422
|
+
/** Whether the leg's effective permission mode is `plan` right now (the one mode seat, whichever entrance set it:
|
|
423
|
+
* the spec's word, a steer, the model's EnterPlanMode). A false here simply stops the producer — no re-entry cycle
|
|
424
|
+
* reset (documented deviation from CC's countPlanModeAttachmentsSinceLastExit). */
|
|
425
425
|
planActive: boolean;
|
|
426
426
|
/** SR-3 budget_usd lane (CC 198 `QFm` producer :480424-480428 + renderer :600225; 220 `_Fy` @609096
|
|
427
427
|
* with the mount at @607474): the run's cost progress against its $ ceiling, in USD. Only passed
|
|
@@ -761,7 +761,7 @@ function mcpDroppedToolsBound(inp) {
|
|
|
761
761
|
const single = first === undefined ? 0 : MCP_DROPPED_FIXED_MAX_BYTES + bytes(first.tool) + bytes(first.server) + bytes(first.reason) + 32;
|
|
762
762
|
return Math.max(MCP_DROPPED_BATCH_MAX_BYTES, single);
|
|
763
763
|
}
|
|
764
|
-
const PLAN_MODE_FULL_BODY = `Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system. This supercedes any other instructions you have received. Instead, you should: research and analyze with read-only tools, and when you have formed a concrete plan, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. That tool inherently requests plan approval — never ask for approval via plain text.`;
|
|
764
|
+
const PLAN_MODE_FULL_BODY = `Plan mode is active. The user indicated that they do not want you to execute yet -- you MUST NOT make any edits, run any non-readonly tools (including changing configs or making commits), or otherwise make any changes to the system — every such call asks the user first, and is refused where nobody can answer. This supercedes any other instructions you have received. Instead, you should: research and analyze with read-only tools, and when you have formed a concrete plan, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. That tool inherently requests plan approval — never ask for approval via plain text.`;
|
|
765
765
|
const PLAN_MODE_SPARSE_BODY = `Plan mode still active (see full instructions earlier in conversation). Read-only: no edits, commits, or config changes. When your plan is ready, present it with the ${PRESENT_PLAN_TOOL_NAME} tool for approval. Never ask about plan approval via plain text.`;
|
|
766
766
|
const TRUNCATION_MARKER = "\n…[truncated]";
|
|
767
767
|
const MIN_KEEP_BYTES = 160;
|
|
@@ -24,6 +24,12 @@ import type { ExecutionEnv } from "../../internal/harness.js";
|
|
|
24
24
|
import type { RunnerDeps } from "../runner-deps.js";
|
|
25
25
|
import type { ToolRoster } from "../tool-roster.js";
|
|
26
26
|
import { type WriteProtectionMount } from "../write-protect.js";
|
|
27
|
+
import type { ShellReadBoundary } from "../../tools/fs/shell-read-judge.js";
|
|
28
|
+
/** The leg's mount plus the same judge over a bare ABSOLUTE target — what the shell station asks about each write target
|
|
29
|
+
* a command names (`ShellReadBoundary.writeProtectionJudge`): `true` ⇔ a row covers it. */
|
|
30
|
+
export interface LegWriteProtection extends WriteProtectionMount {
|
|
31
|
+
readonly judgeTarget: NonNullable<ShellReadBoundary["writeProtectionJudge"]>;
|
|
32
|
+
}
|
|
27
33
|
export declare function mountWriteProtection(input: {
|
|
28
34
|
/** borrowed-readonly — the deployment seats: the table (`writeProtectedPaths`; absent ⇒ the default
|
|
29
35
|
* table, `[]` ⇒ opted out), the data root the memory library is materialized under
|
|
@@ -38,4 +44,4 @@ export declare function mountWriteProtection(input: {
|
|
|
38
44
|
roster: ToolRoster;
|
|
39
45
|
sessionId: string;
|
|
40
46
|
runId: string;
|
|
41
|
-
}):
|
|
47
|
+
}): LegWriteProtection | undefined;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { deliverEngineNotice } from "../engine-notice.js";
|
|
2
2
|
import { protectivePathTargetOf } from "../tool-registry.js";
|
|
3
|
-
import {
|
|
3
|
+
import { compileWriteProtection, writeProtectionMountOver } from "../write-protect.js";
|
|
4
4
|
const ABSENCE_REASON = { symlink: "unresolved-symlink", target: "unresolvable-target" };
|
|
5
5
|
export function mountWriteProtection(input) {
|
|
6
6
|
const announced = new Set();
|
|
7
|
-
const
|
|
7
|
+
const matcher = compileWriteProtection(input.deps.writeProtectedPaths, {
|
|
8
8
|
...(input.deps.memoryEngineDir !== undefined ? { dataRoot: input.deps.memoryEngineDir } : {}),
|
|
9
9
|
...(input.env !== undefined ? { env: input.env } : {}),
|
|
10
10
|
rootPath: input.rootPath,
|
|
@@ -25,7 +25,10 @@ export function mountWriteProtection(input) {
|
|
|
25
25
|
});
|
|
26
26
|
},
|
|
27
27
|
});
|
|
28
|
-
if (
|
|
28
|
+
if (matcher === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
const mount = { ...writeProtectionMountOver(matcher, input.rootPath), judgeTarget: async (target, opts) => (await matcher.judge(target, { ...(opts?.signal !== undefined ? { signal: opts.signal } : {}), ...(opts?.canonical === true ? { canonical: true } : {}) })).kind === "hit" };
|
|
31
|
+
if (mount.targetView !== "spelling-only")
|
|
29
32
|
return mount;
|
|
30
33
|
const covered = input.roster.entries
|
|
31
34
|
.filter((e) => [e.name, ...e.aliases].some((name) => {
|