@sema-agent/core 7.5.1 → 7.5.2
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 +20 -0
- package/dist/core/protocol-table.d.ts +5 -0
- package/dist/core/protocol-table.js +1 -0
- package/dist/core/runner/abort-race.d.ts +41 -0
- package/dist/core/runner/abort-race.js +38 -0
- package/dist/core/runner/checkpoint-scope.d.ts +15 -3
- package/dist/core/runner/checkpoint-scope.js +3 -0
- package/dist/core/runner/compaction-call-options.d.ts +1 -1
- package/dist/core/runner/content-ask-bindings.d.ts +27 -0
- package/dist/core/runner/content-ask-bindings.js +1 -0
- package/dist/core/runner/contracts.d.ts +118 -6
- package/dist/core/runner/denial-limit-arms.d.ts +23 -0
- package/dist/core/runner/denial-limit-arms.js +21 -0
- package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
- package/dist/core/runner/inherited-ask-grants.js +29 -0
- package/dist/core/runner/park-commit.d.ts +108 -0
- package/dist/core/runner/park-commit.js +32 -0
- package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
- package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
- package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
- package/dist/core/runner/prepare-ask-lane.js +133 -0
- package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
- package/dist/core/runner/prepare-boundary-parks.js +169 -0
- package/dist/core/runner/prepare-context-lane.d.ts +119 -0
- package/dist/core/runner/prepare-context-lane.js +230 -0
- package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
- package/dist/core/runner/prepare-gate-stations.js +290 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -4
- package/dist/core/runner/prepare-inherited-gate.d.ts +4 -4
- package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
- package/dist/core/runner/prepare-memory-engine-session.js +233 -0
- package/dist/core/runner/prepare-park-ask.d.ts +164 -0
- package/dist/core/runner/prepare-park-ask.js +377 -0
- package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
- package/dist/core/runner/prepare-policy-chain.js +584 -0
- package/dist/core/runner/prepare-project-context.d.ts +1 -13
- package/dist/core/runner/prepare-project-context.js +1 -3
- package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
- package/dist/core/runner/prepare-prompt-assembly.js +162 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -20
- package/dist/core/runner/prepare-protocol-tools.d.ts +3 -3
- package/dist/core/runner/prepare-protocol-tools.js +0 -3
- package/dist/core/runner/prepare-question-face.d.ts +3 -21
- package/dist/core/runner/prepare-question-face.js +2 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +0 -5
- package/dist/core/runner/prepare-safety-scan.js +1 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
- package/dist/core/runner/prepare-suspend-saga.js +308 -0
- package/dist/core/runner/prepare-task.d.ts +9 -136
- package/dist/core/runner/prepare-task.js +44 -2741
- package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
- package/dist/core/runner/prepare-turn-wiring.js +201 -0
- package/dist/core/runner/prepare-wiring-manifest.d.ts +11 -3
- package/dist/core/runner/prepare-wiring-manifest.js +9 -2
- package/dist/core/runner/prepare-workspace-restore.d.ts +2 -29
- package/dist/core/runner/prepare-workspace-restore.js +3 -16
- package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
- package/dist/core/runner/prompt-hash-salt.js +2 -0
- package/dist/core/runner/remote-env-retry.d.ts +29 -0
- package/dist/core/runner/remote-env-retry.js +16 -0
- package/dist/core/runner/runtask.js +1 -1
- package/dist/core/session.d.ts +12 -0
- package/dist/core/session.js +3 -0
- package/package.json +1 -1
- /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
- /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const DEFAULT_RESOURCE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
2
|
+
export const USAGE_WINDOW_REAP_MARGIN_MS = 60 * 60 * 1000;
|
|
3
|
+
export const DEFAULT_UNATTENDED_APPROVAL_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
4
|
+
export function sanitizedTtlMs(ttlMs) {
|
|
5
|
+
if (ttlMs === undefined)
|
|
6
|
+
return undefined;
|
|
7
|
+
return Number.isFinite(ttlMs) && ttlMs > 0 ? ttlMs : DEFAULT_RESOURCE_TTL_MS;
|
|
8
|
+
}
|
|
9
|
+
export function gatedCallIdOf(p) {
|
|
10
|
+
if (p.suspendRef.token !== undefined)
|
|
11
|
+
return p.suspendRef.gatedCallId;
|
|
12
|
+
if (p.reviewRef.token !== undefined)
|
|
13
|
+
return p.reviewRef.gatedCallId;
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
export function parkContaminationMarker(refs) {
|
|
17
|
+
if (refs.suspendRef.token === undefined && refs.reviewRef.token === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
const gatedCallId = gatedCallIdOf(refs);
|
|
20
|
+
return { code: "gate.parked", ...(gatedCallId !== undefined ? { gatedCallId } : {}) };
|
|
21
|
+
}
|
|
22
|
+
export function publishCommittedSuspend(refs, token, gate, scope, remoteHandle, checkpointId, pendingAction) {
|
|
23
|
+
const ref = gate.kind === "needs_review" || gate.kind === "plan_review" ? refs.reviewRef : refs.suspendRef;
|
|
24
|
+
ref.token = token;
|
|
25
|
+
if (checkpointId !== undefined)
|
|
26
|
+
ref.checkpointId = checkpointId;
|
|
27
|
+
ref.gate = gate;
|
|
28
|
+
ref.gatedCallId = pendingAction?.kind === "tool_approval" ? pendingAction.toolCallId : undefined;
|
|
29
|
+
if (remoteHandle !== undefined)
|
|
30
|
+
ref.restoreMode = remoteHandle.restoreMode === "park_only" ? "park_only" : "snapshot";
|
|
31
|
+
ref.scope = scope;
|
|
32
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* design/389 —
|
|
2
|
+
* design/389 — the permission-rule LANES the unified store feeds (prepare-path machinery, design/390 L1): the two gate
|
|
3
|
+
* lanes, and — since the policy-chain phase took the chain assembly — the rule-offer factory every ask-mint site calls.
|
|
3
4
|
*
|
|
4
5
|
* `prepareTask` used to assemble the personal lane inline (a store read + a hand-spliced session
|
|
5
6
|
* overlay + two admission arms) and the org lane from a second seam. Both now read the ONE store query —
|
|
@@ -23,8 +24,8 @@
|
|
|
23
24
|
* silence the ungated-write warning for deployments that wired no policy at all.
|
|
24
25
|
*/
|
|
25
26
|
import type { AskRuleEvidence, ToolCallRequest } from "../tool-policy.js";
|
|
26
|
-
import type
|
|
27
|
-
import type { PersistedAllowRule } from "../permission-rule-model.js";
|
|
27
|
+
import { persistedRuleMandateOf, type OrgGateVerdict, type PersistedRuleAnswer, type PersistedRuleHit } from "../hooks.js";
|
|
28
|
+
import type { PersistedAllowRule, RuleOffer, SegmentCoverage } from "../permission-rule-model.js";
|
|
28
29
|
import { type OrgRuleResolution } from "../permission-rule-org.js";
|
|
29
30
|
import type { PermissionRuleStoreProvider } from "../permission-rule-provider.js";
|
|
30
31
|
/** The one SHELL tool the persisted-rule lane speaks for. */
|
|
@@ -130,3 +131,108 @@ export declare function createPermissionRuleLanes(cfg: {
|
|
|
130
131
|
* lane took the read. */
|
|
131
132
|
onDisclosure: (message: string) => void;
|
|
132
133
|
}): PermissionRuleLanes;
|
|
134
|
+
/** The closed reason set for the rule-offer factory's empty answer (#490 修②) — the same three
|
|
135
|
+
* spellings the `AskRequest` and durable-row seats declare, kept at the one factory that fills both.
|
|
136
|
+
* Deliberately not exported: this is a wire vocabulary, and its two faces declare it literally (the
|
|
137
|
+
* `previewWithheld` precedent) so a consumer reads the closed set on the type it is holding rather
|
|
138
|
+
* than through an import. */
|
|
139
|
+
type RuleOffersAbsence = "mandated" | "lane_cannot_speak" | "shadowed";
|
|
140
|
+
/** The surviving-ask facts the rule-offer factory reads (see {@link createRuleOffersOf}): the
|
|
141
|
+
* synchronous mint sites spread the surviving decision, which carries them; the park leg threads
|
|
142
|
+
* each as its own parameter. */
|
|
143
|
+
interface RuleOffersAskFacts {
|
|
144
|
+
requiresRealApproval?: boolean;
|
|
145
|
+
persistedRuleShadowed?: string;
|
|
146
|
+
decisionReason?: import("../tool-policy.js").DecisionReason;
|
|
147
|
+
/** #457 ④: an EXPLICIT `ask` permission rule matched this call (design/127 DSL, stamped at
|
|
148
|
+
* the rule policy's three matched-ask exits — bare, covering and param). */
|
|
149
|
+
matchedAskRule?: string;
|
|
150
|
+
/** #502: the engine-stamped probe mandate (the reversibility probe declared this call's
|
|
151
|
+
* demotion structural — for the built-in shell probe, a read outside the session's
|
|
152
|
+
* allowed directories). */
|
|
153
|
+
probeMandated?: boolean;
|
|
154
|
+
inheritedUnresolved?: boolean;
|
|
155
|
+
/** The ask is being resolved at an ANCESTOR's frozen approver (the three inherited-lane
|
|
156
|
+
* mint sites pass it literally) — this task's rule lane never adjudicates it. */
|
|
157
|
+
ancestorResolved?: boolean;
|
|
158
|
+
/** design/375 §5.2②: the surviving ask's engine-stamped per-segment coverage table — the
|
|
159
|
+
* batch offer carries exactly the segments this table calls uncovered. Absent ⇒ all
|
|
160
|
+
* segments read as uncovered (over-offer, the safe direction). */
|
|
161
|
+
segmentCoverage?: readonly SegmentCoverage[];
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* design/179 §4 — the rule forms that could cover this exact call, for a surface's "stop asking me
|
|
165
|
+
* this" option (extracted from the prepare body, the spliceSessionOverlayRows precedent; the maker
|
|
166
|
+
* captures the per-task wiring once and the returned factory serves all five ask-mint sites — the
|
|
167
|
+
* approval-preview field is a standing example of what happens otherwise: it reached two of the
|
|
168
|
+
* five, so a delegated child's card was silently poorer than the top-level one).
|
|
169
|
+
*
|
|
170
|
+
* Empty unless a rule lane is armed — offering an option that redemption would refuse is worse than
|
|
171
|
+
* offering none — and empty for any call the lane cannot speak for (a compound, a redirection, a
|
|
172
|
+
* substitution, or a tool that is not the shell tool).
|
|
173
|
+
*
|
|
174
|
+
* Empty, too, on any ask a persisted rule could never CLEAR — "allow rules silence the classifier's
|
|
175
|
+
* questions, never a mandated one". Two doors, matching the two halves of that boundary:
|
|
176
|
+
* · the marks half, judged from the SAME resolved sources the gate input is built from, through the
|
|
177
|
+
* gate's own single-source predicate (an operator's shellGate:"always", the tool's own
|
|
178
|
+
* egress/irreversibility marks, and — #502 — the PER-CALL member that predicate also takes: a
|
|
179
|
+
* demotion this call's reversibility probe declared structural, read off the ask the gate
|
|
180
|
+
* stamped rather than off the tool seat, because it is true of one call and not of the seat) —
|
|
181
|
+
* a drift here would offer a rule the lane then refuses to honor;
|
|
182
|
+
* · the decision half (`ask`, REQUIRED at every mint site so a site added later cannot skip it):
|
|
183
|
+
* `requiresRealApproval` (org/governance — only judgment clears it), the shadowed-rule
|
|
184
|
+
* disclosure (the standing proof that a matching rule does not clear THIS ask), a hook-raised
|
|
185
|
+
* ask (`decisionReason === "hook"` — the lane's own clearing conjuncts refuse hook asks, so a
|
|
186
|
+
* rule minted from one would never silence it), an EXPLICIT `ask` rule match (`matchedAskRule`
|
|
187
|
+
* — see below), and an inherited-unresolved marked call (the
|
|
188
|
+
* ask is an ANCESTOR's authority, which this task's rule lane never adjudicates). Offering
|
|
189
|
+
* "stop asking me this" on a card that will keep asking lets a person mint a rule that never
|
|
190
|
+
* takes effect where they minted it.
|
|
191
|
+
*
|
|
192
|
+
* #457 ④ (CC 2.1.245 toolPolicy 对表, F11: "wildcard `ask` … keeps every matching tool behind a
|
|
193
|
+
* per-call prompt (no persistent always-allow)") — `matchedAskRule` was the one conjunct of the
|
|
194
|
+
* lane's own clearing predicate this door did not mirror, and the asymmetry was live: the
|
|
195
|
+
* persisted-rule lane refuses to clear ANY ask carrying it (`hooks.ts`, the
|
|
196
|
+
* `decision.matchedAskRule === undefined` conjunct on its allow arm), so a person who wrote a
|
|
197
|
+
* standing `Bash` `ask` rule and had no allow rule yet was still offered "allow, and stop asking
|
|
198
|
+
* me this" — and the rule they minted came back on the very next call as a SHADOWED match
|
|
199
|
+
* (`persistedRuleShadowed`, which this door was already refusing). The offer was only ever
|
|
200
|
+
* suppressed one call too late, after the useless rule existed. The shadowed conjunct stays: it
|
|
201
|
+
* covers the person who ALREADY has the allow rule; this one covers the person about to mint it.
|
|
202
|
+
*
|
|
203
|
+
* #490 修② — every door above that answers "no offers" now also names WHICH door, on the
|
|
204
|
+
* `ruleOffersAbsence` seat (`AskRequest`'s and the durable row's, ONE factory so the two faces
|
|
205
|
+
* cannot disagree). The doctrine it serves is the loud-bad-value one, applied to an absence: a
|
|
206
|
+
* surface reading an empty card could not tell "there is nothing this lane could offer" from
|
|
207
|
+
* "offers were deliberately suppressed", and a person reading it could not tell "write the rule
|
|
208
|
+
* yourself" from "no rule can excuse this approval". The seat is CLOSED
|
|
209
|
+
* (`mandated | lane_cannot_speak | shadowed`), mutually exclusive with `ruleOffers` by
|
|
210
|
+
* construction, and structural-door silent (see the first two guards).
|
|
211
|
+
*/
|
|
212
|
+
export declare function createRuleOffersOf(cfg: {
|
|
213
|
+
/** `permissionRuleLane !== undefined` at wiring time — the armed-lane structural door. */
|
|
214
|
+
laneArmed: boolean;
|
|
215
|
+
principal: string | undefined;
|
|
216
|
+
/** `deps.localOwnerRules === true` — the declared local-owner exception to the anonymous floor. */
|
|
217
|
+
localOwnerDeclared: boolean;
|
|
218
|
+
/** The live mark sources the gate input is built from — passed as REFERENCES (read per call, so a
|
|
219
|
+
* late-registered tool seat is seen exactly as the gate sees it). */
|
|
220
|
+
egressTools: {
|
|
221
|
+
has(name: string): boolean;
|
|
222
|
+
};
|
|
223
|
+
irreversibilityTier: {
|
|
224
|
+
get(name: string): Parameters<typeof persistedRuleMandateOf>[0]["irreversibility"];
|
|
225
|
+
};
|
|
226
|
+
shellGatedBash: boolean;
|
|
227
|
+
taskRoot: string | undefined;
|
|
228
|
+
/** The live tracked-cwd ref (read per call — the relative-cd resolution base, adversarial-review P1/r3). */
|
|
229
|
+
cwdRef: {
|
|
230
|
+
current: string;
|
|
231
|
+
} | undefined;
|
|
232
|
+
deniesDirectoryRead: ((directory: string) => boolean) | undefined;
|
|
233
|
+
}): (toolName: string, args: unknown, ask: RuleOffersAskFacts | undefined) => {
|
|
234
|
+
ruleOffers?: readonly RuleOffer[];
|
|
235
|
+
ruleOffersAbsence?: RuleOffersAbsence;
|
|
236
|
+
execCwd?: string;
|
|
237
|
+
};
|
|
238
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { persistedRuleMandateOf } from "../hooks.js";
|
|
2
|
+
import { directoryRuleAdmits, eligiblePersisted, findAdmittingRule, lexicalNormalAbsolutePathOf, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
2
3
|
import { orgRuleVerdictFor } from "../permission-rule-org.js";
|
|
3
4
|
export const PERSISTED_RULE_TOOL = "Bash";
|
|
4
5
|
export const DIRECTORY_RULE_TOOL = "Read";
|
|
@@ -138,3 +139,48 @@ export function createPermissionRuleLanes(cfg) {
|
|
|
138
139
|
: undefined;
|
|
139
140
|
return { personal, org };
|
|
140
141
|
}
|
|
142
|
+
export function createRuleOffersOf(cfg) {
|
|
143
|
+
return (toolName, args, ask) => {
|
|
144
|
+
if (!cfg.laneArmed || toolName !== PERSISTED_RULE_TOOL)
|
|
145
|
+
return {};
|
|
146
|
+
if ((cfg.principal === undefined || cfg.principal === "") && !cfg.localOwnerDeclared)
|
|
147
|
+
return {};
|
|
148
|
+
const closedDoor = (() => {
|
|
149
|
+
if (ask?.requiresRealApproval === true)
|
|
150
|
+
return "mandated";
|
|
151
|
+
if (ask?.persistedRuleShadowed !== undefined)
|
|
152
|
+
return "shadowed";
|
|
153
|
+
if (ask?.decisionReason === "hook")
|
|
154
|
+
return "mandated";
|
|
155
|
+
if (ask?.matchedAskRule !== undefined)
|
|
156
|
+
return "shadowed";
|
|
157
|
+
if (ask?.inheritedUnresolved === true)
|
|
158
|
+
return "mandated";
|
|
159
|
+
if (ask?.ancestorResolved === true)
|
|
160
|
+
return "mandated";
|
|
161
|
+
return undefined;
|
|
162
|
+
})();
|
|
163
|
+
if (closedDoor !== undefined)
|
|
164
|
+
return { ruleOffersAbsence: closedDoor };
|
|
165
|
+
if (persistedRuleMandateOf({
|
|
166
|
+
egress: cfg.egressTools.has(toolName),
|
|
167
|
+
irreversibility: cfg.irreversibilityTier.get(toolName),
|
|
168
|
+
shellGated: cfg.shellGatedBash,
|
|
169
|
+
probeMandated: ask?.probeMandated === true,
|
|
170
|
+
}) !== undefined) {
|
|
171
|
+
return { ruleOffersAbsence: "mandated" };
|
|
172
|
+
}
|
|
173
|
+
const command = args?.command;
|
|
174
|
+
if (typeof command !== "string")
|
|
175
|
+
return { ruleOffersAbsence: "lane_cannot_speak" };
|
|
176
|
+
const offers = suggestRulesForCommand(command, {
|
|
177
|
+
...(ask?.segmentCoverage !== undefined ? { coverage: ask.segmentCoverage } : {}),
|
|
178
|
+
...(cfg.taskRoot !== undefined ? { scope: { kind: "project", root: cfg.taskRoot }, cwd: cfg.taskRoot } : {}),
|
|
179
|
+
...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}),
|
|
180
|
+
...(cfg.deniesDirectoryRead !== undefined ? { deniesDirectoryRead: cfg.deniesDirectoryRead } : {}),
|
|
181
|
+
});
|
|
182
|
+
return offers.length > 0
|
|
183
|
+
? { ruleOffers: offers, ...(cfg.cwdRef?.current !== undefined ? { execCwd: cfg.cwdRef.current } : {}) }
|
|
184
|
+
: { ruleOffersAbsence: "lane_cannot_speak" };
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/390 §1.2 M19 (ask lane) — prepareTask's IN-STREAM ASK LANE, verbatim from the driver's gate-machinery block:
|
|
3
|
+
* the per-call signal composition bound to this task's run signal, the abort-bound `adjudicate` wrapper (the budget
|
|
4
|
+
* snapshot and the live tracked cwd stamped onto every request), the approval-preview projection over the live roster,
|
|
5
|
+
* the `resolveAskBound` mint (the inherited-unavailable intercept, the duplicate-frame grant reuse, the human-review
|
|
6
|
+
* ledger, the bare-human-rejection record), the pre-wrapped deny observer and the hook-crash notifier. The three
|
|
7
|
+
* module-scope helpers only this segment called moved with it (the deny-observer factory, the preview sanitizer and
|
|
8
|
+
* resolver). The interface is the dependency list the segment had implicitly (design/238 R-1) — the closures below
|
|
9
|
+
* used to capture every seat here off the driver's scope.
|
|
10
|
+
*
|
|
11
|
+
* SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await of its own (every await lives inside the closures
|
|
12
|
+
* it builds, which run per tool call), so the phase adds no yield between the wiring-manifest station before it and
|
|
13
|
+
* the suspend saga after it (design/390 §1.5: a stretch with no await is extracted as a sync function). Read-stability
|
|
14
|
+
* of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
|
|
15
|
+
*
|
|
16
|
+
* The lane exists exactly when the wiring-manifest phase's `gateMachineryActive` holds — the driver's `if` around the
|
|
17
|
+
* whole block became this phase's one early return (the block's other four phases key off the lane's presence), so
|
|
18
|
+
* the orchestrator grows no branch.
|
|
19
|
+
*/
|
|
20
|
+
import type { AgentTool } from "../../internal/harness.js";
|
|
21
|
+
import type { AutoModeDenialTracker } from "../auto-mode.js";
|
|
22
|
+
import { type Hooks } from "../hooks.js";
|
|
23
|
+
import { type OnAsk, type ToolCallRequest, type ToolPolicy } from "../tool-policy.js";
|
|
24
|
+
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
25
|
+
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
26
|
+
import type { AskLane, Prepared } from "./contracts.js";
|
|
27
|
+
import { consumeInheritedAskGrant } from "./inherited-ask-grants.js";
|
|
28
|
+
import type { createRuleOffersOf } from "./permission-rule-lanes.js";
|
|
29
|
+
export interface PrepareAskLaneInput {
|
|
30
|
+
/** borrowed-readonly — the wiring-manifest phase's ONE activation predicate; false ⇒ this phase builds nothing and
|
|
31
|
+
* returns an absent lane. */
|
|
32
|
+
gateMachineryActive: boolean;
|
|
33
|
+
/** borrowed-readonly — prepare's abort seat; only `.signal` is read (composed into every per-call signal and bound
|
|
34
|
+
* onto the deny observer). Never aborted here. */
|
|
35
|
+
abortController: {
|
|
36
|
+
readonly signal: AbortSignal;
|
|
37
|
+
};
|
|
38
|
+
/** borrowed-readonly — the policy chain's effective policy, or undefined (then no `adjudicate` wrapper is built and
|
|
39
|
+
* the gate reads a missing policy as allow). Called at RUN time, per tool call, through the abort-bound wrapper. */
|
|
40
|
+
effectivePolicy: ToolPolicy | undefined;
|
|
41
|
+
/** borrowed-readonly — the per-leg-immutable durable budget snapshot stamped onto EVERY adjudicated request. Same
|
|
42
|
+
* object every call; the policy cannot write it back. */
|
|
43
|
+
budgetSnapshot: NonNullable<ToolCallRequest["budget"]>;
|
|
44
|
+
/** borrowed-readonly — the hand's live tracked cwd, or undefined (hands-less). Read per call at RUN time (`current`),
|
|
45
|
+
* never captured: the fs hand tools resolve their relative paths against this very ref. Not written here. */
|
|
46
|
+
handsCwdRef: CwdRef | undefined;
|
|
47
|
+
/** borrowed-readonly — the shared mount roster; read at RUN time by the approval-preview projection (alias-aware
|
|
48
|
+
* lookup over whatever the roster holds when the ask is minted). Never mutated here. */
|
|
49
|
+
tools: AgentTool[];
|
|
50
|
+
/** borrowed-mutable — the inherited-unavailable marker set. Writer here: `resolveAskBound` consumes (`delete`) a
|
|
51
|
+
* marked call at its fail-closed intercept; the policy-chain phase records, the gate station sweeps. */
|
|
52
|
+
inheritedUnavailableAsks: Set<string>;
|
|
53
|
+
/** borrowed-mutable — the duplicate-frame ask-grant record. Writer here: the grant-reuse consume half
|
|
54
|
+
* (`consumeInheritedAskGrant`); the policy-chain phase records, the gate station sweeps. */
|
|
55
|
+
inheritedAskGrants: Parameters<typeof consumeInheritedAskGrant>[0];
|
|
56
|
+
/** borrowed-readonly — the run's own approver seat (spec over deps), or undefined: the resolve target, the
|
|
57
|
+
* live-approver test the carry mint reads, and the grant-reuse identity law's approver. */
|
|
58
|
+
onAsk: OnAsk | undefined;
|
|
59
|
+
/** borrowed-mutable — the per-task human-review accumulator (`Prepared.humanReviewRef`). Writers here: every
|
|
60
|
+
* synchronous resolve that a person actually judged (count / totalWaitMs / gates push); runtask adds the durable
|
|
61
|
+
* resume latency. */
|
|
62
|
+
humanReviewRef: Prepared["humanReviewRef"];
|
|
63
|
+
/** borrowed-mutable — the bare-human-rejection sideband. Writer here: a `settledBy:"human"` deny with no note
|
|
64
|
+
* (`add`); the gate station judges and sweeps it. */
|
|
65
|
+
humanBareRejections: Set<string>;
|
|
66
|
+
/** borrowed-readonly — the injectable wall clock every human-time observable reads (`Prepared.now`). */
|
|
67
|
+
now: () => number;
|
|
68
|
+
/** borrowed-readonly — the policy-chain phase's rule-offer factory (the suggestion members the ask mint threads). */
|
|
69
|
+
ruleOffersOf: ReturnType<typeof createRuleOffersOf>;
|
|
70
|
+
/** borrowed-readonly — the policy-chain phase's Runner-filled source identity (one factory, every ask mint site). */
|
|
71
|
+
askSourceIdentity: () => {
|
|
72
|
+
principal?: string;
|
|
73
|
+
sourceTaskId?: string;
|
|
74
|
+
fromSubagent?: true;
|
|
75
|
+
sourceAgentName?: string;
|
|
76
|
+
isDelegatedChild?: true;
|
|
77
|
+
};
|
|
78
|
+
/** borrowed-readonly — the policy-chain phase's per-tool risk-axes projection (`riskAxes` on the ask). */
|
|
79
|
+
riskAxesOf: (toolName: string) => {
|
|
80
|
+
riskAxes?: {
|
|
81
|
+
irreversible?: boolean;
|
|
82
|
+
egress?: boolean;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
/** borrowed-mutable — the auto-mode denial tracker, or undefined. Writer here: the carry mint arms the auto-deny
|
|
86
|
+
* window on it iff the seat about to be called is live (the tracker's one writer). */
|
|
87
|
+
autoModeDenialTracking: AutoModeDenialTracker | undefined;
|
|
88
|
+
/** borrowed-readonly — the REBOUND spec; only `taskId` is read (the late-settlement observer's tag). */
|
|
89
|
+
spec: Pick<TaskSpec, "taskId">;
|
|
90
|
+
/** borrowed-readonly — the deployment seats this lane reports on, as a Pick over the SAME `deps` object (receiver
|
|
91
|
+
* preserved for `deps.onError?.()`): the late-settlement observer's notice/error sinks and the hook-crash notifier. */
|
|
92
|
+
deps: Pick<RunnerDeps, "onNotice" | "onError">;
|
|
93
|
+
/** borrowed-readonly — the acquired session id (the observer's tag, the hook-phase error tag). */
|
|
94
|
+
sessionId: string;
|
|
95
|
+
/** borrowed-readonly — the run's invocation id (the observer's tag). */
|
|
96
|
+
runId: string;
|
|
97
|
+
/** borrowed-readonly — the resolved hook record; only `permissionDenied` is read (the deny observer's seat). */
|
|
98
|
+
hooks: Hooks | undefined;
|
|
99
|
+
/** borrowed-readonly — the validated per-prepare hook seat bound (the deny observer's timeout). */
|
|
100
|
+
hookTimeoutMs: number;
|
|
101
|
+
/** borrowed-readonly — the own hook-crash reporter (the deny observer's swallow-guarded report sink). */
|
|
102
|
+
notifyOwnHookCrash: (err: unknown) => void;
|
|
103
|
+
}
|
|
104
|
+
export interface PrepareAskLaneResult {
|
|
105
|
+
/** owned — the lane, or undefined exactly when `gateMachineryActive` is false (then the suspend saga, the boundary
|
|
106
|
+
* parks, the park closure and the gate station are not built either — they key off this seat). */
|
|
107
|
+
askLane: AskLane | undefined;
|
|
108
|
+
}
|
|
109
|
+
/** The M19 ask-lane phase body — prepareTask's in-stream ask stretch, verbatim (see the module header). */
|
|
110
|
+
export declare function prepareAskLane(input: PrepareAskLaneInput): PrepareAskLaneResult;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { primaryActivityArg } from "../arg-summary.js";
|
|
2
|
+
import { hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
3
|
+
import { isLiveApproverSeat, resolveAsk } from "../tool-policy.js";
|
|
4
|
+
import { composeCallSignal, raceAbort } from "./abort-race.js";
|
|
5
|
+
import { gateAskCarry, lateAskSettlementObserver } from "./denial-limit-arms.js";
|
|
6
|
+
import { consumeInheritedAskGrant } from "./inherited-ask-grants.js";
|
|
7
|
+
function createDenyObserverNotifier(hooks, hookTimeoutMs, signal, report) {
|
|
8
|
+
if (hooks?.permissionDenied === undefined)
|
|
9
|
+
return undefined;
|
|
10
|
+
return async (payload) => {
|
|
11
|
+
try {
|
|
12
|
+
const seat = await runHookSeat("permissionDenied", { timeoutMs: hookTimeoutMs, signal }, (sig) => hooks.permissionDenied({ ...payload, signal: sig }));
|
|
13
|
+
if (seat.expired)
|
|
14
|
+
report(hookSeatExpiredError("permissionDenied", hookTimeoutMs, seat.cause, "the deny observation was abandoned; the deny itself is unchanged"));
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
report(err);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const sanitizePreview = (node, depth = 0) => {
|
|
22
|
+
if (depth > 6)
|
|
23
|
+
return undefined;
|
|
24
|
+
if (typeof node === "string") {
|
|
25
|
+
return node.replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g, "\u2400");
|
|
26
|
+
}
|
|
27
|
+
if (node === null || typeof node !== "object")
|
|
28
|
+
return node;
|
|
29
|
+
if (Array.isArray(node))
|
|
30
|
+
return node.map((v) => sanitizePreview(v, depth + 1));
|
|
31
|
+
const out = {};
|
|
32
|
+
for (const [k, v] of Object.entries(node)) {
|
|
33
|
+
out[sanitizePreview(k, depth + 1)] = sanitizePreview(v, depth + 1);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
};
|
|
37
|
+
function resolveApprovalPreview(tools, toolName, args) {
|
|
38
|
+
const t = tools.find((x) => x.name === toolName || (x.aliases?.includes(toolName) ?? false));
|
|
39
|
+
if (t?.approvalPreview === undefined)
|
|
40
|
+
return {};
|
|
41
|
+
try {
|
|
42
|
+
const raw = t.approvalPreview(args);
|
|
43
|
+
if (raw === undefined)
|
|
44
|
+
return {};
|
|
45
|
+
const bytes = JSON.stringify(raw);
|
|
46
|
+
if (bytes === undefined)
|
|
47
|
+
return { withheld: "unavailable" };
|
|
48
|
+
if (bytes.length > 16_384) {
|
|
49
|
+
return { preview: { truncated: true, note: `approval preview exceeded 16KiB (${bytes.length} chars serialized)` }, withheld: "oversize" };
|
|
50
|
+
}
|
|
51
|
+
return { preview: sanitizePreview(raw) };
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return { withheld: "unavailable" };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function prepareAskLane(input) {
|
|
58
|
+
const { gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, humanBareRejections, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash } = input;
|
|
59
|
+
if (!gateMachineryActive)
|
|
60
|
+
return { askLane: undefined };
|
|
61
|
+
const composedCallSignal = (callSignal) => composeCallSignal(abortController.signal, callSignal);
|
|
62
|
+
const adjudicate = effectivePolicy
|
|
63
|
+
? (req, callSignal) => {
|
|
64
|
+
const signal = composedCallSignal(callSignal);
|
|
65
|
+
return raceAbort(Promise.resolve(effectivePolicy.check({ ...req, budget: budgetSnapshot, ...(handsCwdRef !== undefined ? { cwd: handsCwdRef.current } : {}) }, signal)), signal, () => ({
|
|
66
|
+
action: "deny",
|
|
67
|
+
message: "policy check aborted (task timed out or cancelled)",
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
: undefined;
|
|
71
|
+
const approvalPreviewOf = (toolName, args) => resolveApprovalPreview(tools, toolName, args);
|
|
72
|
+
const resolveAskBound = async (decision, req, callSignal) => {
|
|
73
|
+
if (inheritedUnavailableAsks.delete(req.toolCallId)) {
|
|
74
|
+
return {
|
|
75
|
+
action: "deny",
|
|
76
|
+
message: `approval for "${req.toolName}" requires an ancestor task's approval that cannot be resolved here ` +
|
|
77
|
+
`(no live approver reachable, or a durable-park mandate applies), and no durable approval park is ` +
|
|
78
|
+
`available — denied fail-closed (the inherited constraint stands).`,
|
|
79
|
+
decisionReason: "mode",
|
|
80
|
+
approverUnavailable: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const grantReuse = consumeInheritedAskGrant(inheritedAskGrants, onAsk, humanReviewRef, decision, req);
|
|
84
|
+
if (grantReuse !== undefined)
|
|
85
|
+
return grantReuse;
|
|
86
|
+
const t0 = now();
|
|
87
|
+
const resolved = await resolveAsk({
|
|
88
|
+
toolName: req.toolName,
|
|
89
|
+
toolCallId: req.toolCallId,
|
|
90
|
+
args: req.args,
|
|
91
|
+
...(() => {
|
|
92
|
+
const p = approvalPreviewOf(req.toolName, req.args);
|
|
93
|
+
return { ...(p.preview !== undefined ? { preview: p.preview } : {}), ...(p.withheld !== undefined ? { previewWithheld: p.withheld } : {}) };
|
|
94
|
+
})(),
|
|
95
|
+
...ruleOffersOf(req.toolName, req.args, decision.action === "ask" ? decision : undefined),
|
|
96
|
+
message: decision.message ?? `approval required for "${req.toolName}"`,
|
|
97
|
+
...askSourceIdentity(),
|
|
98
|
+
...riskAxesOf(req.toolName),
|
|
99
|
+
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
100
|
+
...(decision.action === "ask" && decision.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: decision.persistedRuleShadowed } : {}),
|
|
101
|
+
...(decision.action === "ask" ? gateAskCarry(decision, isLiveApproverSeat(onAsk), autoModeDenialTracking) : {}),
|
|
102
|
+
...(decision.action === "ask" && decision.probeReason !== undefined ? { probeReason: decision.probeReason } : {}),
|
|
103
|
+
...(decision.action === "ask" && decision.probeCause !== undefined ? { probeCause: decision.probeCause } : {}),
|
|
104
|
+
...(decision.action === "ask" && decision.ruleEvidence !== undefined ? { ruleEvidence: decision.ruleEvidence } : {}),
|
|
105
|
+
}, onAsk, composedCallSignal(callSignal), lateAskSettlementObserver({ toolName: req.toolName, toolCallId: req.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
106
|
+
const waitMs = Math.max(0, now() - t0);
|
|
107
|
+
if (resolved.approverUnavailable !== true && resolved.resolution !== "task_aborted") {
|
|
108
|
+
humanReviewRef.count += 1;
|
|
109
|
+
humanReviewRef.totalWaitMs += waitMs;
|
|
110
|
+
const toolArg = primaryActivityArg(req.args);
|
|
111
|
+
humanReviewRef.gates.push({
|
|
112
|
+
kind: "human",
|
|
113
|
+
waitMs,
|
|
114
|
+
decision: resolved.action === "allow" ? "allow" : "deny",
|
|
115
|
+
toolName: req.toolName,
|
|
116
|
+
...(toolArg !== undefined ? { toolArg } : {}),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
if (resolved.action === "deny" && resolved.settledBy === "human" && resolved.humanRefusalNote !== true) {
|
|
120
|
+
humanBareRejections.add(req.toolCallId);
|
|
121
|
+
}
|
|
122
|
+
return resolved;
|
|
123
|
+
};
|
|
124
|
+
const notifyPermissionDenied = createDenyObserverNotifier(hooks, hookTimeoutMs, abortController.signal, notifyOwnHookCrash);
|
|
125
|
+
const notifyHookError = (err) => {
|
|
126
|
+
try {
|
|
127
|
+
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId });
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
return { askLane: { composedCallSignal, adjudicate, approvalPreviewOf, resolveAskBound, notifyPermissionDenied, notifyHookError } };
|
|
133
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* design/390 §1.2 M19 (boundary parks) — prepareTask's three BOUNDARY-FAMILY park lanes, verbatim from the driver's
|
|
3
|
+
* gate-machinery block: the ONE resource mint (`mintResourceSuspend`) behind the task-axis `suspendForResource` (exposed
|
|
4
|
+
* under the caller's `resourceSuspend` opt-in) and the infrastructure `suspendForPlatformLimit` (exposed on durable infra
|
|
5
|
+
* alone), and the `plan_review` mint behind `suspendForReview` (exposed with a checkpoint store). Every mint reuses the
|
|
6
|
+
* suspend saga's serializer, commit saga, loop cap and env split — this phase adds no second copy of any of them. The
|
|
7
|
+
* interface is the dependency list the segment had implicitly (design/238 R-1); the three lanes used to be forward-declared
|
|
8
|
+
* `let`s at task scope, assigned inside the block, so the driver's Prepared could expose them — they are this phase's
|
|
9
|
+
* Result now, and the driver binds them as consts.
|
|
10
|
+
*
|
|
11
|
+
* SYNCHRONOUS FUNCTION, SYNCHRONOUS CALL: the stretch has no await of its own (the mints run at a turn boundary), so the
|
|
12
|
+
* phase adds no yield between the suspend saga before it and the park closure after it (design/390 §1.5). Read-stability
|
|
13
|
+
* of the host handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
|
|
14
|
+
*
|
|
15
|
+
* The lanes exist exactly when the saga does (the ask lane's presence law) — an absent saga answers three absent lanes,
|
|
16
|
+
* so the orchestrator grows no branch.
|
|
17
|
+
*/
|
|
18
|
+
import type { AgentHarness } from "../../internal/harness.js";
|
|
19
|
+
import { type CheckpointStore } from "../checkpoint-store.js";
|
|
20
|
+
import type { SessionStore } from "../session.js";
|
|
21
|
+
import type { RunnerDeps, TaskSpec } from "../types.js";
|
|
22
|
+
import type { Prepared, SuspendSaga } from "./contracts.js";
|
|
23
|
+
export interface PrepareBoundaryParksInput {
|
|
24
|
+
/** borrowed-readonly — the suspend saga's eight seats (the serializer, the commit saga, the loop cap, the env split, the
|
|
25
|
+
* park-only handle, the in-flight spend), or undefined when the gate machinery is inactive (then all three lanes are
|
|
26
|
+
* absent). Called at park time; never rebuilt here. */
|
|
27
|
+
saga: SuspendSaga | undefined;
|
|
28
|
+
/** borrowed-readonly — the REBOUND spec. Read: `resourceSuspend` (the slice scope/ttl/maxSlices), `retainBackgroundProcesses`
|
|
29
|
+
* (the park-only sweep gate), `durableApproval` (the review pause's ttl), `principal` (the rows' identity record and
|
|
30
|
+
* the review scope). Never mutated. */
|
|
31
|
+
spec: Pick<TaskSpec, "resourceSuspend" | "retainBackgroundProcesses" | "durableApproval" | "principal">;
|
|
32
|
+
/** borrowed-readonly — the leg's checkpoint store, or undefined (then the review lane is absent and the resource mint
|
|
33
|
+
* answers false). */
|
|
34
|
+
checkpointStore: CheckpointStore | undefined;
|
|
35
|
+
/** borrowed-mutable — prepare's abort seat. `.signal` is read (a cancelling run mints nothing); `abort()` is called on
|
|
36
|
+
* the review lane's commit (the review pause stops the run the human gate's way). */
|
|
37
|
+
abortController: {
|
|
38
|
+
readonly signal: AbortSignal;
|
|
39
|
+
abort(): void;
|
|
40
|
+
};
|
|
41
|
+
/** borrowed-mutable — the built harness. Writers here: `requestStopAfterTurn()` (the resource lanes' clean stop) and
|
|
42
|
+
* `abort()` (the review lane's stop). */
|
|
43
|
+
harness: Pick<AgentHarness, "abort" | "requestStopAfterTurn">;
|
|
44
|
+
/** borrowed-readonly — the acquired session; only `getLeafId` is read (no committed leaf ⇒ no consistent resume point). */
|
|
45
|
+
session: Pick<Prepared["session"], "getLeafId">;
|
|
46
|
+
/** borrowed-readonly — the session store; only the optional `pin` is called (best-effort, after commit). */
|
|
47
|
+
sessions: Pick<SessionStore, "pin">;
|
|
48
|
+
/** borrowed-readonly — the acquired session id (the rows' session, the source attribution, every disclosure's tag). */
|
|
49
|
+
sessionId: string;
|
|
50
|
+
/** borrowed-readonly — the deployment's error face, as a Pick over the SAME `deps` object (receiver preserved for
|
|
51
|
+
* `deps.onError?.()`): the suspendVM refusals, the skipped-review lines, the pin failures. */
|
|
52
|
+
deps: Pick<RunnerDeps, "onError">;
|
|
53
|
+
/** borrowed-readonly — the cross-slice ledger carried on the resumed checkpoint, or undefined on the first slice. */
|
|
54
|
+
priorLedger: Prepared["resourceLedger"];
|
|
55
|
+
/** borrowed-readonly — the resource-slice cap, or undefined (no slice ceiling). */
|
|
56
|
+
maxSlices: number | undefined;
|
|
57
|
+
/** borrowed-readonly — the restart-loop cap the review lane is bounded by. */
|
|
58
|
+
maxSuspends: number;
|
|
59
|
+
/** borrowed-readonly — the human total the first slice seeds the ledger from, or undefined. */
|
|
60
|
+
resourceTotal: {
|
|
61
|
+
totalBudgetMicroUsd?: number;
|
|
62
|
+
totalTokens?: number;
|
|
63
|
+
} | undefined;
|
|
64
|
+
/** borrowed-readonly — how many times this task already suspended (carried on the resumed checkpoint); a slice carries
|
|
65
|
+
* it UNCHANGED. */
|
|
66
|
+
priorSuspendCount: number;
|
|
67
|
+
/** borrowed-readonly — the restart-loop chain base (resets after an approved execution); the review lane's count. */
|
|
68
|
+
suspendChainBase: () => number;
|
|
69
|
+
/** borrowed-readonly — the per-task human-review accumulator; snapshotted onto every row (never written here). */
|
|
70
|
+
humanReviewRef: Prepared["humanReviewRef"];
|
|
71
|
+
/** borrowed-readonly — the loop's live spend seat; `.get` is read at mint time for the review ledger. */
|
|
72
|
+
liveSpendRef: Prepared["liveSpendRef"];
|
|
73
|
+
/** borrowed-readonly — the injectable wall clock (the review pause's `suspendedAt`). */
|
|
74
|
+
now: () => number;
|
|
75
|
+
/** borrowed-readonly — the inherited-gate phase's three version-stamp predicates (face / F-012 / org admission). */
|
|
76
|
+
faceCheckpointState: () => boolean;
|
|
77
|
+
/** borrowed-readonly — see `faceCheckpointState`. */
|
|
78
|
+
f012CheckpointState: () => boolean;
|
|
79
|
+
/** borrowed-readonly — see `faceCheckpointState`. */
|
|
80
|
+
orgAdmissionCheckpointState: () => boolean;
|
|
81
|
+
/** borrowed-mutable — the durable-suspend holder (`Prepared.suspendRef`). Writer here: the commit-side publication of a
|
|
82
|
+
* committed resource row. */
|
|
83
|
+
suspendRef: Prepared["suspendRef"];
|
|
84
|
+
/** borrowed-mutable — the review holder (`Prepared.reviewRef`). Writer here: the commit-side publication of a committed
|
|
85
|
+
* plan_review row. */
|
|
86
|
+
reviewRef: Prepared["reviewRef"];
|
|
87
|
+
/** borrowed-mutable — the run-scoped remote-lifecycle failure log. Writer here: a refused `suspendVM` (`push`). */
|
|
88
|
+
remoteEnvFailures: Prepared["remoteEnvFailures"];
|
|
89
|
+
/** borrowed-readonly — the wiring-manifest phase's task-axis predicate (opt-in ∧ durable infra): exposes `suspendForResource`. */
|
|
90
|
+
resourceSuspendEligible: boolean;
|
|
91
|
+
/** borrowed-readonly — the wiring-manifest phase's durable-infra predicate: exposes `suspendForPlatformLimit`. */
|
|
92
|
+
durableSuspendInfraReady: boolean;
|
|
93
|
+
/** borrowed-readonly — the restore-incomplete adapter's missing members, or undefined (the review lane refuses loudly). */
|
|
94
|
+
incompleteSuspendAdapter: readonly ("resumeVM" | "postResumeInit")[] | undefined;
|
|
95
|
+
}
|
|
96
|
+
export interface PrepareBoundaryParksResult {
|
|
97
|
+
/** owned — the task-axis resource lane (`Prepared.suspendForResource`), or undefined (no opt-in / no infra / inactive). */
|
|
98
|
+
suspendForResource: Prepared["suspendForResource"];
|
|
99
|
+
/** owned — the platform lane (`Prepared.suspendForPlatformLimit`), or undefined (no durable infra / inactive). */
|
|
100
|
+
suspendForPlatformLimit: Prepared["suspendForPlatformLimit"];
|
|
101
|
+
/** owned — the plan-review lane (`Prepared.suspendForReview`), or undefined (no store / inactive). */
|
|
102
|
+
suspendForReview: Prepared["suspendForReview"];
|
|
103
|
+
}
|
|
104
|
+
/** The M19 boundary-parks phase body — prepareTask's three boundary lanes, verbatim (see the module header). */
|
|
105
|
+
export declare function prepareBoundaryParks(input: PrepareBoundaryParksInput): PrepareBoundaryParksResult;
|