@sema-agent/core 5.13.0 → 5.15.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 +418 -0
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +4 -0
- package/dist/agents/subagent.js +133 -41
- package/dist/brain/anthropic.js +33 -10
- package/dist/brain/context-overflow.d.ts +20 -0
- package/dist/brain/context-overflow.js +58 -0
- package/dist/brain/errors.js +21 -1
- package/dist/brain/open-responses.js +24 -10
- package/dist/brain/openai.js +29 -11
- package/dist/brain/request-params.d.ts +2 -0
- package/dist/brain/request-params.js +16 -0
- package/dist/brain/retry.d.ts +5 -0
- package/dist/brain/retry.js +16 -4
- package/dist/brain/stream-engine.d.ts +9 -1
- package/dist/brain/stream-engine.js +261 -28
- package/dist/brain/timeout.d.ts +1 -0
- package/dist/brain/timeout.js +1 -0
- package/dist/core/a2a.d.ts +2 -2
- package/dist/core/a2a.js +3 -3
- package/dist/core/ask-question.d.ts +47 -2
- package/dist/core/ask-question.js +209 -28
- package/dist/core/background-agent-store.d.ts +2 -0
- package/dist/core/checkpoint-store.d.ts +41 -17
- package/dist/core/checkpoint-store.js +137 -6
- package/dist/core/hooks.d.ts +24 -2
- package/dist/core/hooks.js +97 -10
- package/dist/core/human-input-projection.d.ts +12 -0
- package/dist/core/human-input-projection.js +27 -0
- package/dist/core/mcp.d.ts +7 -2
- package/dist/core/mcp.js +7 -7
- package/dist/core/memory-admission.d.ts +4 -0
- package/dist/core/memory-admission.js +3 -0
- package/dist/core/memory-engine/engine.d.ts +3 -1
- package/dist/core/memory-engine/engine.js +4 -3
- package/dist/core/memory-recall.d.ts +1 -1
- package/dist/core/memory-recall.js +3 -2
- package/dist/core/runner/assemble-result.d.ts +1 -0
- package/dist/core/runner/assemble-result.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +1 -0
- package/dist/core/runner/prepare-memory.js +3 -3
- package/dist/core/runner/prepare-task.d.ts +19 -6
- package/dist/core/runner/prepare-task.js +343 -35
- package/dist/core/runner/runtask.d.ts +3 -6
- package/dist/core/runner/runtask.js +290 -64
- package/dist/core/runner/tool-disclosure.d.ts +5 -0
- package/dist/core/runner/tool-disclosure.js +65 -16
- package/dist/core/runner/tool-output-projection.js +1 -0
- package/dist/core/runner/turn-attachments.d.ts +4 -0
- package/dist/core/runner/turn-attachments.js +15 -2
- package/dist/core/session-store.d.ts +3 -0
- package/dist/core/session-store.js +4 -0
- package/dist/core/session.d.ts +1 -0
- package/dist/core/store-contracts/background-agent-store-contract.js +19 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +62 -3
- package/dist/core/task-notification.d.ts +2 -0
- package/dist/core/task-notification.js +5 -3
- package/dist/core/task-registry-agent.d.ts +1 -0
- package/dist/core/task-registry-agent.js +6 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/task-tool-shape.js +4 -3
- package/dist/core/tool-policy.d.ts +5 -0
- package/dist/core/tool-policy.js +2 -1
- package/dist/core/trace.d.ts +6 -0
- package/dist/core/types.d.ts +32 -1
- package/dist/core/wiring-manifest.d.ts +97 -0
- package/dist/core/wiring-manifest.js +186 -0
- package/dist/engine/compaction/compaction.js +2 -2
- package/dist/engine/harness/agent-harness.d.ts +2 -1
- package/dist/engine/harness/agent-harness.js +8 -1
- package/dist/engine/harness/types.d.ts +3 -1
- package/dist/engine/llm/types.d.ts +7 -0
- package/dist/engine/llm/types.js +8 -1
- package/dist/engine/session/import-validate.d.ts +6 -1
- package/dist/engine/session/import-validate.js +29 -6
- package/dist/engine/session/memory-repo.d.ts +3 -1
- package/dist/engine/session/memory-repo.js +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +8 -5
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/internal/llm.d.ts +2 -2
- package/dist/internal/llm.js +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +4 -0
- package/dist/orchestration/run-workflow-tool.js +3 -0
- package/dist/orchestration/workflow-types.d.ts +8 -0
- package/dist/orchestration/workflow-types.js +14 -0
- package/dist/orchestration/workflow.d.ts +4 -0
- package/dist/orchestration/workflow.js +134 -5
- package/dist/prompts/default.js +1 -1
- package/dist/prompts/supervisor.d.ts +2 -2
- package/dist/prompts/supervisor.js +5 -4
- package/dist/stores/file/checkpoint-store.d.ts +3 -5
- package/dist/stores/file/checkpoint-store.js +31 -2
- package/dist/stores/file/index.js +1 -1
- package/dist/stores/file/session-store.d.ts +3 -1
- package/dist/stores/file/session-store.js +2 -2
- package/dist/stores/file/shared-ledger.js +8 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -0
- package/dist/tools/fs/bash-readonly-classifier.js +94 -0
- package/dist/tools/fs/fs-bash.d.ts +1 -0
- package/dist/tools/fs/fs-bash.js +32 -13
- package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
- package/dist/tools/fs/gh-rate-limit.js +4 -3
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +1 -0
- package/dist/tools/fs/safety.js +34 -10
- package/package.json +1 -1
|
@@ -12,6 +12,8 @@ import { primaryActivityArg } from "../arg-summary.js";
|
|
|
12
12
|
import { materializeMcpTools } from "../mcp.js";
|
|
13
13
|
import { materializeA2aTools } from "../a2a.js";
|
|
14
14
|
import { Type } from "typebox";
|
|
15
|
+
import { Value } from "typebox/value";
|
|
16
|
+
import { uuidv7 } from "../../engine/session/uuid.js";
|
|
15
17
|
import { brainToRuntime } from "../runtime.js";
|
|
16
18
|
import { StoredSession, isSessionConflict, hasSessionFork } from "../session.js";
|
|
17
19
|
import { createSubagentWorktreeHelper, forkGovernanceDenial } from "../../agents/subagent.js";
|
|
@@ -24,7 +26,7 @@ import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentList
|
|
|
24
26
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
25
27
|
import { emitTrace } from "../trace.js";
|
|
26
28
|
import { createSessionRulePolicy } from "./session-rule-policy.js";
|
|
27
|
-
import { cloneObserverInput, formatHookFeedback, runToolGate } from "../hooks.js";
|
|
29
|
+
import { cloneObserverInput, createHookEnvCapabilities, formatHookFeedback, runToolGate } from "../hooks.js";
|
|
28
30
|
import { reconcileInterruptedSession } from "../session-reconcile.js";
|
|
29
31
|
import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
|
|
30
32
|
import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
|
|
@@ -36,7 +38,7 @@ import { pathToUri } from "../lsp-protocol.js";
|
|
|
36
38
|
import { DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, createOffloadPersist, firstPartyOffloadPolicy, InMemoryToolResultStore, RunnerSharedToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, createReadToolResultTool, withToolResultOffload, } from "../tool-result-store.js";
|
|
37
39
|
import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createOutputTool, createReportBlockedTool, createReportFindingsTool, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
|
|
38
40
|
import { compileOutputSchema } from "./strict-output-schema.js";
|
|
39
|
-
import { TOOL_SEARCH_NAME, buildDeferredRegistry, classifyDeferred, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, } from "./tool-disclosure.js";
|
|
41
|
+
import { TOOL_SEARCH_NAME, buildDeferredRegistry, classifyDeferred, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, staticSchemaRenderable, } from "./tool-disclosure.js";
|
|
40
42
|
import { composeMemoryBlock } from "../memory.js";
|
|
41
43
|
import { preflightLockedConfig } from "../locked-config.js";
|
|
42
44
|
import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, complianceCallDenial, resolveComplianceDenies } from "../compliance.js";
|
|
@@ -64,7 +66,7 @@ import { createMonitorTool } from "../../tools/monitor.js";
|
|
|
64
66
|
import { createWorktreeTools } from "../../tools/worktree.js";
|
|
65
67
|
import { applyCompactionToReadFileState, bashReversibilityProbe, createHandsToolkit, isReadDedupStubResult, seedReadFileStateFromContext, seedReadFileStateFromTranscript, HAND_TOOL_EFFECTS, pdfModelCapabilitiesOf } from "../../tools/fs/index.js";
|
|
66
68
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
67
|
-
import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool } from "../ask-question.js";
|
|
69
|
+
import { ASK_USER_QUESTION_TOOL_NAME, createAskUserQuestionTool, classifyQuestionOutcome, isLiveQuestionFace, validateAskQuestions, } from "../ask-question.js";
|
|
68
70
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
69
71
|
import { createPresentPlanTool, createEnterPlanModeTool, PRESENT_PLAN_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME } from "../present-plan-tool.js";
|
|
70
72
|
import { isSelfOrchestrationActive, selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
|
|
@@ -73,8 +75,9 @@ import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-
|
|
|
73
75
|
import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
|
|
74
76
|
import { resolveKey } from "../../tools/fs/safety.js";
|
|
75
77
|
import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
|
|
76
|
-
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, remainingBudgetMicroUsd, } from "../checkpoint-store.js";
|
|
78
|
+
import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, remainingBudgetMicroUsd, resolveCheckpointStore, } from "../checkpoint-store.js";
|
|
77
79
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
80
|
+
import { countElicitOptIns, deriveAskEffective, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam } from "../wiring-manifest.js";
|
|
78
81
|
import { GLOBAL_USAGE_KEY, resolveUsageWindows, usageRetryAfterMs } from "../usage-window-store.js";
|
|
79
82
|
const RECONCILE_MAX_RETRIES = 3;
|
|
80
83
|
const DEFAULT_MAX_SUSPENDS = 5;
|
|
@@ -177,11 +180,7 @@ export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
|
177
180
|
export function checkpointScopeOf(spec) {
|
|
178
181
|
return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
|
|
179
182
|
}
|
|
180
|
-
export
|
|
181
|
-
if (spec.checkpointStore === null)
|
|
182
|
-
return undefined;
|
|
183
|
-
return spec.checkpointStore ?? deps.checkpointStore;
|
|
184
|
-
}
|
|
183
|
+
export { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
185
184
|
export function isFableFamilyModelId(id) {
|
|
186
185
|
const tail = id.toLowerCase().split("/").pop() ?? "";
|
|
187
186
|
return /^claude-fable-\d/.test(tail) || /^claude-mythos-5(?!\d)/.test(tail);
|
|
@@ -351,6 +350,54 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
351
350
|
{ name: "toolResultStore", store: deps.toolResultStore },
|
|
352
351
|
],
|
|
353
352
|
});
|
|
353
|
+
const resolvedInteractionPosture = spec.interactionPosture ?? internals?.parentInteractionPosture ?? deps.interactionPosture;
|
|
354
|
+
{
|
|
355
|
+
const interactionPosture = resolvedInteractionPosture;
|
|
356
|
+
const discloseInteractionPostureRefusal = (err) => {
|
|
357
|
+
try {
|
|
358
|
+
deps.onError?.(err, { phase: "config", sessionId: spec.sessionId ?? "(pre-session)", classification: "interaction-posture-refused" });
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
if (interactionPosture !== undefined && interactionPosture !== "interactive" && interactionPosture !== "headless") {
|
|
364
|
+
const e = new Error(`interactionPosture ${JSON.stringify(interactionPosture)} is not a recognized posture ("interactive" | "headless") — ` +
|
|
365
|
+
`an unevaluable declaration is refused loudly, never folded to either posture.`);
|
|
366
|
+
e.code = "config.interaction_posture";
|
|
367
|
+
discloseInteractionPostureRefusal(e);
|
|
368
|
+
throw e;
|
|
369
|
+
}
|
|
370
|
+
if (interactionPosture === "interactive") {
|
|
371
|
+
const askDoor = resolveAskSeamForm(spec, deps);
|
|
372
|
+
const humanReachable = deriveAskEffective(askDoor.form, "unresolved") === "human_reachable";
|
|
373
|
+
const questionDoor = resolveQuestionSeam(spec, deps);
|
|
374
|
+
const strippedByEngine = internals?.questionFaceStripped === true;
|
|
375
|
+
if (spec.interactiveTools === false) {
|
|
376
|
+
const e = new Error(`interaction posture "interactive" declared together with interactiveTools: false — the hard-headless ` +
|
|
377
|
+
`clamp removes the AskUserQuestion mount, so no content question can ever reach the human this posture ` +
|
|
378
|
+
`promises. Drop one of the two declarations.`);
|
|
379
|
+
e.code = "config.interaction_posture";
|
|
380
|
+
discloseInteractionPostureRefusal(e);
|
|
381
|
+
throw e;
|
|
382
|
+
}
|
|
383
|
+
if (!humanReachable || !(questionDoor.wired || strippedByEngine)) {
|
|
384
|
+
const missing = [];
|
|
385
|
+
if (!humanReachable) {
|
|
386
|
+
missing.push(askDoor.form === "absent"
|
|
387
|
+
? "no onAsk approver is wired (spec.onAsk ?? deps.onAsk is absent — asks would auto-deny or park)"
|
|
388
|
+
: `the resolved onAsk seat is the blanket policy "${askDoor.form}" (${askDoor.provenance ?? "?"}) — a policy setting is not a reachable human`);
|
|
389
|
+
}
|
|
390
|
+
if (!questionDoor.wired && !strippedByEngine) {
|
|
391
|
+
missing.push("no content-question channel (spec.onQuestion ?? deps.onQuestion is absent, and this leg is not an engine-stripped background lane)");
|
|
392
|
+
}
|
|
393
|
+
const e = new Error(`interaction posture "interactive" declared, but this assembly cannot reach a human: ${missing.join("; ")}. ` +
|
|
394
|
+
`Wire the missing seam(s), or drop the posture declaration (absent = no check).`);
|
|
395
|
+
e.code = "config.interaction_posture";
|
|
396
|
+
discloseInteractionPostureRefusal(e);
|
|
397
|
+
throw e;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
354
401
|
resolveTaskLimits(spec.limits);
|
|
355
402
|
if (spec.resourceSuspend !== undefined) {
|
|
356
403
|
const rsus = spec.resourceSuspend;
|
|
@@ -392,6 +439,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
392
439
|
const egressTools = new Set();
|
|
393
440
|
const irreversibleTools = new Set();
|
|
394
441
|
const irreversibilityTier = new Map();
|
|
442
|
+
const axisExplicitNegatives = new Map();
|
|
395
443
|
const reversibilityProbes = new Map();
|
|
396
444
|
let shellGatedBash = false;
|
|
397
445
|
let shellGatedMonitor = false;
|
|
@@ -581,6 +629,13 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
581
629
|
if (extraBodyCollisions.length > 0) {
|
|
582
630
|
deps.onError?.(new Error(`model.extraBody contains reserved key(s) [${extraBodyCollisions.join(", ")}] that core owns and sets itself — they are ignored. Remove them from extraBody.`), { phase: "config", sessionId });
|
|
583
631
|
}
|
|
632
|
+
if (model.extraBody !== undefined && Object.prototype.hasOwnProperty.call(model.extraBody, "max_completion_tokens")) {
|
|
633
|
+
const e = new Error(`model.extraBody supplies "max_completion_tokens" — this spelling is engine-owned (the overflow retry ` +
|
|
634
|
+
`adjusts the per-request output cap, and a constant extraBody value would override that adjustment on ` +
|
|
635
|
+
`every retry). Set the cap via limits.maxOutputTokens (per request) or model.maxTokens instead.`);
|
|
636
|
+
e.code = "config.extra_body_output_cap";
|
|
637
|
+
throw e;
|
|
638
|
+
}
|
|
584
639
|
const nestedStats = { tokens: 0, turns: 0, tasks: 0, costMicroUsd: 0, anyUnpriced: false };
|
|
585
640
|
if (resume) {
|
|
586
641
|
nestedStats.tokens = resume.seed.nestedStats.tokens;
|
|
@@ -958,9 +1013,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
958
1013
|
const seedAdmittedOrg = seedInheritedGate?.admittedOrgScopes;
|
|
959
1014
|
const inheritedAdmittedOrgScopes = liveAdmittedOrg === undefined ? seedAdmittedOrg : seedAdmittedOrg === undefined ? liveAdmittedOrg : liveAdmittedOrg.filter((s) => seedAdmittedOrg.includes(s));
|
|
960
1015
|
const inheritedOrgGoverned = liveInheritedGate?.orgAdmissionGoverned === true || seedInheritedGate?.orgAdmissionGoverned === true;
|
|
961
|
-
const
|
|
1016
|
+
const checkpointOwnOrgVerdict = resume?.seed.inheritedGate?.ownAdmittedOrgScopes !== undefined
|
|
962
1017
|
? { scopes: resume.seed.inheritedGate.ownAdmittedOrgScopes, writeScope: resume.seed.inheritedGate.ownAdmittedOrgWriteScope ?? null }
|
|
963
1018
|
: undefined;
|
|
1019
|
+
const refOwnOrgVerdict = internals?.ownOrgAdmissionRef?.current;
|
|
1020
|
+
const priorOwnOrgVerdict = checkpointOwnOrgVerdict === undefined
|
|
1021
|
+
? refOwnOrgVerdict
|
|
1022
|
+
: refOwnOrgVerdict === undefined
|
|
1023
|
+
? checkpointOwnOrgVerdict
|
|
1024
|
+
: {
|
|
1025
|
+
scopes: checkpointOwnOrgVerdict.scopes.filter((sc) => refOwnOrgVerdict.scopes.includes(sc)),
|
|
1026
|
+
writeScope: checkpointOwnOrgVerdict.writeScope !== null && checkpointOwnOrgVerdict.writeScope === refOwnOrgVerdict.writeScope
|
|
1027
|
+
? checkpointOwnOrgVerdict.writeScope
|
|
1028
|
+
: null,
|
|
1029
|
+
};
|
|
964
1030
|
const orgGovernedProvenance = deps.memoryScopeAdmission !== undefined ||
|
|
965
1031
|
(deps.deploymentMemoryScopes !== undefined && deps.deploymentMemoryScopes.length > 0) ||
|
|
966
1032
|
deps.compliancePostureResolver !== undefined ||
|
|
@@ -1055,6 +1121,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1055
1121
|
: {}),
|
|
1056
1122
|
sessionId,
|
|
1057
1123
|
...(spec.backgroundScope !== undefined ? { backgroundScope: spec.backgroundScope } : {}),
|
|
1124
|
+
...(resolvedInteractionPosture !== undefined ? { interactionPosture: resolvedInteractionPosture } : {}),
|
|
1058
1125
|
...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
|
|
1059
1126
|
...(internals?.onTaskNotification ? { onTaskNotification: internals.onTaskNotification } : {}),
|
|
1060
1127
|
...(forwardEvent ? { forwardEvent } : {}),
|
|
@@ -1208,6 +1275,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1208
1275
|
...(spec.getApiKeyAndHeaders !== undefined ? { parentGetApiKeyAndHeaders: spec.getApiKeyAndHeaders } : {}),
|
|
1209
1276
|
principal: spec.principal,
|
|
1210
1277
|
oneShot: spec.oneShot,
|
|
1278
|
+
...(resolvedInteractionPosture !== undefined ? { parentInteractionPosture: resolvedInteractionPosture } : {}),
|
|
1279
|
+
autoModeReview: () => (autoModeDecider !== undefined ? { decider: autoModeDecider } : undefined),
|
|
1211
1280
|
workflowDepth: internals?.workflowDepth,
|
|
1212
1281
|
parentCwd: taskRootPath,
|
|
1213
1282
|
parentThinking: () => harnessRef.current?.getThinkingLevel() ?? thinking,
|
|
@@ -1380,6 +1449,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1380
1449
|
irreversibilityTier.set(axis.name, "always");
|
|
1381
1450
|
irreversibleTools.add(axis.name);
|
|
1382
1451
|
}
|
|
1452
|
+
if (axis.irreversibility === "never") {
|
|
1453
|
+
axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), irreversible: false });
|
|
1454
|
+
}
|
|
1455
|
+
if (axis.egress === false) {
|
|
1456
|
+
axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), egress: false });
|
|
1457
|
+
}
|
|
1383
1458
|
if (axis.egress) {
|
|
1384
1459
|
if (axis.effect !== undefined && axis.effect !== "write") {
|
|
1385
1460
|
const e = new Error(`${protocolLabel} tool "${axis.name}" resolves to egress:true with effect:"${axis.effect}" — an egress tool (external write) must have effect:"write". Clear egress (toolAxes egress:false) if it is a pure read, or set effect:"write".`);
|
|
@@ -1524,6 +1599,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1524
1599
|
return memoryWriteGateRef.current?.(w);
|
|
1525
1600
|
},
|
|
1526
1601
|
mountBackgroundTaskTools: false,
|
|
1602
|
+
monitorToolActive: backgroundTaskToolsActive && !(toolFaceSnapshot.exclude?.includes("Monitor") ?? false),
|
|
1527
1603
|
});
|
|
1528
1604
|
{
|
|
1529
1605
|
const bandNames = new Set(band.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
@@ -1535,6 +1611,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1535
1611
|
}
|
|
1536
1612
|
tools.push(...band.map((t) => firstPartyOffload(t)));
|
|
1537
1613
|
const shellGate = effectiveShellGate;
|
|
1614
|
+
if (shellGate === "off" && !(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
|
|
1615
|
+
deps.onError?.(new Error(`shell gate doctrine is "off" while a real writable shell (Bash) is mounted — no shell safety-axis ` +
|
|
1616
|
+
`fold applies to this run (commands are adjudicated by the ordinary policy/hook chain only). ` +
|
|
1617
|
+
`Set spec.shellGate to "classify" or "always" if this deployment expects doctrine-gated shell behavior.`), { phase: "config", sessionId, classification: "shell-gate-off" });
|
|
1618
|
+
}
|
|
1538
1619
|
if (shellGate !== "off" && !(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
|
|
1539
1620
|
shellGatedBash = !egressTools.has("Bash") && !irreversibilityTier.has("Bash");
|
|
1540
1621
|
irreversibilityTier.set("Bash", shellGate === "always" ? "always" : "maybe");
|
|
@@ -1655,10 +1736,79 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1655
1736
|
if (offloadStore)
|
|
1656
1737
|
tools.push(createReadToolResultTool(offloadStore));
|
|
1657
1738
|
const onQuestion = frozenOnQuestion;
|
|
1739
|
+
const liveQuestionFace = isLiveQuestionFace(onQuestion) ? onQuestion : undefined;
|
|
1740
|
+
const contentAskBindings = new Map();
|
|
1741
|
+
const CONTENT_ASK_BINDING_CAP = 32;
|
|
1742
|
+
const contentAskRoutable = (toolCallId) => liveQuestionFace !== undefined &&
|
|
1743
|
+
mountedQuestionTool !== undefined &&
|
|
1744
|
+
tools.includes(mountedQuestionTool) &&
|
|
1745
|
+
runtimeCaps?.forceDurableGate !== true &&
|
|
1746
|
+
!inheritedUnavailableAsks.has(toolCallId);
|
|
1747
|
+
const lateStrandedAnswers = [];
|
|
1748
|
+
const discloseStrandedAnswers = (records, why) => {
|
|
1749
|
+
if (records.length === 0)
|
|
1750
|
+
return;
|
|
1751
|
+
try {
|
|
1752
|
+
deps.onError?.(new Error(`AskUserQuestion: ${records.length} question(s) were answered by a person but the call never ` +
|
|
1753
|
+
`executed to collect the answer (${records.map((r) => `${r.toolCallId} [delivery ${r.deliveryId}]`).join(", ")}) — ${why}. The answer(s) were NOT ` +
|
|
1754
|
+
`delivered to the model and are gone with this leg; re-ask if the decision is still needed.`), { phase: "degraded", sessionId, classification: "unconsumed-human-answer" });
|
|
1755
|
+
}
|
|
1756
|
+
catch {
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
const settleContentAskBindings = () => {
|
|
1760
|
+
const stranded = [];
|
|
1761
|
+
for (const [callId, bound] of contentAskBindings) {
|
|
1762
|
+
if (bound.kind === "answered")
|
|
1763
|
+
stranded.push({ deliveryId: bound.deliveryId, toolCallId: callId });
|
|
1764
|
+
}
|
|
1765
|
+
contentAskBindings.clear();
|
|
1766
|
+
discloseStrandedAnswers(stranded, "the leg ended first (abort, batch teardown, or a loop failure)");
|
|
1767
|
+
const byDelivery = new Map();
|
|
1768
|
+
for (const r of [...stranded, ...lateStrandedAnswers])
|
|
1769
|
+
byDelivery.set(r.deliveryId, r);
|
|
1770
|
+
return [...byDelivery.values()];
|
|
1771
|
+
};
|
|
1658
1772
|
const durableQuestionFace = resolveCheckpointStore(spec, deps) !== undefined &&
|
|
1659
1773
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
|
|
1660
|
-
|
|
1661
|
-
|
|
1774
|
+
const mountedQuestionFace = liveQuestionFace !== undefined
|
|
1775
|
+
? async (req, signal) => {
|
|
1776
|
+
const bound = contentAskBindings.get(req.toolCallId);
|
|
1777
|
+
if (bound !== undefined) {
|
|
1778
|
+
if (bound.questionsHash === boundInputHashOf(req.questions)) {
|
|
1779
|
+
contentAskBindings.delete(req.toolCallId);
|
|
1780
|
+
if (bound.kind === "answered")
|
|
1781
|
+
return bound.answer;
|
|
1782
|
+
throw bound.error;
|
|
1783
|
+
}
|
|
1784
|
+
contentAskBindings.delete(req.toolCallId);
|
|
1785
|
+
}
|
|
1786
|
+
return liveQuestionFace(req, signal);
|
|
1787
|
+
}
|
|
1788
|
+
: onQuestion;
|
|
1789
|
+
const questionToolMounted = spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace));
|
|
1790
|
+
let mountedQuestionTool;
|
|
1791
|
+
if (questionToolMounted)
|
|
1792
|
+
tools.push((mountedQuestionTool =
|
|
1793
|
+
createAskUserQuestionTool(mountedQuestionFace, { principal: spec.principal, sourceTaskId: sessionId }, {
|
|
1794
|
+
...(resume?.redeemedContentAskCallId !== undefined
|
|
1795
|
+
? {
|
|
1796
|
+
redeemedApprovalCallId: resume.redeemedContentAskCallId,
|
|
1797
|
+
...(resume.redeemedContentAskQuestionsHash !== undefined ? { redeemedApprovalQuestionsHash: resume.redeemedContentAskQuestionsHash } : {}),
|
|
1798
|
+
}
|
|
1799
|
+
: {}),
|
|
1800
|
+
...(resolvedInteractionPosture !== undefined ? { posture: resolvedInteractionPosture } : {}),
|
|
1801
|
+
...(spec.interactiveQuestionFallback === true ? { interactiveFallback: true } : {}),
|
|
1802
|
+
onSyntheticContinuation: ({ questionId, reason }) => {
|
|
1803
|
+
deps.onError?.(new Error(`AskUserQuestion ${questionId}: no human answer was obtainable (` +
|
|
1804
|
+
(reason === "seam_absent"
|
|
1805
|
+
? "no onQuestion seam is wired"
|
|
1806
|
+
: reason === "declined_unavailable"
|
|
1807
|
+
? "the wired question channel reported nobody was reachable"
|
|
1808
|
+
: "the wired question channel failed") +
|
|
1809
|
+
`) — the model was instructed to self-answer and the run CONTINUES (warning, not a failure).`), { phase: "degraded", sessionId, classification: "no-human-autoanswered" });
|
|
1810
|
+
},
|
|
1811
|
+
})));
|
|
1662
1812
|
if (spec.handsReadOnly !== true) {
|
|
1663
1813
|
const sessionScope = sessionId ?? spec.principal ?? spec.taskId ?? "default";
|
|
1664
1814
|
tools.push(...createSchedulerTools(executionEnv, {
|
|
@@ -1702,6 +1852,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1702
1852
|
sessionId,
|
|
1703
1853
|
taskRootPath,
|
|
1704
1854
|
memoryWriteGateRef,
|
|
1855
|
+
writeToolsMounted: handsEnabled && spec.handsReadOnly !== true && !(toolFaceSnapshot.exclude?.includes("Write") ?? false),
|
|
1705
1856
|
admissionCtx: {
|
|
1706
1857
|
orgMemoryDenied: complianceDenies.has("org_memory_mount"),
|
|
1707
1858
|
complianceDegraded,
|
|
@@ -1721,6 +1872,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1721
1872
|
});
|
|
1722
1873
|
memoryAdmittedOrgScopesRef.current = memoryAdmittedOrgScopes;
|
|
1723
1874
|
ownOrgVerdictRef.current = memoryOwnOrgVerdict ?? priorOwnOrgVerdict;
|
|
1875
|
+
if (internals?.ownOrgAdmissionRef !== undefined && ownOrgVerdictRef.current !== undefined) {
|
|
1876
|
+
internals.ownOrgAdmissionRef.current = ownOrgVerdictRef.current;
|
|
1877
|
+
}
|
|
1724
1878
|
let memoryBlock = memoryBlockFromEngine;
|
|
1725
1879
|
if (memorySeedFiles?.length && seedContextFiles) {
|
|
1726
1880
|
try {
|
|
@@ -2117,6 +2271,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2117
2271
|
model,
|
|
2118
2272
|
deferNames: (toolFaceSnapshot.defer ?? []).filter((n) => tools.some((t) => t.name === n)),
|
|
2119
2273
|
alwaysLoadNames: [
|
|
2274
|
+
ASK_USER_QUESTION_TOOL_NAME,
|
|
2120
2275
|
...(toolFaceSnapshot.alwaysLoad ?? []),
|
|
2121
2276
|
...mcp.tools
|
|
2122
2277
|
.filter((t) => t.mcpAlwaysLoad === true && !(toolFaceSnapshot.defer ?? []).includes(t.name))
|
|
@@ -2136,6 +2291,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2136
2291
|
.map((s) => ({ name: inlineUntrusted(s.name, 160), ...(s.error !== undefined ? { error: inlineUntrusted(s.error, 240) } : {}) }));
|
|
2137
2292
|
let toolsDeltaRef;
|
|
2138
2293
|
let toolMaterializeStatic = false;
|
|
2294
|
+
const staticFaceForRef = {};
|
|
2139
2295
|
if (deferred.size > 0 || failedMcpServers.length > 0) {
|
|
2140
2296
|
toolsDeltaRef = { pending: [], pendingRemoved: [], pendingReadded: [], pendingFailed: failedMcpServers };
|
|
2141
2297
|
}
|
|
@@ -2147,6 +2303,33 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2147
2303
|
throw e;
|
|
2148
2304
|
}
|
|
2149
2305
|
const registry = buildDeferredRegistry(deferred, tools);
|
|
2306
|
+
const envStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
|
|
2307
|
+
if (envStrategy !== undefined && envStrategy !== "swap" && envStrategy !== "static") {
|
|
2308
|
+
const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(envStrategy)}).`);
|
|
2309
|
+
e.code = "config.tool_materialize_invalid";
|
|
2310
|
+
throw e;
|
|
2311
|
+
}
|
|
2312
|
+
const requestedStrategy = spec.toolMaterializeStrategy ?? envStrategy ?? "swap";
|
|
2313
|
+
const laneDegrade = requestedStrategy === "static" && spec.deferSelfResolve === false;
|
|
2314
|
+
const materializeStatic = requestedStrategy === "static" && !laneDegrade;
|
|
2315
|
+
toolMaterializeStatic = materializeStatic;
|
|
2316
|
+
promptManifest.toolDisclosure = {
|
|
2317
|
+
deferredTools: deferred.size,
|
|
2318
|
+
strategy: materializeStatic ? "static" : "swap",
|
|
2319
|
+
source: laneDegrade ? "degraded_no_direct_lane" : spec.toolMaterializeStrategy !== undefined ? "spec" : envStrategy !== undefined ? "env" : "default",
|
|
2320
|
+
};
|
|
2321
|
+
const everExempt = new Set();
|
|
2322
|
+
const staticFaceFor = (name) => {
|
|
2323
|
+
if (!materializeStatic)
|
|
2324
|
+
return false;
|
|
2325
|
+
if (everExempt.has(name))
|
|
2326
|
+
return false;
|
|
2327
|
+
if (staticSchemaRenderable(tools.find((t) => t.name === name)?.parameters))
|
|
2328
|
+
return true;
|
|
2329
|
+
everExempt.add(name);
|
|
2330
|
+
return false;
|
|
2331
|
+
};
|
|
2332
|
+
staticFaceForRef.current = staticFaceFor;
|
|
2150
2333
|
let activationChain = Promise.resolve();
|
|
2151
2334
|
const serializeActivation = (section) => {
|
|
2152
2335
|
const p = activationChain.then(section);
|
|
@@ -2168,6 +2351,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2168
2351
|
};
|
|
2169
2352
|
},
|
|
2170
2353
|
...(executionMode !== undefined ? { executionMode } : {}),
|
|
2354
|
+
staticFace: () => staticFaceFor(name),
|
|
2171
2355
|
activate: async () => serializeActivation(async () => {
|
|
2172
2356
|
if (activeTools.has(name))
|
|
2173
2357
|
return undefined;
|
|
@@ -2212,16 +2396,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2212
2396
|
};
|
|
2213
2397
|
offloadReachableToolsRef.current = callableToolNames;
|
|
2214
2398
|
let toolSearch;
|
|
2215
|
-
const envStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
|
|
2216
|
-
if (envStrategy !== undefined && envStrategy !== "swap" && envStrategy !== "static") {
|
|
2217
|
-
const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(envStrategy)}).`);
|
|
2218
|
-
e.code = "config.tool_materialize_invalid";
|
|
2219
|
-
throw e;
|
|
2220
|
-
}
|
|
2221
|
-
const materializeStatic = (spec.toolMaterializeStrategy ?? envStrategy ?? "static") === "static" && spec.deferSelfResolve !== false;
|
|
2222
|
-
toolMaterializeStatic = materializeStatic;
|
|
2223
2399
|
const buildToolList = (active) => {
|
|
2224
|
-
const list = tools.map((t) => (deferred.has(t.name) && (
|
|
2400
|
+
const list = tools.map((t) => (deferred.has(t.name) && (staticFaceFor(t.name) || !active.has(t.name)) ? placeholders.get(t.name) : t));
|
|
2225
2401
|
list.push(toolSearch);
|
|
2226
2402
|
return list;
|
|
2227
2403
|
};
|
|
@@ -2262,8 +2438,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2262
2438
|
listingRide: (newly) => listingRideRef.current?.(newly),
|
|
2263
2439
|
mountedNames: callableToolNames,
|
|
2264
2440
|
directCallEnabled: spec.deferSelfResolve !== false,
|
|
2441
|
+
isMounted: (name) => tools.some((t) => t.name === name),
|
|
2265
2442
|
...(materializeStatic
|
|
2266
|
-
? { staticSchemaFor: (name) => tools.find((t) => t.name === name)?.parameters }
|
|
2443
|
+
? { staticSchemaFor: (name) => (staticFaceFor(name) ? tools.find((t) => t.name === name)?.parameters : undefined) }
|
|
2267
2444
|
: {}),
|
|
2268
2445
|
serializeActivation,
|
|
2269
2446
|
});
|
|
@@ -2598,6 +2775,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2598
2775
|
? { sourceAgentName: internals?.explicitAgentName ?? internals?.agentName }
|
|
2599
2776
|
: {}),
|
|
2600
2777
|
});
|
|
2778
|
+
const riskAxesOf = (toolName) => {
|
|
2779
|
+
const tier = irreversibilityTier.get(toolName);
|
|
2780
|
+
const negatives = axisExplicitNegatives.get(toolName);
|
|
2781
|
+
const irreversible = tier === "always" || tier === "maybe" ? true : tier === "never" ? false : negatives?.irreversible;
|
|
2782
|
+
const egress = egressTools.has(toolName) ? true : negatives?.egress;
|
|
2783
|
+
if (irreversible === undefined && egress === undefined)
|
|
2784
|
+
return {};
|
|
2785
|
+
return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
|
|
2786
|
+
};
|
|
2601
2787
|
const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal) => {
|
|
2602
2788
|
let editArgs = edit;
|
|
2603
2789
|
for (let round = 0;; round++) {
|
|
@@ -2630,6 +2816,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2630
2816
|
args: editArgs,
|
|
2631
2817
|
message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2632
2818
|
...askSourceIdentity(),
|
|
2819
|
+
...riskAxesOf(creq.toolName),
|
|
2633
2820
|
...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2634
2821
|
}, onAskOf, csignal ?? abortController.signal);
|
|
2635
2822
|
if (rr.action !== "allow")
|
|
@@ -2654,8 +2841,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2654
2841
|
return first;
|
|
2655
2842
|
if (first.action === "allow")
|
|
2656
2843
|
return { action: "allow" };
|
|
2657
|
-
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME)
|
|
2844
|
+
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME && (pc.durableMandate !== true || markInheritedUnavailable(creq.toolCallId))) {
|
|
2658
2845
|
return first;
|
|
2846
|
+
}
|
|
2659
2847
|
if (pc.durableMandate === true) {
|
|
2660
2848
|
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2661
2849
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
|
|
@@ -2676,6 +2864,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2676
2864
|
args: presentedArgs,
|
|
2677
2865
|
message: first.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2678
2866
|
...askSourceIdentity(),
|
|
2867
|
+
...riskAxesOf(creq.toolName),
|
|
2679
2868
|
...(first.action === "ask" && first.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2680
2869
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
2681
2870
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
@@ -2710,8 +2899,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2710
2899
|
}
|
|
2711
2900
|
if (decision.action !== "ask")
|
|
2712
2901
|
return decision;
|
|
2713
|
-
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME)
|
|
2902
|
+
if (creq.toolName === ASK_USER_QUESTION_TOOL_NAME && (pc.durableMandate !== true || markInheritedUnavailable(creq.toolCallId))) {
|
|
2714
2903
|
return decision;
|
|
2904
|
+
}
|
|
2715
2905
|
if (pc.durableMandate === true) {
|
|
2716
2906
|
if (resolveCheckpointStore(spec, deps) !== undefined &&
|
|
2717
2907
|
(spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true) &&
|
|
@@ -2732,6 +2922,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2732
2922
|
args: presentedArgs,
|
|
2733
2923
|
message: decision.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2734
2924
|
...askSourceIdentity(),
|
|
2925
|
+
...riskAxesOf(creq.toolName),
|
|
2735
2926
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2736
2927
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
2737
2928
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
@@ -2917,6 +3108,42 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2917
3108
|
};
|
|
2918
3109
|
})();
|
|
2919
3110
|
const platformSuspendArmed = durableSuspendInfraReady && (envLifetimeSuspendAt !== undefined || usageGovernance !== undefined);
|
|
3111
|
+
const checkpointStore = resolveCheckpointStore(spec, deps);
|
|
3112
|
+
const durableApproval = spec.durableApproval ??
|
|
3113
|
+
(runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
|
|
3114
|
+
const wiringQuestionSeam = resolveQuestionSeam(spec, deps);
|
|
3115
|
+
const wiringQuestionStripped = internals?.questionFaceStripped === true;
|
|
3116
|
+
const wiringAskSeam = resolveAskSeamForm(spec, deps);
|
|
3117
|
+
const wiringManifest = deriveWiringManifest({
|
|
3118
|
+
half: "effective",
|
|
3119
|
+
leg: resume !== undefined ? "resume" : internals?.isDelegatedChild === true ? "child" : "root",
|
|
3120
|
+
askForm: wiringAskSeam.form,
|
|
3121
|
+
...(wiringAskSeam.provenance !== undefined ? { askProvenance: wiringAskSeam.provenance } : {}),
|
|
3122
|
+
questionWired: wiringQuestionSeam.wired,
|
|
3123
|
+
...(wiringQuestionSeam.provenance !== undefined ? { questionProvenance: wiringQuestionSeam.provenance } : {}),
|
|
3124
|
+
...(wiringQuestionStripped ? { questionStrippedByEngine: true } : {}),
|
|
3125
|
+
...(resolvedInteractionPosture !== undefined ? { interactionPosture: resolvedInteractionPosture } : {}),
|
|
3126
|
+
...(spec.interactiveTools === true && !wiringQuestionSeam.wired && !durableQuestionFace && !wiringQuestionStripped
|
|
3127
|
+
? { interactiveToolsWithoutDeliveryFace: true }
|
|
3128
|
+
: {}),
|
|
3129
|
+
elicitSeamWired: resolveElicitSeam(deps),
|
|
3130
|
+
elicitServersOptedIn: countElicitOptIns(spec),
|
|
3131
|
+
parkCapable: checkpointStore !== undefined,
|
|
3132
|
+
parkDurableApprovalOptIn: spec.durableApproval !== undefined,
|
|
3133
|
+
parkForceDurableGate: runtimeCaps?.forceDurableGate === true,
|
|
3134
|
+
parkSafetyVocabularyArmed: irreversibleTools.size > 0 || egressTools.size > 0,
|
|
3135
|
+
...(checkpointStore !== undefined ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
|
|
3136
|
+
sessionDurability: resolveDeclaredDurability(sessions, "sessionStore"),
|
|
3137
|
+
backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
|
|
3138
|
+
hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
|
|
3139
|
+
lockedConfigWired: deps.lockedConfig !== undefined,
|
|
3140
|
+
complianceWired: deps.compliancePostureResolver !== undefined,
|
|
3141
|
+
memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
|
|
3142
|
+
retentionPolicyWired: deps.retentionPolicy !== undefined,
|
|
3143
|
+
});
|
|
3144
|
+
const parkLaneArmed = wiringManifest.parkLane.effective === true;
|
|
3145
|
+
const hookContextConsumerWired = hooks?.preToolUse !== undefined || hooks?.postToolUse !== undefined || hooks?.postToolUseFailure !== undefined;
|
|
3146
|
+
const hookEnvFace = hookContextConsumerWired && (ownedEnv ?? deps.executionEnv) != null ? createHookEnvCapabilities(executionEnv) : undefined;
|
|
2920
3147
|
if (effectivePolicy || hooks?.preToolUse || egressTools.size > 0 || irreversibleTools.size > 0 || resourceSuspendEligible || platformSuspendArmed || spec.enablePlanMode === true) {
|
|
2921
3148
|
const adjudicate = effectivePolicy
|
|
2922
3149
|
? (req) => raceAbort(Promise.resolve(effectivePolicy.check({ ...req, budget: budgetSnapshot }, abortController.signal)), abortController.signal, () => ({
|
|
@@ -2998,6 +3225,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2998
3225
|
})(),
|
|
2999
3226
|
message: decision.message ?? `approval required for "${req.toolName}"`,
|
|
3000
3227
|
...askSourceIdentity(),
|
|
3228
|
+
...riskAxesOf(req.toolName),
|
|
3001
3229
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
3002
3230
|
}, onAsk, abortController.signal);
|
|
3003
3231
|
const waitMs = Math.max(0, now() - t0);
|
|
@@ -3037,9 +3265,6 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3037
3265
|
catch {
|
|
3038
3266
|
}
|
|
3039
3267
|
};
|
|
3040
|
-
const checkpointStore = resolveCheckpointStore(spec, deps);
|
|
3041
|
-
const durableApproval = spec.durableApproval ??
|
|
3042
|
-
(runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
|
|
3043
3268
|
const inFlightSpendMicroUsd = () => {
|
|
3044
3269
|
const own = liveSpendRef.get?.().costMicroUsd ?? 0;
|
|
3045
3270
|
const seededNested = resume?.seed.nestedStats.costMicroUsd;
|
|
@@ -3078,6 +3303,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3078
3303
|
return live.length > 0 ? live : undefined;
|
|
3079
3304
|
})(),
|
|
3080
3305
|
pendingSteer: undefined,
|
|
3306
|
+
pendingSteerQueue: undefined,
|
|
3081
3307
|
inheritedGate: (() => {
|
|
3082
3308
|
const requiresParentConstraint = (inheritedParentConstraints?.length ?? 0) > 0 || seedInheritedGate?.requiresParentConstraint === true;
|
|
3083
3309
|
const parentConstraintCount = (inheritedParentConstraints?.length ?? 0) > 0 ? inheritedParentConstraints.length : seedInheritedGate?.parentConstraintCount;
|
|
@@ -3325,12 +3551,92 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3325
3551
|
return true;
|
|
3326
3552
|
}
|
|
3327
3553
|
: undefined;
|
|
3328
|
-
const
|
|
3329
|
-
|
|
3330
|
-
|
|
3554
|
+
const resolveContentAsk = async (req) => {
|
|
3555
|
+
if (!contentAskRoutable(req.toolCallId) || liveQuestionFace === undefined || mountedQuestionTool === undefined) {
|
|
3556
|
+
return { kind: "unavailable", parkDeclined: false };
|
|
3557
|
+
}
|
|
3558
|
+
const deliveryId = uuidv7();
|
|
3559
|
+
const bindOutcome = (entry, questionsHash) => {
|
|
3560
|
+
if (contentAskBindings.size >= CONTENT_ASK_BINDING_CAP && !contentAskBindings.has(req.toolCallId))
|
|
3561
|
+
return;
|
|
3562
|
+
contentAskBindings.set(req.toolCallId, { ...entry, questionsHash, deliveryId });
|
|
3563
|
+
};
|
|
3564
|
+
const refuseBeforeDelivery = (code, presentedInput) => ({
|
|
3565
|
+
kind: "delivery_failure",
|
|
3566
|
+
code,
|
|
3567
|
+
...(presentedInput !== undefined ? { presentedInput } : {}),
|
|
3568
|
+
});
|
|
3569
|
+
let retained;
|
|
3570
|
+
let presented;
|
|
3571
|
+
try {
|
|
3572
|
+
retained = structuredClone(req.args);
|
|
3573
|
+
presented = structuredClone(retained.questions);
|
|
3574
|
+
}
|
|
3575
|
+
catch {
|
|
3576
|
+
return refuseBeforeDelivery("question.malformed_request");
|
|
3577
|
+
}
|
|
3578
|
+
const retainedQuestionsHash = boundInputHashOf(retained.questions);
|
|
3579
|
+
if (!Value.Check(mountedQuestionTool.parameters, retained) || validateAskQuestions(presented) !== undefined) {
|
|
3580
|
+
return refuseBeforeDelivery("question.malformed_request", retained);
|
|
3581
|
+
}
|
|
3582
|
+
if (contentAskBindings.size >= CONTENT_ASK_BINDING_CAP && !contentAskBindings.has(req.toolCallId)) {
|
|
3583
|
+
return { kind: "unavailable", parkDeclined: true };
|
|
3584
|
+
}
|
|
3585
|
+
if (abortController.signal.aborted) {
|
|
3586
|
+
return { kind: "delivery_failure", code: "question.aborted", presentedInput: retained };
|
|
3587
|
+
}
|
|
3588
|
+
try {
|
|
3589
|
+
const facePromise = (async () => liveQuestionFace({
|
|
3590
|
+
toolCallId: req.toolCallId,
|
|
3591
|
+
questions: presented,
|
|
3592
|
+
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
3593
|
+
sourceTaskId: sessionId,
|
|
3594
|
+
boundInputHash: boundInputHashOf(retained),
|
|
3595
|
+
deliveryId,
|
|
3596
|
+
}, abortController.signal))();
|
|
3597
|
+
const settlement = await raceAbort(facePromise.then((outcome) => ({ tag: "outcome", outcome }), (error) => ({ tag: "threw", error })), abortController.signal, () => ({ tag: "aborted" }));
|
|
3598
|
+
if (settlement.tag === "aborted") {
|
|
3599
|
+
void facePromise.then((late) => {
|
|
3600
|
+
if (classifyQuestionOutcome(late).shape !== "answered")
|
|
3601
|
+
return;
|
|
3602
|
+
lateStrandedAnswers.push({ deliveryId, toolCallId: req.toolCallId });
|
|
3603
|
+
discloseStrandedAnswers([{ deliveryId, toolCallId: req.toolCallId }], "the task had already ended when the answer arrived");
|
|
3604
|
+
}, () => undefined).catch(() => undefined);
|
|
3605
|
+
}
|
|
3606
|
+
if (settlement.tag === "outcome") {
|
|
3607
|
+
const outcome = settlement.outcome;
|
|
3608
|
+
const reading = classifyQuestionOutcome(outcome);
|
|
3609
|
+
if (reading.shape === "contradictory") {
|
|
3610
|
+
bindOutcome({ kind: "failed", error: new Error("the question channel returned a contradictory outcome (an unavailable answer)") }, retainedQuestionsHash);
|
|
3611
|
+
return { kind: "delivery_failure", code: "question.human_channel_failed", presentedInput: retained };
|
|
3612
|
+
}
|
|
3613
|
+
if (reading.shape === "unavailable")
|
|
3614
|
+
return { kind: "unavailable", parkDeclined: true, presentedInput: retained };
|
|
3615
|
+
const answerSnapshot = reading.answer;
|
|
3616
|
+
bindOutcome({ kind: "answered", answer: answerSnapshot }, retainedQuestionsHash);
|
|
3617
|
+
return { kind: "answered", presentedInput: retained };
|
|
3618
|
+
}
|
|
3619
|
+
const error = settlement.tag === "threw"
|
|
3620
|
+
? settlement.error
|
|
3621
|
+
: Object.assign(new Error("the task ended while this question was still out"), { name: "AbortError" });
|
|
3622
|
+
bindOutcome({ kind: "failed", error }, retainedQuestionsHash);
|
|
3623
|
+
return {
|
|
3624
|
+
kind: "delivery_failure",
|
|
3625
|
+
code: settlement.tag === "aborted" || abortController.signal.aborted ? "question.aborted" : "question.human_channel_failed",
|
|
3626
|
+
presentedInput: retained,
|
|
3627
|
+
};
|
|
3628
|
+
}
|
|
3629
|
+
catch (err) {
|
|
3630
|
+
bindOutcome({ kind: "failed", error: err }, retainedQuestionsHash);
|
|
3631
|
+
return { kind: "delivery_failure", code: "question.human_channel_failed", presentedInput: retained };
|
|
3632
|
+
}
|
|
3633
|
+
};
|
|
3634
|
+
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
|
|
3635
|
+
? async (req, postHookArgs, safety, liveFaceUnavailable) => {
|
|
3636
|
+
const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : onAsk !== undefined;
|
|
3637
|
+
if (syncFirstEligible &&
|
|
3331
3638
|
runtimeCaps?.forceDurableGate !== true &&
|
|
3332
|
-
|
|
3333
|
-
req.toolName !== ASK_USER_QUESTION_TOOL_NAME &&
|
|
3639
|
+
liveFaceUnavailable !== true &&
|
|
3334
3640
|
!inheritedUnavailableAsks.has(req.toolCallId)) {
|
|
3335
3641
|
return undefined;
|
|
3336
3642
|
}
|
|
@@ -3519,9 +3825,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3519
3825
|
onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: spec.taskId ?? sessionId, site: f.site, message: f.error.message, ts: Date.now() })),
|
|
3520
3826
|
event: e,
|
|
3521
3827
|
preToolUse: hooks?.preToolUse,
|
|
3828
|
+
...(hookEnvFace !== undefined ? { hookEnv: hookEnvFace } : {}),
|
|
3522
3829
|
adjudicate,
|
|
3523
3830
|
resolveAsk: resolveAskBound,
|
|
3524
3831
|
suspendAsk,
|
|
3832
|
+
resolveContentAsk,
|
|
3525
3833
|
egress: egressTools.has(e.toolName),
|
|
3526
3834
|
irreversibility: irreversibilityTier.get(e.toolName),
|
|
3527
3835
|
reversibilityProbe: reversibilityProbes.get(e.toolName),
|
|
@@ -3574,7 +3882,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3574
3882
|
isInterrupt: abortController.signal.aborted || spec.signal?.aborted === true,
|
|
3575
3883
|
content: e.content.map((c) => ({ ...c })),
|
|
3576
3884
|
details: clonedDetails,
|
|
3577
|
-
}, { toolCallId: e.toolCallId, toolName: e.toolName });
|
|
3885
|
+
}, { toolCallId: e.toolCallId, toolName: e.toolName, ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}) });
|
|
3578
3886
|
if (patch?.additionalContext) {
|
|
3579
3887
|
content = [...content, { type: "text", text: formatHookFeedback(patch.additionalContext) }];
|
|
3580
3888
|
changed = true;
|
|
@@ -3582,7 +3890,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3582
3890
|
}
|
|
3583
3891
|
}
|
|
3584
3892
|
else if (hooks?.postToolUse) {
|
|
3585
|
-
const patch = await hooks.postToolUse(e.toolName, e.input, { content: e.content, details: e.details, isError: e.isError }, { toolCallId: e.toolCallId, toolName: e.toolName });
|
|
3893
|
+
const patch = await hooks.postToolUse(e.toolName, e.input, { content: e.content, details: e.details, isError: e.isError }, { toolCallId: e.toolCallId, toolName: e.toolName, ...(hookEnvFace !== undefined ? { env: hookEnvFace } : {}) });
|
|
3586
3894
|
if (patch?.updatedOutput) {
|
|
3587
3895
|
content = patch.updatedOutput;
|
|
3588
3896
|
changed = true;
|
|
@@ -3831,7 +4139,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3831
4139
|
: undefined;
|
|
3832
4140
|
overheadState.promptChars = systemPrompt.length;
|
|
3833
4141
|
const preparedHolder = {};
|
|
3834
|
-
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
4142
|
+
const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3835
4143
|
const prepared = buildPrepared();
|
|
3836
4144
|
preparedHolder.current = prepared;
|
|
3837
4145
|
return prepared;
|