@sema-agent/core 7.35.1 → 7.36.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 +85 -0
- package/dist/brain/anthropic.js +6 -6
- package/dist/brain/errors.d.ts +1 -14
- package/dist/brain/errors.js +2 -6
- package/dist/brain/open-responses.js +7 -7
- package/dist/brain/openai.js +7 -7
- package/dist/brain/provider-echo-shape.d.ts +26 -0
- package/dist/brain/provider-echo-shape.js +12 -0
- package/dist/brain/stream-engine.d.ts +2 -1
- package/dist/brain/stream-engine.js +28 -14
- package/dist/brain/tool-call-id.d.ts +27 -15
- package/dist/brain/tool-call-id.js +43 -0
- package/dist/core/ask-question.d.ts +0 -30
- package/dist/core/ask-question.js +3 -2
- package/dist/core/checkpoint-store.d.ts +5 -3
- package/dist/core/checkpoint-store.js +2 -1
- package/dist/core/context-edit.d.ts +4 -0
- package/dist/core/context-edit.js +5 -2
- package/dist/core/engine-notice.d.ts +7 -0
- package/dist/core/gate-lanes.js +12 -10
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +3 -0
- package/dist/core/hooks.js +4 -2
- package/dist/core/mode-policy.js +2 -1
- package/dist/core/permission-rule-model.d.ts +2 -2
- package/dist/core/permission-rule-model.js +2 -2
- package/dist/core/permission-rules.js +2 -5
- package/dist/core/runner/active-skill-scope.js +5 -5
- package/dist/core/runner/decide-continuation.js +2 -1
- package/dist/core/runner/permission-rule-lanes.d.ts +3 -0
- package/dist/core/runner/permission-rule-lanes.js +7 -6
- package/dist/core/runner/prepare-context-lane.d.ts +4 -0
- package/dist/core/runner/prepare-context-lane.js +7 -5
- package/dist/core/runner/prepare-gate-stations.js +17 -12
- package/dist/core/runner/prepare-park-ask.d.ts +2 -0
- package/dist/core/runner/prepare-park-ask.js +7 -6
- package/dist/core/runner/prepare-policy-chain.d.ts +2 -0
- package/dist/core/runner/prepare-policy-chain.js +10 -9
- package/dist/core/runner/prepare-safety-scan.js +9 -2
- package/dist/core/runner/prepare-task.js +3 -3
- package/dist/core/runner/resume-apply.js +2 -1
- package/dist/core/runner/run-attachment-seats.js +3 -1
- package/dist/core/runner/run-harness-handlers.js +6 -1
- package/dist/core/runner/run-leg.js +11 -9
- package/dist/core/runner/run-recovery-lanes.js +2 -0
- package/dist/core/runner/session-rule-policy.js +5 -4
- package/dist/core/runner/tool-call-id-fold-seat.d.ts +13 -0
- package/dist/core/runner/tool-call-id-fold-seat.js +31 -0
- package/dist/core/sensitive-path-policy.js +2 -2
- package/dist/core/side-query.d.ts +5 -1
- package/dist/core/side-query.js +5 -1
- package/dist/core/task-event.d.ts +2 -1
- package/dist/core/tool-name-readings.d.ts +366 -0
- package/dist/core/tool-name-readings.js +136 -0
- package/dist/core/tool-policy.d.ts +5 -5
- package/dist/core/tool-policy.js +15 -13
- package/dist/core/tool-registry.d.ts +1 -31
- package/dist/core/tool-registry.js +2 -22
- package/dist/core/tool-result-budget.d.ts +3 -0
- package/dist/core/tool-result-budget.js +5 -3
- package/dist/core/tool-roster.d.ts +30 -9
- package/dist/core/tool-roster.js +45 -5
- package/dist/engine/llm/types.d.ts +4 -0
- package/dist/tools/fs/file-read-check.d.ts +1 -1
- package/dist/tools/fs/file-read-check.js +4 -3
- package/dist/tools/fs/shell-read-check.d.ts +15 -1
- package/dist/tools/fs/shell-read-check.js +5 -2
- package/package.json +2 -1
- package/wire-changes/core@7.35.2.json +61 -0
- package/wire-changes/core@7.36.0.json +288 -0
|
@@ -9,8 +9,10 @@ import { askUnresolvableNotice } from "../ask-unresolvable-notice.js";
|
|
|
9
9
|
import { askReachesPerson } from "../wiring-manifest.js";
|
|
10
10
|
import { classifierUnavailableSeat } from "./classifier-ladder.js";
|
|
11
11
|
import { PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
|
|
12
|
-
import {
|
|
12
|
+
import { callFaceSeat, rosterToolName, toolCallFaceOf } from "../tool-roster.js";
|
|
13
|
+
import { heldBy, heldValues, seatOf, wireOf } from "../tool-name-readings.js";
|
|
13
14
|
import { noteHostRewrite } from "../frame-settlement.js";
|
|
15
|
+
const PEER_MESSAGE_NAMES = new Set([SEND_MESSAGE_TOOL_NAME]);
|
|
14
16
|
function screenGateResult(result) {
|
|
15
17
|
if (result.suspend !== undefined)
|
|
16
18
|
return { defects: [] };
|
|
@@ -88,12 +90,12 @@ export function prepareGateStations(input) {
|
|
|
88
90
|
harness.on("tool_call", async (called) => {
|
|
89
91
|
const e = { ...called, toolName: rosterToolName(toolRosterDeltas.current, called.toolName) };
|
|
90
92
|
const call = { toolName: e.toolName, ...callFaceSeat(toolCallFaceOf(toolRosterDeltas.current, called.toolName)) };
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
+
const tierSeat = seatOf("gate.shellSeat", irreversibilityTier, call);
|
|
94
|
+
const markSources = new Set(heldValues("gate.markSources", toolMarkSources, call).flatMap((s) => [...s]));
|
|
93
95
|
blockedToolCalls.delete(e.toolCallId);
|
|
94
96
|
inheritedAskGrants.delete(e.toolCallId);
|
|
95
97
|
{
|
|
96
|
-
const complianceDeny =
|
|
98
|
+
const complianceDeny = heldValues("gate.compliance", { get: (n) => complianceCallDenial(complianceDenies, n) }, call)[0];
|
|
97
99
|
if (complianceDeny !== undefined) {
|
|
98
100
|
if (blockedTracked)
|
|
99
101
|
blockedToolCalls.add(e.toolCallId);
|
|
@@ -122,19 +124,19 @@ export function prepareGateStations(input) {
|
|
|
122
124
|
resolveAsk: resolveAskBound,
|
|
123
125
|
suspendAsk,
|
|
124
126
|
resolveContentAsk,
|
|
125
|
-
egress:
|
|
126
|
-
peerMessage:
|
|
127
|
+
egress: heldBy("gate.egress", egressTools, call),
|
|
128
|
+
peerMessage: heldBy("gate.peerMessage", PEER_MESSAGE_NAMES, call),
|
|
127
129
|
...(writeProtectionCheck !== undefined ? { writeProtectionCheck } : {}),
|
|
128
130
|
...(shellReadCheck !== undefined ? { shellReadCheck } : {}),
|
|
129
131
|
irreversibility: tierSeat?.value,
|
|
130
|
-
reversibilityProbe: tierSeat
|
|
132
|
+
reversibilityProbe: tierSeat?.read(reversibilityProbes),
|
|
131
133
|
abortSignal: abortController.signal,
|
|
132
134
|
...(notifyPermissionDenied ? { permissionDenied: notifyPermissionDenied } : {}),
|
|
133
135
|
onHookError: notifyHookError,
|
|
134
|
-
shellGated: tierSeat
|
|
136
|
+
shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
|
|
135
137
|
...(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 }) } } : {}),
|
|
136
138
|
permissionMode: () => permissionMode.current,
|
|
137
|
-
...(
|
|
139
|
+
...(markSources.size > 0 ? { toolMarkSources: markSources } : {}),
|
|
138
140
|
...(a2aIsolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
139
141
|
onModeReleased,
|
|
140
142
|
ancestorArbitrationOf: (toolCallId) => ancestorArbitrations.get(toolCallId),
|
|
@@ -243,6 +245,7 @@ export function prepareGateStations(input) {
|
|
|
243
245
|
}
|
|
244
246
|
let content = e.content;
|
|
245
247
|
let changed = false;
|
|
248
|
+
const post = { toolName: rosterToolName(toolRosterDeltas.current, e.toolName) };
|
|
246
249
|
if (e.isError) {
|
|
247
250
|
if (hooks?.postToolUseFailure) {
|
|
248
251
|
let clonedDetails = e.details;
|
|
@@ -251,7 +254,8 @@ export function prepareGateStations(input) {
|
|
|
251
254
|
}
|
|
252
255
|
catch {
|
|
253
256
|
}
|
|
254
|
-
const
|
|
257
|
+
const failedName = wireOf("hook.postToolUseFailure", post);
|
|
258
|
+
const failureSeat = await runHookSeat("postToolUseFailure", { timeoutMs: hookTimeoutMs, signal: abortController.signal }, (sig) => hooks.postToolUseFailure(failedName, e.input, {
|
|
255
259
|
error: e.content
|
|
256
260
|
.filter((c) => c.type === "text")
|
|
257
261
|
.map((c) => c.text)
|
|
@@ -259,7 +263,7 @@ export function prepareGateStations(input) {
|
|
|
259
263
|
isInterrupt: abortController.signal.aborted || spec.signal?.aborted === true,
|
|
260
264
|
content: e.content.map((c) => ({ ...c })),
|
|
261
265
|
details: clonedDetails,
|
|
262
|
-
}, { toolCallId: e.toolCallId, toolName:
|
|
266
|
+
}, { toolCallId: e.toolCallId, toolName: failedName, signal: sig, ...(handsCwdRef?.current !== undefined ? { cwd: handsCwdRef.current } : {}), ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}), identity: hookIdentity }));
|
|
263
267
|
if (failureSeat.expired) {
|
|
264
268
|
notifyOwnHookCrash(hookSeatExpiredError("postToolUseFailure", hookTimeoutMs, failureSeat.cause, "the observation was abandoned and its additionalContext dropped; the failure result itself is unchanged"));
|
|
265
269
|
}
|
|
@@ -271,7 +275,8 @@ export function prepareGateStations(input) {
|
|
|
271
275
|
}
|
|
272
276
|
}
|
|
273
277
|
else if (hooks?.postToolUse) {
|
|
274
|
-
const
|
|
278
|
+
const ranName = wireOf("hook.postToolUse", post);
|
|
279
|
+
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 }));
|
|
275
280
|
if (successSeat.expired) {
|
|
276
281
|
notifyOwnHookCrash(hookSeatExpiredError("postToolUse", hookTimeoutMs, successSeat.cause, "the seat was abandoned; the tool result is delivered to the model UNMODIFIED (no updatedOutput, no additionalContext)"));
|
|
277
282
|
}
|
|
@@ -116,6 +116,8 @@ export interface PrepareParkAskInput {
|
|
|
116
116
|
shellGatedTools: ReadonlySet<string>;
|
|
117
117
|
/** borrowed-readonly — the per-tool tier, read only for the name the call's tier seat is read under (as at the gate). */
|
|
118
118
|
irreversibilityTier: IrreversibilityTier;
|
|
119
|
+
/** borrowed-readonly — who marked each name's seat (the descriptor's `shellGated` reads it as the gate does). */
|
|
120
|
+
toolMarkSources: ReadonlyMap<string, ReadonlySet<import("../ask-origin.js").ToolMarkSource>>;
|
|
119
121
|
/** borrowed-readonly — the operator's shell floor (`"always"`), persisted on a shell-gated descriptor; undefined = no floor. */
|
|
120
122
|
effectiveShellGate: ShellGateDoctrine | undefined;
|
|
121
123
|
/** borrowed-readonly — the EFFECTIVE durable-approval opt-in (the caller's, or the synthesized mandate scope): picks the
|
|
@@ -15,7 +15,8 @@ import { checkpointScopeOf } from "./checkpoint-scope.js";
|
|
|
15
15
|
import { CONTENT_ASK_BINDING_CAP } from "./content-ask-bindings.js";
|
|
16
16
|
import { DEFAULT_UNATTENDED_APPROVAL_TTL_MS, publishCommittedSuspend, sanitizedTtlMs } from "./park-commit.js";
|
|
17
17
|
import { remoteEnvFailureNote } from "./remote-env-retry.js";
|
|
18
|
-
import { callFaceSeat,
|
|
18
|
+
import { callFaceSeat, parkedToolIdentity, parkedToolFace } from "../tool-roster.js";
|
|
19
|
+
import { seatOf, wireOf } from "../tool-name-readings.js";
|
|
19
20
|
class ParkRefusal extends Error {
|
|
20
21
|
constructor(message, options) {
|
|
21
22
|
super(message, options);
|
|
@@ -26,7 +27,7 @@ const wordlessDemand = (toolName) => {
|
|
|
26
27
|
throw new Error(`a real-approval demand for "${toolName}" reached the park with no origin word — the origin stamp precedes every park`);
|
|
27
28
|
};
|
|
28
29
|
export function prepareParkAsk(input) {
|
|
29
|
-
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, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef } = 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;
|
|
30
31
|
if (askLane === undefined || saga === undefined)
|
|
31
32
|
return { parkAsk: undefined };
|
|
32
33
|
const { composedCallSignal, approvalPreviewOf } = askLane;
|
|
@@ -114,7 +115,7 @@ export function prepareParkAsk(input) {
|
|
|
114
115
|
};
|
|
115
116
|
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
|
|
116
117
|
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated, carry) => {
|
|
117
|
-
const syncFirstEligible =
|
|
118
|
+
const syncFirstEligible = wireOf("park.syncFirstRoute", req) === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
|
|
118
119
|
if (syncFirstEligible &&
|
|
119
120
|
runtimeCaps?.forceDurableGate !== true &&
|
|
120
121
|
liveFaceUnavailable !== true &&
|
|
@@ -234,7 +235,7 @@ export function prepareParkAsk(input) {
|
|
|
234
235
|
remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
|
|
235
236
|
}
|
|
236
237
|
token = mintCheckpointToken();
|
|
237
|
-
const tierSeat =
|
|
238
|
+
const tierSeat = seatOf("park.riskDescriptor", irreversibilityTier, req);
|
|
238
239
|
const riskDescriptor = buildRiskDescriptor({
|
|
239
240
|
toolName: req.toolName,
|
|
240
241
|
args: parkedArgs,
|
|
@@ -242,7 +243,7 @@ export function prepareParkAsk(input) {
|
|
|
242
243
|
...(shadowedRule !== undefined ? { shadowedRule } : {}),
|
|
243
244
|
...(probeReason !== undefined ? { probeReason } : {}),
|
|
244
245
|
...(probeCause !== undefined ? { probeCause } : {}),
|
|
245
|
-
shellGated: tierSeat
|
|
246
|
+
shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
|
|
246
247
|
...(effectiveShellGate !== undefined ? { shellGateDoctrine: effectiveShellGate } : {}),
|
|
247
248
|
});
|
|
248
249
|
gate =
|
|
@@ -303,7 +304,7 @@ export function prepareParkAsk(input) {
|
|
|
303
304
|
kind: "tool_approval",
|
|
304
305
|
toolCallId: req.toolCallId,
|
|
305
306
|
toolName: req.toolName,
|
|
306
|
-
...parkedToolIdentity(toolRosterDeltas.current, req.toolName), ...parkedToolFace(toolRosterDeltas.current, req
|
|
307
|
+
...parkedToolIdentity(toolRosterDeltas.current, req.toolName), ...parkedToolFace(toolRosterDeltas.current, req),
|
|
307
308
|
args: parkedArgs,
|
|
308
309
|
...(() => {
|
|
309
310
|
const p = approvalPreviewOf(req.toolName, parkedArgs);
|
|
@@ -85,6 +85,8 @@ export interface PreparePolicyChainInput {
|
|
|
85
85
|
envHandToolNames: ReadonlySet<string>;
|
|
86
86
|
/** borrowed-readonly — the shell-seat provenance set (the offer factory's `shellGated` mandate term, read per call). */
|
|
87
87
|
shellGatedTools: ReadonlySet<string>;
|
|
88
|
+
/** borrowed-readonly — who marked each name's seat (the offer factory's `shellGated` term reads it as the gate does). */
|
|
89
|
+
toolMarkSources: ReadonlyMap<string, ReadonlySet<import("../ask-origin.js").ToolMarkSource>>;
|
|
88
90
|
/** borrowed-readonly — the tracked-cwd seat, read per call by the lanes and the offer factory (never written here). */
|
|
89
91
|
handsCwdRef: CwdRef | undefined;
|
|
90
92
|
/** borrowed-readonly — the compiled read-deny judge, or undefined (the offer factory's directory negative control). */
|
|
@@ -20,9 +20,9 @@ import { askGrantShapeOf } from "./inherited-ask-grants.js";
|
|
|
20
20
|
import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
|
|
21
21
|
import { createLiveSessionRules, createSessionRulePolicy } from "./session-rule-policy.js";
|
|
22
22
|
import { thrownText } from "../thrown-value.js";
|
|
23
|
-
import {
|
|
23
|
+
import { heldBy, seatOf, wireOf } from "../tool-name-readings.js";
|
|
24
24
|
export async function preparePolicyChain(input) {
|
|
25
|
-
const { spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = 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, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, permissionMode, scratchpadDir, toolRosterDeltas, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef } = input;
|
|
26
26
|
const ancestorArbitrations = new Map();
|
|
27
27
|
const arbitrateInherited = (ask, toolCallId) => {
|
|
28
28
|
const verdict = arbitrateByMode(permissionMode.current, classifyAsk(ask, INHERITED_STATION_FACTS), { ...(ask.origin !== undefined ? { origin: ask.origin } : {}), ...(ask.mandate !== undefined ? { mandate: ask.mandate } : {}) });
|
|
@@ -233,7 +233,8 @@ export async function preparePolicyChain(input) {
|
|
|
233
233
|
list.push({ cls, layer });
|
|
234
234
|
foldAskClasses.set(toolCallId, list);
|
|
235
235
|
};
|
|
236
|
-
const
|
|
236
|
+
const isDelegatedQuestion = (creq) => wireOf("inherited.questionFloat", creq) === ASK_USER_QUESTION_TOOL_NAME;
|
|
237
|
+
const sandboxBoundaryCapable = (call) => heldBy("gate.sandboxBoundary.egress", egressTools, call) || !envHandToolNames.has(wireOf("gate.sandboxBoundary.hand", call));
|
|
237
238
|
const emitSandboxAdmitted = (info) => {
|
|
238
239
|
emitTrace(deps.tracer, () => ({
|
|
239
240
|
kind: "permission.sandbox_admitted",
|
|
@@ -278,11 +279,10 @@ export async function preparePolicyChain(input) {
|
|
|
278
279
|
...(delegation.isDelegatedChild ? { isDelegatedChild: true } : {}),
|
|
279
280
|
});
|
|
280
281
|
const riskAxesOf = (call) => {
|
|
281
|
-
const
|
|
282
|
-
const
|
|
283
|
-
const negatives = firstNamed(axisExplicitNegatives, names)?.value;
|
|
282
|
+
const tier = seatOf("report.riskAxes", irreversibilityTier, call)?.value;
|
|
283
|
+
const negatives = seatOf("report.riskAxes.negatives", axisExplicitNegatives, call)?.value;
|
|
284
284
|
const irreversible = tier === "always" || tier === "maybe" ? true : tier === "never" ? false : negatives?.irreversible;
|
|
285
|
-
const egress =
|
|
285
|
+
const egress = heldBy("report.riskAxes.egress", egressTools, call) ? true : negatives?.egress;
|
|
286
286
|
if (irreversible === undefined && egress === undefined)
|
|
287
287
|
return {};
|
|
288
288
|
return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
|
|
@@ -310,6 +310,7 @@ export async function preparePolicyChain(input) {
|
|
|
310
310
|
egressTools,
|
|
311
311
|
irreversibilityTier,
|
|
312
312
|
shellGatedTools,
|
|
313
|
+
toolMarkSources,
|
|
313
314
|
taskRoot: taskRootFinal,
|
|
314
315
|
cwdRef: handsCwdRef,
|
|
315
316
|
deniesDirectoryRead: readDenyMatcher !== undefined ? (d) => readDenyMatcher.matchPath(d) !== null : undefined,
|
|
@@ -420,7 +421,7 @@ export async function preparePolicyChain(input) {
|
|
|
420
421
|
return first;
|
|
421
422
|
if (first.action === "allow")
|
|
422
423
|
return { action: "allow" };
|
|
423
|
-
if (creq
|
|
424
|
+
if (isDelegatedQuestion(creq)) {
|
|
424
425
|
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
|
|
425
426
|
return first;
|
|
426
427
|
return {
|
|
@@ -493,7 +494,7 @@ export async function preparePolicyChain(input) {
|
|
|
493
494
|
}
|
|
494
495
|
if (decision.action !== "ask")
|
|
495
496
|
return decision;
|
|
496
|
-
if (creq
|
|
497
|
+
if (isDelegatedQuestion(creq)) {
|
|
497
498
|
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
|
|
498
499
|
return decision;
|
|
499
500
|
return {
|
|
@@ -4,6 +4,8 @@ import { callerCardIdOf, offendingResultCard, rosterMemberBoundProblem, schemaKe
|
|
|
4
4
|
import { toolFaceProblem } from "./tool-face-overlay.js";
|
|
5
5
|
import { OFFLOAD_TOOL_NAME } from "../tool-result-store.js";
|
|
6
6
|
import { PRESENT_PLAN_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME } from "../present-plan-tool.js";
|
|
7
|
+
const REPEAT_RISK = { read: 0, idempotent: 1, write: 2 };
|
|
8
|
+
const leastRepeatSafe = (a, b) => (REPEAT_RISK[a] >= REPEAT_RISK[b] ? a : b);
|
|
7
9
|
export function prepareSafetyScan(input) {
|
|
8
10
|
const { spec, deps } = input;
|
|
9
11
|
const toolEffects = new Map();
|
|
@@ -120,11 +122,16 @@ export function prepareSafetyScan(input) {
|
|
|
120
122
|
}
|
|
121
123
|
}
|
|
122
124
|
toolEffects.set(OFFLOAD_TOOL_NAME, "read");
|
|
125
|
+
const claimerEffects = new Map([...claimedAliases].map((a) => [a, toolEffects.get(a) ?? "write"]));
|
|
126
|
+
const recordHandEffect = (name, effect) => {
|
|
127
|
+
const claimed = claimerEffects.get(name);
|
|
128
|
+
toolEffects.set(name, claimed === undefined ? effect : leastRepeatSafe(claimed, effect));
|
|
129
|
+
};
|
|
123
130
|
if (deps.executionEnvFactory || deps.executionEnv) {
|
|
124
131
|
for (const [name, effect] of handBandEffects())
|
|
125
|
-
|
|
132
|
+
recordHandEffect(name, effect);
|
|
126
133
|
if (spec.handsReadOnly === true)
|
|
127
|
-
|
|
134
|
+
recordHandEffect("Bash", "read");
|
|
128
135
|
}
|
|
129
136
|
if (spec.enablePlanMode === true) {
|
|
130
137
|
toolEffects.set(PRESENT_PLAN_TOOL_NAME, "read");
|
|
@@ -740,7 +740,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
740
740
|
releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
|
|
741
741
|
}
|
|
742
742
|
}
|
|
743
|
-
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, additionalRootsCanonical, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, announceRuleStoreSkipped, questionToolMounted, runtimeCaps, inheritedAncestorRules, ancestorSessionRulesRef, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
743
|
+
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions, ancestorArbitrations } = await preparePolicyChain({ 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, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider: () => autoModeDeciderRef.current, permissionMode, scratchpadDir: spec.envFacts?.scratchpadDir, toolRosterDeltas: rosterSeat, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
744
744
|
const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, writeProtection, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace, sessionBoxBound } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, taskRootFinal, mcp, irreversibleTools, egressTools, shellReadStationArmed: handsReadFace.shellReadStationArmed, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, permissionMode, lockedPreflight, peerLaneRefs, frameSettlement, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster, readDenyBuiltinTiersActive: handsReadFace.readDenyBuiltinTiersActive, lspManagerWired, lspDiagnosticsLive: lspDiagnostics !== undefined, fileHistoryFact, sessionBox });
|
|
745
745
|
const writeProtectionCheck = writeProtection?.check;
|
|
746
746
|
writeProtectionJudgeRef.current = writeProtection?.judgeTarget;
|
|
@@ -753,10 +753,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
753
753
|
const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking: autoModeDenialTrackingRef, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash, grantReadRoot: handsReadFace.grantReadRoot });
|
|
754
754
|
const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, permissionMode, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
|
|
755
755
|
const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
|
|
756
|
-
const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, irreversibilityTier, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
|
|
756
|
+
const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, 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 });
|
|
757
757
|
prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, 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, readDenyInForce: () => ({ tiers: handsReadFace.readDenyBuiltinTiersActive, rows: [...readDenyAdditionsNormalized.map((e) => e.pattern), ...handsReadFaceInput.pathDenyRules.filter((r) => r.faces.includes("read")).map((r) => r.rule)] }), hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider: autoModeDeciderRef, autoModeDenialTracking: autoModeDenialTrackingRef, askRoute: wiringManifest.ask, permissionMode, toolMarkSources, ...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}), ancestorArbitrations, runId, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash, frameSettlement });
|
|
758
758
|
const postureFrameRef = {};
|
|
759
|
-
const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
|
|
759
|
+
const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, toolRosterDeltas: rosterSeat, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, contextOverlay: legContextOverlay, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
|
|
760
760
|
const preparedHolder = {};
|
|
761
761
|
const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
|
|
762
762
|
await onceLedger.settle(session, announcedListingsRef);
|
|
@@ -10,6 +10,7 @@ import { inlineUntrusted } from "../untrusted-text.js";
|
|
|
10
10
|
import { deepJsonEqual, describeSuppliedValue } from "./decide-continuation.js";
|
|
11
11
|
import { mintGateOutcome } from "./gate-exit.js";
|
|
12
12
|
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
13
|
+
import { wireOf } from "../tool-name-readings.js";
|
|
13
14
|
export async function resumeApply(input) {
|
|
14
15
|
const { cp, config, internals, outcomeGate, wakeMessage, resumeSignal, recheckGovernanceWindow, plainReviewOutcome, plainParkOutcome, runner, effectiveDelegationFacts } = input;
|
|
15
16
|
let plainPolicyOutcome = input.plainPolicyOutcome;
|
|
@@ -78,7 +79,7 @@ export async function resumeApply(input) {
|
|
|
78
79
|
plainPolicyOutcome.decision === "allow" &&
|
|
79
80
|
plainPolicyOutcome.updatedInput !== undefined &&
|
|
80
81
|
cp.pendingAction.kind === "tool_approval" &&
|
|
81
|
-
cp.pendingAction
|
|
82
|
+
wireOf("resume.editRecheckQuestionSkip", cp.pendingAction) !== ASK_USER_QUESTION_TOOL_NAME) {
|
|
82
83
|
const editedArgs = plainPolicyOutcome.updatedInput;
|
|
83
84
|
const editBudget = {
|
|
84
85
|
resourceRemainingMicroUsd: remainingBudgetMicroUsd(cp.resourceLedger),
|
|
@@ -2,6 +2,8 @@ import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
|
2
2
|
import { stripGitStatusUnits } from "./git-status-frame.js";
|
|
3
3
|
import { SKILLS_LISTING_PROBE_HEADER } from "./synthetic-tools.js";
|
|
4
4
|
import { AGENT_LISTING_REMOVED_HEADER, SKILLS_LISTING_DELTA_HEADER, SKILLS_LISTING_REMOVED_HEADER, agentListingDeltaHeader, agentListingInitialHeader, createAttachmentState, replayAnnouncedListing, replayAnnouncedModels } from "./turn-attachments.js";
|
|
5
|
+
import { wireOf } from "../tool-name-readings.js";
|
|
6
|
+
import { rosterToolName } from "../tool-roster.js";
|
|
5
7
|
export async function runAttachmentSeats(input) {
|
|
6
8
|
const { spec, prepared, resume, rs, runner, next } = input;
|
|
7
9
|
rs.counters.wroteThisRun = false;
|
|
@@ -174,7 +176,7 @@ export async function runAttachmentSeats(input) {
|
|
|
174
176
|
if ((fvMarked || text.includes("<system-reminder>[final verification]")) && rs.counters.finalVerifyInjections < 2)
|
|
175
177
|
rs.counters.finalVerifyInjections += 1;
|
|
176
178
|
}
|
|
177
|
-
else if (m.role === "toolResult" && !rs.counters.wroteThisRun && (prepared.toolEffects.get(m.toolName) ?? "write") !== "read") {
|
|
179
|
+
else if (m.role === "toolResult" && !rs.counters.wroteThisRun && (prepared.toolEffects.get(wireOf("verify.wroteThisRunSeed", { toolName: rosterToolName(prepared.toolRosterDeltas.current, m.toolName) })) ?? "write") !== "read") {
|
|
178
180
|
rs.counters.wroteThisRun = true;
|
|
179
181
|
}
|
|
180
182
|
}
|
|
@@ -11,9 +11,13 @@ import { gatedCallIdOf } from "./park-commit.js";
|
|
|
11
11
|
import { hasVerifiableStructureSignal } from "./grounding-signal.js";
|
|
12
12
|
import { batchResponseDigest, toolEndBodyFrom } from "./tool-end-body.js";
|
|
13
13
|
import { createResponseModelSeat } from "./response-model-seat.js";
|
|
14
|
+
import { createToolCallIdFoldSeat } from "./tool-call-id-fold-seat.js";
|
|
15
|
+
import { wireOf } from "../tool-name-readings.js";
|
|
16
|
+
import { rosterToolName } from "../tool-roster.js";
|
|
14
17
|
export function createHarnessHandlers(input) {
|
|
15
18
|
const { prepared, stats, rs, deps } = input;
|
|
16
19
|
const responseModelSeat = createResponseModelSeat(deps.runnerHooks.onNotice, { sessionId: prepared.sessionId, runId: prepared.runId });
|
|
20
|
+
const toolCallIdFoldSeat = createToolCallIdFoldSeat(deps.runnerHooks.onNotice, { sessionId: prepared.sessionId, runId: prepared.runId });
|
|
17
21
|
const { spec, queue, internals, ident, parentToolCallId, subagentName, pushContent, emitCommitted, startedToolCallIds, toolStartAt, toolProgress, writeFamilyOf, toolLabels, postToolBatchHook, batchArgs, internalsNotifier } = deps;
|
|
18
22
|
let lastWorkspaceCwd = prepared.cwdRef?.current;
|
|
19
23
|
const announceWorkspaceMove = () => {
|
|
@@ -147,6 +151,7 @@ export function createHarnessHandlers(input) {
|
|
|
147
151
|
}
|
|
148
152
|
if (m.role === "assistant") {
|
|
149
153
|
rs.turn.turnResponseModel = responseModelSeat.observe(m);
|
|
154
|
+
toolCallIdFoldSeat.observe(m);
|
|
150
155
|
}
|
|
151
156
|
if (m.role === "assistant") {
|
|
152
157
|
const rep = m.repetition;
|
|
@@ -219,7 +224,7 @@ export function createHarnessHandlers(input) {
|
|
|
219
224
|
toolStartAt.delete(event.toolCallId);
|
|
220
225
|
toolProgress.noteEnd(event.toolCallId);
|
|
221
226
|
const toolNow = Date.now();
|
|
222
|
-
if (!rs.counters.wroteThisRun && (prepared.toolEffects.get(event.toolName) ?? "write") !== "read") {
|
|
227
|
+
if (!rs.counters.wroteThisRun && (prepared.toolEffects.get(wireOf("verify.wroteThisRun", { toolName: rosterToolName(prepared.toolRosterDeltas.current, event.toolName) })) ?? "write") !== "read") {
|
|
223
228
|
rs.counters.wroteThisRun = true;
|
|
224
229
|
}
|
|
225
230
|
if (rs.attach.attachState !== undefined && !event.isError) {
|
|
@@ -13,7 +13,7 @@ import { composeApprovalPostureDirective } from "../auto-mode-directive.js";
|
|
|
13
13
|
import { mintSystemReminder, statedReminderBodiesOf } from "../reminder-mint.js";
|
|
14
14
|
import { hasDestroy } from "../remote-env.js";
|
|
15
15
|
import { advertisedWritableRootsSeat, policyRefusalMessage, refuseOutOfContractDecision } from "../tool-policy.js";
|
|
16
|
-
import { callFaceSeat, judgeParkedToolIdentity,
|
|
16
|
+
import { callFaceSeat, judgeParkedToolIdentity, parkedRowLiveFace, rosterEntryNamed } from "../tool-roster.js";
|
|
17
17
|
import { deliverEngineNotice } from "../types.js";
|
|
18
18
|
import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
19
19
|
import { errorCodeOf } from "./assemble-result.js";
|
|
@@ -34,6 +34,7 @@ import { attachmentEnvelopeTags, collectDateChange, commitAgentListing, commitSk
|
|
|
34
34
|
import { flushOwedAnnouncedSnapshot } from "./announce-once-ledger.js";
|
|
35
35
|
import { asThrownError, thrownMarksOf, thrownText } from "../thrown-value.js";
|
|
36
36
|
import { ruleSourcePhrase } from "../rule-denial-sentence.js";
|
|
37
|
+
import { wireOf } from "../tool-name-readings.js";
|
|
37
38
|
export async function runLeg(input) {
|
|
38
39
|
const { spec, queue, prepared, resume, rs, isDelegatedChild, entryActor, effectiveTimeoutMs, timeout, pushContent, ident, emitCommitted, writeFamilyOf, startedToolCallIds, announceWorkspaceMove, withBrainSinks, flushGitMirror, loopLatch, notificationLane, unsubscribeTaskNotifications, unsubGitRetry, unsubBoundary, unsub, runner, next } = input;
|
|
39
40
|
let final;
|
|
@@ -563,12 +564,13 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
563
564
|
if (decidedGate === undefined) {
|
|
564
565
|
throw new CheckpointError("checkpoint.invalid_outcome", "pending action reached the resolver with no settlement record on its decision (gateOutcome) — refusing to execute or settle a decision that carries no record");
|
|
565
566
|
}
|
|
566
|
-
const resolvedArgs =
|
|
567
|
+
const resolvedArgs = wireOf("resume.questionArgsShown", pendingAction) === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput !== undefined ? outcome.updatedInput : pendingAction.args);
|
|
567
568
|
const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
|
|
568
|
-
|
|
569
|
+
const spelledName = pendingAction.names?.[0] ?? pendingAction.toolName;
|
|
570
|
+
emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: spelledName, ...pendingLabel, args: resolvedArgs });
|
|
569
571
|
const settleEnd = async (isError, result, gate = decidedGate) => {
|
|
570
572
|
prepared.toolProgressRef.current?.noteEnd(pendingAction.toolCallId);
|
|
571
|
-
emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName:
|
|
573
|
+
emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: spelledName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError, prepared.structuredProjector, gate) });
|
|
572
574
|
const word = await resume.recordExecutionOutcome(gate);
|
|
573
575
|
settleExecutionRecord(runner.deps.onNotice, word, { toolName: pendingAction.toolName, gate, sessionId: prepared.sessionId, runId: prepared.runId, scope: resume.cp.scope, ...(resume.cp.checkpointId !== undefined ? { checkpointId: resume.cp.checkpointId } : {}) });
|
|
574
576
|
};
|
|
@@ -611,7 +613,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
611
613
|
budget: beltBudget,
|
|
612
614
|
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
613
615
|
...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
|
|
614
|
-
...callFaceSeat(
|
|
616
|
+
...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)),
|
|
615
617
|
}, prepared.abortController.signal));
|
|
616
618
|
const beltUnsafe = rechecked.action === "deny" ||
|
|
617
619
|
(rechecked.updatedInput !== undefined && !deepJsonEqual(rechecked.updatedInput, resolvedArgs));
|
|
@@ -634,7 +636,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
634
636
|
}
|
|
635
637
|
if (prepared.permissionRuleOrg !== undefined) {
|
|
636
638
|
const orgVerdict = prepared.permissionRuleOrg
|
|
637
|
-
.adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(
|
|
639
|
+
.adjudicate({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)) })
|
|
638
640
|
.catch(() => ({ status: "unavailable", disclosures: ["the org adjudication face threw on resume"] }));
|
|
639
641
|
let orgWaitCancelled = false;
|
|
640
642
|
const org = await settleOrgVerdictWithin(orgVerdict, { status: "unavailable", disclosures: [`the org adjudication face did not answer within ${ORG_ADJUDICATION_TIMEOUT_MS}ms (or the task ended first)`] }, {
|
|
@@ -669,7 +671,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
669
671
|
toolName: pendingAction.toolName,
|
|
670
672
|
args: resolvedArgs,
|
|
671
673
|
toolCallId: pendingAction.toolCallId,
|
|
672
|
-
...callFaceSeat(
|
|
674
|
+
...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)),
|
|
673
675
|
budget: beltBudget,
|
|
674
676
|
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
675
677
|
...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
|
|
@@ -691,7 +693,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
691
693
|
toolName: pendingAction.toolName,
|
|
692
694
|
args: resolvedArgs,
|
|
693
695
|
toolCallId: pendingAction.toolCallId,
|
|
694
|
-
...callFaceSeat(
|
|
696
|
+
...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)),
|
|
695
697
|
budget: beltBudget,
|
|
696
698
|
...(prepared.cwdRef !== undefined ? { cwd: prepared.cwdRef.current } : {}),
|
|
697
699
|
...advertisedWritableRootsSeat(prepared.advertisedWritableDirs),
|
|
@@ -717,7 +719,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
717
719
|
}
|
|
718
720
|
if (prepared.shellReadCheck !== undefined) {
|
|
719
721
|
const edited = outcome.updatedInput !== undefined && !deepJsonEqual(outcome.updatedInput, pendingAction.args);
|
|
720
|
-
const readCall = { toolName: pendingAction.toolName, ...callFaceSeat(
|
|
722
|
+
const readCall = { toolName: pendingAction.toolName, ...callFaceSeat(parkedRowLiveFace(prepared.toolRosterDeltas.current, pendingAction)) };
|
|
721
723
|
const judged = prepared.shellReadCheck.judge(readCall, resolvedArgs, prepared.cwdRef?.current, prepared.abortController.signal, {
|
|
722
724
|
toolCallId: pendingAction.toolCallId,
|
|
723
725
|
...(edited ? { seat: "rejudge" } : {}),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { compactionKindOf } from "../task-notification.js";
|
|
2
2
|
import { planRejectionClears } from "../context-edit.js";
|
|
3
|
+
import { rosterToolName } from "../tool-roster.js";
|
|
3
4
|
import { landStaleResultClears } from "./prepare-context-lane.js";
|
|
4
5
|
import { DEFAULT_COMPACTION_INSTRUCTIONS, maybeCompact, mintCompactedEvent } from "../auto-compaction.js";
|
|
5
6
|
import { emitTrace } from "../trace.js";
|
|
@@ -122,6 +123,7 @@ export function runRecoveryLanes(input) {
|
|
|
122
123
|
? { declined: "no_candidates" }
|
|
123
124
|
: planRejectionClears(proj.messages, {
|
|
124
125
|
keyOf: proj.keyOf,
|
|
126
|
+
toolNameOf: (recorded) => rosterToolName(prepared.toolRosterDeltas.current, recorded),
|
|
125
127
|
...(prepared.microCompact.offloadPersist ? { offload: { persist: prepared.microCompact.offloadPersist } } : {}),
|
|
126
128
|
});
|
|
127
129
|
if ("declined" in plan) {
|
|
@@ -2,7 +2,7 @@ import { canonicalizeTarget, isWinFormPath, pathFamilyOf, writeTargetPath } from
|
|
|
2
2
|
import { createCoarseCommandNamePolicy, namespacedCoveringEntries, namespacedCoveringHit } from "../tool-policy.js";
|
|
3
3
|
import { continuesLineage } from "../session-policy-store.js";
|
|
4
4
|
import { thrownText } from "../thrown-value.js";
|
|
5
|
-
import {
|
|
5
|
+
import { heldBy, wireOf } from "../tool-name-readings.js";
|
|
6
6
|
import { ruleDenialSentence } from "../rule-denial-sentence.js";
|
|
7
7
|
import { DEFAULT_GATE_AWAIT_TIMEOUT_MS, gateWaitFailureOf, withGateTimeout } from "../gate-bounded-await.js";
|
|
8
8
|
import { declaredPathTargetOf, isProtectedWrite, pathConfinableWriteToolNames, skillScopeWriteToolNames } from "../tool-registry.js";
|
|
@@ -137,9 +137,10 @@ export function createSessionRulePolicy(rules, opts) {
|
|
|
137
137
|
async check(req, signal) {
|
|
138
138
|
const { toolDeny, toolAllow, toolDenyCovering, toolAllowCovering, cmdPolicy, allowDirs } = compile(await readRules(req.toolCallId, signal));
|
|
139
139
|
const toolName = req.toolName;
|
|
140
|
-
|
|
140
|
+
const listed = (set) => heldBy("policy.sessionRules", set, req);
|
|
141
|
+
if (listed(toolDeny) || namespacedCoveringHit(toolDenyCovering, toolName))
|
|
141
142
|
return deny(req.toolName);
|
|
142
|
-
if (toolAllow && !
|
|
143
|
+
if (toolAllow && !listed(toolAllow) && !namespacedCoveringHit(toolAllowCovering, toolName)) {
|
|
143
144
|
return deny(req.toolName, "it is not in the session-rule allowlist");
|
|
144
145
|
}
|
|
145
146
|
if (cmdPolicy) {
|
|
@@ -151,7 +152,7 @@ export function createSessionRulePolicy(rules, opts) {
|
|
|
151
152
|
}
|
|
152
153
|
if (allowDirs) {
|
|
153
154
|
if (!isProtectedWrite(req)) {
|
|
154
|
-
const eff = toolEffects?.get(
|
|
155
|
+
const eff = toolEffects?.get(wireOf("policy.sessionRules.effect", req)) ?? "write";
|
|
155
156
|
if (eff !== "read") {
|
|
156
157
|
return deny(req.toolName, "it is write-capable and cannot be path-confined, while the rule confines writes to allowDirs");
|
|
157
158
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { EngineNotice } from "../engine-notice.js";
|
|
2
|
+
export interface ToolCallIdFoldSeat {
|
|
3
|
+
/** Observe one assistant message; announces the run's first folded call. */
|
|
4
|
+
observe(message: {
|
|
5
|
+
readonly content?: unknown;
|
|
6
|
+
}): void;
|
|
7
|
+
}
|
|
8
|
+
/** The notice's attribution ids — the same pair, for the same reason, as {@link ResponseModelSeatIds}. */
|
|
9
|
+
export interface ToolCallIdFoldSeatIds {
|
|
10
|
+
sessionId: string;
|
|
11
|
+
runId: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function createToolCallIdFoldSeat(onNotice: ((notice: EngineNotice) => void) | undefined, ids: ToolCallIdFoldSeatIds): ToolCallIdFoldSeat;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { deliverEngineNotice } from "../engine-notice.js";
|
|
2
|
+
import { providerToolCallId, toolCallIdFoldReason } from "../../brain/tool-call-id.js";
|
|
3
|
+
function isCall(block) {
|
|
4
|
+
return typeof block === "object" && block !== null && block.type === "toolCall" && typeof block.id === "string";
|
|
5
|
+
}
|
|
6
|
+
export function createToolCallIdFoldSeat(onNotice, ids) {
|
|
7
|
+
let announced = false;
|
|
8
|
+
return {
|
|
9
|
+
observe(message) {
|
|
10
|
+
if (announced || !Array.isArray(message.content))
|
|
11
|
+
return;
|
|
12
|
+
for (const call of message.content.filter(isCall)) {
|
|
13
|
+
const wireId = providerToolCallId(call);
|
|
14
|
+
const reason = wireId === call.id ? undefined : toolCallIdFoldReason(wireId);
|
|
15
|
+
if (reason === undefined)
|
|
16
|
+
continue;
|
|
17
|
+
announced = true;
|
|
18
|
+
deliverEngineNotice(onNotice, {
|
|
19
|
+
code: "route.tool_call_id_folded",
|
|
20
|
+
message: `The provider named a tool call with an id outside the shape the engine keys calls under ` +
|
|
21
|
+
`(${reason === "length" ? `${wireId.length} characters, over 128` : "a control byte, a space, a non-ASCII character or a `~`"}). ` +
|
|
22
|
+
`The engine keys it as "${call.id}" on every face — frames, cards, transcript, park rows — and hands the ` +
|
|
23
|
+
`provider its own id back for the call and its result, so the run is unchanged. Once per run; later calls ` +
|
|
24
|
+
`with such ids are folded the same way without another notice.`,
|
|
25
|
+
detail: { toolCallId: call.id, rawLength: wireId.length, reason, sessionId: ids.sessionId, runId: ids.runId },
|
|
26
|
+
});
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -5,7 +5,7 @@ import { compileSegmentPattern, matchSegmentPatterns, READ_FACE_BUILTIN_DENY_TAB
|
|
|
5
5
|
import { isProtectedWrite, protectivePathTargetOf } from "./tool-registry.js";
|
|
6
6
|
import { pathTargetBaseOf } from "./effective-path-target.js";
|
|
7
7
|
import { symlinkChainHops } from "./path-identity.js";
|
|
8
|
-
import {
|
|
8
|
+
import { heldBy } from "./tool-name-readings.js";
|
|
9
9
|
export function agentConfigDenyPatterns() {
|
|
10
10
|
return READ_FACE_BUILTIN_DENY_TABLE.filter((row) => row.tier === "agent-config").map((row) => readDenyPatternRun(row.pattern).run);
|
|
11
11
|
}
|
|
@@ -147,7 +147,7 @@ export function createSensitivePathPolicy(opts) {
|
|
|
147
147
|
async check(req, signal) {
|
|
148
148
|
if (compiled.length === 0)
|
|
149
149
|
return { action: "allow" };
|
|
150
|
-
if (guardedByName !== undefined ? !
|
|
150
|
+
if (guardedByName !== undefined ? !heldBy("policy.sensitivePathGuardedByName", guardedByName, req) : !isProtectedWrite(req))
|
|
151
151
|
return { action: "allow" };
|
|
152
152
|
const protective = protectivePathTargetOf(req);
|
|
153
153
|
const path = writeTargetPath(req, protective);
|
|
@@ -76,9 +76,13 @@ export interface SideQuerySpec {
|
|
|
76
76
|
export interface SideQueryResult {
|
|
77
77
|
/** Concatenated assistant text (may be empty when the reply is tool-calls-only). */
|
|
78
78
|
text: string;
|
|
79
|
-
/** Tool calls the model made against the DEFINITIONS (never executed by the engine).
|
|
79
|
+
/** Tool calls the model made against the DEFINITIONS (never executed by the engine). `id` is the engine id;
|
|
80
|
+
* `wireId` is the provider's own spelling, present only when the two differ (`ToolCall.wireId`, the same
|
|
81
|
+
* source). A caller replaying a call carries both on its `toolCall` block and keys the result by `id`; one that
|
|
82
|
+
* talks to the provider itself hands it `wireId ?? id`. */
|
|
80
83
|
toolCalls: Array<{
|
|
81
84
|
id: string;
|
|
85
|
+
wireId?: string;
|
|
82
86
|
name: string;
|
|
83
87
|
args: unknown;
|
|
84
88
|
}>;
|
package/dist/core/side-query.js
CHANGED
|
@@ -2,6 +2,7 @@ import { resolveTaskModel } from "./roles.js";
|
|
|
2
2
|
import { readDegradation } from "../brain/degrading.js";
|
|
3
3
|
import { cacheFamilyOf, uncachedInputTokensOf } from "./runner/usage-accounting.js";
|
|
4
4
|
import { providerReportedModel } from "./message-utils.js";
|
|
5
|
+
import { providerToolCallId } from "../brain/tool-call-id.js";
|
|
5
6
|
function normalizeSideQueryUsage(model, u) {
|
|
6
7
|
const input = uncachedInputTokensOf(cacheFamilyOf(model), u);
|
|
7
8
|
return input === u.input ? u : { ...u, input };
|
|
@@ -61,7 +62,10 @@ export async function runSideQuery(spec, deps) {
|
|
|
61
62
|
.join("");
|
|
62
63
|
const toolCalls = msg.content
|
|
63
64
|
.filter((c) => c.type === "toolCall")
|
|
64
|
-
.map((c) =>
|
|
65
|
+
.map((c) => {
|
|
66
|
+
const wireId = providerToolCallId(c);
|
|
67
|
+
return { id: c.id, ...(wireId !== c.id ? { wireId } : {}), name: c.name, args: c.arguments };
|
|
68
|
+
});
|
|
65
69
|
const degraded = readDegradation(msg);
|
|
66
70
|
const servedModel = typeof msg.model === "string" && msg.model.length > 0 ? msg.model : resolved.model.id;
|
|
67
71
|
const reportedModel = providerReportedModel(msg);
|