@sema-agent/core 7.40.1 → 7.41.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 +188 -0
- package/dist/agents/agent-transcript-tool.d.ts +10 -2
- package/dist/agents/agent-transcript-tool.js +52 -26
- package/dist/agents/list-agents-tool.d.ts +17 -2
- package/dist/agents/list-agents-tool.js +28 -16
- package/dist/agents/peer-session-drain.d.ts +3 -1
- package/dist/agents/peer-session-drain.js +1 -0
- package/dist/agents/subagent-steps.d.ts +12 -2
- package/dist/agents/subagent-steps.js +8 -4
- package/dist/agents/subagent.d.ts +1 -17
- package/dist/agents/subagent.js +12 -24
- package/dist/core/agent-definition.d.ts +19 -40
- package/dist/core/agent-state.d.ts +91 -0
- package/dist/core/agent-state.js +79 -0
- package/dist/core/ask-origin.d.ts +28 -6
- package/dist/core/ask-origin.js +9 -5
- package/dist/core/ask-unresolvable-notice.d.ts +27 -4
- package/dist/core/ask-unresolvable-notice.js +8 -0
- package/dist/core/auto-promote.d.ts +14 -27
- package/dist/core/auto-promote.js +5 -5
- package/dist/core/checkpoint-store.d.ts +11 -12
- package/dist/core/decision-receipt.d.ts +5 -2
- package/dist/core/decision-receipt.js +1 -1
- package/dist/core/engine-notice.d.ts +7 -1
- package/dist/core/gate-crash-trace.d.ts +1 -1
- package/dist/core/gate-fold.js +7 -3
- package/dist/core/gate-lanes.js +22 -3
- package/dist/core/governance-codes.d.ts +3 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/hooks.d.ts +20 -6
- package/dist/core/hooks.js +28 -28
- package/dist/core/mcp-tool-meta.d.ts +24 -0
- package/dist/core/mcp-tool-meta.js +17 -0
- package/dist/core/mcp.d.ts +1 -6
- package/dist/core/mcp.js +6 -7
- package/dist/core/mode-policy.d.ts +25 -0
- package/dist/core/mode-policy.js +8 -0
- package/dist/core/protocol-table.d.ts +3 -0
- package/dist/core/protocol-table.js +3 -0
- package/dist/core/reminder-mint.d.ts +21 -1
- package/dist/core/reminder-mint.js +20 -0
- package/dist/core/retired-keys.d.ts +2 -1
- package/dist/core/retired-keys.js +13 -2
- package/dist/core/rule-denial-sentence.d.ts +2 -2
- package/dist/core/runner/call-hook-stations.d.ts +106 -0
- package/dist/core/runner/call-hook-stations.js +118 -0
- package/dist/core/runner/contracts.d.ts +35 -25
- package/dist/core/runner/gate-exit.d.ts +5 -1
- package/dist/core/runner/gate-exit.js +10 -6
- package/dist/core/runner/prepare-delegation-surface.d.ts +1 -0
- package/dist/core/runner/prepare-delegation-surface.js +4 -4
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -3
- package/dist/core/runner/prepare-gate-stations.js +13 -70
- package/dist/core/runner/prepare-inherited-gate.d.ts +6 -3
- package/dist/core/runner/prepare-inherited-gate.js +5 -3
- package/dist/core/runner/prepare-park-ask.d.ts +4 -0
- package/dist/core/runner/prepare-park-ask.js +4 -1
- package/dist/core/runner/prepare-policy-chain.d.ts +6 -5
- package/dist/core/runner/prepare-policy-chain.js +14 -25
- package/dist/core/runner/prepare-project-context.d.ts +15 -16
- package/dist/core/runner/prepare-project-context.js +15 -11
- package/dist/core/runner/prepare-run-refs.d.ts +8 -8
- package/dist/core/runner/prepare-run-refs.js +3 -3
- package/dist/core/runner/prepare-safety-scan.js +3 -3
- package/dist/core/runner/prepare-suspend-saga.d.ts +2 -1
- package/dist/core/runner/prepare-suspend-saga.js +2 -1
- package/dist/core/runner/prepare-task.js +14 -12
- package/dist/core/runner/prepare-turn-wiring.d.ts +3 -1
- package/dist/core/runner/prepare-turn-wiring.js +16 -13
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-apply.js +12 -23
- package/dist/core/runner/resume-checkpoint-screen.js +3 -0
- package/dist/core/runner/run-leg.js +61 -47
- package/dist/core/runner/runtask.d.ts +1 -1
- package/dist/core/runner/session-rule-policy.d.ts +7 -19
- package/dist/core/runner/session-rule-policy.js +1 -2
- package/dist/core/runner/skill-base-dir.d.ts +10 -0
- package/dist/core/runner/skill-base-dir.js +4 -0
- package/dist/core/runner/synthetic-tools.d.ts +11 -17
- package/dist/core/runner/synthetic-tools.js +7 -8
- package/dist/core/safety-axis-vocab.d.ts +2 -2
- package/dist/core/safety-axis-vocab.js +2 -0
- package/dist/core/session-policy-store.d.ts +63 -11
- package/dist/core/session-policy-store.js +52 -6
- package/dist/core/skill-grant.d.ts +239 -0
- package/dist/core/skill-grant.js +313 -0
- package/dist/core/skills-directory.d.ts +5 -34
- package/dist/core/skills-directory.js +9 -58
- package/dist/core/task-registry-agent.d.ts +14 -2
- package/dist/core/task-registry-agent.js +23 -8
- package/dist/core/task-registry-shared.d.ts +6 -0
- package/dist/core/task-registry.d.ts +16 -0
- package/dist/core/task-registry.js +19 -6
- package/dist/core/task-spec.d.ts +10 -0
- package/dist/core/tool-face.d.ts +3 -2
- package/dist/core/tool-name-aliases.d.ts +38 -12
- package/dist/core/tool-name-aliases.js +16 -0
- package/dist/core/tool-name-readings.d.ts +6 -2
- package/dist/core/tool-name-readings.js +6 -4
- package/dist/core/tool-policy.d.ts +2 -2
- package/dist/core/tool-registry.d.ts +1 -4
- package/dist/core/tool-registry.js +0 -3
- package/dist/core/tool-spec.d.ts +11 -13
- package/dist/core/tools.d.ts +1 -1
- package/dist/core/trace.d.ts +15 -0
- package/dist/core/types.d.ts +1 -1
- package/dist/engine/harness/agent-harness.d.ts +12 -1
- package/dist/engine/harness/agent-harness.js +17 -13
- package/dist/engine/loop/agent-loop.d.ts +16 -1
- package/dist/engine/loop/agent-loop.js +9 -15
- package/dist/engine/loop/types.d.ts +2 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +4 -2
- package/dist/stores/file/adoption/adopt.js +23 -16
- package/dist/stores/file/index.d.ts +8 -2
- package/dist/stores/file/index.js +2 -2
- package/dist/stores/file/mailbox-store.js +2 -2
- package/dist/stores/file/permission-rule-adopt.js +10 -1
- package/dist/stores/file/permission-rule-store.d.ts +6 -4
- package/dist/stores/file/permission-rule-store.js +127 -59
- package/dist/stores/file/session-policy-store.d.ts +86 -14
- package/dist/stores/file/session-policy-store.js +135 -118
- package/dist/stores/file/task-list-store.d.ts +5 -0
- package/dist/stores/file/task-list-store.js +18 -4
- package/dist/stores/file/usage-window-store.d.ts +9 -1
- package/dist/stores/file/usage-window-store.js +47 -33
- package/dist/tools/fs/safety.d.ts +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +45 -5
- package/wire-changes/core@7.41.0.json +1865 -0
- package/dist/core/runner/active-skill-scope.d.ts +0 -58
- package/dist/core/runner/active-skill-scope.js +0 -133
- package/dist/core/skill-tool-specifier.d.ts +0 -80
- package/dist/core/skill-tool-specifier.js +0 -57
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
2
|
-
import {
|
|
2
|
+
import { mountedToolRequiresUserInteraction } from "../mode-policy.js";
|
|
3
3
|
import { complianceCallDenial } from "../compliance.js";
|
|
4
|
-
import { cloneObserverInput, formatHookFeedback,
|
|
4
|
+
import { cloneObserverInput, formatHookFeedback, runToolGate } from "../hooks.js";
|
|
5
5
|
import { screenGateOutcome } from "../gate-outcome.js";
|
|
6
6
|
import { directDeny } from "./gate-exit.js";
|
|
7
7
|
import { emitTrace } from "../trace.js";
|
|
@@ -12,7 +12,7 @@ import { classifierUnavailableSeat } from "./classifier-ladder.js";
|
|
|
12
12
|
import { PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
|
|
13
13
|
import { callFaceSeat, rosterToolName, toolCallFaceOf } from "../tool-roster.js";
|
|
14
14
|
import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
|
|
15
|
-
import {
|
|
15
|
+
import { createApprovedCallScreen, createToolResultStation } from "./call-hook-stations.js";
|
|
16
16
|
const PEER_MESSAGE_NAMES = new Set([SEND_MESSAGE_TOOL_NAME]);
|
|
17
17
|
function screenGateResult(result) {
|
|
18
18
|
if (result.suspend !== undefined)
|
|
@@ -46,7 +46,8 @@ export function maybeHumanRejectionHalt(input) {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
export function prepareGateStations(input) {
|
|
49
|
-
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, askRoute, permissionMode, toolMarkSources, a2aIsolatePeers, ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement } = input;
|
|
49
|
+
const { askLane, parkAsk, tools, toolRosterDeltas, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, askRoute, permissionMode, toolMarkSources, a2aIsolatePeers, ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement, seatInputsFor } = input;
|
|
50
|
+
const gatePreToolUse = ownGatePreToolUse !== undefined ? (name, args, ctx) => ownGatePreToolUse(name, args, { ...ctx, permissionMode: permissionMode.observed }) : undefined;
|
|
50
51
|
const releasesAnnounced = new Map();
|
|
51
52
|
const onModeReleased = (info) => {
|
|
52
53
|
const table = info.readUnestablished === true ? input.readDenyInForce() : undefined;
|
|
@@ -116,7 +117,7 @@ export function prepareGateStations(input) {
|
|
|
116
117
|
...(e.signal !== undefined ? { callSignal: e.signal } : {}),
|
|
117
118
|
identity: hookIdentity,
|
|
118
119
|
reminderMark,
|
|
119
|
-
...(
|
|
120
|
+
...(gatePreToolUse !== undefined ? { preToolUse: gatePreToolUse } : {}),
|
|
120
121
|
hookTimeoutMs,
|
|
121
122
|
...(handsCwdRef !== undefined ? { trackedCwd: () => handsCwdRef.current } : {}),
|
|
122
123
|
...(hookEnvFace !== undefined ? { hookEnv: hookEnvFace } : {}),
|
|
@@ -138,7 +139,8 @@ export function prepareGateStations(input) {
|
|
|
138
139
|
...(autoModeDecider.current !== undefined ? { autoMode: { decider: autoModeDecider.current, ...(autoModeDenialTracking.current !== undefined ? { denialTracking: autoModeDenialTracking.current } : {}), unavailable: classifierUnavailableSeat({ station: "leg", ...(autoModeDecider.current.timeoutMs !== undefined ? { budgetMs: autoModeDecider.current.timeoutMs } : {}), approvalSeat: askReachesPerson(askRoute), onNotice: deps.onNotice, sessionId, runId }) } } : {}),
|
|
139
140
|
permissionMode: () => permissionMode.current,
|
|
140
141
|
...(markSources.size > 0 ? { toolMarkSources: markSources } : {}),
|
|
141
|
-
...(
|
|
142
|
+
...seatInputsFor(e.toolName, wireOf("gate.skillActivationSeat", call)),
|
|
143
|
+
...(mountedToolRequiresUserInteraction(tools, e.toolName) ? { requiresUserInteraction: true } : {}),
|
|
142
144
|
...(a2aIsolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
143
145
|
onModeReleased,
|
|
144
146
|
ancestorArbitrationOf: (toolCallId) => ancestorArbitrations.get(toolCallId),
|
|
@@ -239,68 +241,9 @@ export function prepareGateStations(input) {
|
|
|
239
241
|
});
|
|
240
242
|
}
|
|
241
243
|
}
|
|
242
|
-
const
|
|
243
|
-
if (
|
|
244
|
-
harness.on("tool_result",
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
let content = e.content;
|
|
249
|
-
let changed = false;
|
|
250
|
-
const post = { toolName: rosterToolName(toolRosterDeltas.current, e.toolName) };
|
|
251
|
-
if (e.isError) {
|
|
252
|
-
if (hooks?.postToolUseFailure) {
|
|
253
|
-
let clonedDetails = e.details;
|
|
254
|
-
try {
|
|
255
|
-
clonedDetails = structuredClone(e.details);
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
}
|
|
259
|
-
const failedName = wireOf("hook.postToolUseFailure", post);
|
|
260
|
-
const failureSeat = await runHookSeat("postToolUseFailure", { timeoutMs: hookTimeoutMs, signal: abortController.signal }, (sig) => hooks.postToolUseFailure(failedName, e.input, {
|
|
261
|
-
error: e.content
|
|
262
|
-
.filter((c) => c.type === "text")
|
|
263
|
-
.map((c) => c.text)
|
|
264
|
-
.join("\n"),
|
|
265
|
-
isInterrupt: abortController.signal.aborted || spec.signal?.aborted === true,
|
|
266
|
-
content: e.content.map((c) => ({ ...c })),
|
|
267
|
-
details: clonedDetails,
|
|
268
|
-
}, { toolCallId: e.toolCallId, toolName: failedName, signal: sig, ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}), ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}), identity: hookIdentity, permissionMode: permissionMode.observed }));
|
|
269
|
-
if (failureSeat.expired) {
|
|
270
|
-
notifyOwnHookCrash(hookSeatExpiredError("postToolUseFailure", hookTimeoutMs, failureSeat.cause, "the observation was abandoned and its additionalContext dropped; the failure result itself is unchanged"));
|
|
271
|
-
}
|
|
272
|
-
const patch = failureSeat.expired ? undefined : failureSeat.value;
|
|
273
|
-
if (patch?.additionalContext) {
|
|
274
|
-
content = [...content, { type: "text", text: formatHookFeedback(patch.additionalContext, reminderMark) }];
|
|
275
|
-
changed = true;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
else if (hooks?.postToolUse) {
|
|
280
|
-
const ranName = wireOf("hook.postToolUse", post);
|
|
281
|
-
const successSeat = await runHookSeat("postToolUse", { timeoutMs: hookTimeoutMs, signal: abortController.signal }, (sig) => hooks.postToolUse(ranName, e.input, { content: e.content, details: e.details, isError: e.isError }, { toolCallId: e.toolCallId, toolName: ranName, signal: sig, ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}), ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}), identity: hookIdentity, permissionMode: permissionMode.observed }));
|
|
282
|
-
if (successSeat.expired) {
|
|
283
|
-
notifyOwnHookCrash(hookSeatExpiredError("postToolUse", hookTimeoutMs, successSeat.cause, "the seat was abandoned; the tool result is delivered to the model UNMODIFIED (no updatedOutput, no additionalContext)"));
|
|
284
|
-
}
|
|
285
|
-
const patch = successSeat.expired ? undefined : successSeat.value;
|
|
286
|
-
if (patch?.updatedOutput) {
|
|
287
|
-
content = patch.updatedOutput;
|
|
288
|
-
changed = true;
|
|
289
|
-
noteHostRewrite(frameSettlement, e.toolCallId);
|
|
290
|
-
}
|
|
291
|
-
if (patch?.additionalContext) {
|
|
292
|
-
content = [...content, { type: "text", text: formatHookFeedback(patch.additionalContext, reminderMark) }];
|
|
293
|
-
changed = true;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
const pre = preToolContexts.get(e.toolCallId);
|
|
297
|
-
if (pre) {
|
|
298
|
-
preToolContexts.delete(e.toolCallId);
|
|
299
|
-
content = [...content, ...pre.map((t) => ({ type: "text", text: formatHookFeedback(t, reminderMark) }))];
|
|
300
|
-
changed = true;
|
|
301
|
-
}
|
|
302
|
-
return changed ? { content } : undefined;
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
return { toolCallStationArmed: toolCallGateArmedRef.armed, toolResultStationArmed };
|
|
244
|
+
const toolResultStation = createToolResultStation({ hooks, blockedToolCalls, toolRosterDeltas, preToolContexts, hookTimeoutMs, abortController, spec, handsCwdRef, hookEnvFace, hookIdentity, permissionMode, notifyOwnHookCrash, reminderMark, frameSettlement });
|
|
245
|
+
if (toolResultStation !== undefined)
|
|
246
|
+
harness.on("tool_result", toolResultStation);
|
|
247
|
+
const approvedCallScreen = createApprovedCallScreen({ preToolUse: gatePreToolUse, hookTimeoutMs, abortController, handsCwdRef, hookEnvFace, hookIdentity, notifyOwnHookCrash, preToolContexts });
|
|
248
|
+
return { toolCallStationArmed: toolCallGateArmedRef.armed, toolResultStationArmed: toolResultStation !== undefined, ...(approvedCallScreen !== undefined ? { approvedCallScreen } : {}) };
|
|
306
249
|
}
|
|
@@ -46,7 +46,7 @@ import type { SessionStore } from "../session.js";
|
|
|
46
46
|
import { type OnAsk } from "../tool-policy.js";
|
|
47
47
|
import type { PermissionMode, RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext, ToolSpec } from "../types.js";
|
|
48
48
|
import { PermissionModeController } from "../mode-policy.js";
|
|
49
|
-
import type
|
|
49
|
+
import { type SkillGrantTable } from "../skill-grant.js";
|
|
50
50
|
import type { InheritedGate, Prepared, PrepareResume, RunInternals, StopForDenialLimit, ToolFaceSnapshot, WriteFaceSeat } from "./contracts.js";
|
|
51
51
|
export interface PrepareInheritedGateInput {
|
|
52
52
|
/** borrowed-readonly — the REBOUND spec. Read: `shellGate` (the own doctrine the chain fold tightens), `durableApproval`
|
|
@@ -107,8 +107,8 @@ export interface PrepareInheritedGateInput {
|
|
|
107
107
|
harnessRef: {
|
|
108
108
|
current?: AgentHarness;
|
|
109
109
|
};
|
|
110
|
-
/** borrowed-readonly — the per-task skill
|
|
111
|
-
|
|
110
|
+
/** borrowed-readonly — the per-task skill grant table (the ctx's `activeSkillGrants` capture and the checkpoint section read it call-time). Not written here. */
|
|
111
|
+
skillGrants: SkillGrantTable;
|
|
112
112
|
/** borrowed-readonly — the filtered display-forwarding wrapper (ctx seat; absent ⇒ no key). */
|
|
113
113
|
forwardEvent: ((e: TaskEvent) => void) | undefined;
|
|
114
114
|
/** borrowed-readonly — the retain ledger (ctx seat; absent ⇒ no key). */
|
|
@@ -276,6 +276,9 @@ export interface PrepareInheritedGateResult {
|
|
|
276
276
|
writeFaceCheckpointSection: () => {
|
|
277
277
|
face: "open" | "roots";
|
|
278
278
|
} | undefined;
|
|
279
|
+
/** owned — the SKILL-GRANT checkpoint section builder: the rows this run's table grew (the model's activations, a
|
|
280
|
+
* delegated child's capture), read off the live table at mint time; absent when there are none. */
|
|
281
|
+
skillGrantCheckpointSection: () => import("../skill-grant.js").SkillGrantRecord[] | undefined;
|
|
279
282
|
/** owned — the F-012 stamp predicate (opaque constraints present ∨ seed frozen chain ∨ provenance-armed). */
|
|
280
283
|
f012CheckpointState: () => boolean;
|
|
281
284
|
/** owned — the FROZEN approver seat (spec over deps) every station of this leg and its children's chain resolve at. */
|
|
@@ -9,10 +9,11 @@ import { deliverEngineNotice } from "../types.js";
|
|
|
9
9
|
import { PermissionModeController, modeRequestAfterResume } from "../mode-policy.js";
|
|
10
10
|
import { SessionModeSeats } from "./session-mode-seats.js";
|
|
11
11
|
import { recordedModeSeed } from "../checkpoint-store.js";
|
|
12
|
+
import { frozenSkillGrants } from "../skill-grant.js";
|
|
12
13
|
import { attachRebuiltDenialTrackers, createDenialLimitStop } from "./denial-limit-arms.js";
|
|
13
14
|
import { asThrownError } from "../thrown-value.js";
|
|
14
15
|
export function prepareInheritedGate(input) {
|
|
15
|
-
const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef,
|
|
16
|
+
const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, mountsCallerTool, promptProfile, resolvedInteractionPosture, permissionModeSeat, handsEnabled, reportUsage, harnessRef, skillGrants, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
|
|
16
17
|
const liveInheritedGate = internals?.inheritedGate;
|
|
17
18
|
const seedInheritedGate = resume?.seed.inheritedGate;
|
|
18
19
|
const inheritedAncestorRules = (() => {
|
|
@@ -108,6 +109,7 @@ export function prepareInheritedGate(input) {
|
|
|
108
109
|
const seat = input.resolvedWriteFace() ?? input.handsLessResolvedWriteFace();
|
|
109
110
|
return seat === undefined ? undefined : seat.inForce() === "open" ? { face: "open" } : seat.fenced ? { face: "roots" } : undefined;
|
|
110
111
|
};
|
|
112
|
+
const skillGrantCheckpointSection = () => frozenSkillGrants(skillGrants);
|
|
111
113
|
const f012CheckpointState = () => (inheritedParentConstraints?.length ?? 0) > 0 ||
|
|
112
114
|
seedInheritedGate?.constraintChain !== undefined ||
|
|
113
115
|
internals?.delegationProvenance !== undefined;
|
|
@@ -247,7 +249,7 @@ export function prepareInheritedGate(input) {
|
|
|
247
249
|
reminderDisclosureCounts,
|
|
248
250
|
...(centerAdoption !== undefined ? { centerArtifactDigest: centerAdoption.artifact.artifactDigest } : {}),
|
|
249
251
|
...(centerAdoption?.sourceRevision !== undefined ? { centerSourceRevision: centerAdoption.sourceRevision } : {}),
|
|
250
|
-
|
|
252
|
+
activeSkillGrants: () => skillGrants.records(),
|
|
251
253
|
inheritedGateForChildren,
|
|
252
254
|
delegationProvenanceForChildren,
|
|
253
255
|
delegationSettlement: delegationSettlementForChildren,
|
|
@@ -294,5 +296,5 @@ export function prepareInheritedGate(input) {
|
|
|
294
296
|
...(spec.enablePlanMode === true ? { enterPlanMode } : {}),
|
|
295
297
|
};
|
|
296
298
|
};
|
|
297
|
-
return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
|
|
299
|
+
return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, permissionMode, commitModeSeats, endModeCeiling, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, skillGrantCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
|
|
298
300
|
}
|
|
@@ -32,6 +32,7 @@ import { type ContentAskBinding } from "./content-ask-bindings.js";
|
|
|
32
32
|
import type { AskLane, IrreversibilityTier, ParkAsk, Prepared, SuspendSaga } from "./contracts.js";
|
|
33
33
|
import type { createRuleOffersOf } from "./permission-rule-lanes.js";
|
|
34
34
|
import { type ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
35
|
+
import { type SkillGrantSource } from "../skill-grant.js";
|
|
35
36
|
export interface PrepareParkAskInput {
|
|
36
37
|
/** borrowed-readonly — the in-stream ask lane, or undefined when the gate machinery is inactive (then both closures are
|
|
37
38
|
* absent). Read: `composedCallSignal` (the fence and the delivery race), `approvalPreviewOf` (the row's preview). */
|
|
@@ -140,6 +141,9 @@ export interface PrepareParkAskInput {
|
|
|
140
141
|
orgAdmissionCheckpointState: () => boolean;
|
|
141
142
|
/** borrowed-readonly — the policy-chain phase's rule-offer factory (the row's suggestion members). */
|
|
142
143
|
ruleOffersOf: ReturnType<typeof createRuleOffersOf>;
|
|
144
|
+
/** borrowed-readonly — the leg's loaded skills (what its `Skill` tool serves); a parked `Skill` call's row binds the named
|
|
145
|
+
* skill — its name and its lists — from them (`boundGrantDigest`). Empty when the leg loads none. */
|
|
146
|
+
skillSpecs: readonly SkillGrantSource[];
|
|
143
147
|
/** borrowed-readonly — the injectable wall clock (the row's `suspendedAt`). */
|
|
144
148
|
now: () => number;
|
|
145
149
|
/** borrowed-readonly — the per-task human-review accumulator; snapshotted onto the row (never written here). */
|
|
@@ -17,6 +17,8 @@ import { DEFAULT_UNATTENDED_APPROVAL_TTL_MS, publishCommittedSuspend, sanitizedT
|
|
|
17
17
|
import { remoteEnvFailureNote } from "./remote-env-retry.js";
|
|
18
18
|
import { callFaceSeat, parkedToolIdentity, parkedToolFace } from "../tool-roster.js";
|
|
19
19
|
import { seatOf, wireOf } from "../tool-name-readings.js";
|
|
20
|
+
import { skillCallGrantDigest } from "../skill-grant.js";
|
|
21
|
+
import { SKILL_TOOL_NAME } from "./synthetic-tools.js";
|
|
20
22
|
class ParkRefusal extends Error {
|
|
21
23
|
constructor(message, options) {
|
|
22
24
|
super(message, options);
|
|
@@ -27,7 +29,7 @@ const wordlessDemand = (toolName) => {
|
|
|
27
29
|
throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
|
|
28
30
|
};
|
|
29
31
|
export function prepareParkAsk(input) {
|
|
30
|
-
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
32
|
+
const { askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, toolMarkSources, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, skillSpecs, now, humanReviewRef, abortController, harness, pausedRef } = input;
|
|
31
33
|
if (askLane === undefined || saga === undefined)
|
|
32
34
|
return { parkAsk: undefined };
|
|
33
35
|
const { composedCallSignal, approvalPreviewOf } = askLane;
|
|
@@ -316,6 +318,7 @@ export function prepareParkAsk(input) {
|
|
|
316
318
|
...askCarryRowMembers(carry),
|
|
317
319
|
...ruleOffersOf(req, parkedArgs, clearanceFacts),
|
|
318
320
|
boundInputHash: boundInputHashOf(parkedArgs),
|
|
321
|
+
...((grantDigest) => (grantDigest !== undefined ? { boundGrantDigest: grantDigest } : {}))(skillCallGrantDigest(req, parkedArgs, skillSpecs, SKILL_TOOL_NAME)),
|
|
319
322
|
batchToolCallIds,
|
|
320
323
|
completedCallIds,
|
|
321
324
|
}),
|
|
@@ -30,7 +30,7 @@ import { type OnAsk, type ToolPolicy } from "../tool-policy.js";
|
|
|
30
30
|
import type { RunnerDeps, RuntimeCaps, TaskSpec, ToolEffect } from "../types.js";
|
|
31
31
|
import type { CwdRef } from "../../tools/fs/fs-shared.js";
|
|
32
32
|
import type { ReadDenyMatcher } from "../../tools/fs/read-deny.js";
|
|
33
|
-
import {
|
|
33
|
+
import type { SkillGrantLane, SkillGrantTable } from "../skill-grant.js";
|
|
34
34
|
import type { InheritedGate, IrreversibilityTier, PrepareResume, RunInternals, StopForDenialLimit } from "./contracts.js";
|
|
35
35
|
import { type Arbitration } from "../ask-origin.js";
|
|
36
36
|
import { type ModeGateSeats, type PermissionModeController } from "../mode-policy.js";
|
|
@@ -118,10 +118,9 @@ export interface PreparePolicyChainInput {
|
|
|
118
118
|
inheritedParentConstraints: NonNullable<InheritedGate["parentConstraints"]>[number][] | undefined;
|
|
119
119
|
/** borrowed-readonly — the resumed leg's SEED half; only `constraintChain` (the frozen deny projections). */
|
|
120
120
|
seedInheritedGate: Pick<NonNullable<PrepareResume["seed"]["inheritedGate"]>, "constraintChain"> | undefined;
|
|
121
|
-
/** borrowed-readonly —
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
skillScope: ActiveSkillScope;
|
|
121
|
+
/** borrowed-readonly — the run's skill grant table; this phase binds its lane to the call facts the person's rule lane
|
|
122
|
+
* reads (root, session, live cwd, the execution environment's home). Not written here. */
|
|
123
|
+
skillGrants: SkillGrantTable;
|
|
125
124
|
/** borrowed-readonly — the FROZEN approver seat (the pass-through test's approver identity). */
|
|
126
125
|
frozenOnAsk: OnAsk | undefined;
|
|
127
126
|
/** borrowed-readonly — the identity of this task's hook-env source (the pass-through test's environment term). */
|
|
@@ -172,6 +171,8 @@ export interface PreparePolicyChainResult {
|
|
|
172
171
|
basePolicyForResumeEdit: ToolPolicy | undefined;
|
|
173
172
|
/** owned — the deny-narrowing fold (`Prepared.denyNarrowingPolicy`), or undefined when no layer exists. */
|
|
174
173
|
denyNarrowingPolicy: ToolPolicy | undefined;
|
|
174
|
+
/** owned — the skill grant lane (`Prepared.skillGrants`; the gate's skill member and deny rows read it). */
|
|
175
|
+
skillGrantLane: SkillGrantLane;
|
|
175
176
|
/** owned — the caller-facing policy layers (the wiring manifest reads their COUNT). */
|
|
176
177
|
policyLayers: ToolPolicy[];
|
|
177
178
|
/** owned — THE effective policy the gate station adjudicates with (the fold plus its final-form re-check), or undefined. */
|
|
@@ -7,10 +7,9 @@ import { assertHookSeatsCallable } from "../hook-seats.js";
|
|
|
7
7
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
|
|
8
8
|
import { isIsolated } from "../remote-env.js";
|
|
9
9
|
import { askApproverIdentity, carryEngineAttestations, carryPrecedingSettlement, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, durableParkOptInDeclared, isLiveApproverSeat, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, wrapToolPolicy, attestAskUnresolvableCause } from "../tool-policy.js";
|
|
10
|
-
import {
|
|
11
|
-
import { NAMESPACED_NAME_SHAPES
|
|
10
|
+
import { judgeRuleToolName } from "../tool-name-aliases.js";
|
|
11
|
+
import { NAMESPACED_NAME_SHAPES } from "../protocol-table.js";
|
|
12
12
|
import { emitTrace } from "../trace.js";
|
|
13
|
-
import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
14
13
|
import { arbitrateByMode, askClassMembersOf, askMandateMember } from "../ask-origin.js";
|
|
15
14
|
import { createModeGatePolicy, mountedToolDeclaresReadOnly } from "../mode-policy.js";
|
|
16
15
|
const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
|
|
@@ -22,7 +21,7 @@ import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-
|
|
|
22
21
|
import { thrownText } from "../thrown-value.js";
|
|
23
22
|
import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
|
|
24
23
|
export async function preparePolicyChain(input) {
|
|
25
|
-
const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate,
|
|
24
|
+
const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, toolMarkSources, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, skillGrants, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
|
|
26
25
|
const ancestorArbitrations = new Map();
|
|
27
26
|
const arbitrateInherited = (ask, toolCallId, judged) => {
|
|
28
27
|
const verdict = arbitrateByMode(permissionMode.current, judged.classification, { origin: judged.origin, ...askMandateMember(ancestorFacts(judged.ask.action === "ask" ? judged.ask : undefined)) });
|
|
@@ -44,21 +43,18 @@ export async function preparePolicyChain(input) {
|
|
|
44
43
|
for (const g of nameGroups) {
|
|
45
44
|
for (const [kind, list] of [["deny", g.deny], ["ask", g.ask], ["allow", g.allow]]) {
|
|
46
45
|
for (const n of list ?? []) {
|
|
46
|
+
const verdict = judgeRuleToolName(n, known);
|
|
47
|
+
if (verdict.kind !== "current") {
|
|
48
|
+
const err = new Error(verdict.kind === "retired"
|
|
49
|
+
? `tool policy ${kind}-list entry "${n}" is a RETIRED tool name (${verdict.guidance}) and matches nothing in this run's roster — legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Update the deployment's rule to the current name.`
|
|
50
|
+
: `tool policy ${kind}-list entry "${n}" carries the "__" namespace separator but no protocol prefix, and matches nothing in this run's roster — protocol tools are named ${NAMESPACED_NAME_SHAPES}, and legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Prefix the entry with the owning protocol's marker.`);
|
|
51
|
+
err.code = "config.legacy_tool_name";
|
|
52
|
+
throw err;
|
|
53
|
+
}
|
|
47
54
|
if (known.has(n))
|
|
48
55
|
continue;
|
|
49
56
|
if (isNamespacedCoveringRuleName(n) && [...known].some((k) => namespacedRuleNameCovers(n, k)))
|
|
50
57
|
continue;
|
|
51
|
-
const retired = RETIRED_TOOL_NAMES.get(n);
|
|
52
|
-
if (retired !== undefined) {
|
|
53
|
-
const err = new Error(`tool policy ${kind}-list entry "${n}" is a RETIRED tool name (${retired}) and matches nothing in this run's roster — legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Update the deployment's rule to the current name.`);
|
|
54
|
-
err.code = "config.legacy_tool_name";
|
|
55
|
-
throw err;
|
|
56
|
-
}
|
|
57
|
-
if (n.includes("__") && protocolOf(n) === undefined) {
|
|
58
|
-
const err = new Error(`tool policy ${kind}-list entry "${n}" carries the "__" namespace separator but no protocol prefix, and matches nothing in this run's roster — protocol tools are named ${NAMESPACED_NAME_SHAPES}, and legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Prefix the entry with the owning protocol's marker.`);
|
|
59
|
-
err.code = "config.legacy_tool_name";
|
|
60
|
-
throw err;
|
|
61
|
-
}
|
|
62
58
|
unmatched.add(`"${n}" (${kind})`);
|
|
63
59
|
}
|
|
64
60
|
}
|
|
@@ -131,20 +127,12 @@ export async function preparePolicyChain(input) {
|
|
|
131
127
|
: {}),
|
|
132
128
|
}, { env: executionEnv, rootPath: taskRootFinal, toolEffects }));
|
|
133
129
|
}
|
|
134
|
-
const skillScopePolicy = hasSkillManifest
|
|
135
|
-
? createActiveSkillScopePolicy({
|
|
136
|
-
scope: skillScope,
|
|
137
|
-
env: executionEnv,
|
|
138
|
-
rootPath: taskRootFinal,
|
|
139
|
-
toolEffects,
|
|
140
|
-
})
|
|
141
|
-
: undefined;
|
|
142
130
|
const frozenConstraintLayers = (seedInheritedGate?.constraintChain ?? [])
|
|
143
131
|
.filter((e) => !("opaque" in e))
|
|
144
132
|
.map((entry) => ({
|
|
145
133
|
check: (creq) => checkToolPolicyProjection(entry, creq) ?? { action: "allow" },
|
|
146
134
|
}));
|
|
147
|
-
const narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, ...frozenConstraintLayers
|
|
135
|
+
const narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, ...frozenConstraintLayers].filter((p) => p !== undefined);
|
|
148
136
|
for (const layer of narrowingLayers)
|
|
149
137
|
auditPolicyNames(layer);
|
|
150
138
|
const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
|
|
@@ -306,6 +294,7 @@ export async function preparePolicyChain(input) {
|
|
|
306
294
|
};
|
|
307
295
|
};
|
|
308
296
|
const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
|
|
297
|
+
const skillGrantLane = skillGrants.lane({ root: taskRootFinal, sessionId, liveCwd: () => handsCwdRef?.current, home: executionEnv.homeDir });
|
|
309
298
|
const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({
|
|
310
299
|
provider: deps.permissionRuleStore,
|
|
311
300
|
localOwnerDeclared: deps.localOwnerRules === true,
|
|
@@ -677,7 +666,7 @@ export async function preparePolicyChain(input) {
|
|
|
677
666
|
return decision;
|
|
678
667
|
})
|
|
679
668
|
: foldedPolicy;
|
|
680
|
-
return { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations };
|
|
669
|
+
return { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, skillGrantLane, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations };
|
|
681
670
|
}
|
|
682
671
|
const advisedPolicyNames = new WeakMap();
|
|
683
672
|
const ADVISED_KEYS_CAP = 64;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ResolvedSkillListingBudget } from "../skill-listing-budget.js";
|
|
2
2
|
import { type StoredSession } from "../session.js";
|
|
3
3
|
import type { RunnerDeps, TaskSpec, ToolSpec } from "../types.js";
|
|
4
|
-
import type
|
|
4
|
+
import { type SkillGrantTable, type SkillSeatReader } from "../skill-grant.js";
|
|
5
5
|
import type { AnnounceOnceSink, Prepared, PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
|
|
6
6
|
import { normalizeSkills } from "./synthetic-tools.js";
|
|
7
7
|
import { RosterBuilder } from "../tool-roster.js";
|
|
@@ -11,12 +11,12 @@ export interface PrepareProjectContextInput {
|
|
|
11
11
|
* (the shared-memory pair's switch: wired ⇒ mount). Receiver preserved (`deps.onError?.(…)`). */
|
|
12
12
|
deps: Pick<RunnerDeps, "loadProjectMemory" | "onError" | "sharedMemoryStores" | "onNotice">;
|
|
13
13
|
/** borrowed-readonly — the REBOUND spec (`spec′`). Read: `sessionId` (phase derivation + the
|
|
14
|
-
* continuation projections), `skills` (the roster), `
|
|
15
|
-
* identity). Never mutated. */
|
|
16
|
-
spec: Pick<TaskSpec, "sessionId" | "skills" | "principal">;
|
|
17
|
-
/** borrowed-readonly — trusted internals: `agentName` (seam ctx), `
|
|
18
|
-
*
|
|
19
|
-
internals: Pick<RunInternals, "agentName" | "
|
|
14
|
+
* continuation projections), `skills` (the roster), `userInvokedSkills` (the person's invoked skills — their
|
|
15
|
+
* rows are written here), `principal` (the shared-memory tools' trusted identity). Never mutated. */
|
|
16
|
+
spec: Pick<TaskSpec, "sessionId" | "skills" | "userInvokedSkills" | "principal">;
|
|
17
|
+
/** borrowed-readonly — trusted internals: `agentName` (seam ctx), `inheritedSkillGrants` (the parent's captured
|
|
18
|
+
* skill grant rows, seeded as `inherited`). `undefined` on a standalone prepare. */
|
|
19
|
+
internals: Pick<RunInternals, "agentName" | "inheritedSkillGrants"> | undefined;
|
|
20
20
|
/** borrowed-readonly — the acquired session; only `getBranch()` is read (phase derivation walk).
|
|
21
21
|
* The read is best-effort: a throw keeps the provisional phase. */
|
|
22
22
|
session: Pick<StoredSession, "getBranch">;
|
|
@@ -43,10 +43,10 @@ export interface PrepareProjectContextInput {
|
|
|
43
43
|
* files). This phase APPENDS the project layer and hands the result back as
|
|
44
44
|
* {@link PrepareProjectContextResult.memoryBlock}; the input value itself is never mutated. */
|
|
45
45
|
memoryBlockFromEngine: string | undefined;
|
|
46
|
-
/** borrowed-mutable — the run's
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
|
|
46
|
+
/** borrowed-mutable — the run's skill grant table (identity is the contract: the `skill` tool, the gate's lane and the
|
|
47
|
+
* ctx capture alias this exact object). Mutated here: the parent's captured rows (`inherited`) and the person's invoked
|
|
48
|
+
* skills (`user`); after this phase only the `skill` tool writes (on activation). */
|
|
49
|
+
skillGrants: SkillGrantTable;
|
|
50
50
|
/** borrowed-mutable — the run's roster builder (later refs alias its read face; in-place contract). Mutated
|
|
51
51
|
* here: `mount` of the `skill` tool, the shared-memory pair and the engine-memory pair. Occupancy
|
|
52
52
|
* is judged on the ALREADY-MOUNTED read face (names + aliases). */
|
|
@@ -67,8 +67,8 @@ export interface PrepareProjectContextInput {
|
|
|
67
67
|
memoryPairExcludedName: string | undefined;
|
|
68
68
|
/** borrowed-readonly — the engine-pair name a declared tool already occupies, if any (disclosure text). */
|
|
69
69
|
memoryPairOccupiedName: string | undefined;
|
|
70
|
-
/** borrowed-readonly — the durable-resume carrier; read for `!== undefined` (seedAnnounced)
|
|
71
|
-
* `seed.announcedListings.skills` (the removal-to-zero projection's gate). */
|
|
70
|
+
/** borrowed-readonly — the durable-resume carrier; read for `!== undefined` (seedAnnounced),
|
|
71
|
+
* `seed.announcedListings.skills` (the removal-to-zero projection's gate) and `seed.skillGrants` (the frozen grant rows). */
|
|
72
72
|
resume: Pick<PrepareResume, "seed"> | undefined;
|
|
73
73
|
/** borrowed-readonly — the lowered listing-budget seats from the config door. The listing projection
|
|
74
74
|
* carries it to the boundary renderer, and the over-budget disclosure below is priced against it. */
|
|
@@ -91,9 +91,8 @@ export interface PrepareProjectContextResult {
|
|
|
91
91
|
/** owned — the normalized, load-gated skills (first-wins dedupe, name-sorted). Consumed inside this
|
|
92
92
|
* phase (tool + listing); exposed as the phase's own product. */
|
|
93
93
|
skillSpecs: ReturnType<typeof normalizeSkills>;
|
|
94
|
-
/** owned —
|
|
95
|
-
|
|
96
|
-
hasSkillManifest: boolean;
|
|
94
|
+
/** owned — the `Skill` tool's per-call seat over the loaded skills (`Execute skill: <name>`); `undefined` with no skill. */
|
|
95
|
+
skillSeatOf: SkillSeatReader | undefined;
|
|
97
96
|
/** owned — the skills-listing projection the run loop's announce lane consumes (`Prepared.skillsListing`). */
|
|
98
97
|
skillsListing: Prepared["skillsListing"];
|
|
99
98
|
/** owned — whether the shared-memory pair mounted here (the support-name pre-check reads it). */
|
|
@@ -5,12 +5,14 @@ import { hasConversationContent } from "../session.js";
|
|
|
5
5
|
import { createSharedMemoryTools } from "../shared-memory/tools.js";
|
|
6
6
|
import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
|
|
7
7
|
import { defineTool } from "../tools.js";
|
|
8
|
+
import { readFrozenSkillGrants, seedSkillGrants, skillEntriesNotGrantedNotice, skillSeatReader } from "../skill-grant.js";
|
|
9
|
+
import { deliverEngineNotice } from "../engine-notice.js";
|
|
8
10
|
import { SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
|
|
9
|
-
import {
|
|
11
|
+
import { skillLoadReading } from "./skill-base-dir.js";
|
|
10
12
|
import { RosterBuilder } from "../tool-roster.js";
|
|
11
13
|
import { asThrownError } from "../thrown-value.js";
|
|
12
14
|
export async function prepareProjectContext(input) {
|
|
13
|
-
const { deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine,
|
|
15
|
+
const { deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillGrants, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume, skillListingBudget } = input;
|
|
14
16
|
const tools = roster.tools;
|
|
15
17
|
let memoryBlock = memoryBlockFromEngine;
|
|
16
18
|
let instructionSources;
|
|
@@ -76,25 +78,27 @@ export async function prepareProjectContext(input) {
|
|
|
76
78
|
if (!declaredSkillRank.has(s.name))
|
|
77
79
|
declaredSkillRank.set(s.name, declaredSkillRank.size);
|
|
78
80
|
const skillSpecs = normalizeSkills(spec.skills ?? []).filter((s) => {
|
|
79
|
-
const delivered =
|
|
80
|
-
if (
|
|
81
|
+
const { loads, delivered } = skillLoadReading(s, SKILL_CONTENT_MAX_CHARS);
|
|
82
|
+
if (loads)
|
|
81
83
|
return true;
|
|
82
84
|
deps.onError?.(new Error(`Skill "${s.name}" content is ${delivered} chars${delivered === s.content.length ? "" : ` once its base directory resolves (${s.content.length} as written)`}, over the ${SKILL_CONTENT_MAX_CHARS}-char load limit — skill not loaded (skills are never truncated; shrink the body or move material to attachments/files).`), { phase: "config", sessionId });
|
|
83
85
|
return false;
|
|
84
86
|
});
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
87
|
+
const frozenSkillGrants = readFrozenSkillGrants(resume?.seed.skillGrants);
|
|
88
|
+
if (frozenSkillGrants === undefined)
|
|
89
|
+
throw new Error("the resumed checkpoint's skill-grant section does not read as grant rows — refusing to seed the run without the refusals it carries (the resume screen refuses this row before the claim)");
|
|
90
|
+
const inert = seedSkillGrants(skillGrants, { skills: skillSpecs, userInvokedSkills: spec.userInvokedSkills, inherited: [...(internals?.inheritedSkillGrants ?? []), ...frozenSkillGrants] });
|
|
91
|
+
if (inert.length > 0)
|
|
92
|
+
deliverEngineNotice(deps.onNotice, skillEntriesNotGrantedNotice(inert, sessionId));
|
|
90
93
|
if (skillSpecs.length > 0) {
|
|
91
94
|
if (tools.some((t) => t.name === SKILL_TOOL_NAME)) {
|
|
92
95
|
const e = new Error(`Tool name "${SKILL_TOOL_NAME}" is reserved when spec.skills is present.`);
|
|
93
96
|
e.code = "config.reserved_tool_name";
|
|
94
97
|
throw e;
|
|
95
98
|
}
|
|
96
|
-
roster.mount(createSkillTool(skillSpecs,
|
|
99
|
+
roster.mount(createSkillTool(skillSpecs, skillGrants), { source: "builtin", mountedBy: "optional" });
|
|
97
100
|
}
|
|
101
|
+
const skillSeatOf = skillSeatReader(skillSpecs);
|
|
98
102
|
const sharedMemoryProvider = deps.sharedMemoryStores;
|
|
99
103
|
let sharedMemoryPairMounted = false;
|
|
100
104
|
if (sharedMemoryProvider !== undefined) {
|
|
@@ -150,5 +154,5 @@ export async function prepareProjectContext(input) {
|
|
|
150
154
|
?
|
|
151
155
|
{ entries: [], seedAnnounced: true, budget: skillListingBudget }
|
|
152
156
|
: undefined;
|
|
153
|
-
return { memoryBlock, instructionSources, projectInstructionContent, skillSpecs,
|
|
157
|
+
return { memoryBlock, instructionSources, projectInstructionContent, skillSpecs, skillSeatOf, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted };
|
|
154
158
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PrepareTask's RUN-REFS phase, verbatim from the driver: the run's own edited-files
|
|
3
|
-
* ledger, the harness ref, the per-task skill
|
|
3
|
+
* ledger, the harness ref, the per-task skill grant table, the display-forwarding wrapper, the three run-local refs
|
|
4
4
|
* (review request / stop-after-turn / plan mode) with the ctx primitives that write them, the subagent-retain
|
|
5
5
|
* ledger and the worktree-isolation helper. Nothing here has behavior of its own: the phase MINTS SEATS —
|
|
6
6
|
* mutable cells and the closures bound to them — that later phases, the tool ctx and the run loop read and
|
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
* await is extracted as a sync function — an `async` wrapper would open a microtask window).
|
|
12
12
|
*
|
|
13
13
|
* Every cell that leaves here as `borrowed-mutable` carries its writer table on the Result member:
|
|
14
|
-
* the closures minted here, the driver's harness build, the project-context phase's
|
|
15
|
-
*
|
|
14
|
+
* the closures minted here, the driver's harness build, the project-context phase's skill-grant
|
|
15
|
+
* seeds, the skill tool, and the run loop's two writes — nothing else writes them. Read-stability of the host
|
|
16
16
|
* handles for the call: {@link RunInternals} (`@contract prepare.deps-read-stable`).
|
|
17
17
|
*/
|
|
18
18
|
import type { AgentHarness, ExecutionEnv } from "../../internal/harness.js";
|
|
19
19
|
import type { SubagentWorktreeIsolation } from "../../agents/subagent.js";
|
|
20
20
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
21
21
|
import type { TaskEvent, TaskSpec } from "../types.js";
|
|
22
|
-
import {
|
|
22
|
+
import { SkillGrantTable } from "../skill-grant.js";
|
|
23
23
|
import type { Prepared, RunInternals, ToolProgressLane } from "./contracts.js";
|
|
24
24
|
import type { PermissionModeController } from "../mode-policy.js";
|
|
25
25
|
import { type EditedFilesLedger } from "./edited-files-ledger.js";
|
|
@@ -63,10 +63,10 @@ export interface PrepareRunRefsResult {
|
|
|
63
63
|
toolProgressRef: {
|
|
64
64
|
current?: ToolProgressLane;
|
|
65
65
|
};
|
|
66
|
-
/** borrowed-mutable — the per-task
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
|
|
66
|
+
/** borrowed-mutable — the per-task skill grant table (its rows live for this run). Writers: the project-context phase (the
|
|
67
|
+
* parent's captured rows, the person's invoked skills) and the `Skill` tool minted over it (a successful activation).
|
|
68
|
+
* Readers: the tool ctx's `activeSkillGrants()` capture and the gate's skill lane. */
|
|
69
|
+
skillGrants: SkillGrantTable;
|
|
70
70
|
/** owned — the FILTERED display-forwarding wrapper, or undefined when the deployment wired no sink (the
|
|
71
71
|
* tool ctx then carries no `forwardEvent` key). */
|
|
72
72
|
forwardEvent: ((e: TaskEvent) => void) | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createSubagentWorktreeHelper } from "../../agents/subagent.js";
|
|
2
2
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
3
|
-
import {
|
|
3
|
+
import { SkillGrantTable } from "../skill-grant.js";
|
|
4
4
|
import { forwardSafely } from "./forward-sink.js";
|
|
5
5
|
import { createEditedFilesLedger } from "./edited-files-ledger.js";
|
|
6
6
|
export function forwardsSubagentEvents(spec) {
|
|
@@ -14,7 +14,7 @@ export function prepareRunRefs(input) {
|
|
|
14
14
|
const { note: noteFileEdited, snapshot: editedFilesSnapshot } = createEditedFilesLedger();
|
|
15
15
|
const harnessRef = {};
|
|
16
16
|
const toolProgressRef = {};
|
|
17
|
-
const
|
|
17
|
+
const skillGrants = new SkillGrantTable();
|
|
18
18
|
const sinkWired = internals?.onForwardEvent !== undefined;
|
|
19
19
|
const forwardEvent = sinkWired
|
|
20
20
|
? (e) => {
|
|
@@ -48,5 +48,5 @@ export function prepareRunRefs(input) {
|
|
|
48
48
|
};
|
|
49
49
|
const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
|
|
50
50
|
const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootFinal) : undefined;
|
|
51
|
-
return { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef,
|
|
51
|
+
return { noteFileEdited, editedFilesSnapshot, harnessRef, toolProgressRef, skillGrants, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, bindPermissionModeSeat, enterPlanMode, subagentRetain, worktreeIsolation };
|
|
52
52
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NAMESPACED_NAME_SHAPES } from "../protocol-table.js";
|
|
1
|
+
import { NAMESPACED_NAME_SHAPES, carriesNamespaceSeparator } from "../protocol-table.js";
|
|
2
2
|
import { engineCardTypes, handBandEffects } from "../tool-registry.js";
|
|
3
3
|
import { callerCardIdOf, offendingResultCard, rosterMemberBoundProblem, schemaKeyBoundProblem } from "../tool-roster.js";
|
|
4
4
|
import { toolFaceProblem } from "./tool-face-overlay.js";
|
|
@@ -64,13 +64,13 @@ export function prepareSafetyScan(input) {
|
|
|
64
64
|
throw e;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
if (t.name
|
|
67
|
+
if (carriesNamespaceSeparator(t.name)) {
|
|
68
68
|
const e = new Error(`Tool name "${t.name}" is invalid: "__" is reserved for the engine's protocol tool namespaces (${NAMESPACED_NAME_SHAPES}) and must not appear in a caller tool name.`);
|
|
69
69
|
e.code = "config.tool_name_invalid";
|
|
70
70
|
throw e;
|
|
71
71
|
}
|
|
72
72
|
for (const alias of t.aliases ?? []) {
|
|
73
|
-
if (alias
|
|
73
|
+
if (carriesNamespaceSeparator(alias)) {
|
|
74
74
|
const e = new Error(`Tool alias "${alias}" (of "${t.name}") is invalid: "__" is reserved for the engine's protocol tool namespaces (${NAMESPACED_NAME_SHAPES}) and must not appear in a caller tool alias.`);
|
|
75
75
|
e.code = "config.tool_name_invalid";
|
|
76
76
|
throw e;
|
|
@@ -112,6 +112,8 @@ export interface PrepareSuspendSagaInput {
|
|
|
112
112
|
faceCheckpointSection: () => SerializedCheckpointState["readFace"];
|
|
113
113
|
/** borrowed-readonly — the inherited-gate phase's WRITE-face section builder (the row's `writeFace`). */
|
|
114
114
|
writeFaceCheckpointSection: () => SerializedCheckpointState["writeFace"];
|
|
115
|
+
/** borrowed-readonly — the inherited-gate phase's SKILL-GRANT section builder (the row's `skillGrants`). */
|
|
116
|
+
skillGrantCheckpointSection: () => SerializedCheckpointState["skillGrants"];
|
|
115
117
|
/** borrowed-readonly — the leg's reminder provenance mark (travels with the row). */
|
|
116
118
|
reminderMark: string;
|
|
117
119
|
/** borrowed-readonly — the hand's live tracked cwd, or undefined; `current` is read at mint time. Not written here. */
|
|
@@ -136,7 +138,6 @@ export interface PrepareSuspendSagaInput {
|
|
|
136
138
|
/** borrowed-readonly — the leg's live permission-mode seat; the DECLARED mode (request clamped under the chain's ceiling,
|
|
137
139
|
* before the deployment's fold) is what the row records — the resume leg re-judges the fold for its own deployment. */
|
|
138
140
|
permissionMode: PermissionModeController;
|
|
139
|
-
/** borrowed-readonly — the auto-mode decider, or undefined (the latch-health reading only). */
|
|
140
141
|
/** borrowed-readonly — the inherited admitted org scopes, or undefined (copied onto the row). */
|
|
141
142
|
inheritedAdmittedOrgScopes: readonly string[] | undefined;
|
|
142
143
|
/** borrowed-readonly — this leg's OWN org verdict seat; `current` is read at mint time. Not written here. */
|