@sema-agent/core 7.31.1 → 7.32.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 +64 -0
- package/dist/agents/inherited-tool-pool.d.ts +28 -0
- package/dist/agents/inherited-tool-pool.js +15 -0
- package/dist/agents/list-agents-tool.d.ts +7 -0
- package/dist/agents/list-agents-tool.js +9 -2
- package/dist/agents/peer-session-drain.d.ts +2 -0
- package/dist/agents/peer-session-drain.js +1 -0
- package/dist/agents/send-message-tool.d.ts +17 -0
- package/dist/agents/send-message-tool.js +11 -4
- package/dist/agents/subagent.d.ts +10 -1
- package/dist/agents/subagent.js +24 -8
- package/dist/core/ask-origin.d.ts +15 -6
- package/dist/core/ask-origin.js +15 -9
- package/dist/core/background-shell.d.ts +45 -0
- package/dist/core/background-shell.js +13 -0
- package/dist/core/checkpoint-store.d.ts +5 -0
- package/dist/core/checkpoint-store.js +3 -2
- package/dist/core/engine-notice.d.ts +9 -0
- package/dist/core/gate-lanes.js +1 -1
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/hooks.d.ts +2 -0
- package/dist/core/read-root-grant-screen.d.ts +23 -0
- package/dist/core/read-root-grant-screen.js +17 -0
- package/dist/core/runner/contracts.d.ts +1 -1
- package/dist/core/runner/prepare-artifact.d.ts +12 -7
- package/dist/core/runner/prepare-artifact.js +10 -8
- package/dist/core/runner/prepare-ask-lane.d.ts +3 -0
- package/dist/core/runner/prepare-ask-lane.js +5 -1
- package/dist/core/runner/prepare-boundary-parks.js +5 -5
- package/dist/core/runner/prepare-delegation-surface.d.ts +18 -0
- package/dist/core/runner/prepare-delegation-surface.js +6 -25
- package/dist/core/runner/prepare-hands-readface.d.ts +12 -89
- package/dist/core/runner/prepare-hands-readface.js +14 -48
- package/dist/core/runner/prepare-inherited-gate.d.ts +3 -1
- package/dist/core/runner/prepare-inherited-gate.js +2 -1
- package/dist/core/runner/prepare-park-ask.js +6 -4
- package/dist/core/runner/prepare-policy-chain.js +4 -0
- package/dist/core/runner/prepare-task.js +5 -3
- package/dist/core/runner/read-root-grant.d.ts +66 -0
- package/dist/core/runner/read-root-grant.js +77 -0
- package/dist/core/runner/run-notification-lane.d.ts +1 -1
- package/dist/core/runner/run-settle-and-teardown.js +3 -9
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/runner/send-message-continuation.d.ts +45 -0
- package/dist/core/runner/send-message-continuation.js +27 -0
- package/dist/core/runner/session-read-file-states.d.ts +91 -0
- package/dist/core/runner/session-read-file-states.js +43 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +19 -0
- package/dist/core/task-registry-monitor.js +3 -0
- package/dist/core/task-registry-shared.d.ts +16 -3
- package/dist/core/task-registry.d.ts +65 -29
- package/dist/core/task-registry.js +78 -28
- package/dist/core/task-spec.d.ts +5 -3
- package/dist/core/tool-policy.d.ts +23 -0
- package/dist/core/tool-policy.js +15 -4
- package/dist/core/tool-spec.d.ts +2 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +13 -3
- package/dist/engine/execution-env/node-execution-env.js +26 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/scenarios/full-body.d.ts +5 -2
- package/dist/scenarios/full-body.js +14 -7
- package/dist/tools/fs/fs-bash-background-text.d.ts +16 -0
- package/dist/tools/fs/fs-bash-background-text.js +22 -0
- package/dist/tools/fs/fs-bash.d.ts +6 -0
- package/dist/tools/fs/fs-bash.js +9 -27
- package/dist/tools/fs/fs-read.d.ts +2 -2
- package/dist/tools/fs/fs-search-tools.d.ts +3 -2
- package/dist/tools/fs/index.d.ts +8 -1
- package/dist/tools/fs/index.js +2 -3
- package/dist/tools/fs/repo-map.d.ts +2 -1
- package/dist/tools/fs/safety.d.ts +3 -1
- package/dist/tools/fs/safety.js +2 -1
- package/dist/tools/fs/shell-read-check.d.ts +3 -0
- package/dist/tools/fs/shell-read-check.js +1 -1
- package/dist/tools/fs/shell-read-establish.d.ts +3 -1
- package/dist/tools/fs/shell-read-establish.js +2 -2
- package/dist/tools/monitor.d.ts +5 -4
- package/dist/tools/monitor.js +5 -4
- package/dist/tools/todo.d.ts +3 -3
- package/dist/tools/todo.js +5 -4
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +13 -1
- package/wire-changes/core@7.32.0.json +850 -0
|
@@ -816,6 +816,9 @@ export type PendingAction = {
|
|
|
816
816
|
* when a person's rule also speaks — never a substitute for this one). Display metadata: no gate, CAS or
|
|
817
817
|
* resume path reads it. */
|
|
818
818
|
mandated?: true;
|
|
819
|
+
/** The PARK twin of `AskRequest.mandate` (same derivation, same precedent): the table word the bit stands on,
|
|
820
|
+
* present only when it is the whole reason. Written only as a member of the closed set. */
|
|
821
|
+
mandate?: import("./ask-origin.js").PersistedRuleMandate;
|
|
819
822
|
/** The PARK twin of `AskRequest.denialLimitFallback`: present
|
|
820
823
|
* ⇔ this parked ask is the classifier DENIAL-LIMIT fallback, carrying the counts that tripped
|
|
821
824
|
* the bound. Its `autoDenyAfterMs` is ALWAYS `0` on a row: the window is a fact of the ask's
|
|
@@ -1947,6 +1950,8 @@ export interface CheckpointSummary {
|
|
|
1947
1950
|
* OMITTED (the {@link toolCallId} absence-shape contract), screened to the literal `true` — a pre-#1079 row
|
|
1948
1951
|
* reads absent (unreported). ECHO-ONLY; nothing gates on it. */
|
|
1949
1952
|
mandated?: true;
|
|
1953
|
+
/** The row's `PendingAction.tool_approval.mandate`, echoed ONLY as a member of the closed set. ECHO-ONLY. */
|
|
1954
|
+
mandate?: import("./ask-origin.js").PersistedRuleMandate;
|
|
1950
1955
|
/** The row's `PendingAction.tool_approval.denialLimitFallback`, projected BESIDE the preview
|
|
1951
1956
|
* for the same one-call reason: a card that shows the fallback's counts and limit must not re-open
|
|
1952
1957
|
* the row to learn them (`autoDenyAfterMs` is `0` on every row — see the row member). Pure echo;
|
|
@@ -3,7 +3,7 @@ import { uuidv7 } from "../internal/harness.js";
|
|
|
3
3
|
import { PROBE_CAUSE_PATH_MAX, PROBE_REASON_MAX, inlineUntrusted, sanitizeUntrustedText } from "./untrusted-text.js";
|
|
4
4
|
import { carriesBidiControls } from "./tool-policy.js";
|
|
5
5
|
import { readDenialLimitFallback } from "./auto-mode.js";
|
|
6
|
-
import { isAskOrigin, isRuleStoreUnreadable } from "./ask-origin.js";
|
|
6
|
+
import { isAskOrigin, isPersistedRuleMandate, isRuleStoreUnreadable } from "./ask-origin.js";
|
|
7
7
|
import { checkpointExecutionRecorded, executionRecordDisposition, ExecutionOutcomeConflictError, } from "./checkpoint-execution-record.js";
|
|
8
8
|
import { renderUntrustedCommandText, stripFormatCharacters } from "./permission-rule-model.js";
|
|
9
9
|
import { redactSecrets } from "./untrusted-egress.js";
|
|
@@ -305,7 +305,8 @@ export function summarizeCheckpoint(cp) {
|
|
|
305
305
|
...(() => {
|
|
306
306
|
const origin = tool?.origin;
|
|
307
307
|
const ruleStoreUnreadable = tool?.ruleStoreUnreadable;
|
|
308
|
-
|
|
308
|
+
const mandate = tool?.mandate;
|
|
309
|
+
return { ...(isAskOrigin(origin) ? { origin } : {}), ...(isRuleStoreUnreadable(ruleStoreUnreadable) ? { ruleStoreUnreadable } : {}), ...(isPersistedRuleMandate(mandate) ? { mandate } : {}) };
|
|
309
310
|
})(),
|
|
310
311
|
...(hasBidi ? { hasBidiControls: true } : {}),
|
|
311
312
|
};
|
|
@@ -400,6 +400,15 @@ export interface EngineNotice {
|
|
|
400
400
|
* AFTER the loop's end is refused typed (`steering.not_running`, every verb alike) and mints
|
|
401
401
|
* nothing: the refusal itself is the loud answer.
|
|
402
402
|
*
|
|
403
|
+
* - `"approval.read_root_granted"` / `"approval.read_root_grant_rejected"` — an approver's `readRootGrant`
|
|
404
|
+
* (`AskOutcome`). GRANTED: it equalled the card's `readRootCandidate.dir` beside `allow: true`; this run's READ
|
|
405
|
+
* directories now include it (never the write roots; this run only); `detail: { dir, covers?, root, toolName,
|
|
406
|
+
* toolCallId, sessionId, runId, taskId? }`, `root` = the canonical spelling held. REJECTED: nothing changed,
|
|
407
|
+
* the verdict stands; `detail: { reason, offered?, requested?, …same ids }`, `reason` ∈ `on_refusal` |
|
|
408
|
+
* `not_a_string` | `no_candidate` | `mismatch` | `unresolvable` | `resolves_elsewhere` | `admits_artifact_store`
|
|
409
|
+
* (the directory contains the wired local artifact store, lies inside it or is it) | `no_read_roots`. Once per
|
|
410
|
+
* answered ask. Audience `"user"`.
|
|
411
|
+
*
|
|
403
412
|
* - `"checkpoint.execution_outcome_unrecorded"` — the resumed leg's pending-call resolver disposed
|
|
404
413
|
* the decided action (executed it, delivered the refusal, or vetoed the person's yes) and filed
|
|
405
414
|
* the frame's record through `CheckpointStore.recordExecutionOutcome`, and the store answered
|
package/dist/core/gate-lanes.js
CHANGED
|
@@ -467,7 +467,7 @@ export async function runGateLanes(pass) {
|
|
|
467
467
|
...(d.action === "ask" && d.denialLimitFallback !== undefined ? { denialLimitFallback: unarmedWindow(d.denialLimitFallback) } : {}),
|
|
468
468
|
...(d.action === "ask" && d.origin !== undefined ? { origin: d.origin } : {}),
|
|
469
469
|
...(d.action === "ask" && d.ruleStoreUnreadable !== undefined ? { ruleStoreUnreadable: d.ruleStoreUnreadable } : {}),
|
|
470
|
-
...(d.action === "ask" && d.mandated === true ? { mandated: true } : {}),
|
|
470
|
+
...(d.action === "ask" && d.mandated === true ? { mandated: true, ...(d.mandate !== undefined ? { mandate: d.mandate } : {}) } : {}),
|
|
471
471
|
});
|
|
472
472
|
if (suspendAsk && pass.decision.action === "ask") {
|
|
473
473
|
const parkArgs = [pass.req, pass.currentInput, safety, undefined, realApprovalOf(pass.decision), pass.decision.action === "ask" ? pass.decision.persistedRuleShadowed : undefined, pass.decision.action === "ask" ? pass.decision.decisionReason : undefined, pass.decision.action === "ask" ? pass.decision.probeReason : undefined, pass.decision.action === "ask" ? pass.decision.probeCause : undefined, pass.decision.action === "ask" ? pass.decision.segmentCoverage : undefined, pass.decision.action === "ask" ? pass.decision.matchedAskRule : undefined, pass.decision.action === "ask" ? pass.decision.probeMandated : undefined];
|
|
@@ -119,7 +119,7 @@ export type NoticeAudience = "user" | "operator";
|
|
|
119
119
|
* the union's catalog ({@link EngineNotice.code}). The groups below say only which family a row joins,
|
|
120
120
|
* so the catalog is not written a second time here and the two cannot drift.
|
|
121
121
|
*/
|
|
122
|
-
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_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.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "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", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "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", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
|
|
122
|
+
export declare const ENGINE_NOTICE_CODES: readonly ["config.autocompact_window_clamped", "config.env_timeout_discarded", "config.materialize_env_discarded", "config.models_swapped", "config.context_settings_swapped", "config.read_face_swapped", "config.read_face_deployment_clamped", "config.write_face_swapped", "config.write_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.task_root_not_canonical", "config.write_protection_target_view_absent", "config.write_protection_unresolved", "config.execution_env_capability_invalid", "config.tool_card_undeclared", "config.tool_face_undeclared", "config.tool_face_invalid", "config.forward_sink_failed", "config.durable_gate_unavailable", "config.peer_admission_out_of_range", "config.peer_lane_unmounted", "config.secret_env_scrubbed", "peer.inbound_disposition", "peer.held_settled", "peer.idle_subscription", "classifier.denial_limit", "classifier.fallback", "classifier.role_defaulted", "classifier.contract_broken", "checkpoint.execution_outcome_unrecorded", "delegation.transcript_integrity", "delegation.ask_unresolvable", "mcp.injection_dropped", "mcp.revocation_probe_failed", "mcp.server_redialed", "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", "memory.restore_withheld", "memory.project_scope_absent", "route.fallback_to_primary", "route.classifier_unroutable", "route.base_url_changed_key_unchanged", "route.response_model_mismatch", "task.user_steer_undrained", "task.user_followup_undrained", "steering.parked_input_blocked", "task.turn_interrupted", "task.halt_unconsumed", "task.late_approval", "task.interrupt_unconsumed", "approval.read_root_granted", "approval.read_root_grant_rejected", "memory.capture_opted_out", "memory.capture_optout_unpersisted", "skills.listing_truncated", "agents.listing_truncated", "tool_result.offload_put_failed"];
|
|
123
123
|
/** A code this engine mints (see {@link ENGINE_NOTICE_CODES}). NOT the type of
|
|
124
124
|
* `EngineNotice.code`, which stays `string` — a host forwarding its own notices through the same
|
|
125
125
|
* sink is a supported shape, and narrowing that field would break it. */
|
|
@@ -169,6 +169,8 @@ export const ENGINE_NOTICE_CODES = [
|
|
|
169
169
|
"task.halt_unconsumed",
|
|
170
170
|
"task.late_approval",
|
|
171
171
|
"task.interrupt_unconsumed",
|
|
172
|
+
"approval.read_root_granted",
|
|
173
|
+
"approval.read_root_grant_rejected",
|
|
172
174
|
"memory.capture_opted_out",
|
|
173
175
|
"memory.capture_optout_unpersisted",
|
|
174
176
|
"skills.listing_truncated",
|
|
@@ -193,6 +195,8 @@ const NOTICE_AUDIENCE_TABLE = {
|
|
|
193
195
|
"task.halt_unconsumed": "user",
|
|
194
196
|
"task.late_approval": "user",
|
|
195
197
|
"task.interrupt_unconsumed": "user",
|
|
198
|
+
"approval.read_root_granted": "user",
|
|
199
|
+
"approval.read_root_grant_rejected": "user",
|
|
196
200
|
"config.durable_gate_unavailable": "user",
|
|
197
201
|
"memory.capture_opted_out": "user",
|
|
198
202
|
"memory.capture_optout_unpersisted": "user",
|
package/dist/core/hooks.d.ts
CHANGED
|
@@ -1714,6 +1714,8 @@ export interface AskCarry {
|
|
|
1714
1714
|
/** The stamped mandate ({@link import("./tool-policy.js").AskRequest.mandated}) — the park site's
|
|
1715
1715
|
* rule-offer facts read it, so a parked mandated ask offers no rule, like the live card. */
|
|
1716
1716
|
readonly mandated?: true;
|
|
1717
|
+
/** The table word stamped beside it ({@link import("./tool-policy.js").AskRequest.mandate}), for the row's twin. */
|
|
1718
|
+
readonly mandate?: import("./ask-origin.js").PersistedRuleMandate;
|
|
1717
1719
|
}
|
|
1718
1720
|
/** What a durable row persists of an {@link AskCarry}: the fallback member COPIED (the row never aliases
|
|
1719
1721
|
* the gate's object; its window is already `0` — the gate's carry mint is the one writer of that
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The resolver's reading of an approver's `readRootGrant` (`AskOutcome`) against the card it answered. It only
|
|
3
|
+
* READS: widening a run's read roots is that run's own station's act, and the resolver holds no run.
|
|
4
|
+
*/
|
|
5
|
+
import type { ReadRootGrantCandidate } from "./types.js";
|
|
6
|
+
/** `granted` ⇔ an `allow` whose grant is byte-equal to the request's `readRootCandidate.dir` (the candidate rides
|
|
7
|
+
* back whole, `covers` included); otherwise `rejected` with ONE reason, the card's `offered` dir when it had one,
|
|
8
|
+
* and the `requested` grant when it was a string (verbatim host text — a notice bounds it). */
|
|
9
|
+
export type ReadRootGrantScreen = {
|
|
10
|
+
readonly verdict: "granted";
|
|
11
|
+
readonly candidate: ReadRootGrantCandidate;
|
|
12
|
+
} | {
|
|
13
|
+
readonly verdict: "rejected";
|
|
14
|
+
readonly reason: "on_refusal" | "not_a_string" | "no_candidate" | "mismatch";
|
|
15
|
+
readonly offered?: string;
|
|
16
|
+
readonly requested?: string;
|
|
17
|
+
};
|
|
18
|
+
/** Screen one grant read; `{}` when the answer carried none. */
|
|
19
|
+
export declare function readRootGrantSeat(grant: unknown, candidate: ReadRootGrantCandidate | undefined, allowed: boolean): {
|
|
20
|
+
readRootGrant?: ReadRootGrantScreen;
|
|
21
|
+
};
|
|
22
|
+
/** An engine-owned copy of a card's offer (one read per member), taken before an approver can touch the object. */
|
|
23
|
+
export declare function ownReadRootCandidate(c: ReadRootGrantCandidate | undefined): ReadRootGrantCandidate | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function readRootGrantSeat(grant, candidate, allowed) {
|
|
2
|
+
if (grant === undefined)
|
|
3
|
+
return {};
|
|
4
|
+
const offered = candidate !== undefined ? { offered: candidate.dir } : {};
|
|
5
|
+
const requested = typeof grant === "string" ? { requested: grant } : {};
|
|
6
|
+
const reason = !allowed ? "on_refusal" : typeof grant !== "string" ? "not_a_string" : candidate === undefined ? "no_candidate" : grant !== candidate.dir ? "mismatch" : undefined;
|
|
7
|
+
if (reason !== undefined)
|
|
8
|
+
return { readRootGrant: { verdict: "rejected", reason, ...offered, ...requested } };
|
|
9
|
+
return { readRootGrant: { verdict: "granted", candidate: candidate } };
|
|
10
|
+
}
|
|
11
|
+
export function ownReadRootCandidate(c) {
|
|
12
|
+
if (c === undefined)
|
|
13
|
+
return undefined;
|
|
14
|
+
const dir = c.dir;
|
|
15
|
+
const covers = c.covers;
|
|
16
|
+
return typeof dir === "string" ? { dir, clearsThisAsk: true, ...(covers === "exact" ? { covers } : {}) } : undefined;
|
|
17
|
+
}
|
|
@@ -52,7 +52,7 @@ import type { WiringManifest } from "../wiring-manifest.js";
|
|
|
52
52
|
import type { ActiveSkillFrame } from "./active-skill-scope.js";
|
|
53
53
|
import type { BudgetAxis } from "./assemble-result.js";
|
|
54
54
|
import type { GitStatusLaneRef } from "./git-status-frame.js";
|
|
55
|
-
import type { SessionReadFileStates } from "./
|
|
55
|
+
import type { SessionReadFileStates } from "./session-read-file-states.js";
|
|
56
56
|
import type { BlockedRef, OutputRef, SkillListingEntry } from "./synthetic-tools.js";
|
|
57
57
|
import type { createAttachmentState, WriteFamily } from "./turn-attachments.js";
|
|
58
58
|
import type { cacheFamilyOf } from "./usage-accounting.js";
|
|
@@ -6,11 +6,10 @@
|
|
|
6
6
|
* seat uses. The tool's local-file read goes through the run's fs fence (root containment, read-deny, read
|
|
7
7
|
* face) exactly as `Read` does, then reads the bytes once from the execution env. A local stub's store
|
|
8
8
|
* (`capabilities.storageDir`) must lie OUTSIDE every root the run may write or read — a ledger the model can
|
|
9
|
-
* edit through the fs tools is not a trust boundary —
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* containment claim is made against it.
|
|
9
|
+
* edit or read through the fs tools is not a trust boundary — judged by {@link rootAdmittingArtifactStore} at
|
|
10
|
+
* every point a root is admitted: here over the prepare-time roots (a store that overlaps one refuses the mount,
|
|
11
|
+
* naming both), and at an in-flight read-root grant over the granted root (the grant is refused). The phase
|
|
12
|
+
* publishes the declared store into the run's {@link ArtifactStoreRef} for that second point.
|
|
14
13
|
* Synchronous: the stretch has no await (the file read is a closure the tool calls later).
|
|
15
14
|
*/
|
|
16
15
|
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
@@ -19,6 +18,7 @@ import { type ArtifactPlane } from "../artifact-host.js";
|
|
|
19
18
|
import { RosterBuilder } from "../tool-roster.js";
|
|
20
19
|
import type { AgentTool, RunnerDeps } from "../types.js";
|
|
21
20
|
import type { ReadDenyMatcher, ContainmentFace } from "../../tools/fs/index.js";
|
|
21
|
+
import { type ArtifactStoreRef } from "./read-root-grant.js";
|
|
22
22
|
import type { Prepared } from "./contracts.js";
|
|
23
23
|
export interface PrepareArtifactInput {
|
|
24
24
|
/** borrowed-readonly — the deployment's hosting seat and the notice sink the refusal arm announces on. */
|
|
@@ -38,8 +38,9 @@ export interface PrepareArtifactInput {
|
|
|
38
38
|
executionEnv: ExecutionEnv;
|
|
39
39
|
/** borrowed-readonly — the canonical task root the fs fence contains reads to; undefined ⇒ no local-file publish. */
|
|
40
40
|
rootCanonical: string | undefined;
|
|
41
|
-
/** borrowed-readonly — the extra containment roots (write and read-only) the fence admits
|
|
42
|
-
|
|
41
|
+
/** borrowed-readonly — the extra containment roots (write and read-only) the fence admits, read per call (the run's
|
|
42
|
+
* live set: an in-flight read-root grant widens it). */
|
|
43
|
+
additionalRootsCanonical: () => readonly string[];
|
|
43
44
|
/** borrowed-readonly — the read-deny matcher in force for the run, when any. */
|
|
44
45
|
readDenyMatcher: ReadDenyMatcher | undefined;
|
|
45
46
|
/** borrowed-readonly — the resolved read face (`"open"` lifts root containment for reads). */
|
|
@@ -52,6 +53,10 @@ export interface PrepareArtifactInput {
|
|
|
52
53
|
firstPartyOffload: (tool: AgentTool) => AgentTool;
|
|
53
54
|
/** borrowed-mutable — the env-hand membership set the memory content-origin face reads (a tool that reads the execution env's files is a hand); writer here: `add("Artifact")` on the mount. */
|
|
54
55
|
envHandToolNames: Set<string>;
|
|
56
|
+
/** borrowed-mutable — the run's store cell the in-flight read-root grant reads (minted by the hands phase; absent on
|
|
57
|
+
* a hands-less leg, which has no grant); writer here: the declared `storageDir`'s physical path once the
|
|
58
|
+
* declaration validates. */
|
|
59
|
+
artifactStoreRef?: ArtifactStoreRef;
|
|
55
60
|
}
|
|
56
61
|
export interface PrepareArtifactResult {
|
|
57
62
|
/** owned — the mounted face's plane, or undefined when nothing mounted (absent seat, or an invalid one). */
|
|
@@ -5,11 +5,12 @@ import { RosterBuilder } from "../tool-roster.js";
|
|
|
5
5
|
import { defineTool } from "../tools.js";
|
|
6
6
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
7
7
|
import { physicalPathOfExisting } from "../physical-path.js";
|
|
8
|
-
import {
|
|
8
|
+
import { resolveKey, violationDetails, violationText } from "../../tools/fs/safety.js";
|
|
9
|
+
import { rootAdmittingArtifactStore } from "./read-root-grant.js";
|
|
9
10
|
import { createArtifactTool } from "../../tools/artifact/artifact-tool.js";
|
|
10
11
|
import { thrownText } from "../thrown-value.js";
|
|
11
12
|
export function prepareArtifact(input) {
|
|
12
|
-
const { deps, runId, taskId, sessionId, agentName, onAsk, executionEnv, rootCanonical, additionalRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames } = input;
|
|
13
|
+
const { deps, runId, taskId, sessionId, agentName, onAsk, executionEnv, rootCanonical, additionalRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames, artifactStoreRef } = input;
|
|
13
14
|
const host = deps.artifactHost;
|
|
14
15
|
if (host === undefined)
|
|
15
16
|
return { artifactPlane: undefined };
|
|
@@ -25,12 +26,13 @@ export function prepareArtifact(input) {
|
|
|
25
26
|
if (!verdict.ok)
|
|
26
27
|
return refuse(verdict.problem);
|
|
27
28
|
const storageDir = verdict.capabilities.storageDir;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
const storePhysical = storageDir !== undefined && rootCanonical !== undefined ? physicalPathOfExisting(storageDir) : undefined;
|
|
30
|
+
if (artifactStoreRef !== undefined)
|
|
31
|
+
artifactStoreRef.current = storePhysical;
|
|
32
|
+
if (storePhysical !== undefined && rootCanonical !== undefined) {
|
|
33
|
+
const overlapping = rootAdmittingArtifactStore(storePhysical, [rootCanonical, ...additionalRootsCanonical()]);
|
|
34
|
+
if (overlapping !== undefined)
|
|
35
|
+
return refuse(`the local artifact store ${storageDir} and a root this run may reach through its file tools (${overlapping}) overlap — the store lies inside a root, or the root inside the store; put the store outside every task root and additional directory`);
|
|
34
36
|
}
|
|
35
37
|
try {
|
|
36
38
|
host.onMount?.(runId);
|
|
@@ -26,6 +26,7 @@ import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
|
26
26
|
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
27
27
|
import type { AskLane, Prepared } from "./contracts.js";
|
|
28
28
|
import { consumeInheritedAskGrant } from "./inherited-ask-grants.js";
|
|
29
|
+
import { type GrantReadRoot } from "./read-root-grant.js";
|
|
29
30
|
import type { createRuleOffersOf } from "./permission-rule-lanes.js";
|
|
30
31
|
export interface PrepareAskLaneInput {
|
|
31
32
|
/** borrowed-readonly — the wiring-manifest phase's ONE activation predicate; false ⇒ this phase builds nothing and
|
|
@@ -100,6 +101,8 @@ export interface PrepareAskLaneInput {
|
|
|
100
101
|
hookTimeoutMs: number;
|
|
101
102
|
/** borrowed-readonly — the own hook-crash reporter (the deny observer's swallow-guarded report sink). */
|
|
102
103
|
notifyOwnHookCrash: (err: unknown) => void;
|
|
104
|
+
/** borrowed-readonly — the hands phase's in-flight read-root widening (an approver's accepted `readRootGrant`), or undefined (hands-less). */
|
|
105
|
+
grantReadRoot: GrantReadRoot | undefined;
|
|
103
106
|
}
|
|
104
107
|
export interface PrepareAskLaneResult {
|
|
105
108
|
/** owned — the lane, or undefined exactly when `gateMachineryActive` is false (then the suspend saga, the boundary
|
|
@@ -5,6 +5,7 @@ import { composeCallSignal, raceAbort } from "./abort-race.js";
|
|
|
5
5
|
import { askMandateMember } from "../ask-origin.js";
|
|
6
6
|
import { gateAskCarry, lateAskSettlementObserver } from "./denial-limit-arms.js";
|
|
7
7
|
import { consumeInheritedAskGrant } from "./inherited-ask-grants.js";
|
|
8
|
+
import { settleReadRootGrant } from "./read-root-grant.js";
|
|
8
9
|
import { asThrownError } from "../thrown-value.js";
|
|
9
10
|
function createDenyObserverNotifier(hooks, hookTimeoutMs, signal, report) {
|
|
10
11
|
if (hooks?.permissionDenied === undefined)
|
|
@@ -57,7 +58,7 @@ function resolveApprovalPreview(tools, toolName, args) {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
export function prepareAskLane(input) {
|
|
60
|
-
const { gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash } = input;
|
|
61
|
+
const { gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot } = input;
|
|
61
62
|
if (!gateMachineryActive)
|
|
62
63
|
return { askLane: undefined };
|
|
63
64
|
const composedCallSignal = (callSignal) => composeCallSignal(abortController.signal, callSignal);
|
|
@@ -109,6 +110,9 @@ export function prepareAskLane(input) {
|
|
|
109
110
|
...(decision.action === "ask" && decision.ruleEvidence !== undefined ? { ruleEvidence: decision.ruleEvidence } : {}),
|
|
110
111
|
}, onAsk, composedCallSignal(callSignal), lateAskSettlementObserver({ toolName: req.toolName, toolCallId: req.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
111
112
|
const waitMs = Math.max(0, now() - t0);
|
|
113
|
+
const granting = settleReadRootGrant(resolved.readRootGrant, grantReadRoot, { toolName: req.toolName, toolCallId: req.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, signal: composedCallSignal(callSignal) });
|
|
114
|
+
if (granting !== undefined)
|
|
115
|
+
await granting;
|
|
112
116
|
if (resolved.approverUnavailable !== true && resolved.settlement?.kind !== "task_aborted") {
|
|
113
117
|
humanReviewRef.count += 1;
|
|
114
118
|
humanReviewRef.totalWaitMs += waitMs;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
|
|
1
|
+
import { hasBackgroundShell, sweepBackgroundShells, sweepParkOnlyBackgroundShells } from "../background-shell.js";
|
|
2
2
|
import { checkpointVersionFor, debitLedger, mintCheckpointId, mintCheckpointToken, } from "../checkpoint-store.js";
|
|
3
3
|
import { defaultTaskRegistry } from "../task-registry.js";
|
|
4
4
|
import { checkpointScopeOf } from "./checkpoint-scope.js";
|
|
@@ -36,8 +36,8 @@ export function prepareBoundaryParks(input) {
|
|
|
36
36
|
remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value };
|
|
37
37
|
}
|
|
38
38
|
else if (parkOnlyRemoteEnv !== undefined) {
|
|
39
|
-
if (hasBackgroundShell(parkOnlyRemoteEnv)
|
|
40
|
-
await
|
|
39
|
+
if (hasBackgroundShell(parkOnlyRemoteEnv))
|
|
40
|
+
await sweepParkOnlyBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry, { task: spec.retainBackgroundProcesses === true });
|
|
41
41
|
remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
|
|
42
42
|
}
|
|
43
43
|
const token = mintCheckpointToken();
|
|
@@ -119,8 +119,8 @@ export function prepareBoundaryParks(input) {
|
|
|
119
119
|
remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value };
|
|
120
120
|
}
|
|
121
121
|
else if (parkOnlyRemoteEnv !== undefined) {
|
|
122
|
-
if (hasBackgroundShell(parkOnlyRemoteEnv)
|
|
123
|
-
await
|
|
122
|
+
if (hasBackgroundShell(parkOnlyRemoteEnv))
|
|
123
|
+
await sweepParkOnlyBackgroundShells(parkOnlyRemoteEnv, defaultTaskRegistry, { task: spec.retainBackgroundProcesses === true });
|
|
124
124
|
remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
|
|
125
125
|
}
|
|
126
126
|
const token = mintCheckpointToken();
|
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PrepareTask's DELEGATION-SURFACE phase, verbatim from the driver: the surface's two doors
|
|
3
|
+
* (the background/workflow door the read/kill faces keep; the delegated-child arm SendMessage additionally mounts
|
|
4
|
+
* on), the TaskOutput / TaskStop mounts, the SendMessage mount with its tier-3 revival spawner and peer seats, the
|
|
5
|
+
* AgentTranscript residual lane, the peer lane's ListAgents, the Monitor tool, the EnterWorktree / ExitWorktree pair
|
|
6
|
+
* with the worktree session seat it seeds from the checkpoint or the workspace snapshot, and the offload reader. The
|
|
7
|
+
* revival spawner and the continuation tier it yields are machinery below (send-message-continuation.ts). The
|
|
8
|
+
* interface is the dependency list the segment had implicitly.
|
|
9
|
+
*
|
|
10
|
+
* SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await, so the phase adds no yield between the hands
|
|
11
|
+
* mount before it and the question-face phase after it (a stretch with no await is extracted as
|
|
12
|
+
* a sync function). Read-stability of the host handles for the call: {@link RunInternals}
|
|
13
|
+
* (`@contract prepare.deps-read-stable`).
|
|
14
|
+
*
|
|
15
|
+
* The roster (`tools`) is borrowed-mutable here — the writer table lives on {@link Prepared.tools};
|
|
16
|
+
* this phase's rows are the eight mounts named above. `childPolicy` is NOT a seat yet: the
|
|
17
|
+
* delegation carriers still ride the enriched ctx's individual clamp fields, as they did in the driver.
|
|
18
|
+
*/
|
|
1
19
|
import type { AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
|
|
2
20
|
import { type PeerLaneRefs } from "../../agents/peer-session-drain.js";
|
|
3
21
|
import type { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
2
1
|
import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
|
|
3
2
|
import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
4
3
|
import { listAgentsMountable, mountListAgents, peerLaneSendMessageSeats } from "../../agents/peer-session-drain.js";
|
|
@@ -11,30 +10,12 @@ import { createReadToolResultTool } from "../tool-result-store.js";
|
|
|
11
10
|
import { emitTrace } from "../trace.js";
|
|
12
11
|
import { deliverEngineNotice } from "../types.js";
|
|
13
12
|
import { RosterBuilder } from "../tool-roster.js";
|
|
14
|
-
|
|
15
|
-
if (delegationForRevive === undefined)
|
|
16
|
-
return undefined;
|
|
17
|
-
return async (req) => {
|
|
18
|
-
const out = await delegationForRevive.execute({
|
|
19
|
-
description: req.row.description ?? "revived teammate",
|
|
20
|
-
prompt: req.prompt,
|
|
21
|
-
run_in_background: true,
|
|
22
|
-
...(req.row.agentType !== undefined ? { subagent_type: req.row.agentType } : {}),
|
|
23
|
-
...(req.row.name !== undefined ? { name: req.row.name } : {}),
|
|
24
|
-
...(req.row.model !== undefined ? { model: req.row.model } : {}),
|
|
25
|
-
}, enrichSpecToolCtx({ toolCallId: `rv-${randomBytes(8).toString("hex")}`, reviveClaim: { row: req.row, rev: req.rev, ...(req.peerSeed !== undefined ? { peerSeed: req.peerSeed } : {}) } }));
|
|
26
|
-
const o = (typeof out === "string" ? { content: out } : out);
|
|
27
|
-
return {
|
|
28
|
-
content: typeof o.content === "string" ? o.content : JSON.stringify(o.content),
|
|
29
|
-
...(o.isError === true ? { isError: true } : {}),
|
|
30
|
-
...(o.details !== undefined ? { details: o.details } : {}),
|
|
31
|
-
};
|
|
32
|
-
};
|
|
33
|
-
}
|
|
13
|
+
import { sendMessageContinuationSeats } from "./send-message-continuation.js";
|
|
34
14
|
export function prepareDelegationSurface(input) {
|
|
35
15
|
const { spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, taskRootCanonical, executionEnv, runnerSelf, backgroundTaskToolsActive, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, sessionBox, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle } = input;
|
|
36
16
|
let worktreeSessionRef;
|
|
37
17
|
const delegationSurfaceActive = backgroundTaskToolsActive || workflowToolsActive;
|
|
18
|
+
let sendMessageTier;
|
|
38
19
|
if (delegationSurfaceActive || internals?.parentNotify !== undefined || peerLaneActive) {
|
|
39
20
|
if (delegationSurfaceActive) {
|
|
40
21
|
roster.mountAll([
|
|
@@ -44,8 +25,8 @@ export function prepareDelegationSurface(input) {
|
|
|
44
25
|
}
|
|
45
26
|
if (runnerSelf && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME)) {
|
|
46
27
|
axisExplicitNegatives.set(SEND_MESSAGE_TOOL_NAME, { ...axisExplicitNegatives.get(SEND_MESSAGE_TOOL_NAME), egress: false });
|
|
47
|
-
const
|
|
48
|
-
|
|
28
|
+
const { reviveSpawn, tier } = sendMessageContinuationSeats({ specTools: spec.tools ?? [], agentStore: deps.backgroundAgentStore, mailbox: deps.mailboxStore, runner: runnerSelf, enrichSpecToolCtx });
|
|
29
|
+
sendMessageTier = tier;
|
|
49
30
|
roster.mount(firstPartyOffload(createSendMessageTool({
|
|
50
31
|
runner: runnerSelf,
|
|
51
32
|
registry: defaultTaskRegistry,
|
|
@@ -103,7 +84,7 @@ export function prepareDelegationSurface(input) {
|
|
|
103
84
|
toolEffects.set(READ_INBOX_TOOL_NAME, "read");
|
|
104
85
|
roster.mount(firstPartyOffload(createReadInboxTool({ pull: () => peerLaneRefs.pull })), { source: "builtin", mountedBy: "peerLane" });
|
|
105
86
|
}
|
|
106
|
-
const listAgents = (peerLaneActive || delegationSurfaceActive) && peerSendMessageBuiltIn && listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) ? mountListAgents({ ...(peerLaneActive ? { peerDirectory: deps.peerDirectory } : {}), freshWindowMs: resolvePeerLaneConfig(deps.peerLane, deps.onNotice).listingFreshWindowMs, sessionId, scope: taskScope, hostTaskId, ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}), ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}), registry: defaultTaskRegistry, ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}), specTools: spec.tools ?? [], toolEffects }) : undefined;
|
|
87
|
+
const listAgents = (peerLaneActive || delegationSurfaceActive) && peerSendMessageBuiltIn && listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) ? mountListAgents({ ...(peerLaneActive ? { peerDirectory: deps.peerDirectory } : {}), freshWindowMs: resolvePeerLaneConfig(deps.peerLane, deps.onNotice).listingFreshWindowMs, sessionId, scope: taskScope, hostTaskId, ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}), ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}), registry: defaultTaskRegistry, ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}), ...(sendMessageTier !== undefined ? { continuationTier: sendMessageTier } : {}), specTools: spec.tools ?? [], toolEffects }) : undefined;
|
|
107
88
|
if (listAgents !== undefined)
|
|
108
89
|
roster.mount(firstPartyOffload(listAgents), { source: "builtin", mountedBy: "peerLane" });
|
|
109
90
|
if (backgroundTaskToolsActive) {
|
|
@@ -118,7 +99,7 @@ export function prepareDelegationSurface(input) {
|
|
|
118
99
|
...(handsCwdRef !== undefined ? { cwdRef: handsCwdRef } : {}),
|
|
119
100
|
rootCanonical: taskRootCanonical,
|
|
120
101
|
...(offloadStore !== undefined ? { toolResultStore: offloadStore } : {}),
|
|
121
|
-
|
|
102
|
+
retainBackgroundProcesses: spec.retainBackgroundProcesses === true,
|
|
122
103
|
})), { source: "builtin", mountedBy: "backgroundTasks" });
|
|
123
104
|
}
|
|
124
105
|
if (handsCwdRef !== undefined) {
|
|
@@ -5,6 +5,7 @@ import type { RunnerDeps, ShellGateDoctrine, TaskSpec } from "../types.js";
|
|
|
5
5
|
import type { Model } from "../../internal/llm.js";
|
|
6
6
|
import type { AgentHarness, AgentTool, ExecutionEnv, WorkspaceState } from "../../internal/harness.js";
|
|
7
7
|
import type { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
|
|
8
|
+
import { type ArtifactStoreRef, type GrantReadRoot } from "./read-root-grant.js";
|
|
8
9
|
import type { StoredSession } from "../session.js";
|
|
9
10
|
import type { IrreversibilityTier, PrepareResume, ReversibilityProbes, RunInternals, ToolFaceSnapshot, ToolProgressLane } from "./contracts.js";
|
|
10
11
|
import { RosterBuilder } from "../tool-roster.js";
|
|
@@ -14,91 +15,6 @@ import { RosterBuilder } from "../tool-roster.js";
|
|
|
14
15
|
* the console arm's key is the process itself, which only this seam can refresh. Moved WITH the
|
|
15
16
|
* phase; prepare-task.ts keeps a re-export so the existing import sites stand. */
|
|
16
17
|
export declare function __resetReadFaceClampAnnouncement(): void;
|
|
17
|
-
/**
|
|
18
|
-
* The Runner's per-session read-file-state seats (CC parity: readFileState is minted
|
|
19
|
-
* once per REPL session and mutated across turns; it has NO turn-boundary and NO abort clear site —
|
|
20
|
-
* its only clears are compaction/fork/subagent-cleanup/dispose). sema's map stays per-task (the §11
|
|
21
|
-
* ruling-A closure wiring), so the SESSION scope lives here instead: prepare adopts each task's live
|
|
22
|
-
* map under its sessionId, and the next same-session, same-Runner task INHERITS it (through the same
|
|
23
|
-
* containment judgment the transcript-replay seed applies — see `inheritSessionReadFileState`).
|
|
24
|
-
*
|
|
25
|
-
* This seat is a CACHE, never an authority:
|
|
26
|
-
* - a durable resume's checkpoint seed stays the sole authority on that leg (the seed was cut
|
|
27
|
-
* atomically with the leaf; this seat may be older OR newer than it);
|
|
28
|
-
* - a cross-process continuation misses here and falls back to the transcript replay (rung-2);
|
|
29
|
-
* - eviction (the LRU cap, a Runner restart) degrades to that same replay — never to wrong state.
|
|
30
|
-
*
|
|
31
|
-
* Bounded: an LRU over sessionIds (insertion-order refresh on read), because the SessionStore's own
|
|
32
|
-
* eviction is not observable from here — a dropped seat costs one transcript replay, nothing else.
|
|
33
|
-
* The stored value is the task's LIVE map (object identity), so in-turn mutations — tool write-backs,
|
|
34
|
-
* and above all the compaction clear — are the seat's truth by construction; a snapshot copy
|
|
35
|
-
* here would resurrect summarized-away reads on the next turn.
|
|
36
|
-
*
|
|
37
|
-
* BRANCH BINDING: a sessionId alone does not name a transcript — the same id
|
|
38
|
-
* can be rebranched (`resumeAt`), rewound out-of-band, or DELETED and re-created (`release()` + the
|
|
39
|
-
* default create-on-miss acquire), and the seat's map would then vouch for reads the CURRENT branch
|
|
40
|
-
* never saw. Each seat therefore records `anchorLeafId` — a leaf on the lineage its task produced —
|
|
41
|
-
* and inheritance must first prove the CURRENT branch still descends from that anchor
|
|
42
|
-
* (see {@link sessionSeatAnchorHolds}). The anchor is written twice: at ADOPT (the leaf at prepare;
|
|
43
|
-
* `null` when the branch was still empty) and SEALED at the task's settle to the task's own tail
|
|
44
|
-
* (`reanchor`). A `null` anchor is NEVER inheritable ("the branch is non-empty now" proves
|
|
45
|
-
* nothing about WHOSE messages those are — another Runner repopulating a released id, or a session
|
|
46
|
-
* import, makes a non-empty branch that never contained this seat's reads). An unprovable anchor
|
|
47
|
-
* falls back to the transcript replay, which reads the current branch by construction — cost, never
|
|
48
|
-
* wrongness.
|
|
49
|
-
*/
|
|
50
|
-
export declare class SessionReadFileStates {
|
|
51
|
-
private readonly seats;
|
|
52
|
-
private static readonly CAP;
|
|
53
|
-
/** The session's seat row, or undefined (cross-process / evicted / first turn) ⇒ caller replays.
|
|
54
|
-
* The caller must verify the anchor against the session's CURRENT branch before using `state`. */
|
|
55
|
-
get(sessionId: string): {
|
|
56
|
-
state: ReadFileState;
|
|
57
|
-
anchorLeafId: string | null;
|
|
58
|
-
} | undefined;
|
|
59
|
-
/** Adopt THIS task's live map as the session's seat (every hands leg: fresh, continuation, resume).
|
|
60
|
-
* `anchorLeafId` = the session's leaf as read at THIS prepare (null = the branch was empty; a
|
|
61
|
-
* null-anchored seat is not inheritable until the settle-time {@link reanchor} names a real leaf). */
|
|
62
|
-
adopt(sessionId: string, state: ReadFileState, anchorLeafId: string | null): void;
|
|
63
|
-
/** The settle-time SEAL: re-anchor the seat to the finished task's own tail, so a
|
|
64
|
-
* first-task (null-anchor) seat becomes inheritable only through a leaf its task provably wrote.
|
|
65
|
-
* Identity-guarded: touches the row only while it still holds THIS task's map (a later prepare on
|
|
66
|
-
* the same session has already replaced the seat and owns its own anchor). */
|
|
67
|
-
reanchor(sessionId: string, state: ReadFileState, anchorLeafId: string): void;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Does the session's CURRENT branch still descend from the seat's anchor?
|
|
71
|
-
* TRUE is the licence to inherit; anything unprovable answers FALSE (the caller then replays the
|
|
72
|
-
* transcript — the fail-closed direction costs a rebuild, never admits a wrong entry).
|
|
73
|
-
*
|
|
74
|
-
* - anchor `null`: NEVER inheritable (r2). The seat was adopted on an empty branch and its task
|
|
75
|
-
* never sealed (crashed pre-settle, or the leaf read failed) — "the branch is non-empty now"
|
|
76
|
-
* would also be true of a released-and-repopulated id or an imported log, whose messages are not
|
|
77
|
-
* this seat's lineage. The settle-time reanchor is what upgrades such a seat to a real leaf.
|
|
78
|
-
* - anchor non-null: holds iff walking parentIds from the current leaf reaches the anchor within
|
|
79
|
-
* a bounded number of steps (the walk length is one task's appends in the ordinary continuation).
|
|
80
|
-
* A rebranch/rewind moved the leaf OFF the anchor's subtree ⇒ the walk misses ⇒ no inherit; a
|
|
81
|
-
* recreated/foreign log does not contain the anchor ⇒ same answer; a re-IMPORT of the same log
|
|
82
|
-
* keeps the anchor's lineage ⇒ inheritance stays correct there by the same test.
|
|
83
|
-
* - a COMPACTION on the walked segment (A-082 idx2): refuses, even though the branch is perfectly
|
|
84
|
-
* intact. The walked segment is exactly what landed AFTER this seat was anchored, and a
|
|
85
|
-
* compaction there summarized older Read results OUT of the context the model will be shown —
|
|
86
|
-
* the very state the compaction clear removes from the live map at the landing site. That clear reaches the seat
|
|
87
|
-
* only when the SAME Runner did both (the seat IS that map, by object identity); a second Runner
|
|
88
|
-
* on the shared durable session clears its own map and commits the compaction as an ORDINARY
|
|
89
|
-
* LINEAR APPEND, which every other test here passes. Inheriting across it would let requireRead
|
|
90
|
-
* and checkStale both pass on a read whose card the model no longer has — a blind edit from a
|
|
91
|
-
* summarized-away recollection, the exact failure the compaction clear exists to prevent. Refusing
|
|
92
|
-
* costs one transcript replay, and the replay reads the POST-compaction branch by construction,
|
|
93
|
-
* so the fail-closed direction is also the CORRECT state. A compaction at or ABOVE the anchor is
|
|
94
|
-
* not on the walked segment and does not refuse: the seat that observed it already carries its
|
|
95
|
-
* clear (P-483-4), and an ordinary foreign turn with no compaction still inherits (G-483-5) —
|
|
96
|
-
* dropping every seat a foreign Runner touched would restore the cross-turn read-state amnesia this seat-anchor check already ended.
|
|
97
|
-
*/
|
|
98
|
-
export declare function sessionSeatAnchorHolds(getEntry: (id: string) => Promise<{
|
|
99
|
-
parentId: string | null;
|
|
100
|
-
type?: string;
|
|
101
|
-
} | null | undefined>, anchorLeafId: string | null, currentLeafId: string | null): Promise<boolean>;
|
|
102
18
|
export interface PrepareHandsReadFaceInput {
|
|
103
19
|
/** owned (value) — `ownedEnv !== undefined || deps.executionEnv !== undefined`, computed in the
|
|
104
20
|
* driver: the phase's own branch condition (hands mount vs the hands-less resolver arm). */
|
|
@@ -149,8 +65,9 @@ export interface PrepareHandsReadFaceInput {
|
|
|
149
65
|
* (the Read tool's document-degradation fork). */
|
|
150
66
|
model: Model;
|
|
151
67
|
/** borrowed-readonly — the task-spec seats this phase reads, as a Pick over the SAME spec object
|
|
152
|
-
* (R-4). `tools` feeds only the band shadow check; `envFacts` only the advertised scratchpad
|
|
153
|
-
|
|
68
|
+
* (R-4). `tools` feeds only the band shadow check; `envFacts` only the advertised scratchpad;
|
|
69
|
+
* `retainBackgroundProcesses` only the band's retain lane (Bash's lifetime sentence + row stamp). */
|
|
70
|
+
spec: Pick<TaskSpec, "additionalDirectories" | "additionalReadDirectories" | "envFacts" | "handsReadOnly" | "readDenyPatterns" | "readFace" | "writeFace" | "oneShot" | "resumeAt" | "retainBackgroundProcesses" | "sessionId" | "tools">;
|
|
154
71
|
/** borrowed-readonly — the deployment seats this phase reads, as a Pick over the SAME deps
|
|
155
72
|
* object (R-4: `deps.onError?.()` keeps its receiver; a new deps read must widen this Pick).
|
|
156
73
|
* `readDenyBuiltinTiers`/`readDenyBuiltinExclude` are SNAPSHOTTED before the first await that
|
|
@@ -302,8 +219,14 @@ export interface PrepareHandsReadFaceResult {
|
|
|
302
219
|
* ARRAY identity. Settled here (no mutation after return). */
|
|
303
220
|
additionalRootsCanonical: string[];
|
|
304
221
|
/** owned — the read-only arm of the fence widening (2026-08-05 both-arms ruling); same identity
|
|
305
|
-
* contract as above
|
|
222
|
+
* contract as above, and the run's LIVE read-root set: {@link grantReadRoot} is its one writer after prepare,
|
|
223
|
+
* and every read face reads it per call (never a copy). */
|
|
306
224
|
additionalReadRootsCanonical: string[];
|
|
225
|
+
/** owned — widen {@link additionalReadRootsCanonical} by one granted candidate (an approver's in-flight
|
|
226
|
+
* `readRootGrant`); undefined ⇔ hands-less (no read face to widen). */
|
|
227
|
+
grantReadRoot: GrantReadRoot | undefined;
|
|
228
|
+
/** owned — the store cell {@link grantReadRoot} judges against; the driver threads it to its one writer, the Artifact phase. */
|
|
229
|
+
artifactStoreRef: ArtifactStoreRef | undefined;
|
|
307
230
|
/** owned — the canonical containment root, published so the working-file ATTACHMENT
|
|
308
231
|
* reader applies the same containment the hands tools do. Undefined ⇔ hands-less. */
|
|
309
232
|
attachmentRootCanonical: string | undefined;
|
|
@@ -326,7 +249,7 @@ export interface PrepareHandsReadFaceResult {
|
|
|
326
249
|
* a hands-less leg (nothing mounts on the env; the driver's env mounts are hands-gated too). */
|
|
327
250
|
envHandToolNames: Set<string>;
|
|
328
251
|
/** owned — the settle-time SEAT SEAL: re-anchors the session seat to the
|
|
329
|
-
* task's OWN last-committed entry (see {@link SessionReadFileStates.reanchor}). The driver calls
|
|
252
|
+
* task's OWN last-committed entry (see {@link import("./session-read-file-states.js").SessionReadFileStates.reanchor}). The driver calls
|
|
330
253
|
* it once at the run's settle (every terminal, suspend included), under the session lock, passing
|
|
331
254
|
* the last entryId ITS OWN run committed (the message_committed lane) — never a re-read of the
|
|
332
255
|
* shared session leaf, which a concurrent writer on another Runner could have moved onto a branch
|