@sema-agent/core 7.35.2 → 7.37.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 +142 -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/tool-call-id.d.ts +27 -15
- package/dist/brain/tool-call-id.js +43 -0
- package/dist/core/ask-origin.d.ts +35 -5
- package/dist/core/ask-origin.js +23 -4
- package/dist/core/ask-question.d.ts +0 -30
- package/dist/core/ask-question.js +3 -2
- package/dist/core/checkpoint-store.d.ts +18 -6
- 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/decision-receipt.d.ts +21 -0
- package/dist/core/decision-receipt.js +63 -0
- package/dist/core/engine-notice.d.ts +15 -5
- package/dist/core/gate-fold.js +2 -1
- package/dist/core/gate-lanes.js +20 -23
- package/dist/core/gate-outcome.d.ts +7 -5
- package/dist/core/gate-outcome.js +5 -1
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +14 -2
- package/dist/core/hooks.js +7 -3
- package/dist/core/mode-policy.js +2 -1
- package/dist/core/park-selfcheck.js +2 -0
- 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/denial-limit-arms.d.ts +28 -18
- package/dist/core/runner/denial-limit-arms.js +11 -7
- package/dist/core/runner/inherited-ask-grants.d.ts +32 -31
- package/dist/core/runner/inherited-ask-grants.js +5 -0
- 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-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-park-ask.d.ts +4 -2
- package/dist/core/runner/prepare-park-ask.js +8 -11
- package/dist/core/runner/prepare-policy-chain.d.ts +5 -8
- package/dist/core/runner/prepare-policy-chain.js +50 -31
- 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/store-contracts/checkpoint-store-contract.js +18 -0
- package/dist/core/tool-name-readings.d.ts +370 -0
- package/dist/core/tool-name-readings.js +137 -0
- package/dist/core/tool-policy.d.ts +79 -89
- package/dist/core/tool-policy.js +86 -49
- 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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- 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/test/export-surface.snapshot.json +7 -1
- package/wire-changes/core@7.36.0.json +288 -0
- package/wire-changes/core@7.37.0.json +405 -0
|
@@ -6,12 +6,12 @@ import { cloneObserverInput, resolveHookTimeoutMs } from "../hooks.js";
|
|
|
6
6
|
import { assertHookSeatsCallable } from "../hook-seats.js";
|
|
7
7
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
|
|
8
8
|
import { isIsolated } from "../remote-env.js";
|
|
9
|
-
import { askApproverIdentity, carryEngineAttestations, carryPrecedingSettlement, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, durableParkOptInDeclared, isLiveApproverSeat, resolveAsk, toolPolicyNameSets, wrapToolPolicy, attestAskUnresolvableCause } from "../tool-policy.js";
|
|
9
|
+
import { askApproverIdentity, carryEngineAttestations, carryPrecedingSettlement, checkToolPolicyProjection, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, durableParkOptInDeclared, isLiveApproverSeat, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, wrapToolPolicy, attestAskUnresolvableCause } from "../tool-policy.js";
|
|
10
10
|
import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
|
|
11
11
|
import { NAMESPACED_NAME_SHAPES, protocolOf } from "../protocol-table.js";
|
|
12
12
|
import { emitTrace } from "../trace.js";
|
|
13
13
|
import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
14
|
-
import { arbitrateByMode,
|
|
14
|
+
import { arbitrateByMode, askClassMembersOf, askMandateMember } from "../ask-origin.js";
|
|
15
15
|
import { createModeGatePolicy, mountedToolDeclaresReadOnly } from "../mode-policy.js";
|
|
16
16
|
const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
|
|
17
17
|
import { headlessDenyAtFold, headlessDenyAtRecheck, INHERITED_STATION_FACTS, inheritedAskCarry, judgeInheritedClassifier, lateAskSettlementObserver, settleDenialLimitFallback } from "./denial-limit-arms.js";
|
|
@@ -20,12 +20,12 @@ 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, heldValues, 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
|
-
const arbitrateInherited = (ask, toolCallId) => {
|
|
28
|
-
const verdict = arbitrateByMode(permissionMode.current,
|
|
27
|
+
const arbitrateInherited = (ask, toolCallId, classification) => {
|
|
28
|
+
const verdict = arbitrateByMode(permissionMode.current, classification, { ...(ask.origin !== undefined ? { origin: ask.origin } : {}), ...(ask.mandate !== undefined ? { mandate: ask.mandate } : {}) });
|
|
29
29
|
if (verdict.kind === "ask" || verdict.kind === "accepted")
|
|
30
30
|
return undefined;
|
|
31
31
|
ancestorArbitrations.set(toolCallId, verdict);
|
|
@@ -188,7 +188,7 @@ export async function preparePolicyChain(input) {
|
|
|
188
188
|
}, "mandate_unreconstructible", creq);
|
|
189
189
|
const markerChannelFullDeny = (creq) => attestAskUnresolvableCause({ action: "deny", message: `no approver is reachable for the inherited approval of "${creq.toolName}" and the marker channel is at capacity — denied fail-closed` }, "approver_unavailable", creq);
|
|
190
190
|
const inheritedAskGrants = new Map();
|
|
191
|
-
const recordInheritedAskGrant = (toolCallId, approver, presented, waitMs, settled) => {
|
|
191
|
+
const recordInheritedAskGrant = (toolCallId, approver, presented, waitMs, settled, classes) => {
|
|
192
192
|
if (typeof approver !== "function")
|
|
193
193
|
return;
|
|
194
194
|
if (presented === undefined)
|
|
@@ -198,7 +198,7 @@ export async function preparePolicyChain(input) {
|
|
|
198
198
|
const argsJson = askGrantShapeOf(presented);
|
|
199
199
|
if (argsJson === undefined)
|
|
200
200
|
return;
|
|
201
|
-
inheritedAskGrants.set(toolCallId, { approver, argsJson, presented, waitMs, settled });
|
|
201
|
+
inheritedAskGrants.set(toolCallId, { approver, argsJson, presented, waitMs, settled, classes });
|
|
202
202
|
};
|
|
203
203
|
const sandboxDeclared = isIsolated(executionEnv);
|
|
204
204
|
const sandboxAdmissionArmed = (() => {
|
|
@@ -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",
|
|
@@ -258,7 +259,7 @@ export async function preparePolicyChain(input) {
|
|
|
258
259
|
};
|
|
259
260
|
const callerPolicyLayer = policy !== undefined && sameInstanceAncestorCount > 0
|
|
260
261
|
? wrapToolPolicy(policy, async (creq, csignal) => {
|
|
261
|
-
const decision = await policy.check(creq, csignal ?? abortController.signal);
|
|
262
|
+
const decision = refuseOutOfContractDecision(await policy.check(creq, csignal ?? abortController.signal));
|
|
262
263
|
sharedFirstDecision.set(creq.toolCallId, decision);
|
|
263
264
|
if (sharedFirstDecision.size > SHARED_FIRST_DECISION_CAP) {
|
|
264
265
|
const oldest = sharedFirstDecision.keys().next().value;
|
|
@@ -277,16 +278,33 @@ export async function preparePolicyChain(input) {
|
|
|
277
278
|
: {}),
|
|
278
279
|
...(delegation.isDelegatedChild ? { isDelegatedChild: true } : {}),
|
|
279
280
|
});
|
|
281
|
+
const seatMarksOf = (call) => {
|
|
282
|
+
const tierSeat = seatOf("report.riskAxes", irreversibilityTier, call);
|
|
283
|
+
return {
|
|
284
|
+
tier: tierSeat?.value,
|
|
285
|
+
egress: heldBy("report.riskAxes.egress", egressTools, call),
|
|
286
|
+
shellGated: tierSeat?.shellGated(shellGatedTools, toolMarkSources) === true,
|
|
287
|
+
sources: new Set(heldValues("report.markSources", toolMarkSources, call).flatMap((s) => [...s])),
|
|
288
|
+
};
|
|
289
|
+
};
|
|
280
290
|
const riskAxesOf = (call) => {
|
|
281
|
-
const
|
|
282
|
-
const
|
|
283
|
-
const
|
|
284
|
-
const
|
|
285
|
-
const egress = anyNamed(egressTools, names) ? true : negatives?.egress;
|
|
291
|
+
const seat = seatMarksOf(call);
|
|
292
|
+
const negatives = seatOf("report.riskAxes.negatives", axisExplicitNegatives, call)?.value;
|
|
293
|
+
const irreversible = seat.tier === "always" || seat.tier === "maybe" ? true : seat.tier === "never" ? false : negatives?.irreversible;
|
|
294
|
+
const egress = seat.egress ? true : negatives?.egress;
|
|
286
295
|
if (irreversible === undefined && egress === undefined)
|
|
287
296
|
return {};
|
|
288
297
|
return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
|
|
289
298
|
};
|
|
299
|
+
const inheritedStationFacts = (call) => {
|
|
300
|
+
const seat = seatMarksOf(call);
|
|
301
|
+
return {
|
|
302
|
+
...INHERITED_STATION_FACTS,
|
|
303
|
+
mandateMarks: { egress: seat.egress, shellGated: seat.shellGated, ...(seat.tier !== undefined ? { irreversibility: seat.tier } : {}) },
|
|
304
|
+
...(seat.sources.size > 0 ? { toolMarkSources: seat.sources } : {}),
|
|
305
|
+
...(deps.a2a?.isolatePeers === true ? { a2aIsolatePeers: true } : {}),
|
|
306
|
+
};
|
|
307
|
+
};
|
|
290
308
|
const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
|
|
291
309
|
const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({
|
|
292
310
|
provider: deps.permissionRuleStore,
|
|
@@ -310,6 +328,7 @@ export async function preparePolicyChain(input) {
|
|
|
310
328
|
egressTools,
|
|
311
329
|
irreversibilityTier,
|
|
312
330
|
shellGatedTools,
|
|
331
|
+
toolMarkSources,
|
|
313
332
|
taskRoot: taskRootFinal,
|
|
314
333
|
cwdRef: handsCwdRef,
|
|
315
334
|
deniesDirectoryRead: readDenyMatcher !== undefined ? (d) => readDenyMatcher.matchPath(d) !== null : undefined,
|
|
@@ -334,7 +353,7 @@ export async function preparePolicyChain(input) {
|
|
|
334
353
|
}
|
|
335
354
|
let re;
|
|
336
355
|
try {
|
|
337
|
-
re = await pol.check({ ...creq, args: editArgs }, csignal ?? abortController.signal);
|
|
356
|
+
re = refuseOutOfContractDecision(await pol.check({ ...creq, args: editArgs }, csignal ?? abortController.signal));
|
|
338
357
|
}
|
|
339
358
|
catch (err) {
|
|
340
359
|
return vetoed({
|
|
@@ -352,7 +371,7 @@ export async function preparePolicyChain(input) {
|
|
|
352
371
|
}
|
|
353
372
|
if (re.updatedInput !== undefined)
|
|
354
373
|
editArgs = re.updatedInput;
|
|
355
|
-
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: ancestorAutoMode, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit", allowRewrite: editArgs, unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(ancestorDecider?.timeoutMs !== undefined ? { budgetMs: ancestorDecider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(onAskOf), onNotice: deps.onNotice, sessionId, runId }) });
|
|
374
|
+
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: ancestorAutoMode, ask: re, req: { ...creq, args: editArgs }, signal: csignal ?? abortController.signal, subject: "the approved edit", allowRewrite: editArgs, facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(ancestorDecider?.timeoutMs !== undefined ? { budgetMs: ancestorDecider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(onAskOf), onNotice: deps.onNotice, sessionId, runId }) });
|
|
356
375
|
if (judged.kind === "allow") {
|
|
357
376
|
const cleared = { ...judged.result };
|
|
358
377
|
carryEngineAttestations(judged.result, cleared);
|
|
@@ -363,7 +382,7 @@ export async function preparePolicyChain(input) {
|
|
|
363
382
|
return vetoed(judged.result);
|
|
364
383
|
const { ask: editAsk, fallback: editFallback, mintedHere: editMintedHere } = judged;
|
|
365
384
|
re = editAsk;
|
|
366
|
-
const editArbitrated = arbitrateInherited(re, creq.toolCallId);
|
|
385
|
+
const editArbitrated = arbitrateInherited(re, creq.toolCallId, judged.classification);
|
|
367
386
|
if (editArbitrated !== undefined) {
|
|
368
387
|
const out = editArbitrated.action === "allow" ? { action: "allow", updatedInput: editArgs } : editArbitrated;
|
|
369
388
|
carryPrecedingSettlement(settledBy, out);
|
|
@@ -420,7 +439,7 @@ export async function preparePolicyChain(input) {
|
|
|
420
439
|
return first;
|
|
421
440
|
if (first.action === "allow")
|
|
422
441
|
return { action: "allow" };
|
|
423
|
-
if (creq
|
|
442
|
+
if (isDelegatedQuestion(creq)) {
|
|
424
443
|
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
|
|
425
444
|
return first;
|
|
426
445
|
return {
|
|
@@ -434,17 +453,17 @@ export async function preparePolicyChain(input) {
|
|
|
434
453
|
return first;
|
|
435
454
|
return mandateUnreconstructibleDeny(creq);
|
|
436
455
|
}
|
|
437
|
-
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: first, req: creq, signal: csignal ?? abortController.signal, subject: "this call", unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
|
|
456
|
+
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: first, req: creq, signal: csignal ?? abortController.signal, subject: "this call", facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
|
|
438
457
|
if (judged.kind === "allow")
|
|
439
458
|
return judged.result;
|
|
440
459
|
if (judged.kind === "deny")
|
|
441
460
|
return judged.result;
|
|
442
461
|
const { ask: inheritedAsk, fallback, mintedHere } = judged;
|
|
443
|
-
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) &&
|
|
462
|
+
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) && judged.classification.row.sandboxAdmission) {
|
|
444
463
|
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
|
|
445
464
|
return { action: "allow" };
|
|
446
465
|
}
|
|
447
|
-
const arbitrated = arbitrateInherited(inheritedAsk, creq.toolCallId);
|
|
466
|
+
const arbitrated = arbitrateInherited(inheritedAsk, creq.toolCallId, judged.classification);
|
|
448
467
|
if (arbitrated !== undefined)
|
|
449
468
|
return arbitrated;
|
|
450
469
|
const presentedArgs = creq.args;
|
|
@@ -476,14 +495,14 @@ export async function preparePolicyChain(input) {
|
|
|
476
495
|
if (resolved.updatedInput !== undefined) {
|
|
477
496
|
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode);
|
|
478
497
|
}
|
|
479
|
-
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved);
|
|
498
|
+
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved, askClassMembersOf(judged.classification).classes);
|
|
480
499
|
return { action: "allow" };
|
|
481
500
|
},
|
|
482
501
|
}
|
|
483
502
|
: wrapToolPolicy(pc.policy, async (creq, csignal) => {
|
|
484
503
|
let decision;
|
|
485
504
|
try {
|
|
486
|
-
decision = await pc.policy.check(creq, csignal ?? abortController.signal);
|
|
505
|
+
decision = refuseOutOfContractDecision(await pc.policy.check(creq, csignal ?? abortController.signal));
|
|
487
506
|
}
|
|
488
507
|
catch (err) {
|
|
489
508
|
return {
|
|
@@ -493,7 +512,7 @@ export async function preparePolicyChain(input) {
|
|
|
493
512
|
}
|
|
494
513
|
if (decision.action !== "ask")
|
|
495
514
|
return decision;
|
|
496
|
-
if (creq
|
|
515
|
+
if (isDelegatedQuestion(creq)) {
|
|
497
516
|
if (pc.contentMandate !== true || markInheritedUnavailable(creq.toolCallId))
|
|
498
517
|
return decision;
|
|
499
518
|
return {
|
|
@@ -508,17 +527,17 @@ export async function preparePolicyChain(input) {
|
|
|
508
527
|
return mandateUnreconstructibleDeny(creq);
|
|
509
528
|
}
|
|
510
529
|
const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
|
|
511
|
-
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call", allowRewrite: decision.updatedInput, unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
|
|
530
|
+
const judged = await judgeInheritedClassifier({ liveMode: () => permissionMode.current, autoMode: pc.autoMode, ask: decision, req: { ...creq, args: presentedArgs }, signal: csignal ?? abortController.signal, subject: "this call", allowRewrite: decision.updatedInput, facts: inheritedStationFacts(creq), unavailableSeat: classifierUnavailableSeat({ station: "ancestor", ...(pc.autoMode?.decider.timeoutMs !== undefined ? { budgetMs: pc.autoMode.decider.timeoutMs } : {}), approvalSeat: isLiveApproverSeat(pc.onAsk), onNotice: deps.onNotice, sessionId, runId }) });
|
|
512
531
|
if (judged.kind === "allow")
|
|
513
532
|
return judged.result;
|
|
514
533
|
if (judged.kind === "deny")
|
|
515
534
|
return judged.result;
|
|
516
535
|
const { ask: inheritedAsk, fallback, mintedHere } = judged;
|
|
517
|
-
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) &&
|
|
536
|
+
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq) && judged.classification.row.sandboxAdmission) {
|
|
518
537
|
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
|
|
519
538
|
return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
|
|
520
539
|
}
|
|
521
|
-
const arbitrated = arbitrateInherited(inheritedAsk.action === "ask" ? { ...inheritedAsk, ...(decision.updatedInput !== undefined ? { updatedInput: decision.updatedInput } : {}) } : inheritedAsk, creq.toolCallId);
|
|
540
|
+
const arbitrated = arbitrateInherited(inheritedAsk.action === "ask" ? { ...inheritedAsk, ...(decision.updatedInput !== undefined ? { updatedInput: decision.updatedInput } : {}) } : inheritedAsk, creq.toolCallId, judged.classification);
|
|
522
541
|
if (arbitrated !== undefined)
|
|
523
542
|
return arbitrated;
|
|
524
543
|
const askT0 = now();
|
|
@@ -550,7 +569,7 @@ export async function preparePolicyChain(input) {
|
|
|
550
569
|
return recheckApprovedEdit(pc.policy, pc.onAsk, creq, resolved.updatedInput, resolved, csignal, pc.autoMode);
|
|
551
570
|
}
|
|
552
571
|
if (pc.preToolUse === undefined) {
|
|
553
|
-
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved);
|
|
572
|
+
recordInheritedAskGrant(creq.toolCallId, pc.onAsk, resolved.presentedInput, askWaitMs, resolved, askClassMembersOf(judged.classification).classes);
|
|
554
573
|
}
|
|
555
574
|
if (decision.updatedInput === undefined)
|
|
556
575
|
return resolved;
|
|
@@ -571,7 +590,7 @@ export async function preparePolicyChain(input) {
|
|
|
571
590
|
sameInstanceAdapterIndices.add(j + callerSlotOffset);
|
|
572
591
|
});
|
|
573
592
|
const instrumentedChain = rewriteCapableChain.map((layer, idx) => wrapToolPolicy(layer, async (creq, csignal) => {
|
|
574
|
-
const d = await layer.check(creq, csignal);
|
|
593
|
+
const d = refuseOutOfContractDecision(await layer.check(creq, csignal));
|
|
575
594
|
if (d.updatedInput !== undefined)
|
|
576
595
|
rewriteEmitterIndex.set(creq.toolCallId, idx);
|
|
577
596
|
if (d.action === "ask") {
|
|
@@ -618,7 +637,7 @@ export async function preparePolicyChain(input) {
|
|
|
618
637
|
const revalidated = {};
|
|
619
638
|
try {
|
|
620
639
|
for (const layer of recheckLayers) {
|
|
621
|
-
const d = await layer.check(finalReq, signal);
|
|
640
|
+
const d = refuseOutOfContractDecision(await layer.check(finalReq, signal));
|
|
622
641
|
if (d.action === "deny") {
|
|
623
642
|
const carried = { ...d, updatedInput: decision.updatedInput };
|
|
624
643
|
carryEngineAttestations(d, carried);
|
|
@@ -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);
|