@sema-agent/core 7.4.0 → 7.5.1
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 +69 -0
- package/dist/agents/cascade.d.ts +2 -1
- package/dist/agents/peer-notices.d.ts +11 -1
- package/dist/agents/peer-session-drain.js +2 -0
- package/dist/agents/verify.d.ts +2 -1
- package/dist/core/ask-origin.d.ts +130 -0
- package/dist/core/ask-origin.js +35 -0
- package/dist/core/auto-mode-arming.d.ts +40 -1
- package/dist/core/auto-mode-arming.js +51 -3
- package/dist/core/auto-mode.d.ts +87 -10
- package/dist/core/auto-mode.js +34 -6
- package/dist/core/checkpoint-store.d.ts +41 -6
- package/dist/core/checkpoint-store.js +8 -0
- package/dist/core/hooks.d.ts +63 -19
- package/dist/core/hooks.js +37 -16
- package/dist/core/mcp.d.ts +47 -55
- package/dist/core/mcp.js +89 -31
- package/dist/core/park-selfcheck.js +3 -0
- package/dist/core/permission-rule-consent.d.ts +2 -11
- package/dist/core/permission-rule-consent.js +13 -62
- package/dist/core/permission-rule-org.d.ts +70 -54
- package/dist/core/permission-rule-org.js +47 -61
- package/dist/core/permission-rule-provider.d.ts +162 -0
- package/dist/core/permission-rule-provider.js +148 -0
- package/dist/core/permission-rule-session.d.ts +23 -19
- package/dist/core/permission-rule-session.js +5 -5
- package/dist/core/permission-rule-store.d.ts +46 -21
- package/dist/core/permission-rule-store.js +13 -6
- package/dist/core/permission-rule-sync.d.ts +2 -1
- package/dist/core/permission-rule-sync.js +11 -0
- package/dist/core/runner/assemble-result.d.ts +3 -2
- package/dist/core/runner/checkpoint-scope.d.ts +32 -0
- package/dist/core/runner/checkpoint-scope.js +4 -0
- package/dist/core/runner/contracts.d.ts +1878 -0
- package/dist/core/runner/contracts.js +1 -0
- package/dist/core/runner/denial-limit-arms.d.ts +57 -31
- package/dist/core/runner/denial-limit-arms.js +42 -17
- package/dist/core/runner/derived-route-fallback.d.ts +34 -0
- package/dist/core/runner/derived-route-fallback.js +16 -0
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
- package/dist/core/runner/prepare-announce-once.d.ts +83 -0
- package/dist/core/runner/prepare-announce-once.js +105 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
- package/dist/core/runner/prepare-config-doors.d.ts +2 -10
- package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
- package/dist/core/runner/prepare-defer-classify.js +107 -0
- package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
- package/dist/core/runner/prepare-delegation-surface.js +144 -0
- package/dist/core/runner/prepare-execution-env.d.ts +54 -0
- package/dist/core/runner/prepare-execution-env.js +86 -0
- package/dist/core/runner/prepare-file-history.d.ts +95 -0
- package/dist/core/runner/prepare-file-history.js +383 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +6 -8
- package/dist/core/runner/prepare-hands-readface.js +3 -3
- package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
- package/dist/core/runner/prepare-inherited-gate.js +266 -0
- package/dist/core/runner/prepare-listings.d.ts +77 -0
- package/dist/core/runner/prepare-listings.js +76 -0
- package/dist/core/runner/prepare-lsp.d.ts +55 -0
- package/dist/core/runner/prepare-lsp.js +27 -0
- package/dist/core/runner/prepare-memory.d.ts +1 -1
- package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
- package/dist/core/runner/prepare-offload-wrappers.js +45 -0
- package/dist/core/runner/prepare-permission-rules.d.ts +132 -0
- package/dist/core/runner/prepare-permission-rules.js +140 -0
- package/dist/core/runner/prepare-project-context.d.ts +131 -0
- package/dist/core/runner/prepare-project-context.js +150 -0
- package/dist/core/runner/prepare-prompt-inputs.d.ts +138 -0
- package/dist/core/runner/prepare-prompt-inputs.js +141 -0
- package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
- package/dist/core/runner/prepare-protocol-tools.js +182 -0
- package/dist/core/runner/prepare-question-face.d.ts +119 -0
- package/dist/core/runner/prepare-question-face.js +83 -0
- package/dist/core/runner/prepare-run-refs.d.ts +89 -0
- package/dist/core/runner/prepare-run-refs.js +39 -0
- package/dist/core/runner/prepare-safety-scan.d.ts +3 -2
- package/dist/core/runner/prepare-task.d.ts +11 -1815
- package/dist/core/runner/prepare-task.js +138 -2542
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
- package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
- package/dist/core/runner/prepare-wiring-manifest.d.ts +184 -0
- package/dist/core/runner/prepare-wiring-manifest.js +240 -0
- package/dist/core/runner/prepare-workspace-restore.d.ts +1 -27
- package/dist/core/runner/prepare-workspace-restore.js +1 -22
- package/dist/core/runner/rollback-stack.d.ts +32 -0
- package/dist/core/runner/rollback-stack.js +30 -0
- package/dist/core/runner/runtask.d.ts +11 -2
- package/dist/core/runner/runtask.js +27 -9
- package/dist/core/runner/workspace-path.d.ts +33 -0
- package/dist/core/runner/workspace-path.js +22 -0
- package/dist/core/sensitive-path-policy.d.ts +16 -0
- package/dist/core/sensitive-path-policy.js +1 -1
- package/dist/core/tool-policy.d.ts +57 -9
- package/dist/core/tool-policy.js +11 -0
- package/dist/core/types.d.ts +63 -51
- package/dist/core/wiring-manifest.d.ts +40 -3
- package/dist/core/wiring-manifest.js +4 -3
- package/dist/core/write-protect.d.ts +13 -2
- package/dist/core/write-protect.js +58 -29
- package/dist/engine/harness/types.d.ts +38 -16
- package/dist/engine/harness/types.js +25 -1
- package/dist/engine/session/session.d.ts +3 -11
- package/dist/index.d.ts +10 -6
- package/dist/index.js +9 -5
- package/dist/internal/harness.d.ts +1 -0
- package/dist/stores/file/adoption/adopt.d.ts +1 -1
- package/dist/stores/file/adoption/marker.d.ts +1 -1
- package/dist/stores/file/permission-rule-adopt.js +4 -3
- package/dist/stores/file/permission-rule-store.d.ts +65 -25
- package/dist/stores/file/permission-rule-store.js +215 -37
- package/dist/stores/file/task-list-store.d.ts +1 -1
- package/dist/tools/fs/read-face.d.ts +1 -1
- package/package.json +8 -2
- package/test/export-surface.snapshot.json +76 -28
|
@@ -1,75 +1,78 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { realpathSync } from "node:fs";
|
|
3
3
|
import { resolve as resolveFsPath } from "node:path";
|
|
4
|
-
import { AgentHarness, DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens,
|
|
4
|
+
import { AgentHarness, DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, summaryOutputBudgetTokens } from "../../internal/harness.js";
|
|
5
5
|
const PROMPT_HASH_SALT = randomBytes(16);
|
|
6
6
|
import { sanitizeCompactionSettings } from "../auto-compaction.js";
|
|
7
7
|
import { projectStaleToolResults, resolveStaleToolResultOffload } from "./compaction-call-options.js";
|
|
8
|
-
import {
|
|
9
|
-
import { attachRebuiltDenialTrackers, createDenialLimitStop, headlessDenyAtFold, headlessDenyAtRecheck, judgeInheritedClassifier, settleDenialLimitFallback } from "./denial-limit-arms.js";
|
|
10
|
-
import { autoModeArmingRecipeOf } from "../auto-mode-arming.js";
|
|
11
|
-
import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
|
|
12
|
-
import { resolveTaskModel } from "../roles.js";
|
|
8
|
+
import { gateAskCarry, inheritedAskCarry, headlessDenyAtFold, headlessDenyAtRecheck, judgeInheritedClassifier, settleDenialLimitFallback } from "./denial-limit-arms.js";
|
|
13
9
|
import { primaryActivityArg } from "../arg-summary.js";
|
|
14
|
-
import { materializeMcpTools } from "../mcp.js";
|
|
15
|
-
import { materializeA2aTools } from "../a2a.js";
|
|
16
|
-
import { Type } from "typebox";
|
|
17
10
|
import { Value } from "typebox/value";
|
|
18
11
|
import { uuidv7 } from "../../engine/session/uuid.js";
|
|
19
12
|
import { brainToRuntime } from "../runtime.js";
|
|
20
13
|
import { hasSessionFork } from "../session.js";
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
const DIRECTORY_RULE_TOOL = "Read";
|
|
28
|
-
import { directoryRuleAdmits, eligiblePersisted, findAdmittingRule, lexicalNormalAbsolutePathOf, segmentCoverageOf, suggestRulesForCommand } from "../permission-rule-model.js";
|
|
29
|
-
import { normalizePersistedRule } from "../permission-rule-store.js";
|
|
30
|
-
import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
31
|
-
import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentListingDelta } from "./turn-attachments.js";
|
|
14
|
+
import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
|
|
15
|
+
import { askApproverIdentity, isLiveApproverSeat, carriesBidiControls, checkToolPolicyProjection, combinePolicies, constraintChainDigest, constraintChainEntryOfLayer, isApprovalSettledBy, isAskDenyResolution, screenApproverAttribution, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
|
|
16
|
+
import { suggestRulesForCommand } from "../permission-rule-model.js";
|
|
17
|
+
import { PERSISTED_RULE_TOOL, inheritedAskRuleEvidence, createPermissionRuleLanes } from "./prepare-permission-rules.js";
|
|
18
|
+
import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
|
|
19
|
+
import { CHANGED_FILES_MTIME_EPS_MS } from "./turn-attachments.js";
|
|
32
20
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
21
|
+
import { loadAnnounceOnceLedger, serializeAnnouncedListings } from "./prepare-announce-once.js";
|
|
33
22
|
import { isValidReminderMark, mintReminderMark, reminderMarkDeclaration } from "../reminder-mint.js";
|
|
34
23
|
import { policyAskClassOf } from "../ask-class.js";
|
|
35
24
|
import { emitTrace } from "../trace.js";
|
|
36
25
|
import { createSessionRulePolicy, PATH_CONFINABLE_WRITE_TOOLS } from "./session-rule-policy.js";
|
|
37
|
-
import {
|
|
26
|
+
import { askCarryRowMembers, cloneObserverInput, formatHookFeedback, persistedRuleMandateOf, resolveHookTimeoutMs, runHookSeat, hookSeatExpiredError, runToolGate } from "../hooks.js";
|
|
38
27
|
import { createWriteProtectionCheck } from "../write-protect.js";
|
|
39
|
-
import { orgRuleVerdictFor } from "../permission-rule-org.js";
|
|
40
28
|
import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
|
|
41
29
|
import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
|
|
42
|
-
import { adjudicateDerivedRoute, fallbackToPrimaryNotice, sameRouteIdentity } from "../../brain/route-adjudicator.js";
|
|
43
30
|
import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
|
|
44
|
-
import { defineTool, isDefineToolProduct } from "../tools.js";
|
|
45
31
|
import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
|
|
46
32
|
import { protocolOf } from "../protocol-table.js";
|
|
47
33
|
import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "../permission-rules.js";
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createOutputTool, createReportBlockedTool, createReportFindingsTool, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
|
|
34
|
+
import { createOffloadPersist, InMemoryToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, } from "../tool-result-store.js";
|
|
35
|
+
import { OUTPUT_TOOL_NAME, createOutputTool } from "./synthetic-tools.js";
|
|
51
36
|
import { compileOutputSchema } from "./strict-output-schema.js";
|
|
52
|
-
import { TOOL_SEARCH_NAME, buildDeferredRegistry, classifyDeferred, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, staticSchemaRenderable, } from "./tool-disclosure.js";
|
|
53
|
-
import { createSharedMemoryTools } from "../shared-memory/tools.js";
|
|
54
|
-
import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
|
|
55
37
|
import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
|
|
56
38
|
import { classifyToolContentOrigin, contentOriginPollutes, delegationCallIsExternal } from "../memory-engine/content-origin.js";
|
|
57
39
|
import { narrowContentSafety, readCardAttestation } from "../memory-engine/delegation-provenance.js";
|
|
58
40
|
import { recordSyncUnattestable, sessionSettlements } from "../memory-engine/delegation-settlement.js";
|
|
59
|
-
import {
|
|
60
|
-
import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, complianceCallDenial, resolveComplianceDenies } from "../compliance.js";
|
|
41
|
+
import { complianceCallDenial } from "../compliance.js";
|
|
61
42
|
import { foldAdmissionFreeze } from "../memory-admission.js";
|
|
62
43
|
import { prepareMemory } from "./prepare-memory.js";
|
|
44
|
+
import { prepareDeferClassify } from "./prepare-defer-classify.js";
|
|
45
|
+
import { prepareListings } from "./prepare-listings.js";
|
|
46
|
+
import { preparePromptInputs } from "./prepare-prompt-inputs.js";
|
|
47
|
+
import { hasConversationContent, prepareProjectContext } from "./prepare-project-context.js";
|
|
63
48
|
import { limitConfigError, prepareConfigDoors } from "./prepare-config-doors.js";
|
|
64
49
|
import { NAMESPACED_NAME_SHAPES, prepareSafetyScan } from "./prepare-safety-scan.js";
|
|
65
50
|
import { prepareAcquireReconcile } from "./prepare-acquire-reconcile.js";
|
|
66
51
|
import { prepareHandsMount, resolveHandsLessReadFace } from "./prepare-hands-readface.js";
|
|
67
|
-
import { createEditedFilesLedger } from "./edited-files-ledger.js";
|
|
68
52
|
import { prepareWorkspaceRestore, remoteEnvFailureNote, restoreWorkspaceWithRetry } from "./prepare-workspace-restore.js";
|
|
69
|
-
import {
|
|
70
|
-
import {
|
|
53
|
+
import { prepareOffloadWrappers } from "./prepare-offload-wrappers.js";
|
|
54
|
+
import { prepareFileHistory } from "./prepare-file-history.js";
|
|
55
|
+
import { prepareExecutionEnv } from "./prepare-execution-env.js";
|
|
56
|
+
import { prepareRunRefs } from "./prepare-run-refs.js";
|
|
57
|
+
import { prepareInheritedGate } from "./prepare-inherited-gate.js";
|
|
58
|
+
import { prepareCapsAndWorkflow } from "./prepare-caps-and-workflow.js";
|
|
59
|
+
import { prepareDelegationSurface } from "./prepare-delegation-surface.js";
|
|
60
|
+
import { prepareToolDisclosureMount } from "./prepare-tool-disclosure-mount.js";
|
|
61
|
+
import { prepareWiringManifest } from "./prepare-wiring-manifest.js";
|
|
62
|
+
import { checkpointScopeOf } from "./checkpoint-scope.js";
|
|
63
|
+
export { DEFAULT_IRREVERSIBLE_SCOPE, checkpointScopeOf } from "./checkpoint-scope.js";
|
|
64
|
+
export { mcpManifestEntries } from "./prepare-wiring-manifest.js";
|
|
65
|
+
import { derivedRouteFallsBack } from "./derived-route-fallback.js";
|
|
66
|
+
import { prepareProtocolTools } from "./prepare-protocol-tools.js";
|
|
67
|
+
import { CONTENT_ASK_BINDING_CAP, prepareQuestionFace } from "./prepare-question-face.js";
|
|
68
|
+
import { prepareLsp } from "./prepare-lsp.js";
|
|
69
|
+
import { createRollbackStack } from "./rollback-stack.js";
|
|
70
|
+
export { __resetMaterializeEnvAnnouncements } from "./prepare-tool-disclosure-mount.js";
|
|
71
|
+
export { fileHistoryFilesystemIdentity, resolveFileHistoryScope } from "./prepare-file-history.js";
|
|
72
|
+
import { defaultPromptProvider, buildEnvironmentContext } from "../../prompts/default.js";
|
|
73
|
+
import { applyGitFrameGuard } from "./git-status-frame.js";
|
|
71
74
|
import { assemblePrompt } from "../../prompt-assembly/assemble.js";
|
|
72
|
-
import { auditToolCollisions,
|
|
75
|
+
import { auditToolCollisions, projectToolManifest } from "../../prompt-assembly/tool-catalog.js";
|
|
73
76
|
import { resolveEpochAgainstBundled } from "../../prompt-assembly/epoch.js";
|
|
74
77
|
import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
|
|
75
78
|
import { buildTurnPromptSnapshot } from "../../prompt-assembly/turn-snapshot.js";
|
|
@@ -78,56 +81,22 @@ import { clearStaleToolResults, createClearedProjectionLedger, dropEmptyFailureA
|
|
|
78
81
|
import { capAggregateToolResults } from "../tool-result-budget.js";
|
|
79
82
|
import { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "../media-byte-cap.js";
|
|
80
83
|
import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
|
|
81
|
-
import {
|
|
82
|
-
import { hasDestroy, isIsolated, isRemoteExecutionEnv, isSuspendable, missingRestoreSurface, RemoteExecutionError } from "../remote-env.js";
|
|
84
|
+
import { isIsolated, isRemoteExecutionEnv, isSuspendable, RemoteExecutionError } from "../remote-env.js";
|
|
83
85
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
84
86
|
import { hasBackgroundShell, sweepBackgroundShells } from "../background-shell.js";
|
|
85
|
-
import {
|
|
86
|
-
import {
|
|
87
|
-
import {
|
|
88
|
-
import { announcePeerLaneMount, bindPeerLaneDrain, listAgentsMountable, mountListAgents, peerLaneSendMessageSeats } from "../../agents/peer-session-drain.js";
|
|
89
|
-
import { CROSS_SESSION_CLASSIFIER_RULE } from "../../agents/cross-session-envelope.js";
|
|
90
|
-
import { NodeExecutionEnv } from "../../engine/execution-env/node-execution-env.js";
|
|
91
|
-
import { applyCompactionToReadFileState, isReadDedupStubResult, FULL_SHELL_CONTRACT_ID, HAND_TOOL_EFFECTS } from "../../tools/fs/index.js";
|
|
87
|
+
import { defaultTaskRegistry } from "../task-registry.js";
|
|
88
|
+
import { announcePeerLaneMount } from "../../agents/peer-session-drain.js";
|
|
89
|
+
import { applyCompactionToReadFileState, isReadDedupStubResult } from "../../tools/fs/index.js";
|
|
92
90
|
import { decodeTextBytes } from "../../tools/fs/encoding.js";
|
|
93
|
-
import { ASK_USER_QUESTION_TOOL_NAME,
|
|
91
|
+
import { ASK_USER_QUESTION_TOOL_NAME, classifyQuestionOutcome, validateAskQuestions, } from "../ask-question.js";
|
|
94
92
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
95
|
-
import {
|
|
96
|
-
import {
|
|
97
|
-
import { createRunWorkflowTool, RUN_WORKFLOW_TOOL_NAME } from "../../orchestration/run-workflow-tool.js";
|
|
98
|
-
import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
99
|
-
import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
|
|
93
|
+
import { PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
|
|
94
|
+
import { selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
|
|
100
95
|
import { fileArgPath, resolveKey } from "../../tools/fs/safety.js";
|
|
101
96
|
import { BINDING_CHECKPOINT_VERSION, mintCheckpointId, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, F012_CHECKPOINT_VERSION, FACE_CHECKPOINT_VERSION, REAL_APPROVAL_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, encodeAtFidelity, remainingBudgetMicroUsd, resolveCheckpointStore, resolveDeclaredFidelity, samePlainValue, } from "../checkpoint-store.js";
|
|
102
97
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
103
|
-
import { countElicitOptIns, deriveWiringManifest, resolveAskSeamForm, resolveDeclaredDurability, resolveElicitSeam, resolveQuestionSeam, resolveSubagentTranscriptTier } from "../wiring-manifest.js";
|
|
104
98
|
import { durableParkGapFor } from "../park-selfcheck.js";
|
|
105
|
-
import { GLOBAL_USAGE_KEY, usageRetryAfterMs, windowsGovernCost } from "../usage-window-store.js";
|
|
106
99
|
import { deliverEngineNotice } from "../types.js";
|
|
107
|
-
import { resolveTrackKey } from "../file-history-store.js";
|
|
108
|
-
let announcedMaterializeEnvBySink = new WeakMap();
|
|
109
|
-
const announcedMaterializeEnvConsole = new Set();
|
|
110
|
-
function materializeEnvLedger(onNotice) {
|
|
111
|
-
if (typeof onNotice !== "function")
|
|
112
|
-
return announcedMaterializeEnvConsole;
|
|
113
|
-
let lines = announcedMaterializeEnvBySink.get(onNotice);
|
|
114
|
-
if (lines === undefined) {
|
|
115
|
-
lines = new Set();
|
|
116
|
-
announcedMaterializeEnvBySink.set(onNotice, lines);
|
|
117
|
-
}
|
|
118
|
-
return lines;
|
|
119
|
-
}
|
|
120
|
-
export function __resetMaterializeEnvAnnouncements() {
|
|
121
|
-
announcedMaterializeEnvBySink = new WeakMap();
|
|
122
|
-
announcedMaterializeEnvConsole.clear();
|
|
123
|
-
}
|
|
124
|
-
function emitMaterializeEnvNotice(onNotice, message, detail) {
|
|
125
|
-
const ledger = materializeEnvLedger(onNotice);
|
|
126
|
-
if (ledger.has(message))
|
|
127
|
-
return;
|
|
128
|
-
ledger.add(message);
|
|
129
|
-
deliverEngineNotice(onNotice, { code: "config.materialize_env_discarded", message, detail });
|
|
130
|
-
}
|
|
131
100
|
function warnCompactionWindowHazard(tracer, spec, model, compModel, hostTaskId) {
|
|
132
101
|
if (compModel === undefined)
|
|
133
102
|
return;
|
|
@@ -247,65 +216,9 @@ function announceDeclaredMcpContentClasses(args) {
|
|
|
247
216
|
}
|
|
248
217
|
export { __resetReadFaceClampAnnouncement } from "./prepare-hands-readface.js";
|
|
249
218
|
const DEFAULT_MAX_SUSPENDS = 5;
|
|
250
|
-
const ULTRA_REASONING_TIERS = new Set(["xhigh", "max"]);
|
|
251
219
|
const DEFAULT_RESOURCE_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
252
220
|
export const ENV_LIFETIME_SUSPEND_MARGIN_MS = 60_000;
|
|
253
221
|
export const USAGE_WINDOW_REAP_MARGIN_MS = 60 * 60 * 1000;
|
|
254
|
-
function buildUsageGovernance(windows, deps, principal, sessionId) {
|
|
255
|
-
if (windows === undefined || windows.length === 0)
|
|
256
|
-
return undefined;
|
|
257
|
-
const store = deps.usageWindowStore;
|
|
258
|
-
if (store === undefined) {
|
|
259
|
-
deps.onError?.(new Error("RunnerDeps.usageWindows is set but INACTIVE: no `usageWindowStore` is wired, so cross-task usage cannot be counted and no window can be enforced. Wire InMemoryUsageWindowStore (process-local) or FileUsageWindowStore (restart-surviving)."), { phase: "config", sessionId });
|
|
260
|
-
return undefined;
|
|
261
|
-
}
|
|
262
|
-
const key = principal || GLOBAL_USAGE_KEY;
|
|
263
|
-
const governsCost = windowsGovernCost(windows);
|
|
264
|
-
let chargedTokens = 0;
|
|
265
|
-
let chargedCostMicroUsd = 0;
|
|
266
|
-
let announcedCostGap = false;
|
|
267
|
-
let recordedUnpricedGap = false;
|
|
268
|
-
return {
|
|
269
|
-
key,
|
|
270
|
-
governsCost,
|
|
271
|
-
async check(now) {
|
|
272
|
-
const readings = await store.read(key, windows, now);
|
|
273
|
-
if (!announcedCostGap && readings.some((r) => r.costUnknown === true)) {
|
|
274
|
-
announcedCostGap = true;
|
|
275
|
-
deps.onError?.(new Error(`the usage window for ledger key ${JSON.stringify(key)} holds spend that nothing could price, so its maxCostUsd ceiling is being evaluated against a LOWER BOUND until that charge ages out of the window. Price every model this deployment can reach (including degrade targets and the compaction model).`), { phase: "config", sessionId });
|
|
276
|
-
}
|
|
277
|
-
return usageRetryAfterMs(readings, windows);
|
|
278
|
-
},
|
|
279
|
-
async commit(cumulativeTokens, cumulativeCostMicroUsd, now) {
|
|
280
|
-
if (!governsCost) {
|
|
281
|
-
const delta = cumulativeTokens - chargedTokens;
|
|
282
|
-
if (delta <= 0)
|
|
283
|
-
return;
|
|
284
|
-
await store.charge(key, delta, now, windows);
|
|
285
|
-
chargedTokens = cumulativeTokens;
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
if (cumulativeCostMicroUsd === undefined) {
|
|
289
|
-
const tokenDelta = cumulativeTokens - chargedTokens;
|
|
290
|
-
if (tokenDelta > 0 || !recordedUnpricedGap) {
|
|
291
|
-
await store.charge(key, Math.max(0, tokenDelta), now, windows, null);
|
|
292
|
-
chargedTokens = Math.max(chargedTokens, cumulativeTokens);
|
|
293
|
-
recordedUnpricedGap = true;
|
|
294
|
-
}
|
|
295
|
-
const e = new Error("a deployment usage window declares maxCostUsd, but this run's spend has NO cost figure (RB-368 unpriced: a model served without a RunnerDeps.pricing entry or a Model.cost declaration). Refused rather than charged a fabricated 0 — the money ceiling would have silently stopped applying. Price every model this run can reach, or drop maxCostUsd from the window.");
|
|
296
|
-
e.code = "config.usage_window_unpriced";
|
|
297
|
-
throw e;
|
|
298
|
-
}
|
|
299
|
-
const tokenDelta = cumulativeTokens - chargedTokens;
|
|
300
|
-
const costDelta = cumulativeCostMicroUsd - chargedCostMicroUsd;
|
|
301
|
-
if (tokenDelta <= 0 && costDelta <= 0)
|
|
302
|
-
return;
|
|
303
|
-
await store.charge(key, Math.max(0, tokenDelta), now, windows, Math.max(0, costDelta));
|
|
304
|
-
chargedTokens = cumulativeTokens;
|
|
305
|
-
chargedCostMicroUsd = cumulativeCostMicroUsd;
|
|
306
|
-
},
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
222
|
export function resolveEnvLifetimeExpiry(env, observedAt) {
|
|
310
223
|
const lifetimeMs = env.lifetimeMs;
|
|
311
224
|
if (lifetimeMs === undefined)
|
|
@@ -322,22 +235,14 @@ export function resolveEnvLifetimeExpiry(env, observedAt) {
|
|
|
322
235
|
return { unanchored: true };
|
|
323
236
|
return { expiresAt: anchor + lifetimeMs };
|
|
324
237
|
}
|
|
325
|
-
function isLiveApproverSeat(onAsk) {
|
|
326
|
-
return typeof onAsk === "function";
|
|
327
|
-
}
|
|
328
238
|
const DEFAULT_UNATTENDED_APPROVAL_TTL_MS = 30 * 24 * 60 * 60 * 1000;
|
|
329
239
|
function sanitizedTtlMs(ttlMs) {
|
|
330
240
|
if (ttlMs === undefined)
|
|
331
241
|
return undefined;
|
|
332
242
|
return Number.isFinite(ttlMs) && ttlMs > 0 ? ttlMs : DEFAULT_RESOURCE_TTL_MS;
|
|
333
243
|
}
|
|
334
|
-
const ungatedWarnedShapes = new WeakMap();
|
|
335
244
|
const advisedPolicyNames = new WeakMap();
|
|
336
245
|
const ADVISED_KEYS_CAP = 64;
|
|
337
|
-
export const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
338
|
-
export function checkpointScopeOf(spec) {
|
|
339
|
-
return spec.durableApproval?.scope || spec.principal || DEFAULT_IRREVERSIBLE_SCOPE;
|
|
340
|
-
}
|
|
341
246
|
class ParkRefusal extends Error {
|
|
342
247
|
constructor(message, options) {
|
|
343
248
|
super(message, options);
|
|
@@ -346,7 +251,7 @@ class ParkRefusal extends Error {
|
|
|
346
251
|
}
|
|
347
252
|
export { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
348
253
|
export { isFableFamilyModelId, resolveAttachmentsConfig, resolveModelPromptTraits, resolveTaskLimits } from "./prepare-config-doors.js";
|
|
349
|
-
export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./
|
|
254
|
+
export { rebaseWorkspacePath, rebaseWorkspacePathAcross } from "./workspace-path.js";
|
|
350
255
|
function buildMicroCompactState(deps, model, sessionId, runId, offloadStore, knob) {
|
|
351
256
|
const triggerWindow = resolveTriggerWindow(model);
|
|
352
257
|
if (triggerWindow.clamped) {
|
|
@@ -457,226 +362,6 @@ export async function runGuardChain(args) {
|
|
|
457
362
|
const t = applyGuardTrim(working, anchoredWorking);
|
|
458
363
|
return { working, trimmed: t.trimmed, trimDroppedMessages: t.dropped };
|
|
459
364
|
}
|
|
460
|
-
function restoredAbsPathsOf(restored, root) {
|
|
461
|
-
const out = [];
|
|
462
|
-
for (const key of [...restored.filesChanged, ...restored.failed.map((f) => f.path)]) {
|
|
463
|
-
const abs = resolveTrackKey(root, key);
|
|
464
|
-
if (abs.ok)
|
|
465
|
-
out.push(abs.abs);
|
|
466
|
-
}
|
|
467
|
-
return out;
|
|
468
|
-
}
|
|
469
|
-
function resolveWiredFileHistoryStore(candidate) {
|
|
470
|
-
if (candidate === undefined)
|
|
471
|
-
return undefined;
|
|
472
|
-
const verbs = ["trackEdit", "annulTrack", "snapshot", "restore", "canRestore", "reap", "adoptScope"];
|
|
473
|
-
const verbOf = (v) => {
|
|
474
|
-
try {
|
|
475
|
-
return candidate[v];
|
|
476
|
-
}
|
|
477
|
-
catch {
|
|
478
|
-
return undefined;
|
|
479
|
-
}
|
|
480
|
-
};
|
|
481
|
-
const probeable = (typeof candidate === "object" && candidate !== null) || typeof candidate === "function";
|
|
482
|
-
const missing = probeable ? verbs.filter((v) => typeof verbOf(v) !== "function") : [...verbs];
|
|
483
|
-
if (missing.length > 0) {
|
|
484
|
-
const shape = candidate === null ? "null" : probeable ? "a value that is not a FileHistoryStore" : `a ${typeof candidate}`;
|
|
485
|
-
const e = new Error(`RunnerDeps.fileHistoryStore was wired with ${shape} — missing contract verbs: ${missing.join(", ")}. Refusing prepare rather than half-wiring the rewind seat (a present-but-unusable store makes every Edit/Write/NotebookEdit fail its first-touch backup while restore requests report "no store wired"). Omit the key to run without file history.`);
|
|
486
|
-
e.code = "config.invalid_file_history_store";
|
|
487
|
-
throw e;
|
|
488
|
-
}
|
|
489
|
-
return candidate;
|
|
490
|
-
}
|
|
491
|
-
const LEGACY_PROBE_TIMEOUT_MS = 2_000;
|
|
492
|
-
const LEGACY_PROBE_TIMED_OUT = Symbol("legacy-rewind-probe-timeout");
|
|
493
|
-
async function legacyRewindEpochTail(deps, sessionId, entryId, form) {
|
|
494
|
-
const probe = deps.legacyRewindBoundaryProbe;
|
|
495
|
-
let verdict;
|
|
496
|
-
if (probe !== undefined) {
|
|
497
|
-
try {
|
|
498
|
-
let timer;
|
|
499
|
-
const timeout = new Promise((resolve) => {
|
|
500
|
-
timer = setTimeout(() => resolve(LEGACY_PROBE_TIMED_OUT), LEGACY_PROBE_TIMEOUT_MS);
|
|
501
|
-
});
|
|
502
|
-
try {
|
|
503
|
-
verdict = await Promise.race([Promise.resolve(probe({ sessionId, entryId })), timeout]);
|
|
504
|
-
}
|
|
505
|
-
finally {
|
|
506
|
-
clearTimeout(timer);
|
|
507
|
-
}
|
|
508
|
-
if (verdict === LEGACY_PROBE_TIMED_OUT) {
|
|
509
|
-
verdict = undefined;
|
|
510
|
-
try {
|
|
511
|
-
deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe did not answer within ${LEGACY_PROBE_TIMEOUT_MS}ms while explaining a rewind refusal — the refusal stands and names both possibilities`), { phase: "rewind", sessionId });
|
|
512
|
-
}
|
|
513
|
-
catch {
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
catch (err) {
|
|
518
|
-
verdict = undefined;
|
|
519
|
-
try {
|
|
520
|
-
deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe threw while explaining a rewind refusal (${err.message}) — the refusal stands and names both possibilities`), { phase: "rewind", sessionId });
|
|
521
|
-
}
|
|
522
|
-
catch {
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
if (verdict !== undefined && typeof verdict !== "boolean") {
|
|
526
|
-
try {
|
|
527
|
-
deps.onError?.(new Error(`RunnerDeps.legacyRewindBoundaryProbe answered ${JSON.stringify(String(verdict))} for entry "${entryId}" — only true/false/undefined are meaningful, so the refusal names both possibilities instead of trusting it`), { phase: "rewind", sessionId });
|
|
528
|
-
}
|
|
529
|
-
catch {
|
|
530
|
-
}
|
|
531
|
-
verdict = undefined;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
if (verdict === true) {
|
|
535
|
-
return `This deployment's legacy-epoch probe reports that entry "${entryId}" DOES carry a boundary from the retired whole-tree snapshot era, which this engine's per-edited-file seat does not restore — the two epochs are not interchangeable (a whole-tree manifest carries no tracked-set provenance, so replaying it here would converge files this session never edited). Restore it with the whole-tree-era tooling this deployment kept, or re-run the turns under the current engine.`;
|
|
536
|
-
}
|
|
537
|
-
if (verdict === false) {
|
|
538
|
-
return `This deployment's legacy-epoch probe reports no whole-tree-era boundary for entry "${entryId}" either, so the boundary simply does not exist: its turns ran without a RunnerDeps.fileHistoryStore wired, or it was reaped.`;
|
|
539
|
-
}
|
|
540
|
-
return form === "keyed"
|
|
541
|
-
? `Boundaries are keyed by a turn's INITIAL entry and exist only for turns that ran with a RunnerDeps.fileHistoryStore wired; a boundary from the retired whole-tree snapshot era is not restorable by this seat, and reaped boundaries have none.`
|
|
542
|
-
: `Either the boundary does not exist (its turns ran without a RunnerDeps.fileHistoryStore, or it was reaped), or it was produced in the retired whole-tree snapshot era, which this engine's per-edited-file seat does not restore.`;
|
|
543
|
-
}
|
|
544
|
-
const FILE_HISTORY_BOUNDARY_TIMEOUT_MS = 30_000;
|
|
545
|
-
const FILE_HISTORY_CARRY_MEMO = new WeakMap();
|
|
546
|
-
async function adoptForkedFileHistory(store, session, sessionId, onError) {
|
|
547
|
-
const memo = FILE_HISTORY_CARRY_MEMO.get(store) ?? { settled: new Set(), disclosed: new Set(), inFlight: new Map() };
|
|
548
|
-
FILE_HISTORY_CARRY_MEMO.set(store, memo);
|
|
549
|
-
const discloseOnce = (err) => {
|
|
550
|
-
if (memo.disclosed.has(sessionId))
|
|
551
|
-
return;
|
|
552
|
-
memo.disclosed.add(sessionId);
|
|
553
|
-
onError?.(err, { phase: "rewind", sessionId });
|
|
554
|
-
};
|
|
555
|
-
try {
|
|
556
|
-
if (memo.settled.has(sessionId))
|
|
557
|
-
return;
|
|
558
|
-
const forkedFrom = (await session.getMetadata()).forkedFrom;
|
|
559
|
-
if (forkedFrom === undefined || forkedFrom === sessionId)
|
|
560
|
-
return;
|
|
561
|
-
const shared = memo.inFlight.get(sessionId);
|
|
562
|
-
if (shared !== undefined)
|
|
563
|
-
return await shared;
|
|
564
|
-
if (memo.settled.has(sessionId))
|
|
565
|
-
return;
|
|
566
|
-
const attempt = store.adoptScope(forkedFrom, sessionId).then((carried) => {
|
|
567
|
-
if (carried.ok || carried.error.code === "conflict") {
|
|
568
|
-
memo.settled.add(sessionId);
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
discloseOnce(new Error(`file-history fork carry from session "${forkedFrom}" failed (${carried.error.code}): ${carried.error.message} — this forked session starts with an EMPTY rewind history, so boundaries recorded before the fork are not restorable from it (the run itself proceeds; the carry is retried on this session's next turn while its scope is still empty)`));
|
|
572
|
-
}, (err) => discloseOnce(err));
|
|
573
|
-
memo.inFlight.set(sessionId, attempt);
|
|
574
|
-
try {
|
|
575
|
-
await attempt;
|
|
576
|
-
}
|
|
577
|
-
finally {
|
|
578
|
-
memo.inFlight.delete(sessionId);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
catch (err) {
|
|
582
|
-
try {
|
|
583
|
-
discloseOnce(err);
|
|
584
|
-
}
|
|
585
|
-
catch {
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
function createFileHistoryBoundarySeat(opts) {
|
|
590
|
-
let inFlight;
|
|
591
|
-
let begun = false;
|
|
592
|
-
return {
|
|
593
|
-
begin(entryId) {
|
|
594
|
-
if (begun)
|
|
595
|
-
return;
|
|
596
|
-
begun = true;
|
|
597
|
-
const ac = new AbortController();
|
|
598
|
-
let timer;
|
|
599
|
-
const timeout = new Promise((resolve) => {
|
|
600
|
-
timer = setTimeout(() => {
|
|
601
|
-
ac.abort();
|
|
602
|
-
resolve({ ok: false });
|
|
603
|
-
}, FILE_HISTORY_BOUNDARY_TIMEOUT_MS);
|
|
604
|
-
});
|
|
605
|
-
let work;
|
|
606
|
-
try {
|
|
607
|
-
work = opts.store.snapshot(opts.sessionId, entryId, opts.env, opts.root, ac.signal);
|
|
608
|
-
}
|
|
609
|
-
catch (err) {
|
|
610
|
-
work = Promise.resolve({ ok: false, error: { code: "snapshot_failed", message: `history store threw synchronously: ${err.message}` } });
|
|
611
|
-
}
|
|
612
|
-
work.catch(() => { });
|
|
613
|
-
inFlight = Promise.race([
|
|
614
|
-
work.then((r) => (r.ok ? undefined : `boundary capture failed (${r.error.code}): ${r.error.message}`)),
|
|
615
|
-
timeout.then(() => `boundary capture timed out after ${FILE_HISTORY_BOUNDARY_TIMEOUT_MS / 1000}s (fenced — the attempt can never publish)`),
|
|
616
|
-
])
|
|
617
|
-
.then((failure) => {
|
|
618
|
-
if (failure !== undefined) {
|
|
619
|
-
try {
|
|
620
|
-
opts.onError?.(new Error(`file-history ${failure} — this turn has no boundary and cannot be rewound to (the turn itself proceeds)`), { phase: "rewind", sessionId: opts.sessionId });
|
|
621
|
-
}
|
|
622
|
-
catch {
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
})
|
|
626
|
-
.catch((err) => {
|
|
627
|
-
try {
|
|
628
|
-
opts.onError?.(err, { phase: "rewind", sessionId: opts.sessionId });
|
|
629
|
-
}
|
|
630
|
-
catch {
|
|
631
|
-
}
|
|
632
|
-
})
|
|
633
|
-
.finally(() => clearTimeout(timer));
|
|
634
|
-
},
|
|
635
|
-
async settle() {
|
|
636
|
-
if (inFlight !== undefined)
|
|
637
|
-
await inFlight;
|
|
638
|
-
},
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
async function resolveFileHistoryCoordinates(enabled, env, taskRoot, lineage, sessionId) {
|
|
642
|
-
if (!enabled)
|
|
643
|
-
return { historyRoot: taskRoot, historyScope: sessionId, historyFs: "" };
|
|
644
|
-
const canonRoot = await env.canonicalPath(taskRoot);
|
|
645
|
-
const historyRoot = canonRoot.ok ? canonRoot.value : taskRoot;
|
|
646
|
-
const historyFs = fileHistoryFilesystemIdentity(env);
|
|
647
|
-
return { historyRoot, historyScope: resolveFileHistoryScope(lineage, historyRoot, historyFs, sessionId), historyFs };
|
|
648
|
-
}
|
|
649
|
-
export function resolveFileHistoryScope(lineage, historyRoot, historyFs, sessionId) {
|
|
650
|
-
if (lineage === undefined || lineage.scope === "" || lineage.root !== historyRoot || lineage.fs !== historyFs)
|
|
651
|
-
return sessionId;
|
|
652
|
-
return lineage.scope;
|
|
653
|
-
}
|
|
654
|
-
const envFilesystemTokens = new WeakMap();
|
|
655
|
-
let envFilesystemSeq = 0;
|
|
656
|
-
export function fileHistoryFilesystemIdentity(env) {
|
|
657
|
-
if (isRemoteExecutionEnv(env)) {
|
|
658
|
-
try {
|
|
659
|
-
const h = env.workspaceHandle();
|
|
660
|
-
return JSON.stringify(["remote", h.provider, h.sandboxId, h.deviceId !== undefined && h.deviceId !== "" ? h.deviceId : null]);
|
|
661
|
-
}
|
|
662
|
-
catch {
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
else if (env.hostLocalPaths === true || (env.hostLocalPaths === undefined && env instanceof NodeExecutionEnv)) {
|
|
666
|
-
return "host";
|
|
667
|
-
}
|
|
668
|
-
let token = envFilesystemTokens.get(env);
|
|
669
|
-
if (token === undefined) {
|
|
670
|
-
token = `instance:${++envFilesystemSeq}`;
|
|
671
|
-
envFilesystemTokens.set(env, token);
|
|
672
|
-
}
|
|
673
|
-
return token;
|
|
674
|
-
}
|
|
675
|
-
function childScopeRewindRefusal(rootScope, target) {
|
|
676
|
-
const e = new Error(`rewind-files: this run is a delegated child recording its edits into the ROOT session "${rootScope}"'s file history, so restoring files to entry "${target}" is the root session's action (rewindFilesTo / resumeAt+restoreFiles on that session), not the child's — the files were NOT rewound`);
|
|
677
|
-
e.code = "rewind.child_scope_unsupported";
|
|
678
|
-
return e;
|
|
679
|
-
}
|
|
680
365
|
export function gatedCallIdOf(p) {
|
|
681
366
|
if (p.suspendRef.token !== undefined)
|
|
682
367
|
return p.suspendRef.gatedCallId;
|
|
@@ -701,9 +386,6 @@ function publishCommittedSuspend(refs, token, gate, scope, remoteHandle, checkpo
|
|
|
701
386
|
ref.restoreMode = remoteHandle.restoreMode === "park_only" ? "park_only" : "snapshot";
|
|
702
387
|
ref.scope = scope;
|
|
703
388
|
}
|
|
704
|
-
function hasConversationContent(branch) {
|
|
705
|
-
return branch.some((e) => e.type === "message" || e.type === "custom_message" || e.type === "compaction");
|
|
706
|
-
}
|
|
707
389
|
export function effectiveDelegationFacts(internals, seedIsDelegatedChild) {
|
|
708
390
|
const isDelegatedChild = internals?.isDelegatedChild !== undefined ? internals.isDelegatedChild === true : seedIsDelegatedChild === true;
|
|
709
391
|
return { isDelegatedChild, isNonForkChild: isDelegatedChild && internals?.insideFork !== true };
|
|
@@ -890,19 +572,6 @@ function createDenyObserverNotifier(hooks, hookTimeoutMs, signal, report) {
|
|
|
890
572
|
}
|
|
891
573
|
};
|
|
892
574
|
}
|
|
893
|
-
function mcpRevocationWiring(deps, runId) {
|
|
894
|
-
if (deps.mcpRevocations === undefined)
|
|
895
|
-
return undefined;
|
|
896
|
-
const ledger = deps.mcpRevocations;
|
|
897
|
-
return {
|
|
898
|
-
isRevoked: (name) => ledger.isRevoked(name),
|
|
899
|
-
onProbeFailure: (e) => deliverEngineNotice(deps.onNotice, {
|
|
900
|
-
code: "mcp.revocation_probe_failed",
|
|
901
|
-
message: `the mcpRevocations.isRevoked probe threw — MCP dispatch fails OPEN (no server treated as revoked) until the probe recovers: ${e instanceof Error ? e.message : String(e)}`,
|
|
902
|
-
detail: { message: e instanceof Error ? e.message : String(e), runId },
|
|
903
|
-
}),
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
575
|
async function forgetQuietly(sessions, sessionId) {
|
|
907
576
|
try {
|
|
908
577
|
if (sessions.forget)
|
|
@@ -937,12 +606,21 @@ function screenGateSettlement(result, settling) {
|
|
|
937
606
|
else
|
|
938
607
|
resolution = reportedResolution;
|
|
939
608
|
}
|
|
609
|
+
const reportedAutoDenied = result.autoDenied;
|
|
610
|
+
let autoDenied;
|
|
611
|
+
if (reportedAutoDenied === true) {
|
|
612
|
+
if (!settling)
|
|
613
|
+
defects.push("a tool-gate settlement reported autoDenied on an executing call — only a BLOCKED call can carry the marker; the frame carries none");
|
|
614
|
+
else
|
|
615
|
+
autoDenied = true;
|
|
616
|
+
}
|
|
940
617
|
const record = {
|
|
941
618
|
...(settledBy !== undefined ? { settledBy } : {}),
|
|
942
619
|
...(attribution.approver !== undefined ? { approver: attribution.approver } : {}),
|
|
943
620
|
...(resolution !== undefined ? { resolution } : {}),
|
|
621
|
+
...(autoDenied !== undefined ? { autoDenied } : {}),
|
|
944
622
|
};
|
|
945
|
-
return { ...(settledBy !== undefined || attribution.approver !== undefined || resolution !== undefined ? { record } : {}), defects };
|
|
623
|
+
return { ...(settledBy !== undefined || attribution.approver !== undefined || resolution !== undefined || autoDenied !== undefined ? { record } : {}), defects };
|
|
946
624
|
}
|
|
947
625
|
function maybeHumanRejectionHalt(input) {
|
|
948
626
|
if (!input.bare || input.settledBy !== "human" || input.isDelegatedChild)
|
|
@@ -995,37 +673,6 @@ function resolveApprovalPreview(tools, toolName, args) {
|
|
|
995
673
|
return { withheld: "unavailable" };
|
|
996
674
|
}
|
|
997
675
|
}
|
|
998
|
-
function inheritedAskRuleEvidence(deps) {
|
|
999
|
-
const org = deps.permissionRuleOrg === undefined ? "not_wired" : "not_adjudicated";
|
|
1000
|
-
const personal = deps.permissionRuleStore === undefined ? "not_wired" : "not_adjudicated";
|
|
1001
|
-
return Object.freeze({ orgRevisionAbsent: org, orgRuleAbsent: org, personalRuleDotsAbsent: personal });
|
|
1002
|
-
}
|
|
1003
|
-
function orgRevisionEvidenceOf(resolution, onDefect) {
|
|
1004
|
-
const reported = resolution.revision;
|
|
1005
|
-
if (reported === undefined)
|
|
1006
|
-
return {};
|
|
1007
|
-
if (typeof reported === "number" && Number.isFinite(reported))
|
|
1008
|
-
return { revision: reported };
|
|
1009
|
-
onDefect(`the org rule overlay reported revision ${JSON.stringify(reported)} — a snapshot revision is a finite number; ` +
|
|
1010
|
-
`the adjudication stands, but the ask carries no revision evidence for this call`);
|
|
1011
|
-
return {};
|
|
1012
|
-
}
|
|
1013
|
-
function persistedRuleHitOf(admitting) {
|
|
1014
|
-
return admitting === undefined
|
|
1015
|
-
? undefined
|
|
1016
|
-
: { rules: admitting.map((r) => ({ rule: r.rule, dots: r.adds.map((a) => ({ actor: a.dot.actor, counter: a.dot.counter })) })) };
|
|
1017
|
-
}
|
|
1018
|
-
function directoryRuleLaneAnswer(table, args, ctx) {
|
|
1019
|
-
const filePath = args?.file_path;
|
|
1020
|
-
if (typeof filePath !== "string" || filePath === "")
|
|
1021
|
-
return undefined;
|
|
1022
|
-
const spelled = filePath.startsWith("/") ? filePath : `${(ctx.liveCwd ?? ctx.root ?? "").replace(/\/+$/, "")}/${filePath}`;
|
|
1023
|
-
const target = lexicalNormalAbsolutePathOf(spelled);
|
|
1024
|
-
if (target === undefined)
|
|
1025
|
-
return undefined;
|
|
1026
|
-
const hit = table.find((r) => eligiblePersisted(r, { tool: DIRECTORY_RULE_TOOL, cwd: ctx.root, sessionId: ctx.sessionId }) && directoryRuleAdmits(r, target));
|
|
1027
|
-
return hit !== undefined ? persistedRuleHitOf([hit]) : undefined;
|
|
1028
|
-
}
|
|
1029
676
|
function makeRuleOffersOf(cfg) {
|
|
1030
677
|
return (toolName, args, ask) => {
|
|
1031
678
|
if (!cfg.laneArmed || toolName !== PERSISTED_RULE_TOOL)
|
|
@@ -1071,38 +718,6 @@ function makeRuleOffersOf(cfg) {
|
|
|
1071
718
|
: { ruleOffersAbsence: "lane_cannot_speak" };
|
|
1072
719
|
};
|
|
1073
720
|
}
|
|
1074
|
-
function makeOrgAdjudicationLane(overlay, questionToolMounted, onRevisionDefect) {
|
|
1075
|
-
if (overlay === undefined)
|
|
1076
|
-
return undefined;
|
|
1077
|
-
return {
|
|
1078
|
-
adjudicate: async (req) => {
|
|
1079
|
-
if (req.toolName === ASK_USER_QUESTION_TOOL_NAME && questionToolMounted)
|
|
1080
|
-
return { status: "available" };
|
|
1081
|
-
let resolution;
|
|
1082
|
-
try {
|
|
1083
|
-
resolution = await overlay.resolve();
|
|
1084
|
-
}
|
|
1085
|
-
catch (err) {
|
|
1086
|
-
return { status: "unavailable", disclosures: [`the org rule overlay threw: ${err instanceof Error ? err.message : String(err)}`] };
|
|
1087
|
-
}
|
|
1088
|
-
if (resolution.status === "unavailable")
|
|
1089
|
-
return { status: "unavailable", disclosures: resolution.disclosures };
|
|
1090
|
-
const revisionCell = orgRevisionEvidenceOf(resolution, onRevisionDefect);
|
|
1091
|
-
const command = req.args?.command;
|
|
1092
|
-
if (req.toolName !== PERSISTED_RULE_TOOL || typeof command !== "string")
|
|
1093
|
-
return { status: "available", ...revisionCell };
|
|
1094
|
-
const verdict = orgRuleVerdictFor(resolution.rules, { tool: req.toolName, command });
|
|
1095
|
-
return verdict === undefined ? { status: "available", ...revisionCell } : { status: "available", verdict, ...revisionCell };
|
|
1096
|
-
},
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
function cwdConflictsRestoreError(requestedCwd) {
|
|
1100
|
-
const e = new Error(`RunInternals.requestedCwd ("${requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
|
|
1101
|
-
`the restored workspace's own mount path is authoritative for the task root, so a requested cwd on this leg ` +
|
|
1102
|
-
`would be ignored (or worse, probed against an unrestored environment). Drop requestedCwd on resume legs.`);
|
|
1103
|
-
e.code = "config.cwd_conflicts_restore";
|
|
1104
|
-
return e;
|
|
1105
|
-
}
|
|
1106
721
|
async function adoptReminderMark(session, sessionId, seedMark, spawnMark, onError) {
|
|
1107
722
|
const sessionReminderMark = await (async () => {
|
|
1108
723
|
try {
|
|
@@ -1130,28 +745,6 @@ async function adoptReminderMark(session, sessionId, seedMark, spawnMark, onErro
|
|
|
1130
745
|
}
|
|
1131
746
|
return { reminderMark, reminderDisclosureCounts: {} };
|
|
1132
747
|
}
|
|
1133
|
-
async function derivedRouteFallsBack(args) {
|
|
1134
|
-
try {
|
|
1135
|
-
if (sameRouteIdentity(args.derived, args.primary))
|
|
1136
|
-
return false;
|
|
1137
|
-
const verdict = await adjudicateDerivedRoute({ brain: args.brain, model: args.derived, getApiKeyAndHeaders: args.getApiKeyAndHeaders });
|
|
1138
|
-
if (verdict === undefined || verdict.ok)
|
|
1139
|
-
return false;
|
|
1140
|
-
deliverEngineNotice(args.onNotice, fallbackToPrimaryNotice({ seat: args.seat, from: args.derived.id, to: args.primary.id, verdict, ...(args.sessionId !== undefined ? { sessionId: args.sessionId } : {}), ...(args.runId !== undefined ? { runId: args.runId } : {}) }));
|
|
1141
|
-
return true;
|
|
1142
|
-
}
|
|
1143
|
-
catch {
|
|
1144
|
-
return false;
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
function mintPlacementRootSessionId(placementRootResolved) {
|
|
1148
|
-
if (placementRootResolved === "") {
|
|
1149
|
-
const e = new Error("placement root resolved EMPTY (internals.placementRoot / internals.rootSessionId carries an empty string) — an empty fixed point cannot key a placement lookup; fix the spawning lane instead of defaulting around it.");
|
|
1150
|
-
e.code = "config.placement_root_invalid";
|
|
1151
|
-
throw e;
|
|
1152
|
-
}
|
|
1153
|
-
return placementRootResolved;
|
|
1154
|
-
}
|
|
1155
748
|
function restorePlacementRoot(internals, seedPlacementRoot) {
|
|
1156
749
|
if (internals?.placementRoot !== undefined)
|
|
1157
750
|
return internals;
|
|
@@ -1166,52 +759,6 @@ export function placementValueOrAbsent(value) {
|
|
|
1166
759
|
function stampPlacementRootSessionId(placementRootResolved) {
|
|
1167
760
|
return placementValueOrAbsent(placementRootResolved);
|
|
1168
761
|
}
|
|
1169
|
-
function explicitlyDeferredMemoryTrio(mounted, roster, deferNames) {
|
|
1170
|
-
return mounted ? MEMORY_ENGINE_TOOL_NAMES.filter((n) => roster.some((t) => t.name === n) && (deferNames ?? []).includes(n)) : [];
|
|
1171
|
-
}
|
|
1172
|
-
function memoryGroupRetractionSet(builtinDeferPairNames, engineTrioInPlay) {
|
|
1173
|
-
return new Set([...builtinDeferPairNames, ...(engineTrioInPlay ? MEMORY_ENGINE_TOOL_NAMES : [])]);
|
|
1174
|
-
}
|
|
1175
|
-
function assembleParentCaptureState(o, i, ctl, ancestors) {
|
|
1176
|
-
const build = (optedOut, indeterminate) => ({
|
|
1177
|
-
optedOut: optedOut === true,
|
|
1178
|
-
indeterminate: indeterminate === true,
|
|
1179
|
-
...(ctl !== undefined ? { controlDir: ctl } : {}),
|
|
1180
|
-
ancestors,
|
|
1181
|
-
});
|
|
1182
|
-
return o instanceof Promise || i instanceof Promise ? Promise.all([o, i]).then(([ov, iv]) => build(ov, iv)) : build(o, i);
|
|
1183
|
-
}
|
|
1184
|
-
async function spliceSessionOverlayRows(overlay, sessionId, persisted, tracer, hostTaskId) {
|
|
1185
|
-
if (overlay === undefined)
|
|
1186
|
-
return persisted;
|
|
1187
|
-
try {
|
|
1188
|
-
const served = structuredClone(await overlay.read(sessionId));
|
|
1189
|
-
const sessionRows = served.filter((r) => {
|
|
1190
|
-
const scope = r?.scope;
|
|
1191
|
-
return scope?.kind === "session" && scope.sessionId === sessionId && !("reject" in normalizePersistedRule(r));
|
|
1192
|
-
});
|
|
1193
|
-
if (sessionRows.length < served.length) {
|
|
1194
|
-
emitTrace(tracer, () => ({
|
|
1195
|
-
kind: "permission.rule_store_unreadable",
|
|
1196
|
-
version: 1,
|
|
1197
|
-
taskId: hostTaskId,
|
|
1198
|
-
message: `session-rule overlay served ${served.length - sessionRows.length} row(s) that are not canonical session rows of this session — dropped, not adjudicated`,
|
|
1199
|
-
ts: Date.now(),
|
|
1200
|
-
}));
|
|
1201
|
-
}
|
|
1202
|
-
return sessionRows.length > 0 ? [...sessionRows, ...persisted] : persisted;
|
|
1203
|
-
}
|
|
1204
|
-
catch (err) {
|
|
1205
|
-
emitTrace(tracer, () => ({
|
|
1206
|
-
kind: "permission.rule_store_unreadable",
|
|
1207
|
-
version: 1,
|
|
1208
|
-
taskId: hostTaskId,
|
|
1209
|
-
message: `session-rule overlay: ${err instanceof Error ? err.message : String(err)}`,
|
|
1210
|
-
ts: Date.now(),
|
|
1211
|
-
}));
|
|
1212
|
-
return persisted;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
762
|
function refuseRequireExistingWithoutSession(spec) {
|
|
1216
763
|
if (spec.requireExistingSession && !spec.sessionId) {
|
|
1217
764
|
const e = new Error(`requireExistingSession requires a sessionId — cannot require an existing session without one (design/114 Phase3)`);
|
|
@@ -1219,31 +766,6 @@ function refuseRequireExistingWithoutSession(spec) {
|
|
|
1219
766
|
throw e;
|
|
1220
767
|
}
|
|
1221
768
|
}
|
|
1222
|
-
function screenAutoModeCap(caps, onError, sessionId) {
|
|
1223
|
-
if (caps === undefined)
|
|
1224
|
-
return { caps, faulted: false };
|
|
1225
|
-
if (typeof caps !== "object" || caps === null || Array.isArray(caps)) {
|
|
1226
|
-
onError?.(new Error(`runtimeCapsResolver returned a non-record value (${JSON.stringify(caps)}) — read as a resolver fault: every per-principal capability is DENIED for this run (auto mode is not armed; asks flow the original chain). Return a RuntimeCaps object or undefined.`), { phase: "config", sessionId });
|
|
1227
|
-
return { caps: { allowWorkflows: false, allowFork: false, autoMode: false }, faulted: true };
|
|
1228
|
-
}
|
|
1229
|
-
const record = caps;
|
|
1230
|
-
if (record.autoMode === undefined || typeof record.autoMode === "boolean")
|
|
1231
|
-
return { caps: record, faulted: false };
|
|
1232
|
-
onError?.(new Error(`runtimeCapsResolver returned a non-boolean autoMode (${JSON.stringify(record.autoMode)}) — read as a resolver fault: auto mode is DENIED for this run (the classifier is not armed; asks flow the original chain). Return true, false, or omit the key.`), { phase: "config", sessionId });
|
|
1233
|
-
return { caps: { ...record, autoMode: false }, faulted: true };
|
|
1234
|
-
}
|
|
1235
|
-
function mintPersistedArming(am, laneRule, classifierSystemPrompt, onError, sessionId) {
|
|
1236
|
-
const arming = autoModeArmingRecipeOf(laneRule ? { ...am, crossSessionMessagesRule: true } : am, {
|
|
1237
|
-
promptDigest: `apv1:${createHash("sha256").update(classifierSystemPrompt).digest("hex")}`,
|
|
1238
|
-
});
|
|
1239
|
-
if (arming === undefined) {
|
|
1240
|
-
onError?.(new Error("RunnerDeps.autoMode.persistArming is set but the auto-mode face did not canonicalize into an arming recipe " +
|
|
1241
|
-
"(a rule list carrying a non-string, or a non-finite/out-of-range timeoutMs / failureThreshold / window bound) — " +
|
|
1242
|
-
"NO arming recipe is recorded on this run's parked constraint chain, and a cross-process redemption keeps the " +
|
|
1243
|
-
"conservative behavior (the ancestor classifier answers unavailable and the inherited ask flows to a human)"), { phase: "config", sessionId });
|
|
1244
|
-
}
|
|
1245
|
-
return arming;
|
|
1246
|
-
}
|
|
1247
769
|
function autoModeLatchHealthy(d) {
|
|
1248
770
|
try {
|
|
1249
771
|
return d.breakerOpen() !== true && typeof d.consecutiveFailures === "function" && d.consecutiveFailures() === 0;
|
|
@@ -1252,15 +774,6 @@ function autoModeLatchHealthy(d) {
|
|
|
1252
774
|
return false;
|
|
1253
775
|
}
|
|
1254
776
|
}
|
|
1255
|
-
function autoModeArmReasonOf(intent, facePresent, capsAutoMode, capsFaulted) {
|
|
1256
|
-
if (!intent)
|
|
1257
|
-
return "no_intent";
|
|
1258
|
-
if (!facePresent)
|
|
1259
|
-
return "no_face";
|
|
1260
|
-
if (capsAutoMode === false)
|
|
1261
|
-
return capsFaulted ? "resolver_fault" : "denied";
|
|
1262
|
-
return "armed";
|
|
1263
|
-
}
|
|
1264
777
|
function validateInputFaultNotice(onError, sessionId) {
|
|
1265
778
|
return ({ toolName, toolCallId, error }) => onError?.(new Error(`tool input pre-validation (validateInput) threw for ${toolName} (call ${toolCallId}) — read as no verdict; the call proceeded to the gate: ${error instanceof Error ? error.message : String(error)}`), { phase: "hook", sessionId });
|
|
1266
779
|
}
|
|
@@ -1272,50 +785,6 @@ function refuseInvalidAutoModeSeat(spec) {
|
|
|
1272
785
|
}
|
|
1273
786
|
return spec.autoModeRequested === true;
|
|
1274
787
|
}
|
|
1275
|
-
function announceDurableGateUnavailable(args) {
|
|
1276
|
-
if (!args.forceDurableGate || args.storeWired)
|
|
1277
|
-
return;
|
|
1278
|
-
const cause = args.taskStoreNull ? "task_store_null" : "no_deployment_store";
|
|
1279
|
-
const inheritedContentMandate = (args.parentConstraints ?? []).some((pc) => pc.contentMandate === true);
|
|
1280
|
-
const liveQuestionFace = args.liveQuestionFace && !inheritedContentMandate;
|
|
1281
|
-
deliverEngineNotice(args.onNotice, {
|
|
1282
|
-
code: "config.durable_gate_unavailable",
|
|
1283
|
-
message: `Durable approval gate mandated but unavailable: the runtime entitlement forceDurableGate is in force for this ` +
|
|
1284
|
-
`principal, but ${cause === "task_store_null" ? "this task disabled the checkpoint store (checkpointStore: null)" : "this deployment wired no checkpoint store"}, ` +
|
|
1285
|
-
`so no interactive ask of this task can be parked for a durable answer. Asks resolve on the live chain instead ` +
|
|
1286
|
-
`(${args.liveApprover ? "a live approver answers permission asks in-stream" : "permission asks are denied fail-closed — no live approver is wired"}; ` +
|
|
1287
|
-
`${liveQuestionFace ? "a live question face answers AskUserQuestion in-stream" : args.liveQuestionFace ? "AskUserQuestion is withheld from the live face by an inherited durable mandate and refused" : "AskUserQuestion has no live face and is refused"}) ` +
|
|
1288
|
-
`and NO durable approval record is written. ${cause === "task_store_null" ? "Run this task with its store, or withdraw the entitlement for principals whose runs are machine-started." : "Wire a checkpoint store on this deployment, or withdraw the entitlement for principals served here."}`,
|
|
1289
|
-
detail: {
|
|
1290
|
-
sessionId: args.sessionId,
|
|
1291
|
-
runId: args.runId,
|
|
1292
|
-
...(args.principal ? { principal: args.principal } : {}),
|
|
1293
|
-
cause,
|
|
1294
|
-
liveApprover: args.liveApprover,
|
|
1295
|
-
liveQuestionFace,
|
|
1296
|
-
},
|
|
1297
|
-
});
|
|
1298
|
-
}
|
|
1299
|
-
function buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) {
|
|
1300
|
-
if (delegationForRevive === undefined)
|
|
1301
|
-
return undefined;
|
|
1302
|
-
return async (req) => {
|
|
1303
|
-
const out = await delegationForRevive.execute({
|
|
1304
|
-
description: req.row.description ?? "revived teammate",
|
|
1305
|
-
prompt: req.prompt,
|
|
1306
|
-
run_in_background: true,
|
|
1307
|
-
...(req.row.agentType !== undefined ? { subagent_type: req.row.agentType } : {}),
|
|
1308
|
-
...(req.row.name !== undefined ? { name: req.row.name } : {}),
|
|
1309
|
-
...(req.row.model !== undefined ? { model: req.row.model } : {}),
|
|
1310
|
-
}, enrichSpecToolCtx({ toolCallId: `rv-${randomBytes(8).toString("hex")}`, reviveClaim: { row: req.row, rev: req.rev, ...(req.peerSeed !== undefined ? { peerSeed: req.peerSeed } : {}) } }));
|
|
1311
|
-
const o = (typeof out === "string" ? { content: out } : out);
|
|
1312
|
-
return {
|
|
1313
|
-
content: typeof o.content === "string" ? o.content : JSON.stringify(o.content),
|
|
1314
|
-
...(o.isError === true ? { isError: true } : {}),
|
|
1315
|
-
...(o.details !== undefined ? { details: o.details } : {}),
|
|
1316
|
-
};
|
|
1317
|
-
};
|
|
1318
|
-
}
|
|
1319
788
|
export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf, runIdSink) {
|
|
1320
789
|
const runId = uuidv7();
|
|
1321
790
|
if (runIdSink !== undefined)
|
|
@@ -1332,6 +801,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1332
801
|
refuseRequireExistingWithoutSession(spec);
|
|
1333
802
|
const { acquired, session, conflictRef, wakeRecovered, resumeAtBeforeParentId } = await prepareAcquireReconcile({ sessions, spec, resume, toolEffects, ...(internals?.sessionPlacement !== undefined ? { placement: internals.sessionPlacement } : {}), ...(() => { const g = durableParkGapFor(deps, spec); return g !== undefined ? { durableParkGap: g } : {}; })() });
|
|
1334
803
|
const sessionId = acquired.sessionId;
|
|
804
|
+
const onceLedger = await loadAnnounceOnceLedger(session, resume?.seed.announcedListings, deps.onError);
|
|
1335
805
|
const hostTaskId = spec.taskId ?? sessionId;
|
|
1336
806
|
const delegation = effectiveDelegationFacts(internals, resume?.seed.isDelegatedChild);
|
|
1337
807
|
const placementRootResolved = (internals = restorePlacementRoot(internals, resume?.seed.placementRootSessionId))?.placementRoot ?? internals?.rootSessionId ?? sessionId;
|
|
@@ -1366,112 +836,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1366
836
|
e.code = "config.extra_body_output_cap";
|
|
1367
837
|
throw e;
|
|
1368
838
|
}
|
|
1369
|
-
const nestedStats
|
|
1370
|
-
if (resume) {
|
|
1371
|
-
nestedStats.tokens = resume.seed.nestedStats.tokens;
|
|
1372
|
-
nestedStats.turns = resume.seed.nestedStats.turns;
|
|
1373
|
-
nestedStats.tasks = resume.seed.nestedStats.tasks;
|
|
1374
|
-
nestedStats.costMicroUsd = resume.seed.nestedStats.costMicroUsd;
|
|
1375
|
-
nestedStats.anyUnpriced = resume.seed.nestedStats.anyUnpriced ?? true;
|
|
1376
|
-
}
|
|
1377
|
-
const reportUsage = (u) => {
|
|
1378
|
-
nestedStats.tokens += u.tokens;
|
|
1379
|
-
nestedStats.turns += u.turns;
|
|
1380
|
-
nestedStats.tasks += u.tasks;
|
|
1381
|
-
nestedStats.costMicroUsd += u.costMicroUsd ?? 0;
|
|
1382
|
-
if (u.costMicroUsd === undefined)
|
|
1383
|
-
nestedStats.anyUnpriced = true;
|
|
1384
|
-
};
|
|
1385
|
-
const explicitGlobalThreshold = spec.toolResultThresholdChars ?? deps.toolResultThresholdChars;
|
|
1386
|
-
const offloadThreshold = explicitGlobalThreshold ?? DEFAULT_TOOL_RESULT_THRESHOLD_CHARS;
|
|
1387
|
-
const offloadEnabled = Number.isFinite(offloadThreshold) && offloadThreshold > 0;
|
|
1388
|
-
const rawOffloadStore = offloadEnabled ? (deps.toolResultStore ?? new InMemoryToolResultStore()) : undefined;
|
|
1389
|
-
const offloadStore = rawOffloadStore instanceof RunnerSharedToolResultStore
|
|
1390
|
-
? new ScopedToolResultStore(rawOffloadStore, offloadScope)
|
|
1391
|
-
: rawOffloadStore;
|
|
1392
|
-
const offloadReachableToolsRef = {};
|
|
1393
|
-
const maybeOffload = (tool, perTool) => {
|
|
1394
|
-
if (!offloadStore || perTool?.offload === false)
|
|
1395
|
-
return tool;
|
|
1396
|
-
return withToolResultOffload(tool, offloadStore, perTool?.offloadThresholdChars ?? offloadThreshold, sessionId, () => offloadReachableToolsRef.current?.());
|
|
1397
|
-
};
|
|
1398
|
-
const firstPartyOffload = (tool) => {
|
|
1399
|
-
const policy = firstPartyOffloadPolicy(tool.name);
|
|
1400
|
-
if (policy.offload === false)
|
|
1401
|
-
return maybeOffload(tool, policy);
|
|
1402
|
-
return maybeOffload(tool, explicitGlobalThreshold === undefined ? policy : undefined);
|
|
1403
|
-
};
|
|
1404
|
-
const remoteToolOffload = (tool) => {
|
|
1405
|
-
if (explicitGlobalThreshold !== undefined)
|
|
1406
|
-
return maybeOffload(tool);
|
|
1407
|
-
return maybeOffload(tool, { offloadThresholdChars: tool.mcpMaxResultSizeChars ?? 50_000 });
|
|
1408
|
-
};
|
|
839
|
+
const { nestedStats, reportUsage, offloadStore, offloadReachableToolsRef, maybeOffload, firstPartyOffload, remoteToolOffload } = prepareOffloadWrappers({ spec, deps, resume, offloadScope, sessionId });
|
|
1409
840
|
if (offloadStore && (spec.tools ?? []).some((t) => t.name === OFFLOAD_TOOL_NAME)) {
|
|
1410
841
|
const e = new Error(`Tool name "${OFFLOAD_TOOL_NAME}" is reserved when large-result offload is enabled.`);
|
|
1411
842
|
e.code = "config.reserved_tool_name";
|
|
1412
843
|
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (reserved-name leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
1413
844
|
throw e;
|
|
1414
845
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
let mcp;
|
|
1418
|
-
let a2a;
|
|
1419
|
-
if (internals?.requestedCwd !== undefined && deps.executionEnvFactory === undefined) {
|
|
1420
|
-
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-unsupported leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
1421
|
-
const e = new Error(`Agent cwd "${internals.requestedCwd}" cannot take effect: this deployment has no executionEnvFactory (a static execution environment cannot be re-rooted per agent). Drop the cwd parameter or deploy a factory.`);
|
|
1422
|
-
e.code = "config.cwd_unsupported";
|
|
1423
|
-
throw e;
|
|
1424
|
-
}
|
|
1425
|
-
if (internals?.requestedCwd !== undefined && resume?.workspaceHandle !== undefined) {
|
|
1426
|
-
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-conflicts-restore leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
1427
|
-
throw cwdConflictsRestoreError(internals.requestedCwd);
|
|
1428
|
-
}
|
|
1429
|
-
try {
|
|
1430
|
-
ownedEnv = deps.executionEnvFactory
|
|
1431
|
-
? await deps.executionEnvFactory({
|
|
1432
|
-
sessionId, placementRootSessionId: mintPlacementRootSessionId(placementRootResolved),
|
|
1433
|
-
taskId: spec.taskId,
|
|
1434
|
-
...(internals?.isolation ? { isolation: internals.isolation } : {}),
|
|
1435
|
-
...(internals?.parentCwd ? { parentCwd: internals.parentCwd } : {}),
|
|
1436
|
-
})
|
|
1437
|
-
: undefined;
|
|
1438
|
-
if (ownedEnv !== undefined)
|
|
1439
|
-
ownedEnvObservedAt = Date.now();
|
|
1440
|
-
}
|
|
1441
|
-
catch (factoryErr) {
|
|
1442
|
-
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (env-factory-throw leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
1443
|
-
throw factoryErr;
|
|
1444
|
-
}
|
|
1445
|
-
const executionEnv = ownedEnv ?? deps.executionEnv ?? new StubExecutionEnv();
|
|
846
|
+
const rollback = createRollbackStack((e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
847
|
+
const { executionEnv, ownedEnvSeat, ownedEnvObservedAt, handsEnabled, taskRootInitial } = await prepareExecutionEnv({ deps, spec, internals, resume, placementRootResolved, sessionId, forgetOnThrow, rollback });
|
|
1446
848
|
const externalContentTargetActive = executionEnv.externalContentTarget === true || resume?.seed.externalContentTarget === true;
|
|
1447
|
-
if (internals?.requestedCwd !== undefined) {
|
|
1448
|
-
const canon = async (p) => {
|
|
1449
|
-
const r = await executionEnv.canonicalPath(p);
|
|
1450
|
-
return r.ok ? r.value : p;
|
|
1451
|
-
};
|
|
1452
|
-
const want = await canon(internals.requestedCwd);
|
|
1453
|
-
const rejectCwd = async (message, code) => {
|
|
1454
|
-
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-reject leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
1455
|
-
await settleTeardownLeg(() => (ownedEnv && hasDestroy(ownedEnv) ? ownedEnv.destroy() : undefined), "ownedEnv.destroy (cwd-reject leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
|
|
1456
|
-
const e = new Error(message);
|
|
1457
|
-
e.code = code;
|
|
1458
|
-
throw e;
|
|
1459
|
-
};
|
|
1460
|
-
if (deps.rootPath !== undefined) {
|
|
1461
|
-
const root = await canon(deps.rootPath);
|
|
1462
|
-
const sep = root.includes("\\") && !root.includes("/") ? "\\" : "/";
|
|
1463
|
-
const rootPrefix = root.endsWith(sep) ? root : root + sep;
|
|
1464
|
-
if (want !== root && !want.startsWith(rootPrefix)) {
|
|
1465
|
-
await rejectCwd(`Agent cwd "${internals.requestedCwd}" is outside this deployment's declared root "${deps.rootPath}" — the cwd must be located within the deployment root.`, "config.cwd_outside_root");
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
const got = await canon(executionEnv.cwd);
|
|
1469
|
-
if (want !== got) {
|
|
1470
|
-
await rejectCwd(`Agent cwd "${internals.requestedCwd}" was not honored by the execution environment (it rooted at "${executionEnv.cwd}") — failing loud instead of running the agent in the wrong tree.`, "config.cwd_not_honored");
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
const handsEnabled = ownedEnv !== undefined || deps.executionEnv !== undefined;
|
|
1474
|
-
const taskRootInitial = internals?.isolation === "worktree" || internals?.requestedCwd !== undefined || !deps.rootPath ? executionEnv.cwd : deps.rootPath;
|
|
1475
849
|
const abortController = new AbortController();
|
|
1476
850
|
if (spec.signal?.aborted)
|
|
1477
851
|
abortController.abort();
|
|
@@ -1487,15 +861,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1487
861
|
e.code = "isolation.unavailable";
|
|
1488
862
|
throw e;
|
|
1489
863
|
};
|
|
1490
|
-
if (
|
|
864
|
+
if (ownedEnvSeat.current === undefined) {
|
|
1491
865
|
failIsolation("no executionEnvFactory is configured (a static/shared execution env cannot mint a per-agent worktree)");
|
|
1492
866
|
}
|
|
1493
|
-
else if (
|
|
1494
|
-
|
|
867
|
+
else if (ownedEnvSeat.current === deps.executionEnv) {
|
|
868
|
+
ownedEnvSeat.current = undefined;
|
|
1495
869
|
failIsolation("the executionEnvFactory returned the shared static env instead of a worktree-rooted env");
|
|
1496
870
|
}
|
|
1497
|
-
else if (isRemoteExecutionEnv(
|
|
1498
|
-
if (
|
|
871
|
+
else if (isRemoteExecutionEnv(ownedEnvSeat.current)) {
|
|
872
|
+
if (ownedEnvSeat.current.capabilities.isolation !== true) {
|
|
1499
873
|
failIsolation("the executionEnvFactory returned a remote env without capabilities.isolation — a non-isolated remote target is a shared remote checkout, not a per-agent worktree");
|
|
1500
874
|
}
|
|
1501
875
|
}
|
|
@@ -1509,7 +883,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1509
883
|
}
|
|
1510
884
|
};
|
|
1511
885
|
if (deps.rootPath !== undefined || internals.parentCwd !== undefined) {
|
|
1512
|
-
const envCwd = canonical(
|
|
886
|
+
const envCwd = canonical(ownedEnvSeat.current.cwd, "the factory env's cwd");
|
|
1513
887
|
if (deps.rootPath !== undefined && envCwd === canonical(deps.rootPath, "the shared base root")) {
|
|
1514
888
|
failIsolation("the executionEnvFactory rooted the env at the shared base root instead of a worktree");
|
|
1515
889
|
}
|
|
@@ -1519,625 +893,15 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1519
893
|
}
|
|
1520
894
|
}
|
|
1521
895
|
}
|
|
896
|
+
const ownedEnv = ownedEnvSeat.current;
|
|
1522
897
|
const { taskRootFinal, rebaseRestoredPath } = await prepareWorkspaceRestore({ ownedEnv, deps, spec, internals, resume, taskRootInitial, abortController, sessionId });
|
|
1523
|
-
const rewindNotes =
|
|
1524
|
-
const
|
|
1525
|
-
if (onTrackFailure !== "refuse" && onTrackFailure !== "proceed-unprotected") {
|
|
1526
|
-
const e = new Error(`RunnerDeps.onTrackFailure must be "refuse" or "proceed-unprotected"; got ${JSON.stringify(deps.onTrackFailure)} — refusing prepare rather than silently defaulting a first-touch-failure policy.`);
|
|
1527
|
-
e.code = "config.invalid_on_track_failure";
|
|
1528
|
-
throw e;
|
|
1529
|
-
}
|
|
1530
|
-
const fileHistoryStore = resolveWiredFileHistoryStore(deps.fileHistoryStore);
|
|
1531
|
-
const legacyRewindFiles = spec.rewindFiles;
|
|
1532
|
-
if (legacyRewindFiles === true) {
|
|
1533
|
-
if (spec.resumeAt !== undefined) {
|
|
1534
|
-
const e = new Error(`rewind-files: TaskSpec.rewindFiles is retired (design/381 — rewind now converges the per-edited-file history, not a whole-tree snapshot). This request pairs it with resumeAt, i.e. the RESTORE sense: set restoreFiles: true instead. (The capture sense needs no request anymore — first-touch tracking is always on when a RunnerDeps.fileHistoryStore is wired.)`);
|
|
1535
|
-
e.code = "rewind.rewind_files_retired";
|
|
1536
|
-
throw e;
|
|
1537
|
-
}
|
|
1538
|
-
try {
|
|
1539
|
-
deps.onError?.(new Error(`TaskSpec.rewindFiles (capture sense) is retired and was ignored: per-edited-file first-touch tracking is always on when RunnerDeps.fileHistoryStore is wired, so a per-turn capture request has nothing left to ask for. Drop the field; the restore sense moved to TaskSpec.restoreFiles.`), { phase: "config", sessionId });
|
|
1540
|
-
}
|
|
1541
|
-
catch {
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
if (spec.restoreFiles === true && spec.resumeAt === undefined) {
|
|
1545
|
-
const e = new Error(`rewind-files: restoreFiles: true needs a resumeAt anchor — restoring files means converging them to a specific prior entry's boundary, and this spec names none. Use resumeAt + restoreFiles to move conversation and files together, or rewindFilesTo alone for a code-only restore.`);
|
|
1546
|
-
e.code = "rewind.invalid_spec";
|
|
1547
|
-
throw e;
|
|
1548
|
-
}
|
|
1549
|
-
if (spec.resumeAt !== undefined && spec.rewindFilesTo !== undefined) {
|
|
1550
|
-
const e = new Error(`rewind-files: resumeAt ("${spec.resumeAt}") and rewindFilesTo ("${spec.rewindFilesTo}") were both set — resumeAt already anchors the file restore when restoreFiles is true, so a separate rewindFilesTo target is a conflicting request. Drop one of the two.`);
|
|
1551
|
-
e.code = "rewind.conflicting_targets";
|
|
1552
|
-
throw e;
|
|
1553
|
-
}
|
|
1554
|
-
let rewindTarget = spec.resumeAt !== undefined ? (spec.restoreFiles === true ? spec.resumeAt : undefined) : spec.rewindFilesTo;
|
|
1555
|
-
const rewindBefore = rewindTarget !== undefined && spec.resumeAt !== undefined && spec.resumeAtMode === "before";
|
|
1556
|
-
if (spec.resumeAt !== undefined && spec.restoreFiles !== true) {
|
|
1557
|
-
rewindNotes.push({
|
|
1558
|
-
code: "conversation_only",
|
|
1559
|
-
message: `the conversation was branched at entry "${spec.resumeAt}" but the working tree was NOT rewound — this task set resumeAt without restoreFiles, so files remain at their current state`,
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
const fileHistoryEnabled = fileHistoryStore !== undefined && handsEnabled;
|
|
1563
|
-
const { historyRoot, historyScope, historyFs } = await resolveFileHistoryCoordinates(fileHistoryEnabled, executionEnv, taskRootFinal, internals?.fileHistoryLineage, sessionId);
|
|
1564
|
-
if (fileHistoryStore !== undefined && internals?.fileHistoryLineage === undefined)
|
|
1565
|
-
await adoptForkedFileHistory(fileHistoryStore, session, sessionId, deps.onError);
|
|
1566
|
-
if (rewindTarget !== undefined && historyScope !== sessionId)
|
|
1567
|
-
throw childScopeRewindRefusal(historyScope, rewindTarget);
|
|
1568
|
-
if (fileHistoryStore === undefined) {
|
|
1569
|
-
if (rewindTarget !== undefined) {
|
|
1570
|
-
const e = new Error(`rewind-files: restoring files to entry "${rewindTarget}" requires a history backend, but this deployment wired no RunnerDeps.fileHistoryStore — no file history was ever recorded, so the files were NOT rewound`);
|
|
1571
|
-
e.code = "rewind.store_unconfigured";
|
|
1572
|
-
throw e;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
else if (!handsEnabled) {
|
|
1576
|
-
if (rewindTarget !== undefined) {
|
|
1577
|
-
rewindNotes.push({
|
|
1578
|
-
code: "files_env_unsupported",
|
|
1579
|
-
message: "the file side of rewind was inert: this deployment mounts no filesystem-capable ExecutionEnv, so no file history was recorded or restored",
|
|
1580
|
-
});
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
const legacyEpochTail = (entryId, form = "at-or-above") => legacyRewindEpochTail(deps, sessionId, entryId, form);
|
|
1584
|
-
let restoredFilePaths = [];
|
|
1585
|
-
if (rewindTarget !== undefined && fileHistoryStore !== undefined && handsEnabled) {
|
|
1586
|
-
const historyStore = fileHistoryStore;
|
|
1587
|
-
if (rewindBefore) {
|
|
1588
|
-
let anchor;
|
|
1589
|
-
if (await historyStore.canRestore(sessionId, rewindTarget)) {
|
|
1590
|
-
anchor = rewindTarget;
|
|
1591
|
-
}
|
|
1592
|
-
else {
|
|
1593
|
-
const walked = new Set();
|
|
1594
|
-
let cursor = resumeAtBeforeParentId;
|
|
1595
|
-
while (cursor !== null && !walked.has(cursor)) {
|
|
1596
|
-
walked.add(cursor);
|
|
1597
|
-
if (await historyStore.canRestore(sessionId, cursor)) {
|
|
1598
|
-
anchor = cursor;
|
|
1599
|
-
break;
|
|
1600
|
-
}
|
|
1601
|
-
cursor = (await session.getEntry(cursor))?.parentId ?? null;
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
if (anchor === undefined) {
|
|
1605
|
-
const e = new Error(`rewind-files: no file-history boundary exists at or above the "before" branch point on session "${sessionId}" — cannot restore files consistent with the rewound context. ${await legacyEpochTail(rewindTarget)}`);
|
|
1606
|
-
e.code = "rewind_snapshot.unresolvable";
|
|
1607
|
-
throw e;
|
|
1608
|
-
}
|
|
1609
|
-
rewindTarget = anchor;
|
|
1610
|
-
}
|
|
1611
|
-
const restoreRoot = historyRoot;
|
|
1612
|
-
const restoreSignal = spec.signal ? AbortSignal.any([abortController.signal, spec.signal]) : abortController.signal;
|
|
1613
|
-
const restored = await historyStore.restore(sessionId, rewindTarget, executionEnv, restoreRoot, restoreSignal);
|
|
1614
|
-
const ledgerLine = (r) => `applied ${r.filesChanged.length}, identical ${r.identicalSkipped.length}, refused ${r.refused.length}${r.refused.length > 0 ? ` (${r.refused.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}, failed ${r.failed.length}${r.failed.length > 0 ? ` (${r.failed.map((f) => `${f.path}: ${f.reason}`).join("; ")})` : ""}`;
|
|
1615
|
-
if (restored.outcome === "failed") {
|
|
1616
|
-
if (restored.error?.code === "not_found") {
|
|
1617
|
-
const e = new Error(rewindBefore
|
|
1618
|
-
? `rewind-files: the resolved "before" history anchor "${rewindTarget}" disappeared before restore — files were NOT rewound`
|
|
1619
|
-
: `rewind-files: no file-history boundary exists for entry "${rewindTarget}" on session "${sessionId}" — the working tree was NOT rewound. ${await legacyEpochTail(rewindTarget, "keyed")}`);
|
|
1620
|
-
e.code = "rewind_snapshot.unresolvable";
|
|
1621
|
-
throw e;
|
|
1622
|
-
}
|
|
1623
|
-
const e = new Error(`rewind-files restore failed (${restored.error?.code ?? "restore_failed"}): ${restored.error?.message ?? "unknown"} — ledger: ${ledgerLine(restored)}`);
|
|
1624
|
-
e.code = "rewind.restore_failed";
|
|
1625
|
-
throw e;
|
|
1626
|
-
}
|
|
1627
|
-
if (restored.outcome === "partial") {
|
|
1628
|
-
if (spec.acceptPartialRestore === true) {
|
|
1629
|
-
rewindNotes.push({
|
|
1630
|
-
code: "restore_partial",
|
|
1631
|
-
message: `file restore to entry "${rewindTarget}" was PARTIAL and was tolerated by acceptPartialRestore — ledger: ${ledgerLine(restored)}. Re-running the same restore converges (per-file idempotent).`,
|
|
1632
|
-
});
|
|
1633
|
-
}
|
|
1634
|
-
else {
|
|
1635
|
-
const e = new Error(`rewind-files: restore to entry "${rewindTarget}" was PARTIAL — ledger: ${ledgerLine(restored)}. The task is refused rather than run on a mixed-state tree (set acceptPartialRestore: true to tolerate and disclose instead); re-running the same restore converges (per-file idempotent).`);
|
|
1636
|
-
e.code = "rewind.restore_failed";
|
|
1637
|
-
throw e;
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
restoredFilePaths = restoredAbsPathsOf(restored, restoreRoot);
|
|
1641
|
-
}
|
|
1642
|
-
const trackFileEdit = fileHistoryEnabled
|
|
1643
|
-
? async (req) => {
|
|
1644
|
-
const r = await fileHistoryStore.trackEdit(historyScope, req.key, executionEnv, historyRoot, req.signal);
|
|
1645
|
-
if (r.ok) {
|
|
1646
|
-
if (!r.minted)
|
|
1647
|
-
return { ok: true };
|
|
1648
|
-
return {
|
|
1649
|
-
ok: true,
|
|
1650
|
-
annul: async (proof) => {
|
|
1651
|
-
const a = await fileHistoryStore.annulTrack(historyScope, req.key, historyRoot, proof === "verify" ? { env: executionEnv, ...(req.signal !== undefined ? { signal: req.signal } : {}) } : undefined);
|
|
1652
|
-
if (a.ok)
|
|
1653
|
-
return;
|
|
1654
|
-
try {
|
|
1655
|
-
deps.onError?.(new Error(`file-history could not retract the first-touch record for "${req.key}" after its edit failed to land (${a.error.code}: ${a.error.message}) — the path stays tracked, so a rewind to a boundary below it may converge bytes this engine never wrote`), { phase: "rewind", sessionId });
|
|
1656
|
-
}
|
|
1657
|
-
catch {
|
|
1658
|
-
}
|
|
1659
|
-
},
|
|
1660
|
-
};
|
|
1661
|
-
}
|
|
1662
|
-
if (onTrackFailure === "proceed-unprotected") {
|
|
1663
|
-
try {
|
|
1664
|
-
deps.onError?.(new Error(`file-history first-touch backup for "${req.key}" failed (${r.error.code}: ${r.error.message}) — proceeding UNPROTECTED per RunnerDeps.onTrackFailure: the pristine promise for this path is void, and only in this process's memory (no durable trace exists, so after a process restart the next edit can still mint already-modified bytes as pristine v1)`), { phase: "rewind", sessionId });
|
|
1665
|
-
}
|
|
1666
|
-
catch {
|
|
1667
|
-
}
|
|
1668
|
-
return { ok: true };
|
|
1669
|
-
}
|
|
1670
|
-
return {
|
|
1671
|
-
ok: false,
|
|
1672
|
-
refusal: `Error (${req.tool}): the edit was refused because its first-touch file-history backup could not be persisted (${r.error.code}: ${r.error.message}). Proceeding would leave "${req.path}" with no durable pristine record — after the history store recovers, a retried edit would mint already-modified bytes as the pristine baseline. Retry when the file-history store is healthy.`,
|
|
1673
|
-
};
|
|
1674
|
-
}
|
|
1675
|
-
: undefined;
|
|
1676
|
-
const { note: noteFileEdited, snapshot: editedFilesSnapshot } = createEditedFilesLedger();
|
|
1677
|
-
const fileHistoryBoundary = fileHistoryEnabled && historyScope === sessionId
|
|
1678
|
-
? createFileHistoryBoundarySeat({ store: fileHistoryStore, sessionId, env: executionEnv, root: historyRoot, onError: deps.onError })
|
|
1679
|
-
: undefined;
|
|
1680
|
-
const harnessRef = {};
|
|
1681
|
-
const skillScope = new ActiveSkillScope();
|
|
1682
|
-
const forwardSink = internals?.onForwardEvent;
|
|
1683
|
-
const forwardEvent = forwardSink
|
|
1684
|
-
? (e) => {
|
|
1685
|
-
if (e.type === "task_progress")
|
|
1686
|
-
forwardSink(e);
|
|
1687
|
-
else if (spec.forwardSubagentEvents === true &&
|
|
1688
|
-
(e.type === "text_delta" || e.type === "text_end" || e.type === "reasoning_delta" || e.type === "tool_start" || e.type === "tool_end")) {
|
|
1689
|
-
forwardSink(e);
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
: undefined;
|
|
1693
|
-
const reviewRequestRef = {};
|
|
1694
|
-
const requestReview = (opts) => {
|
|
1695
|
-
if (reviewRequestRef.pending === undefined) {
|
|
1696
|
-
reviewRequestRef.pending = opts?.reason !== undefined ? { reason: opts.reason } : {};
|
|
1697
|
-
}
|
|
1698
|
-
};
|
|
1699
|
-
const requestStopAfterTurn = () => {
|
|
1700
|
-
harnessRef.current?.requestStopAfterTurn();
|
|
1701
|
-
};
|
|
1702
|
-
const planModeRef = { active: false };
|
|
1703
|
-
const enterPlanMode = () => {
|
|
1704
|
-
planModeRef.active = true;
|
|
1705
|
-
};
|
|
1706
|
-
const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
|
|
1707
|
-
const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootFinal) : undefined;
|
|
1708
|
-
const liveInheritedGate = internals?.inheritedGate;
|
|
1709
|
-
const seedInheritedGate = resume?.seed.inheritedGate;
|
|
1710
|
-
const inheritedAncestorRules = (() => {
|
|
1711
|
-
const seedRules = seedInheritedGate?.ancestorRules;
|
|
1712
|
-
const liveRules = liveInheritedGate?.ancestorRules;
|
|
1713
|
-
if (seedRules === undefined || seedRules.length === 0)
|
|
1714
|
-
return liveRules ?? seedRules;
|
|
1715
|
-
if (liveRules === undefined || liveRules.length === 0)
|
|
1716
|
-
return seedRules;
|
|
1717
|
-
const merged = seedRules.map((s) => {
|
|
1718
|
-
const live = liveRules.find((l) => l.sessionId === s.sessionId && l.principal === s.principal);
|
|
1719
|
-
return live !== undefined && live.rev >= s.rev ? live : s;
|
|
1720
|
-
});
|
|
1721
|
-
const extras = liveRules.filter((l) => !seedRules.some((s) => s.sessionId === l.sessionId && s.principal === l.principal));
|
|
1722
|
-
return [...merged, ...extras];
|
|
1723
|
-
})();
|
|
1724
|
-
const shellGateRank = { off: 0, classify: 1, always: 2 };
|
|
1725
|
-
const liveShellGate = liveInheritedGate?.shellGate;
|
|
1726
|
-
const seedShellGate = seedInheritedGate?.shellGate;
|
|
1727
|
-
const inheritedShellGate = liveShellGate === undefined
|
|
1728
|
-
? seedShellGate
|
|
1729
|
-
: seedShellGate === undefined
|
|
1730
|
-
? liveShellGate
|
|
1731
|
-
: shellGateRank[liveShellGate] >= shellGateRank[seedShellGate]
|
|
1732
|
-
? liveShellGate
|
|
1733
|
-
: seedShellGate;
|
|
1734
|
-
const inheritedParentConstraints = attachRebuiltDenialTrackers(liveInheritedGate?.parentConstraints, deps.autoMode?.denialLimit);
|
|
1735
|
-
const autoModeIntent = autoModeSeat || liveInheritedGate?.autoModeRequested === true || seedInheritedGate?.autoModeRequested === true;
|
|
1736
|
-
const liveAdmittedOrg = liveInheritedGate?.admittedOrgScopes;
|
|
1737
|
-
const seedAdmittedOrg = seedInheritedGate?.admittedOrgScopes;
|
|
1738
|
-
const inheritedAdmittedOrgScopes = liveAdmittedOrg === undefined ? seedAdmittedOrg : seedAdmittedOrg === undefined ? liveAdmittedOrg : liveAdmittedOrg.filter((s) => seedAdmittedOrg.includes(s));
|
|
1739
|
-
const inheritedOrgGoverned = liveInheritedGate?.orgAdmissionGoverned === true || seedInheritedGate?.orgAdmissionGoverned === true;
|
|
1740
|
-
const checkpointOwnOrgVerdict = resume?.seed.inheritedGate?.ownAdmittedOrgScopes !== undefined
|
|
1741
|
-
? { scopes: resume.seed.inheritedGate.ownAdmittedOrgScopes, writeScope: resume.seed.inheritedGate.ownAdmittedOrgWriteScope ?? null }
|
|
1742
|
-
: undefined;
|
|
1743
|
-
const refOwnOrgVerdict = internals?.ownOrgAdmissionRef?.current;
|
|
1744
|
-
const priorOwnOrgVerdict = checkpointOwnOrgVerdict === undefined
|
|
1745
|
-
? refOwnOrgVerdict
|
|
1746
|
-
: refOwnOrgVerdict === undefined
|
|
1747
|
-
? checkpointOwnOrgVerdict
|
|
1748
|
-
: {
|
|
1749
|
-
scopes: checkpointOwnOrgVerdict.scopes.filter((sc) => refOwnOrgVerdict.scopes.includes(sc)),
|
|
1750
|
-
writeScope: checkpointOwnOrgVerdict.writeScope !== null && checkpointOwnOrgVerdict.writeScope === refOwnOrgVerdict.writeScope
|
|
1751
|
-
? checkpointOwnOrgVerdict.writeScope
|
|
1752
|
-
: null,
|
|
1753
|
-
};
|
|
1754
|
-
const orgGovernedProvenance = deps.memoryScopeAdmission !== undefined ||
|
|
1755
|
-
(deps.deploymentMemoryScopes !== undefined && deps.deploymentMemoryScopes.length > 0) ||
|
|
1756
|
-
deps.compliancePostureResolver !== undefined ||
|
|
1757
|
-
inheritedOrgGoverned ||
|
|
1758
|
-
priorOwnOrgVerdict !== undefined;
|
|
1759
|
-
const specShellGate = spec.shellGate ?? "off";
|
|
1760
|
-
const effectiveShellGate = inheritedShellGate !== undefined && shellGateRank[inheritedShellGate] > shellGateRank[specShellGate] ? inheritedShellGate : specShellGate;
|
|
1761
|
-
const ownSessionRulesRef = {};
|
|
1762
|
-
const memoryAdmittedOrgScopesRef = { current: [] };
|
|
1763
|
-
const ownOrgVerdictRef = { current: undefined };
|
|
1764
|
-
const orgAdmissionCheckpointState = () => inheritedAdmittedOrgScopes !== undefined || ownOrgVerdictRef.current !== undefined || orgGovernedProvenance;
|
|
1765
|
-
const faceCheckpointSection = () => {
|
|
1766
|
-
if (resolvedReadFace === undefined && resume !== undefined) {
|
|
1767
|
-
const seed = resume.seed.readFace;
|
|
1768
|
-
return seed !== undefined ? { face: seed.face, ...(seed.denyEntries !== undefined ? { denyEntries: seed.denyEntries.map((e) => ({ ...e })) } : {}) } : undefined;
|
|
1769
|
-
}
|
|
1770
|
-
const face = resolvedReadFace ?? handsLessResolvedFace;
|
|
1771
|
-
if (face === undefined)
|
|
1772
|
-
return undefined;
|
|
1773
|
-
if (face === "open" || readDenyAdditionsNormalized.length > 0) {
|
|
1774
|
-
return { face, ...(readDenyAdditionsNormalized.length > 0 ? { denyEntries: readDenyAdditionsNormalized.map((e) => ({ ...e })) } : {}) };
|
|
1775
|
-
}
|
|
1776
|
-
return undefined;
|
|
1777
|
-
};
|
|
1778
|
-
const faceCheckpointState = () => faceCheckpointSection() !== undefined;
|
|
1779
|
-
const f012CheckpointState = () => (inheritedParentConstraints?.length ?? 0) > 0 ||
|
|
1780
|
-
seedInheritedGate?.constraintChain !== undefined ||
|
|
1781
|
-
internals?.delegationProvenance !== undefined;
|
|
1782
|
-
const frozenOnAsk = spec.onAsk ?? deps.onAsk;
|
|
1783
|
-
const hookEnvSource = (ownedEnv ?? deps.executionEnv) != null ? executionEnv : undefined;
|
|
1784
|
-
const notifyOwnHookCrash = (err) => {
|
|
1785
|
-
try {
|
|
1786
|
-
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "hook", sessionId });
|
|
1787
|
-
}
|
|
1788
|
-
catch {
|
|
1789
|
-
}
|
|
1790
|
-
};
|
|
1791
|
-
const frozenOnQuestion = spec.onQuestion ?? deps.onQuestion;
|
|
1792
|
-
const provenanceForChildrenRef = {};
|
|
1793
|
-
const delegationProvenanceForChildren = () => provenanceForChildrenRef.current;
|
|
1794
|
-
const delegationSettlementRef = {};
|
|
1795
|
-
const delegationSettlementForChildren = () => delegationSettlementRef.current;
|
|
1796
|
-
const inheritedGateForChildren = () => {
|
|
1797
|
-
const ancestorRules = [
|
|
1798
|
-
...(inheritedAncestorRules ?? []),
|
|
1799
|
-
...(ownSessionRulesRef.current !== undefined ? [ownSessionRulesRef.current] : []),
|
|
1800
|
-
];
|
|
1801
|
-
const ownCallerPolicy = lockedPreflight.toolPolicy;
|
|
1802
|
-
const durableMandate = runtimeCaps?.forceDurableGate === true ||
|
|
1803
|
-
(spec.durableApproval !== undefined && !isLiveApproverSeat(frozenOnAsk));
|
|
1804
|
-
const contentMandate = runtimeCaps?.forceDurableGate === true ||
|
|
1805
|
-
(spec.durableApproval !== undefined && !isLiveQuestionFace(frozenOnQuestion));
|
|
1806
|
-
const ownPreToolUse = preToolUseObservational ? undefined : hooks?.preToolUse;
|
|
1807
|
-
const hookConstraint = ownPreToolUse !== undefined &&
|
|
1808
|
-
!(inheritedParentConstraints ?? []).some((pc) => pc.preToolUse === ownPreToolUse &&
|
|
1809
|
-
askApproverIdentity(pc.onAsk) === askApproverIdentity(frozenOnAsk) &&
|
|
1810
|
-
(pc.durableMandate === true) === durableMandate &&
|
|
1811
|
-
(pc.contentMandate === true) === contentMandate &&
|
|
1812
|
-
pc.hookEnv === hookEnvSource &&
|
|
1813
|
-
pc.autoMode?.decider === autoModeDecider)
|
|
1814
|
-
? [
|
|
1815
|
-
{
|
|
1816
|
-
policy: createPreToolUseConstraintPolicy(ownPreToolUse, hookEnvFace, notifyOwnHookCrash, hookTimeoutMs),
|
|
1817
|
-
preToolUse: ownPreToolUse,
|
|
1818
|
-
...(hookEnvSource !== undefined ? { hookEnv: hookEnvSource } : {}),
|
|
1819
|
-
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1820
|
-
...(durableMandate ? { durableMandate: true } : {}),
|
|
1821
|
-
...(contentMandate ? { contentMandate: true } : {}),
|
|
1822
|
-
...(autoModeDecider !== undefined
|
|
1823
|
-
? { autoMode: { decider: autoModeDecider, ...(autoModeDenialTracking !== undefined ? { denialTracking: autoModeDenialTracking } : {}), ...(autoModeArming !== undefined ? { arming: autoModeArming } : {}) } }
|
|
1824
|
-
: {}),
|
|
1825
|
-
},
|
|
1826
|
-
]
|
|
1827
|
-
: [];
|
|
1828
|
-
const parentConstraints = [
|
|
1829
|
-
...(inheritedParentConstraints ?? []),
|
|
1830
|
-
...hookConstraint,
|
|
1831
|
-
...(ownCallerPolicy !== undefined
|
|
1832
|
-
? [
|
|
1833
|
-
{
|
|
1834
|
-
policy: ownCallerPolicy,
|
|
1835
|
-
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1836
|
-
...(durableMandate ? { durableMandate: true } : {}),
|
|
1837
|
-
...(contentMandate ? { contentMandate: true } : {}),
|
|
1838
|
-
...(autoModeDecider !== undefined
|
|
1839
|
-
? { autoMode: { decider: autoModeDecider, ...(autoModeDenialTracking !== undefined ? { denialTracking: autoModeDenialTracking } : {}), ...(autoModeArming !== undefined ? { arming: autoModeArming } : {}) } }
|
|
1840
|
-
: {}),
|
|
1841
|
-
},
|
|
1842
|
-
]
|
|
1843
|
-
: []),
|
|
1844
|
-
];
|
|
1845
|
-
return {
|
|
1846
|
-
...(ancestorRules.length > 0 ? { ancestorRules } : {}),
|
|
1847
|
-
...(effectiveShellGate !== "off" ? { shellGate: effectiveShellGate } : {}),
|
|
1848
|
-
...(autoModeIntent ? { autoModeRequested: true } : {}),
|
|
1849
|
-
...(parentConstraints.length > 0 ? { parentConstraints } : {}),
|
|
1850
|
-
admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
|
|
1851
|
-
...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
|
|
1852
|
-
};
|
|
1853
|
-
};
|
|
1854
|
-
let agentForkDenial;
|
|
1855
|
-
let observersActive = false;
|
|
898
|
+
const { trackFileEdit, fileHistoryBoundary, restoredFilePaths, rewindNotes, fileHistoryEnabled, historyScope, historyRoot, historyFs } = await prepareFileHistory({ deps, spec, internals, executionEnv, taskRootFinal, handsEnabled, sessionId, session, resumeAtBeforeParentId, abortController });
|
|
899
|
+
const { noteFileEdited, editedFilesSnapshot, harnessRef, skillScope, forwardEvent, reviewRequestRef, requestReview, stopRequestedRef, requestStopAfterTurn, planModeRef, enterPlanMode, subagentRetain, worktreeIsolation } = prepareRunRefs({ spec, internals, executionEnv, taskRootFinal });
|
|
1856
900
|
let resolvedReadFace;
|
|
1857
901
|
let readDenyAdditionsNormalized = [];
|
|
1858
902
|
let handsLessResolvedFace;
|
|
1859
|
-
const carrierReadFace = () => resolvedReadFace
|
|
1860
|
-
const
|
|
1861
|
-
!(executionEnv instanceof StubExecutionEnv) &&
|
|
1862
|
-
spec.handsReadOnly !== true &&
|
|
1863
|
-
!(toolFaceSnapshot.exclude?.includes("Bash") ?? false);
|
|
1864
|
-
let autoModeDecider;
|
|
1865
|
-
let autoModeDenialTracking;
|
|
1866
|
-
const { gateStopRef, stopForDenialLimit } = createDenialLimitStop({ sessionId, runId, onNotice: deps.onNotice, abort: () => { abortController.abort(); void harness.abort(); } });
|
|
1867
|
-
let autoModeArming;
|
|
1868
|
-
const delegationEntryCapsResolved = resolveDelegationEntryCaps(deps.delegationEntryCaps);
|
|
1869
|
-
const enrichSpecToolCtx = (ctx) => ({
|
|
1870
|
-
...ctx,
|
|
1871
|
-
delegationEntryCaps: delegationEntryCapsResolved,
|
|
1872
|
-
reportUsage,
|
|
1873
|
-
model: harnessRef.current?.getModel(),
|
|
1874
|
-
thinkingLevel: harnessRef.current?.getThinkingLevel(),
|
|
1875
|
-
principal: spec.principal,
|
|
1876
|
-
...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
|
|
1877
|
-
...(frozenOnQuestion !== undefined ? { onQuestion: frozenOnQuestion } : {}),
|
|
1878
|
-
...(spec.handsReadOnly === true ? { handsReadOnly: true } : {}),
|
|
1879
|
-
...(carrierReadFace() === "roots" ? { readFace: "roots" } : {}),
|
|
1880
|
-
...(readDenyAdditionsNormalized.length > 0 ? { readDenyPatterns: Object.freeze(readDenyAdditionsNormalized.map((e) => ({ ...e }))) } : {}),
|
|
1881
|
-
...(spec.interactiveTools === false ? { interactiveTools: false } : {}),
|
|
1882
|
-
oneShot: spec.oneShot,
|
|
1883
|
-
clientContext: spec.clientContext,
|
|
1884
|
-
excludeTools: toolFaceSnapshot.exclude,
|
|
1885
|
-
deferTools: toolFaceSnapshot.defer,
|
|
1886
|
-
alwaysLoadTools: toolFaceSnapshot.alwaysLoad,
|
|
1887
|
-
...(toolFaceSnapshot.restoreGated !== undefined ? { restoreGatedTools: toolFaceSnapshot.restoreGated } : {}),
|
|
1888
|
-
promptProfile,
|
|
1889
|
-
...(spec.additionalDirectories !== undefined ? { additionalDirectories: Object.freeze([...spec.additionalDirectories]) } : {}),
|
|
1890
|
-
...(spec.additionalReadDirectories !== undefined ? { additionalReadDirectories: Object.freeze([...spec.additionalReadDirectories]) } : {}),
|
|
1891
|
-
...(spec.envFacts !== undefined ? { envFacts: { ...spec.envFacts } } : {}),
|
|
1892
|
-
...(spec.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: spec.memoryPersistenceCapable } : {}),
|
|
1893
|
-
get memoryCaptureOptedOut() {
|
|
1894
|
-
return memoryEngineSession?.captureOptOut?.optedOut() ?? false;
|
|
1895
|
-
},
|
|
1896
|
-
get memoryCaptureIndeterminate() {
|
|
1897
|
-
return memoryEngineSession?.captureOptOut?.indeterminate() ?? false;
|
|
1898
|
-
},
|
|
1899
|
-
get memoryCaptureControlDir() {
|
|
1900
|
-
return memoryEngineSession?.engine.controlPlaneDir;
|
|
1901
|
-
},
|
|
1902
|
-
get memoryCaptureAncestors() {
|
|
1903
|
-
const ctl = memoryEngineSession?.engine.controlPlaneDir;
|
|
1904
|
-
return [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }];
|
|
1905
|
-
},
|
|
1906
|
-
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
1907
|
-
parentCwd: taskRootFinal,
|
|
1908
|
-
...(fileHistoryEnabled ? { fileHistoryLineage: { scope: historyScope, root: historyRoot, fs: historyFs } } : {}),
|
|
1909
|
-
reminderMark,
|
|
1910
|
-
reminderDisclosureCounts,
|
|
1911
|
-
...(centerAdoption !== undefined ? { centerArtifactDigest: centerAdoption.artifact.artifactDigest } : {}),
|
|
1912
|
-
...(centerAdoption?.sourceRevision !== undefined ? { centerSourceRevision: centerAdoption.sourceRevision } : {}),
|
|
1913
|
-
activeSkillScope: () => skillScope.active(),
|
|
1914
|
-
inheritedGateForChildren,
|
|
1915
|
-
delegationProvenanceForChildren,
|
|
1916
|
-
delegationSettlement: delegationSettlementForChildren,
|
|
1917
|
-
...(autoModeDecider ? { autoModeReview: { decider: autoModeDecider } } : {}),
|
|
1918
|
-
...(spec.durableApproval !== undefined ? { durableApprovalForChildren: { ...spec.durableApproval } } : {}),
|
|
1919
|
-
...(spec.checkpointStore === null ? { checkpointStoreDisabledForChildren: true } : {}),
|
|
1920
|
-
taskId: hostTaskId,
|
|
1921
|
-
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
1922
|
-
...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
|
|
1923
|
-
...(internals?.rootSessionId !== undefined ? { rootSessionId: internals.rootSessionId } : {}), ...(internals?.placementRoot !== undefined ? { placementRoot: internals.placementRoot } : {}),
|
|
1924
|
-
...(internals?.explicitAgentName !== undefined ? { spawnedAgentName: internals.explicitAgentName } : {}),
|
|
1925
|
-
...(internals?.peerSelfRef !== undefined ? { peerSelfRef: internals.peerSelfRef } : {}),
|
|
1926
|
-
...(internals?.peerInboundChainRef !== undefined ? { peerInboundChainRef: internals.peerInboundChainRef } : {}),
|
|
1927
|
-
...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}),
|
|
1928
|
-
...(sessions.fork !== undefined
|
|
1929
|
-
? {
|
|
1930
|
-
hostSessionFork: async () => {
|
|
1931
|
-
const forked = (await sessions.fork(sessionId, spec.principal ?? null)) ?? null;
|
|
1932
|
-
if (forked === null)
|
|
1933
|
-
return null;
|
|
1934
|
-
return {
|
|
1935
|
-
sessionId: forked,
|
|
1936
|
-
release: async () => {
|
|
1937
|
-
await sessions.release?.(forked);
|
|
1938
|
-
},
|
|
1939
|
-
};
|
|
1940
|
-
},
|
|
1941
|
-
}
|
|
1942
|
-
: {}),
|
|
1943
|
-
sessionId,
|
|
1944
|
-
...(spec.backgroundScope !== undefined ? { backgroundScope: spec.backgroundScope } : {}),
|
|
1945
|
-
...(resolvedInteractionPosture !== undefined ? { interactionPosture: resolvedInteractionPosture } : {}),
|
|
1946
|
-
...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
|
|
1947
|
-
...(internals?.onTaskNotification ? { onTaskNotification: internals.onTaskNotification } : {}),
|
|
1948
|
-
...(forwardEvent ? { forwardEvent } : {}),
|
|
1949
|
-
...(internals?.onSubagentSpawn ? { onSubagentSpawn: internals.onSubagentSpawn } : {}),
|
|
1950
|
-
...(subagentRetain ? { subagentRetain } : {}),
|
|
1951
|
-
...(worktreeIsolation ? { worktreeIsolation } : {}),
|
|
1952
|
-
...(internals?.insideFork === true ? { insideFork: true } : {}),
|
|
1953
|
-
...(agentForkDenial !== undefined ? { forkAccess: { denied: agentForkDenial } } : {}),
|
|
1954
|
-
...(observersActive ? { observersAllowed: true } : {}),
|
|
1955
|
-
requestReview,
|
|
1956
|
-
requestStopAfterTurn,
|
|
1957
|
-
...(spec.enablePlanMode === true ? { enterPlanMode } : {}),
|
|
1958
|
-
});
|
|
1959
|
-
const tools = (spec.tools ?? []).map((t) => {
|
|
1960
|
-
if (isDefineToolProduct(t)) {
|
|
1961
|
-
return maybeOffload(t, t);
|
|
1962
|
-
}
|
|
1963
|
-
return maybeOffload(defineTool({
|
|
1964
|
-
...t,
|
|
1965
|
-
execute: (args, ctx) => t.execute(args, enrichSpecToolCtx(ctx)),
|
|
1966
|
-
}), t);
|
|
1967
|
-
});
|
|
1968
|
-
const blockedRef = {};
|
|
1969
|
-
if (spec.enableBlockedReport !== false) {
|
|
1970
|
-
tools.push(createReportBlockedTool(blockedRef));
|
|
1971
|
-
}
|
|
1972
|
-
if (!(spec.tools ?? []).some((t) => t.name === REPORT_FINDINGS_TOOL_NAME || t.aliases?.includes(REPORT_FINDINGS_TOOL_NAME))) {
|
|
1973
|
-
tools.push(createReportFindingsTool());
|
|
1974
|
-
}
|
|
1975
|
-
if (spec.enablePlanMode === true && spec.interactiveTools !== false) {
|
|
1976
|
-
const planReviewFace = resolveCheckpointStore(spec, deps) !== undefined;
|
|
1977
|
-
if (spec.interactiveTools === true || planReviewFace) {
|
|
1978
|
-
tools.push(defineTool(createPresentPlanTool(requestReview)));
|
|
1979
|
-
if (planReviewFace) {
|
|
1980
|
-
tools.push(defineTool(createEnterPlanModeTool(enterPlanMode)));
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
let runtimeCaps;
|
|
1985
|
-
let runtimeCapsFaulted = false;
|
|
1986
|
-
if (deps.runtimeCapsResolver) {
|
|
1987
|
-
try {
|
|
1988
|
-
runtimeCaps = (await deps.runtimeCapsResolver(spec.principal)) ?? undefined;
|
|
1989
|
-
}
|
|
1990
|
-
catch (err) {
|
|
1991
|
-
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
|
|
1992
|
-
runtimeCaps = { allowWorkflows: false, allowFork: false, autoMode: false };
|
|
1993
|
-
runtimeCapsFaulted = true;
|
|
1994
|
-
}
|
|
1995
|
-
const screened = screenAutoModeCap(runtimeCaps, deps.onError, sessionId);
|
|
1996
|
-
runtimeCaps = screened.caps;
|
|
1997
|
-
runtimeCapsFaulted ||= screened.faulted;
|
|
1998
|
-
}
|
|
1999
|
-
let complianceDenies = new Set();
|
|
2000
|
-
let complianceDegraded = false;
|
|
2001
|
-
if (deps.compliancePostureResolver) {
|
|
2002
|
-
try {
|
|
2003
|
-
const posture = (await deps.compliancePostureResolver(spec.principal)) ?? undefined;
|
|
2004
|
-
if (posture !== undefined)
|
|
2005
|
-
complianceDenies = resolveComplianceDenies(posture);
|
|
2006
|
-
}
|
|
2007
|
-
catch (err) {
|
|
2008
|
-
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
|
|
2009
|
-
complianceDenies = new Set(COMPLIANCE_CAPABILITIES);
|
|
2010
|
-
complianceDegraded = true;
|
|
2011
|
-
}
|
|
2012
|
-
const complianceRefuse = (capability, requested) => {
|
|
2013
|
-
const e = new Error(`${requested} is denied for this principal by the compliance posture (capability "${capability}"` +
|
|
2014
|
-
`${complianceDegraded ? "; the posture resolver is currently failing, so every managed capability is denied fail-closed" : ""}) — ` +
|
|
2015
|
-
`the task is refused rather than silently narrowed.`);
|
|
2016
|
-
e.code = complianceDegraded ? "config.compliance_required" : "config.compliance_denied";
|
|
2017
|
-
throw e;
|
|
2018
|
-
};
|
|
2019
|
-
if (complianceDenies.has("mcp_servers") && lockedPreflight.mcp?.length) {
|
|
2020
|
-
complianceRefuse("mcp_servers", "TaskSpec.mcp (MCP server materialization)");
|
|
2021
|
-
}
|
|
2022
|
-
if (complianceDenies.has("workflows") && spec.selfOrchestration === true) {
|
|
2023
|
-
complianceRefuse("workflows", "TaskSpec.selfOrchestration (workflow self-orchestration)");
|
|
2024
|
-
}
|
|
2025
|
-
if (complianceDenies.has("web_fetch")) {
|
|
2026
|
-
const webTool = (spec.tools ?? []).find((t) => t.name === WEB_FETCH_TOOL_NAME || (t.aliases ?? []).includes(WEB_FETCH_TOOL_NAME));
|
|
2027
|
-
if (webTool !== undefined) {
|
|
2028
|
-
complianceRefuse("web_fetch", `TaskSpec.tools["${webTool.name}"] (the WebFetch tool face)`);
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
}
|
|
2032
|
-
agentForkDenial = forkGovernanceDenial(spec.enableFork, runtimeCaps?.allowFork);
|
|
2033
|
-
observersActive = runtimeCaps?.allowObservers === true;
|
|
2034
|
-
const autoModeArmReason = autoModeArmReasonOf(autoModeIntent, deps.autoMode !== undefined, runtimeCaps?.autoMode, runtimeCapsFaulted);
|
|
2035
|
-
if (autoModeIntent && deps.autoMode !== undefined && runtimeCaps?.autoMode !== false) {
|
|
2036
|
-
const am = deps.autoMode;
|
|
2037
|
-
let classifierModel;
|
|
2038
|
-
try {
|
|
2039
|
-
classifierModel = resolveTaskModel({ modelRole: "classifier" }, deps).model;
|
|
2040
|
-
}
|
|
2041
|
-
catch {
|
|
2042
|
-
classifierModel = model;
|
|
2043
|
-
}
|
|
2044
|
-
if (await derivedRouteFallsBack({ seat: "auto-mode-classifier", derived: classifierModel, primary: model, brain: deps.brain, getApiKeyAndHeaders: spec.getApiKeyAndHeaders, onNotice: deps.onNotice, sessionId, runId })) {
|
|
2045
|
-
classifierModel = model;
|
|
2046
|
-
}
|
|
2047
|
-
const classifierLaneRule = peerLaneActive && peerSendMessageBuiltIn;
|
|
2048
|
-
const classifierSystemPrompt = buildAutoModePrompt(classifierLaneRule ? { ...am, crossSessionMessagesRule: CROSS_SESSION_CLASSIFIER_RULE } : am);
|
|
2049
|
-
const classifierRuntime = brainToRuntime(deps.brain);
|
|
2050
|
-
autoModeDenialTracking = createAutoModeDenialTracker(am.denialLimit);
|
|
2051
|
-
autoModeDecider = createAutoModeDecider({
|
|
2052
|
-
...(am.timeoutMs !== undefined ? { timeoutMs: am.timeoutMs } : {}),
|
|
2053
|
-
...(am.failureThreshold !== undefined ? { failureThreshold: am.failureThreshold } : {}),
|
|
2054
|
-
...(am.onBreakerOpen !== undefined ? { onBreakerOpen: am.onBreakerOpen } : {}),
|
|
2055
|
-
classify: async (input, signal) => {
|
|
2056
|
-
const ctx = await session.buildContext();
|
|
2057
|
-
const known = ctx.messages.filter((m) => !isSyntheticApiErrorMessage(m) &&
|
|
2058
|
-
(m.role === "user" || m.role === "assistant" || m.role === "toolResult"));
|
|
2059
|
-
const userPrompt = renderAutoModeWindow(known, am.window) + renderAutoModeAction(input);
|
|
2060
|
-
const classifierAuth = await spec.getApiKeyAndHeaders?.(classifierModel);
|
|
2061
|
-
const response = await classifierRuntime.completeSimple(classifierModel, { systemPrompt: classifierSystemPrompt, messages: [{ role: "user", content: userPrompt, timestamp: Date.now() }] }, {
|
|
2062
|
-
signal,
|
|
2063
|
-
...(classifierAuth?.apiKey !== undefined ? { apiKey: classifierAuth.apiKey } : {}),
|
|
2064
|
-
...(classifierAuth?.headers !== undefined ? { headers: classifierAuth.headers } : {}),
|
|
2065
|
-
});
|
|
2066
|
-
return response.content
|
|
2067
|
-
.filter((c) => c.type === "text")
|
|
2068
|
-
.map((c) => c.text)
|
|
2069
|
-
.join("");
|
|
2070
|
-
},
|
|
2071
|
-
});
|
|
2072
|
-
if (am.persistArming === true)
|
|
2073
|
-
autoModeArming = mintPersistedArming(am, classifierLaneRule, classifierSystemPrompt, deps.onError, sessionId);
|
|
2074
|
-
}
|
|
2075
|
-
const deploymentWorkflowReady = runnerSelf !== undefined && isSelfOrchestrationActive(spec, deps);
|
|
2076
|
-
const selfOrchestrationActive = deploymentWorkflowReady && runtimeCaps?.allowWorkflows !== false;
|
|
2077
|
-
if (deploymentWorkflowReady && runtimeCaps?.allowWorkflows === false) {
|
|
2078
|
-
deps.onError?.(new Error(`self-orchestration DENIED for principal "${spec.principal ?? ""}" by runtimeCaps.allowWorkflows=false ` +
|
|
2079
|
-
`(per-principal entitlement governance, not a misconfiguration — the run_workflow tool is not mounted)`), { phase: "config", sessionId });
|
|
2080
|
-
}
|
|
2081
|
-
let workflowToolsActive = false;
|
|
2082
|
-
let workflowSizeGuideline;
|
|
2083
|
-
if (selfOrchestrationActive && runnerSelf && deps.workflowScriptRunner && deps.workflowGovernanceBaseline) {
|
|
2084
|
-
workflowToolsActive = true;
|
|
2085
|
-
const currentSizeGuideline = () => resolveWorkflowSizeGuideline(deps.workflowLimits?.sizeGuideline).size;
|
|
2086
|
-
workflowSizeGuideline = { legGuideline: currentSizeGuideline(), current: currentSizeGuideline };
|
|
2087
|
-
toolEffects.set(RUN_WORKFLOW_TOOL_NAME, "write");
|
|
2088
|
-
tools.push(await createRunWorkflowTool({
|
|
2089
|
-
runner: runnerSelf,
|
|
2090
|
-
scriptRunner: deps.workflowScriptRunner,
|
|
2091
|
-
governanceBaseline: deps.workflowGovernanceBaseline,
|
|
2092
|
-
parentExcludeTools: toolFaceSnapshot.exclude,
|
|
2093
|
-
parentDeferTools: toolFaceSnapshot.defer,
|
|
2094
|
-
parentAlwaysLoadTools: toolFaceSnapshot.alwaysLoad,
|
|
2095
|
-
...(toolFaceSnapshot.restoreGated !== undefined ? { parentRestoreGatedTools: toolFaceSnapshot.restoreGated } : {}),
|
|
2096
|
-
parentPromptProfile: promptProfile,
|
|
2097
|
-
models: deps.models,
|
|
2098
|
-
agents: deps.agents,
|
|
2099
|
-
builtinAgents: deps.builtinAgents,
|
|
2100
|
-
store: deps.workflowRunStore,
|
|
2101
|
-
journalStore: deps.workflowJournalStore,
|
|
2102
|
-
...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
|
|
2103
|
-
scriptStore: deps.workflowScriptStore,
|
|
2104
|
-
builtinWorkflows: deps.builtinWorkflows,
|
|
2105
|
-
onAgentSpawn: deps.onWorkflowAgentSpawn,
|
|
2106
|
-
scope: taskScope,
|
|
2107
|
-
notifier: deps.workflowCompletionNotifier,
|
|
2108
|
-
originatingSessionId: sessionId,
|
|
2109
|
-
rootSessionId: internals?.rootSessionId ?? sessionId, ...(internals?.placementRoot !== undefined ? { placementRoot: internals.placementRoot } : {}),
|
|
2110
|
-
taskRegistry: defaultTaskRegistry,
|
|
2111
|
-
taskNotification: internals?.onTaskNotification,
|
|
2112
|
-
taskOwner: hostTaskId,
|
|
2113
|
-
limits: deps.workflowLimits,
|
|
2114
|
-
sourceTaskId: hostTaskId,
|
|
2115
|
-
parentModel: () => harnessRef.current?.getModel(),
|
|
2116
|
-
...(spec.getApiKeyAndHeaders !== undefined ? { parentGetApiKeyAndHeaders: spec.getApiKeyAndHeaders } : {}),
|
|
2117
|
-
...(frozenOnAsk !== undefined ? { parentOnAsk: frozenOnAsk } : {}),
|
|
2118
|
-
principal: spec.principal,
|
|
2119
|
-
oneShot: spec.oneShot,
|
|
2120
|
-
...(resolvedInteractionPosture !== undefined ? { parentInteractionPosture: resolvedInteractionPosture } : {}),
|
|
2121
|
-
parentMemoryCaptureState: () => {
|
|
2122
|
-
const ctl = memoryEngineSession?.engine.controlPlaneDir;
|
|
2123
|
-
const co = memoryEngineSession?.captureOptOut;
|
|
2124
|
-
return assembleParentCaptureState(co?.optedOut() ?? false, co?.indeterminate() ?? false, ctl, [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }]);
|
|
2125
|
-
},
|
|
2126
|
-
autoModeReview: () => (autoModeDecider !== undefined ? { decider: autoModeDecider } : undefined),
|
|
2127
|
-
workflowDepth: internals?.workflowDepth,
|
|
2128
|
-
parentCwd: taskRootFinal,
|
|
2129
|
-
parentThinking: () => harnessRef.current?.getThinkingLevel() ?? thinking,
|
|
2130
|
-
parentReadFace: () => carrierReadFace(),
|
|
2131
|
-
parentReadDenyPatterns: () => (readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized : undefined),
|
|
2132
|
-
...(spec.handsReadOnly === true ? { parentHandsReadOnly: true } : {}), ...(spec.interactiveTools === false ? { parentInteractiveTools: false } : {}),
|
|
2133
|
-
onNotice: deps.onNotice,
|
|
2134
|
-
parentCheckpointStoreDisabled: spec.checkpointStore === null,
|
|
2135
|
-
parentCenterArtifactDigest: () => centerAdoption?.artifact.artifactDigest,
|
|
2136
|
-
parentCenterSourceRevision: () => centerAdoption?.sourceRevision,
|
|
2137
|
-
...(forwardEvent ? { forwardEvent } : {}),
|
|
2138
|
-
inheritedGateForChildren,
|
|
2139
|
-
}));
|
|
2140
|
-
}
|
|
903
|
+
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, centerAdoption: () => centerAdoption, memoryEngineSession: () => memoryEngineSession });
|
|
904
|
+
const { tools, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoption, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
|
|
2141
905
|
const suspendRef = {};
|
|
2142
906
|
const reviewRef = {};
|
|
2143
907
|
const remoteEnvFailures = [];
|
|
@@ -2170,191 +934,36 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2170
934
|
resourceSpentMicroUsd: priorLedger?.spentMicroUsd ?? 0,
|
|
2171
935
|
suspendCount: priorSuspendCount,
|
|
2172
936
|
};
|
|
2173
|
-
let worktreeSessionRef;
|
|
2174
937
|
let suspendForResource;
|
|
2175
938
|
let suspendForPlatformLimit;
|
|
2176
939
|
let suspendForReview;
|
|
2177
|
-
const outputRef = {};
|
|
2178
|
-
if (resume?.seed.outputRef) {
|
|
2179
|
-
outputRef.value = resume.seed.outputRef.value;
|
|
2180
|
-
outputRef.set = resume.seed.outputRef.set;
|
|
2181
|
-
}
|
|
2182
|
-
if (spec.outputSchema) {
|
|
2183
|
-
if ((spec.tools ?? []).some((t) => t.name === OUTPUT_TOOL_NAME)) {
|
|
2184
|
-
const e = new Error(`Tool name "${OUTPUT_TOOL_NAME}" is reserved when TaskSpec.outputSchema is set.`);
|
|
2185
|
-
e.code = "config.reserved_tool_name";
|
|
2186
|
-
throw e;
|
|
2187
|
-
}
|
|
2188
|
-
const compiled = compileOutputSchema(spec.outputSchema);
|
|
2189
|
-
if ("error" in compiled) {
|
|
2190
|
-
const e = new Error(`TaskSpec.outputSchema is not a valid JSON Schema: ${compiled.error}`);
|
|
2191
|
-
e.code = "config.invalid_output_schema";
|
|
2192
|
-
throw e;
|
|
2193
|
-
}
|
|
2194
|
-
if (!compiled.strict) {
|
|
2195
|
-
try {
|
|
2196
|
-
deps.onError?.(new Error(`structured-output: strict schema derivation fell back to non-strict (${compiled.fallbackReason}) — the schema is served to the model as-is`), { phase: "config", sessionId });
|
|
2197
|
-
}
|
|
2198
|
-
catch {
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
tools.push(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined));
|
|
2202
|
-
}
|
|
2203
|
-
mcp = lockedPreflight.mcp?.length
|
|
2204
|
-
? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer, { reminderMark, counts: reminderDisclosureCounts }, mcpRevocationWiring(deps, runId))
|
|
2205
|
-
: { tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], dispose: async () => { } };
|
|
2206
|
-
for (const w of mcp.warnings)
|
|
2207
|
-
deps.onError?.(w, { phase: "mcp", sessionId });
|
|
2208
|
-
for (const d of mcp.droppedTools) {
|
|
2209
|
-
deps.onError?.(new Error(`MCP tool dropped at intake — ${d.tool} (server "${d.server}"): ${d.reason}`), { phase: "mcp", sessionId });
|
|
2210
|
-
}
|
|
2211
|
-
{
|
|
2212
|
-
const callerNames = new Set((spec.tools ?? []).flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
2213
|
-
const clash = mcp.tools.find((t) => [t.name, ...(t.aliases ?? [])].some((name) => callerNames.has(name)));
|
|
2214
|
-
if (clash) {
|
|
2215
|
-
await mcp.dispose();
|
|
2216
|
-
const e = new Error(`Tool name "${clash.name}" is reserved by an injected MCP tool — a caller tool of the same name would silently shadow it.`);
|
|
2217
|
-
e.code = "config.reserved_tool_name";
|
|
2218
|
-
throw e;
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
tools.push(...mcp.tools.map((t) => remoteToolOffload(t)));
|
|
2222
|
-
const rebuildHarnessToolsRef = {};
|
|
2223
|
-
const contentOriginWrapRef = {};
|
|
2224
|
-
const toolCallGateArmedRef = { armed: false };
|
|
2225
|
-
if (lockedPreflight.mcp?.length) {
|
|
2226
|
-
tools.push({
|
|
2227
|
-
name: "RefreshMcpTools",
|
|
2228
|
-
label: "RefreshMcpTools",
|
|
2229
|
-
description: "Refresh the tool list of connected MCP servers. Never dials or re-dials connections — it only re-reads the tool list over the existing connection. The refreshed tools are available immediately; you can call them on your next step. Use when a server's expected tool is missing, or the tool list looks stale after a connection recovered. Omit `server` to refresh every connected server.",
|
|
2230
|
-
parameters: Type.Object({
|
|
2231
|
-
server: Type.Optional(Type.String({ description: "Name of a single MCP server to refresh; omit to refresh all connected servers." })),
|
|
2232
|
-
}),
|
|
2233
|
-
execute: async (_toolCallId, params) => {
|
|
2234
|
-
const serverArg = params?.server;
|
|
2235
|
-
const results = await mcp.refresh(typeof serverArg === "string" && serverArg.length > 0 ? serverArg : undefined);
|
|
2236
|
-
const lines = [];
|
|
2237
|
-
let changed = false;
|
|
2238
|
-
let anyActiveFailure = false;
|
|
2239
|
-
for (const r of results) {
|
|
2240
|
-
if (r.status !== "refreshed" || r.tools === undefined) {
|
|
2241
|
-
lines.push(`${r.server}: ${r.status}${r.error !== undefined ? ` (${r.error})` : ""}`);
|
|
2242
|
-
if (r.status === "failed")
|
|
2243
|
-
anyActiveFailure = true;
|
|
2244
|
-
continue;
|
|
2245
|
-
}
|
|
2246
|
-
const excludedSet = new Set(toolFaceSnapshot.exclude ?? []);
|
|
2247
|
-
const pushable = r.tools.filter((t) => !excludedSet.has(t.name));
|
|
2248
|
-
const excludedNow = r.tools.filter((t) => excludedSet.has(t.name)).map((t) => t.name);
|
|
2249
|
-
const domainSnapshot = (m) => new Map([...m].filter(([name]) => name.startsWith(r.prefix)));
|
|
2250
|
-
const restoreDomain = (m, snap) => {
|
|
2251
|
-
for (const name of [...m.keys()])
|
|
2252
|
-
if (name.startsWith(r.prefix))
|
|
2253
|
-
m.delete(name);
|
|
2254
|
-
for (const [name, v] of snap)
|
|
2255
|
-
m.set(name, v);
|
|
2256
|
-
};
|
|
2257
|
-
const priorDomainEffects = domainSnapshot(toolEffects);
|
|
2258
|
-
const priorDomainNegatives = domainSnapshot(axisExplicitNegatives);
|
|
2259
|
-
for (const name of priorDomainEffects.keys())
|
|
2260
|
-
toolEffects.delete(name);
|
|
2261
|
-
for (const name of priorDomainNegatives.keys())
|
|
2262
|
-
axisExplicitNegatives.delete(name);
|
|
2263
|
-
try {
|
|
2264
|
-
foldProtocolAxes((r.axes ?? []).filter((a) => !excludedSet.has(a.name)), "MCP");
|
|
2265
|
-
}
|
|
2266
|
-
catch (foldErr) {
|
|
2267
|
-
restoreDomain(toolEffects, priorDomainEffects);
|
|
2268
|
-
restoreDomain(axisExplicitNegatives, priorDomainNegatives);
|
|
2269
|
-
lines.push(`${r.server}: failed (${foldErr instanceof Error ? foldErr.message : String(foldErr)})`);
|
|
2270
|
-
anyActiveFailure = true;
|
|
2271
|
-
continue;
|
|
2272
|
-
}
|
|
2273
|
-
let domainAnchor = -1;
|
|
2274
|
-
for (let i = tools.length - 1; i >= 0; i--) {
|
|
2275
|
-
const t = tools[i];
|
|
2276
|
-
if (t.name.startsWith(r.prefix)) {
|
|
2277
|
-
domainAnchor = i;
|
|
2278
|
-
tools.splice(i, 1);
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
const refreshedMounts = pushable.map((t) => remoteToolOffload(t));
|
|
2282
|
-
if (domainAnchor >= 0)
|
|
2283
|
-
tools.splice(domainAnchor, 0, ...refreshedMounts);
|
|
2284
|
-
else
|
|
2285
|
-
tools.push(...refreshedMounts);
|
|
2286
|
-
changed = true;
|
|
2287
|
-
const detail = [];
|
|
2288
|
-
const shownAdded = r.added.filter((n) => !excludedSet.has(n));
|
|
2289
|
-
if (shownAdded.length > 0)
|
|
2290
|
-
detail.push(`added: ${shownAdded.join(", ")}`);
|
|
2291
|
-
if (r.removed.length > 0)
|
|
2292
|
-
detail.push(`removed: ${r.removed.join(", ")}`);
|
|
2293
|
-
if (excludedNow.length > 0)
|
|
2294
|
-
detail.push(`excluded by deployment config (not mounted): ${excludedNow.join(", ")}`);
|
|
2295
|
-
if ((r.dropped?.length ?? 0) > 0)
|
|
2296
|
-
detail.push(`dropped: ${r.dropped.map((d) => `${d.tool} (${d.reason.length > 90 ? `${d.reason.slice(0, 90)}…` : d.reason})`).join("; ")}`);
|
|
2297
|
-
lines.push(`${r.server}: refreshed — ${pushable.length} tool${pushable.length === 1 ? "" : "s"}${detail.length > 0 ? ` (${detail.join("; ")})` : ""}`);
|
|
2298
|
-
}
|
|
2299
|
-
if (changed)
|
|
2300
|
-
await rebuildHarnessToolsRef.current?.();
|
|
2301
|
-
if (changed && !toolCallGateArmedRef.armed && (irreversibleTools.size > 0 || egressTools.size > 0)) {
|
|
2302
|
-
lines.push("WARNING: refreshed tools declare irreversible/egress safety hints, but this task started with no approval gate registered (no policy/hooks and no gated tools at start) — these hints cannot arm a gate mid-task (RB-46); the refreshed tools run ungated on this deployment shape.");
|
|
2303
|
-
}
|
|
2304
|
-
const text = lines.length === 0 ? "No connected MCP servers to refresh." : lines.join("\n");
|
|
2305
|
-
return {
|
|
2306
|
-
content: [{ type: "text", text }],
|
|
2307
|
-
details: { results },
|
|
2308
|
-
terminate: false,
|
|
2309
|
-
...(anyActiveFailure && !changed ? { isError: true } : {}),
|
|
2310
|
-
};
|
|
2311
|
-
},
|
|
2312
|
-
});
|
|
2313
|
-
toolEffects.set("RefreshMcpTools", "read");
|
|
2314
|
-
}
|
|
2315
|
-
const foldProtocolAxes = (axes, protocolLabel) => {
|
|
2316
|
-
for (const axis of axes) {
|
|
2317
|
-
if (axis.irreversibility === "always") {
|
|
2318
|
-
irreversibilityTier.set(axis.name, "always");
|
|
2319
|
-
irreversibleTools.add(axis.name);
|
|
2320
|
-
}
|
|
2321
|
-
if (axis.irreversibility === "never") {
|
|
2322
|
-
axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), irreversible: false });
|
|
2323
|
-
}
|
|
2324
|
-
if (axis.egress === false) {
|
|
2325
|
-
axisExplicitNegatives.set(axis.name, { ...axisExplicitNegatives.get(axis.name), egress: false });
|
|
2326
|
-
}
|
|
2327
|
-
if (axis.egress) {
|
|
2328
|
-
if (axis.effect !== undefined && axis.effect !== "write") {
|
|
2329
|
-
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".`);
|
|
2330
|
-
e.code = "config.egress_requires_write_effect";
|
|
2331
|
-
throw e;
|
|
2332
|
-
}
|
|
2333
|
-
egressTools.add(axis.name);
|
|
2334
|
-
}
|
|
2335
|
-
if (!toolEffects.has(axis.name))
|
|
2336
|
-
toolEffects.set(axis.name, axis.effect ?? "write");
|
|
2337
|
-
}
|
|
2338
|
-
};
|
|
2339
|
-
foldProtocolAxes(mcp.toolAxes, "MCP");
|
|
2340
|
-
a2a = spec.a2a?.length
|
|
2341
|
-
? await materializeA2aTools(spec.a2a, spec.principal, abortController.signal)
|
|
2342
|
-
: { tools: [], toolAxes: [], warnings: [], statuses: [], refresh: async () => [], dispose: async () => { } };
|
|
2343
|
-
for (const w of a2a.warnings)
|
|
2344
|
-
deps.onError?.(w, { phase: "a2a", sessionId });
|
|
2345
|
-
{
|
|
2346
|
-
const callerNames = new Set((spec.tools ?? []).flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
2347
|
-
const clash = a2a.tools.find((t) => callerNames.has(t.name));
|
|
2348
|
-
if (clash) {
|
|
2349
|
-
await a2a.dispose();
|
|
2350
|
-
await mcp.dispose();
|
|
2351
|
-
const e = new Error(`Tool name "${clash.name}" is reserved by an injected A2A tool — a caller tool of the same name would silently shadow it.`);
|
|
940
|
+
const outputRef = {};
|
|
941
|
+
if (resume?.seed.outputRef) {
|
|
942
|
+
outputRef.value = resume.seed.outputRef.value;
|
|
943
|
+
outputRef.set = resume.seed.outputRef.set;
|
|
944
|
+
}
|
|
945
|
+
if (spec.outputSchema) {
|
|
946
|
+
if ((spec.tools ?? []).some((t) => t.name === OUTPUT_TOOL_NAME)) {
|
|
947
|
+
const e = new Error(`Tool name "${OUTPUT_TOOL_NAME}" is reserved when TaskSpec.outputSchema is set.`);
|
|
2352
948
|
e.code = "config.reserved_tool_name";
|
|
2353
949
|
throw e;
|
|
2354
950
|
}
|
|
951
|
+
const compiled = compileOutputSchema(spec.outputSchema);
|
|
952
|
+
if ("error" in compiled) {
|
|
953
|
+
const e = new Error(`TaskSpec.outputSchema is not a valid JSON Schema: ${compiled.error}`);
|
|
954
|
+
e.code = "config.invalid_output_schema";
|
|
955
|
+
throw e;
|
|
956
|
+
}
|
|
957
|
+
if (!compiled.strict) {
|
|
958
|
+
try {
|
|
959
|
+
onceLedger.onError(new Error(`structured-output: strict schema derivation fell back to non-strict (${compiled.fallbackReason}) — the schema is served to the model as-is`), { phase: "config", sessionId });
|
|
960
|
+
}
|
|
961
|
+
catch {
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
tools.push(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined));
|
|
2355
965
|
}
|
|
2356
|
-
|
|
2357
|
-
foldProtocolAxes(a2a.toolAxes, "A2A");
|
|
966
|
+
const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, tools, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
|
|
2358
967
|
const callIssuedAtRef = {};
|
|
2359
968
|
const memoryWriteGateRef = {};
|
|
2360
969
|
const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, firstPartyOffload, tools, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts, ...(trackFileEdit !== undefined ? { trackFileEdit } : {}), onFileEdited: noteFileEdited, ...(restoredFilePaths.length > 0 ? { restoredFilePaths } : {}) };
|
|
@@ -2365,191 +974,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2365
974
|
handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
|
|
2366
975
|
shellGatedBash = handsReadFace.shellGatedBash;
|
|
2367
976
|
shellGatedMonitor = handsReadFace.shellGatedMonitor;
|
|
2368
|
-
const
|
|
2369
|
-
|
|
2370
|
-
if (delegationSurfaceActive) {
|
|
2371
|
-
toolEffects.set("TaskOutput", "read");
|
|
2372
|
-
toolEffects.set("TaskStop", "write");
|
|
2373
|
-
tools.push(firstPartyOffload(createTaskOutputTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore, notificationWired: internals?.onTaskNotification !== undefined, oneShot: spec.oneShot, toolResultStore: offloadStore })), firstPartyOffload(createTaskStopTool({ registry: defaultTaskRegistry, owner: hostTaskId, scope: taskScope, sessionId, workflowStore: deps.workflowRunStore, agentStore: deps.backgroundAgentStore })));
|
|
2374
|
-
}
|
|
2375
|
-
if (runnerSelf && !(spec.tools ?? []).some((t) => t.name === SEND_MESSAGE_TOOL_NAME)) {
|
|
2376
|
-
toolEffects.set(SEND_MESSAGE_TOOL_NAME, "write");
|
|
2377
|
-
axisExplicitNegatives.set(SEND_MESSAGE_TOOL_NAME, { ...axisExplicitNegatives.get(SEND_MESSAGE_TOOL_NAME), egress: false });
|
|
2378
|
-
const delegationForRevive = (spec.tools ?? []).find((t) => t.agentListing !== undefined);
|
|
2379
|
-
const reviveSpawn = deps.backgroundAgentStore !== undefined && deps.mailboxStore !== undefined ? buildTier3ReviveSpawn(delegationForRevive, enrichSpecToolCtx) : undefined;
|
|
2380
|
-
tools.push(firstPartyOffload(createSendMessageTool({
|
|
2381
|
-
runner: runnerSelf,
|
|
2382
|
-
registry: defaultTaskRegistry,
|
|
2383
|
-
...(subagentRetain ? { retain: subagentRetain } : {}),
|
|
2384
|
-
owner: hostTaskId,
|
|
2385
|
-
scope: taskScope,
|
|
2386
|
-
...(sessionId !== undefined ? { sessionId } : {}),
|
|
2387
|
-
...(internals?.onTaskNotification !== undefined ? { notify: internals.onTaskNotification } : {}), ...(spec.oneShot !== undefined ? { oneShot: spec.oneShot } : {}), retrievalToolMounted: delegationSurfaceActive,
|
|
2388
|
-
...(internals?.onSubagentSpawn !== undefined ? { sink: internals.onSubagentSpawn } : {}),
|
|
2389
|
-
...(internals?.parentNotify !== undefined
|
|
2390
|
-
? { uplink: internals.parentNotify, ...(internals.parentPeerRef !== undefined ? { uplinkRecipient: internals.parentPeerRef } : {}) }
|
|
2391
|
-
: {}),
|
|
2392
|
-
...(internals?.explicitAgentName !== undefined ? { senderName: internals.explicitAgentName } : {}),
|
|
2393
|
-
...(internals?.parentRetainLedger !== undefined ? { siblingRetain: internals.parentRetainLedger } : {}),
|
|
2394
|
-
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
2395
|
-
...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}),
|
|
2396
|
-
enrichCtx: enrichSpecToolCtx,
|
|
2397
|
-
...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}),
|
|
2398
|
-
...(deps.peerAdmission !== undefined ? { admission: deps.peerAdmission } : {}),
|
|
2399
|
-
...(deps.onNotice !== undefined ? { onNotice: deps.onNotice } : {}),
|
|
2400
|
-
...(internals?.peerSelfRef !== undefined ? { peerSelf: internals.peerSelfRef } : {}),
|
|
2401
|
-
...(internals?.peerInboundChainRef !== undefined ? { peerInbound: internals.peerInboundChainRef } : {}),
|
|
2402
|
-
...(deps.onBackgroundChildEvent ? { onBackgroundChildEvent: deps.onBackgroundChildEvent } : {}),
|
|
2403
|
-
...(deps.backgroundAgentStore !== undefined ? { agentStore: deps.backgroundAgentStore } : {}),
|
|
2404
|
-
...(deps.mailboxStore !== undefined ? { mailbox: deps.mailboxStore } : {}),
|
|
2405
|
-
...(reviveSpawn !== undefined ? { reviveSpawn } : {}),
|
|
2406
|
-
...(peerLaneActive ? peerLaneSendMessageSeats({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, ...(internals?.explicitAgentName !== undefined ? { name: internals.explicitAgentName } : {}), refs: peerLaneRefs, listingMounted: listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) }) : {}),
|
|
2407
|
-
onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: hostTaskId, site: f.site, message: f.error.message, ts: Date.now() })),
|
|
2408
|
-
onTranscriptIntegrityGap: (handle, scope) => deliverEngineNotice(deps.onNotice, {
|
|
2409
|
-
code: "delegation.transcript_integrity",
|
|
2410
|
-
message: `delegation transcript integrity: agent ${handle}'s durable row binds a transcript session the session store attests is gone — the declared transcript durability is being contradicted (check the session store wiring/retention)`,
|
|
2411
|
-
detail: { handle, ...(scope !== undefined ? { scope } : {}) },
|
|
2412
|
-
}),
|
|
2413
|
-
})));
|
|
2414
|
-
if (delegationSurfaceActive && !(spec.tools ?? []).some((t) => t.name === AGENT_TRANSCRIPT_TOOL_NAME)) {
|
|
2415
|
-
tools.push(firstPartyOffload(createAgentTranscriptTool({
|
|
2416
|
-
runner: runnerSelf,
|
|
2417
|
-
registry: defaultTaskRegistry,
|
|
2418
|
-
agentStore: deps.backgroundAgentStore,
|
|
2419
|
-
owner: hostTaskId,
|
|
2420
|
-
scope: taskScope,
|
|
2421
|
-
...(sessionId !== undefined ? { sessionId } : {}),
|
|
2422
|
-
enrichCtx: enrichSpecToolCtx,
|
|
2423
|
-
onTranscriptIntegrityGap: (handle, scope) => deliverEngineNotice(deps.onNotice, {
|
|
2424
|
-
code: "delegation.transcript_integrity",
|
|
2425
|
-
message: `delegation transcript integrity: agent ${handle}'s durable row binds a transcript session the session store attests is gone — the declared transcript durability is being contradicted (check the session store wiring/retention)`,
|
|
2426
|
-
detail: { handle, ...(scope !== undefined ? { scope } : {}) },
|
|
2427
|
-
}),
|
|
2428
|
-
})));
|
|
2429
|
-
}
|
|
2430
|
-
}
|
|
2431
|
-
}
|
|
2432
|
-
const listAgents = peerLaneActive && peerSendMessageBuiltIn && listAgentsMountable({ exclude: toolFaceSnapshot.exclude, specTools: spec.tools ?? [] }) ? mountListAgents({ peerDirectory: deps.peerDirectory, sessionId, scope: taskScope, hostTaskId, ...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}), ...(internals?.parentSessionId !== undefined ? { parentSessionId: internals.parentSessionId } : {}), registry: defaultTaskRegistry, ...(deps.rosterStore !== undefined ? { roster: deps.rosterStore } : {}), specTools: spec.tools ?? [], toolEffects }) : undefined;
|
|
2433
|
-
if (listAgents !== undefined)
|
|
2434
|
-
tools.push(firstPartyOffload(listAgents));
|
|
2435
|
-
if (backgroundTaskToolsActive) {
|
|
2436
|
-
toolEffects.set("Monitor", "write");
|
|
2437
|
-
envHandToolNames.add("Monitor");
|
|
2438
|
-
tools.push(firstPartyOffload(createMonitorTool(executionEnv, {
|
|
2439
|
-
registry: defaultTaskRegistry,
|
|
2440
|
-
owner: hostTaskId,
|
|
2441
|
-
scope: taskScope,
|
|
2442
|
-
...(sessionId !== undefined ? { sessionId } : {}),
|
|
2443
|
-
...(internals?.onTaskNotification !== undefined ? { onTaskNotification: internals.onTaskNotification } : {}),
|
|
2444
|
-
...(handsCwdRef !== undefined ? { cwdRef: handsCwdRef } : {}),
|
|
2445
|
-
...(offloadStore !== undefined ? { toolResultStore: offloadStore } : {}),
|
|
2446
|
-
...(spec.retainBackgroundProcesses === true ? { retainBackgroundProcesses: true } : {}),
|
|
2447
|
-
})));
|
|
2448
|
-
}
|
|
2449
|
-
if (handsCwdRef !== undefined) {
|
|
2450
|
-
toolEffects.set("EnterWorktree", "write");
|
|
2451
|
-
toolEffects.set("ExitWorktree", "write");
|
|
2452
|
-
worktreeSessionRef = {
|
|
2453
|
-
...(resume?.seed.activeWorktree
|
|
2454
|
-
? {
|
|
2455
|
-
current: {
|
|
2456
|
-
...resume.seed.activeWorktree,
|
|
2457
|
-
worktreeDir: rebaseRestoredPath(resume.seed.activeWorktree.worktreeDir),
|
|
2458
|
-
originalCwd: rebaseRestoredPath(resume.seed.activeWorktree.originalCwd),
|
|
2459
|
-
},
|
|
2460
|
-
}
|
|
2461
|
-
: wsSnapshot?.activeWorktree !== undefined
|
|
2462
|
-
? {
|
|
2463
|
-
current: {
|
|
2464
|
-
...wsSnapshot.activeWorktree,
|
|
2465
|
-
worktreeDir: rebaseWsPath(wsSnapshot.activeWorktree.worktreeDir),
|
|
2466
|
-
originalCwd: rebaseWsPath(wsSnapshot.activeWorktree.originalCwd),
|
|
2467
|
-
},
|
|
2468
|
-
}
|
|
2469
|
-
: {}),
|
|
2470
|
-
};
|
|
2471
|
-
if (workspaceStateSettle !== undefined)
|
|
2472
|
-
workspaceStateSettle.restoredWorktreeDir = worktreeSessionRef.current?.worktreeDir;
|
|
2473
|
-
tools.push(...createWorktreeTools(executionEnv, { repoRoot: taskRootFinal, cwdRef: handsCwdRef, session: worktreeSessionRef }).map((t) => (envHandToolNames.add(t.name), firstPartyOffload(t))));
|
|
2474
|
-
}
|
|
2475
|
-
if (offloadStore)
|
|
2476
|
-
tools.push(createReadToolResultTool(offloadStore));
|
|
2477
|
-
const onQuestion = frozenOnQuestion;
|
|
2478
|
-
const liveQuestionFace = isLiveQuestionFace(onQuestion) ? onQuestion : undefined;
|
|
2479
|
-
const contentAskBindings = new Map();
|
|
2480
|
-
const CONTENT_ASK_BINDING_CAP = 32;
|
|
2481
|
-
const checkpointStore = resolveCheckpointStore(spec, deps);
|
|
2482
|
-
const questionParkStoreWired = checkpointStore !== undefined;
|
|
2483
|
-
const questionDurableMandateBinding = runtimeCaps?.forceDurableGate === true && questionParkStoreWired;
|
|
2484
|
-
const contentAskRoutable = (toolCallId) => liveQuestionFace !== undefined &&
|
|
2485
|
-
mountedQuestionTool !== undefined &&
|
|
2486
|
-
tools.includes(mountedQuestionTool) &&
|
|
2487
|
-
!questionDurableMandateBinding &&
|
|
2488
|
-
!inheritedUnavailableAsks.has(toolCallId);
|
|
2489
|
-
const lateStrandedAnswers = [];
|
|
2490
|
-
const discloseStrandedAnswers = (records, why) => {
|
|
2491
|
-
if (records.length === 0)
|
|
2492
|
-
return;
|
|
2493
|
-
try {
|
|
2494
|
-
deps.onError?.(new Error(`AskUserQuestion: ${records.length} question(s) were answered by a person but the call never ` +
|
|
2495
|
-
`executed to collect the answer (${records.map((r) => `${r.toolCallId} [delivery ${r.deliveryId}]`).join(", ")}) — ${why}. The answer(s) were NOT ` +
|
|
2496
|
-
`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" });
|
|
2497
|
-
}
|
|
2498
|
-
catch {
|
|
2499
|
-
}
|
|
2500
|
-
};
|
|
2501
|
-
const settleContentAskBindings = () => {
|
|
2502
|
-
const stranded = [];
|
|
2503
|
-
for (const [callId, bound] of contentAskBindings) {
|
|
2504
|
-
if (bound.kind === "answered")
|
|
2505
|
-
stranded.push({ deliveryId: bound.deliveryId, toolCallId: callId });
|
|
2506
|
-
}
|
|
2507
|
-
contentAskBindings.clear();
|
|
2508
|
-
discloseStrandedAnswers(stranded, "the leg ended first (abort, batch teardown, or a loop failure)");
|
|
2509
|
-
const byDelivery = new Map();
|
|
2510
|
-
for (const r of [...stranded, ...lateStrandedAnswers])
|
|
2511
|
-
byDelivery.set(r.deliveryId, r);
|
|
2512
|
-
return [...byDelivery.values()];
|
|
2513
|
-
};
|
|
2514
|
-
const durableQuestionFace = questionParkStoreWired && (spec.durableApproval !== undefined || runtimeCaps?.forceDurableGate === true);
|
|
2515
|
-
const mountedQuestionFace = liveQuestionFace !== undefined
|
|
2516
|
-
? async (req, signal) => {
|
|
2517
|
-
const bound = contentAskBindings.get(req.toolCallId);
|
|
2518
|
-
if (bound !== undefined) {
|
|
2519
|
-
if (bound.questionsHash === boundInputHashOf(req.questions)) {
|
|
2520
|
-
contentAskBindings.delete(req.toolCallId);
|
|
2521
|
-
if (bound.kind === "answered")
|
|
2522
|
-
return bound.answer;
|
|
2523
|
-
throw bound.error;
|
|
2524
|
-
}
|
|
2525
|
-
contentAskBindings.delete(req.toolCallId);
|
|
2526
|
-
}
|
|
2527
|
-
return liveQuestionFace(req, signal);
|
|
2528
|
-
}
|
|
2529
|
-
: onQuestion;
|
|
2530
|
-
const questionToolMounted = spec.interactiveTools === true || (spec.interactiveTools !== false && (onQuestion !== undefined || durableQuestionFace));
|
|
2531
|
-
let mountedQuestionTool;
|
|
2532
|
-
if (questionToolMounted)
|
|
2533
|
-
tools.push((mountedQuestionTool =
|
|
2534
|
-
createAskUserQuestionTool(mountedQuestionFace, { principal: spec.principal, sourceTaskId: sessionId }, {
|
|
2535
|
-
...(resume?.redeemedContentAskCallId !== undefined
|
|
2536
|
-
? {
|
|
2537
|
-
redeemedApprovalCallId: resume.redeemedContentAskCallId,
|
|
2538
|
-
...(resume.redeemedContentAskQuestionsHash !== undefined ? { redeemedApprovalQuestionsHash: resume.redeemedContentAskQuestionsHash } : {}),
|
|
2539
|
-
}
|
|
2540
|
-
: {}),
|
|
2541
|
-
...(resolvedInteractionPosture !== undefined ? { posture: resolvedInteractionPosture } : {}),
|
|
2542
|
-
...(spec.interactiveQuestionFallback === true ? { interactiveFallback: true } : {}),
|
|
2543
|
-
onSyntheticContinuation: ({ questionId, reason }) => {
|
|
2544
|
-
deps.onError?.(new Error(`AskUserQuestion ${questionId}: no human answer was obtainable (` +
|
|
2545
|
-
(reason === "seam_absent"
|
|
2546
|
-
? "no onQuestion seam is wired"
|
|
2547
|
-
: reason === "declined_unavailable"
|
|
2548
|
-
? "the wired question channel reported nobody was reachable"
|
|
2549
|
-
: "the wired question channel failed") +
|
|
2550
|
-
`) — the model was instructed to self-answer and the run CONTINUES (warning, not a failure).`), { phase: "degraded", sessionId, classification: "no-human-autoanswered" });
|
|
2551
|
-
},
|
|
2552
|
-
})));
|
|
977
|
+
const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, executionEnv, runnerSelf, backgroundTaskToolsActive, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, tools, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
|
|
978
|
+
const { checkpointStore, liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, questionToolMounted, mountedQuestionTool } = prepareQuestionFace({ frozenOnQuestion, spec, deps, runtimeCaps, resolvedInteractionPosture, resume, sessionId, tools, inheritedUnavailableAsks: () => inheritedUnavailableAsks });
|
|
2553
979
|
if (spec.handsReadOnly !== true) {
|
|
2554
980
|
const sessionScope = sessionId ?? spec.principal ?? spec.taskId ?? "default";
|
|
2555
981
|
tools.push(...createSchedulerTools(executionEnv, {
|
|
@@ -2567,27 +993,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2567
993
|
deps.onError?.(new Error(`Fork DENIED for principal "${spec.principal ?? ""}" by runtimeCaps.allowFork=false ` +
|
|
2568
994
|
`(per-principal entitlement governance, not a misconfiguration — Agent(subagent_type:"fork") will refuse honestly)`), { phase: "config", sessionId });
|
|
2569
995
|
}
|
|
2570
|
-
const
|
|
2571
|
-
if (lspManager) {
|
|
2572
|
-
const lspRoot = taskRootFinal;
|
|
2573
|
-
tools.push(createLspTool(lspManager, { isPathIgnored: gitCheckIgnoreFilter(executionEnv, lspRoot), env: executionEnv }));
|
|
2574
|
-
envHandToolNames.add("LSP");
|
|
2575
|
-
}
|
|
2576
|
-
const lspDiagnostics = spec.lspDiagnostics !== false && lspManager?.diagnostics !== undefined && handsEnabled && spec.handsReadOnly !== true
|
|
2577
|
-
? lspManager.diagnostics
|
|
2578
|
-
: undefined;
|
|
2579
|
-
const lspRunIdent = sessionId;
|
|
2580
|
-
const nudgeLspOnEdit = lspDiagnostics
|
|
2581
|
-
? (rawPath) => {
|
|
2582
|
-
const baseDir = handsCwdRef?.current ?? taskRootFinal;
|
|
2583
|
-
const filePath = resolveLspPath(rawPath, baseDir);
|
|
2584
|
-
lspDiagnostics.fileEdited(lspRunIdent, pathToUri(filePath));
|
|
2585
|
-
void lspManager
|
|
2586
|
-
.sessionFor(filePath, undefined, executionEnv)
|
|
2587
|
-
.then((session) => session?.notifyFileChanged?.(filePath))
|
|
2588
|
-
.catch(() => undefined);
|
|
2589
|
-
}
|
|
2590
|
-
: undefined;
|
|
996
|
+
const { lspDiagnostics, nudgeLspOnEdit, lspRunIdent } = prepareLsp({ spec, deps, executionEnv, taskRootFinal, handsEnabled, sessionId, handsCwdRef, tools, envHandToolNames });
|
|
2591
997
|
const memoryPairNameDomain = new Set();
|
|
2592
998
|
for (const t of tools) {
|
|
2593
999
|
memoryPairNameDomain.add(t.name);
|
|
@@ -2679,7 +1085,6 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2679
1085
|
live.incomplete = true;
|
|
2680
1086
|
}
|
|
2681
1087
|
}
|
|
2682
|
-
let memoryBlock = memoryBlockFromEngine;
|
|
2683
1088
|
if (memorySeedFiles?.length && seedContextFiles) {
|
|
2684
1089
|
try {
|
|
2685
1090
|
await seedContextFiles(memorySeedFiles);
|
|
@@ -2688,366 +1093,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2688
1093
|
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
|
|
2689
1094
|
}
|
|
2690
1095
|
}
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
if (spec.sessionId) {
|
|
2696
|
-
try {
|
|
2697
|
-
const branch = await session.getBranch();
|
|
2698
|
-
const hasConversation = hasConversationContent(branch);
|
|
2699
|
-
if (!hasConversation)
|
|
2700
|
-
projectMemoryPhase = "fresh";
|
|
2701
|
-
else {
|
|
2702
|
-
for (let i = branch.length - 1; i >= 0; i--) {
|
|
2703
|
-
const e = branch[i];
|
|
2704
|
-
if (e.type === "compaction") {
|
|
2705
|
-
projectMemoryPhase = "post-compact";
|
|
2706
|
-
break;
|
|
2707
|
-
}
|
|
2708
|
-
if (e.type === "message" && e.message.role === "user")
|
|
2709
|
-
break;
|
|
2710
|
-
}
|
|
2711
|
-
}
|
|
2712
|
-
}
|
|
2713
|
-
catch {
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
let loaded = null;
|
|
2717
|
-
try {
|
|
2718
|
-
loaded = await Promise.resolve(deps.loadProjectMemory({
|
|
2719
|
-
cwd: taskRootFinal,
|
|
2720
|
-
handsEnabled,
|
|
2721
|
-
isSubagent: delegation.isNonForkChild,
|
|
2722
|
-
...(internals?.agentName ? { agentName: internals.agentName } : {}),
|
|
2723
|
-
sessionId,
|
|
2724
|
-
phase: projectMemoryPhase,
|
|
2725
|
-
}));
|
|
2726
|
-
}
|
|
2727
|
-
catch (err) {
|
|
2728
|
-
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
|
|
2729
|
-
}
|
|
2730
|
-
const projectMem = loaded !== null && typeof loaded === "object" ? loaded.content : loaded;
|
|
2731
|
-
const seededFiles = loaded !== null && typeof loaded === "object" ? loaded.seededFiles : undefined;
|
|
2732
|
-
const declaredSources = loaded !== null && typeof loaded === "object" ? loaded.instructionSources : undefined;
|
|
2733
|
-
if (declaredSources !== undefined && declaredSources.length > 0)
|
|
2734
|
-
instructionSources = declaredSources;
|
|
2735
|
-
if (seededFiles?.length && seedContextFiles) {
|
|
2736
|
-
try {
|
|
2737
|
-
await seedContextFiles(seededFiles);
|
|
2738
|
-
}
|
|
2739
|
-
catch (err) {
|
|
2740
|
-
deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
if (projectMem != null && projectMem.trim()) {
|
|
2744
|
-
projectInstructionContent = projectMem;
|
|
2745
|
-
const projectBlock = `${PROJECT_CONTEXT_FRAMING}\n\n${composeMemoryBlock(projectMem, "project")}`;
|
|
2746
|
-
memoryBlock = memoryBlock ? `${memoryBlock}\n\n${projectBlock}` : projectBlock;
|
|
2747
|
-
}
|
|
2748
|
-
}
|
|
2749
|
-
const declaredSkillRank = new Map();
|
|
2750
|
-
for (const s of spec.skills ?? [])
|
|
2751
|
-
if (!declaredSkillRank.has(s.name))
|
|
2752
|
-
declaredSkillRank.set(s.name, declaredSkillRank.size);
|
|
2753
|
-
const skillSpecs = normalizeSkills(spec.skills ?? []).filter((s) => {
|
|
2754
|
-
if (s.content.length <= SKILL_CONTENT_MAX_CHARS)
|
|
2755
|
-
return true;
|
|
2756
|
-
deps.onError?.(new Error(`Skill "${s.name}" content is ${s.content.length} chars, over the ${SKILL_CONTENT_MAX_CHARS}-char load limit — skill not loaded (skills are never truncated; shrink the body or move material to attachments/files).`), { phase: "config", sessionId });
|
|
2757
|
-
return false;
|
|
2758
|
-
});
|
|
2759
|
-
const inheritedFrames = internals?.inheritedManifestScope ?? [];
|
|
2760
|
-
for (const frame of inheritedFrames) {
|
|
2761
|
-
skillScope.push(frame);
|
|
2762
|
-
}
|
|
2763
|
-
const hasSkillManifest = skillSpecs.some((s) => s.manifest !== undefined) || inheritedFrames.length > 0;
|
|
2764
|
-
if (skillSpecs.length > 0) {
|
|
2765
|
-
if (tools.some((t) => t.name === SKILL_TOOL_NAME)) {
|
|
2766
|
-
const e = new Error(`Tool name "${SKILL_TOOL_NAME}" is reserved when spec.skills is present.`);
|
|
2767
|
-
e.code = "config.reserved_tool_name";
|
|
2768
|
-
throw e;
|
|
2769
|
-
}
|
|
2770
|
-
tools.push(createSkillTool(skillSpecs, skillScope));
|
|
2771
|
-
}
|
|
2772
|
-
const sharedMemoryProvider = deps.sharedMemoryStores;
|
|
2773
|
-
let sharedMemoryPairMounted = false;
|
|
2774
|
-
if (sharedMemoryProvider !== undefined) {
|
|
2775
|
-
const excluded = new Set(toolFaceSnapshot.exclude ?? []);
|
|
2776
|
-
const mountedNameDomain = new Set();
|
|
2777
|
-
for (const t of tools) {
|
|
2778
|
-
mountedNameDomain.add(t.name);
|
|
2779
|
-
for (const alias of t.aliases ?? [])
|
|
2780
|
-
mountedNameDomain.add(alias);
|
|
2781
|
-
}
|
|
2782
|
-
const excludedName = SHARED_MEMORY_TOOL_NAMES.find((n) => excluded.has(n));
|
|
2783
|
-
const occupiedName = SHARED_MEMORY_TOOL_NAMES.find((n) => mountedNameDomain.has(n));
|
|
2784
|
-
if (excludedName !== undefined || occupiedName !== undefined) {
|
|
2785
|
-
deps.onError?.(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: ` +
|
|
2786
|
-
(excludedName !== undefined
|
|
2787
|
-
? `excludeTools removes "${excludedName}"`
|
|
2788
|
-
: `this task already declares a tool named "${occupiedName}" (the built-in yields to it)`) +
|
|
2789
|
-
" — the pair mounts together or not at all."), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
|
|
2790
|
-
}
|
|
2791
|
-
else {
|
|
2792
|
-
tools.push(...createSharedMemoryTools({
|
|
2793
|
-
provider: sharedMemoryProvider,
|
|
2794
|
-
context: { sessionId, taskId: hostTaskId, ...(spec.principal !== undefined ? { principal: spec.principal } : {}) },
|
|
2795
|
-
}).map((s) => defineTool(s)));
|
|
2796
|
-
sharedMemoryPairMounted = true;
|
|
2797
|
-
}
|
|
2798
|
-
}
|
|
2799
|
-
let memoryEnginePairMounted = false;
|
|
2800
|
-
if (memoryTools !== undefined && memoryTools.length > 0) {
|
|
2801
|
-
tools.push(...memoryTools.map((s) => defineTool(s)));
|
|
2802
|
-
memoryEnginePairMounted = true;
|
|
2803
|
-
}
|
|
2804
|
-
else if (memoryEngineSession !== undefined && !memorySearchToolsPlanned) {
|
|
2805
|
-
deps.onError?.(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: ` +
|
|
2806
|
-
(memoryPairExcludedName !== undefined
|
|
2807
|
-
? `excludeTools removes "${memoryPairExcludedName}"`
|
|
2808
|
-
: `this task already declares a tool named "${memoryPairOccupiedName}" (the built-in yields to it)`) +
|
|
2809
|
-
" — these tools mount together or not at all."), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
|
|
2810
|
-
}
|
|
2811
|
-
const skillsListing = skillSpecs.length > 0
|
|
2812
|
-
? {
|
|
2813
|
-
entries: skillSpecs.map((s) => ({
|
|
2814
|
-
name: s.name,
|
|
2815
|
-
description: s.description,
|
|
2816
|
-
...(s.files !== undefined ? { files: s.files.map((f) => ({ path: f.path })) } : {}),
|
|
2817
|
-
...(declaredSkillRank.get(s.name) !== undefined ? { declaredRank: declaredSkillRank.get(s.name) } : {}),
|
|
2818
|
-
})),
|
|
2819
|
-
seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
|
|
2820
|
-
}
|
|
2821
|
-
:
|
|
2822
|
-
(resume?.seed.announcedListings?.skills?.length ?? 0) > 0 || spec.sessionId !== undefined
|
|
2823
|
-
?
|
|
2824
|
-
{ entries: [], seedAnnounced: true }
|
|
2825
|
-
: undefined;
|
|
2826
|
-
const provider = spec.promptProvider ?? deps.promptProvider ?? defaultPromptProvider;
|
|
2827
|
-
const promptPolicyEnabled = Boolean(lockedPreflight.toolPolicy);
|
|
2828
|
-
const promptHooks = spec.hooks ?? deps.hooks;
|
|
2829
|
-
const promptHooksEnabled = Boolean(promptHooks?.preToolUse || promptHooks?.postToolUse);
|
|
2830
|
-
const failClosedReason = selfOrchestrationFailClosedReason(spec, deps);
|
|
2831
|
-
if (failClosedReason) {
|
|
2832
|
-
deps.onError?.(new Error(failClosedReason), { phase: "config", sessionId });
|
|
2833
|
-
}
|
|
2834
|
-
const featureFlags = {
|
|
2835
|
-
policyEnabled: promptPolicyEnabled,
|
|
2836
|
-
hooksEnabled: promptHooksEnabled,
|
|
2837
|
-
isolationEnabled: isIsolated(executionEnv),
|
|
2838
|
-
reminderMark,
|
|
2839
|
-
readFaceOpen: resolvedReadFace === "open",
|
|
2840
|
-
orchestrationEnabled: selfOrchestrationActive,
|
|
2841
|
-
orchestrationDeferred: selfOrchestrationActive && (toolFaceSnapshot.defer?.includes("Workflow") ?? false),
|
|
2842
|
-
promptProfile,
|
|
2843
|
-
fableMitigations,
|
|
2844
|
-
goalEnabled: internals?.goalMode === true,
|
|
2845
|
-
awarenessEnabled: thinking !== undefined && ULTRA_REASONING_TIERS.has(thinking),
|
|
2846
|
-
worktreeIsolated: internals?.isolation === "worktree" && ownedEnv !== undefined,
|
|
2847
|
-
withinTaskCompactionEnabled: (spec.compaction?.enabled ?? true) && (spec.compaction?.withinTask ?? true),
|
|
2848
|
-
isSubagent: delegation.isNonForkChild,
|
|
2849
|
-
};
|
|
2850
|
-
const userSystemPrompt = spec.systemPrompt ?? resolvedRole.systemPrompt ?? internals?.defaultSystemPrompt;
|
|
2851
|
-
const userAppendSystemPrompt = spec.appendSystemPrompt;
|
|
2852
|
-
const mcpInstructionsBlock = mcp.serverInstructions.length
|
|
2853
|
-
? `# MCP Server Instructions\n${mcp.serverInstructions.map((s) => fenceMcpServerInstructions(s.server, s.text)).join("\n\n")}`
|
|
2854
|
-
: undefined;
|
|
2855
|
-
const userTz = spec.clientContext?.timeZone;
|
|
2856
|
-
const tzValid = userTz !== undefined && isValidTimeZone(userTz);
|
|
2857
|
-
const envFacts = { date: formatLocalDate(new Date(), tzValid ? userTz : undefined), modelId: model.id };
|
|
2858
|
-
if (tzValid)
|
|
2859
|
-
envFacts.timeZone = userTz;
|
|
2860
|
-
if (spec.clientContext?.userEmail)
|
|
2861
|
-
envFacts.userEmail = spec.clientContext.userEmail;
|
|
2862
|
-
const declaredCutoff = deps.modelKnowledgeCutoffs?.[model.id];
|
|
2863
|
-
if (declaredCutoff)
|
|
2864
|
-
envFacts.knowledgeCutoff = declaredCutoff;
|
|
2865
|
-
if (spec.envFacts?.profile)
|
|
2866
|
-
envFacts.sandboxProfile = spec.envFacts.profile;
|
|
2867
|
-
if (spec.envFacts?.capabilities && spec.envFacts.capabilities.length > 0)
|
|
2868
|
-
envFacts.sandboxCapabilities = [...spec.envFacts.capabilities];
|
|
2869
|
-
if (spec.envFacts?.pkgSource)
|
|
2870
|
-
envFacts.sandboxPkgSource = spec.envFacts.pkgSource;
|
|
2871
|
-
if (spec.envFacts?.egress === "none" || spec.envFacts?.egress === "allowlist" || spec.envFacts?.egress === "full")
|
|
2872
|
-
envFacts.sandboxEgress = spec.envFacts.egress;
|
|
2873
|
-
if (spec.envFacts?.scratchpadDir)
|
|
2874
|
-
envFacts.scratchpadDir = spec.envFacts.scratchpadDir;
|
|
2875
|
-
if (externalContentTargetActive)
|
|
2876
|
-
envFacts.externalContentTarget = true;
|
|
2877
|
-
if (resume !== undefined && spec.envFacts?.resumeFacts) {
|
|
2878
|
-
const rf = spec.envFacts.resumeFacts;
|
|
2879
|
-
const copy = {};
|
|
2880
|
-
if (rf.processes === "preserved" || rf.processes === "lost")
|
|
2881
|
-
copy.processes = rf.processes;
|
|
2882
|
-
if (rf.scratch === "preserved" || rf.scratch === "lost")
|
|
2883
|
-
copy.scratch = rf.scratch;
|
|
2884
|
-
if (typeof rf.note === "string" && rf.note.length > 0)
|
|
2885
|
-
copy.note = rf.note;
|
|
2886
|
-
if (Object.keys(copy).length > 0)
|
|
2887
|
-
envFacts.resumeFacts = copy;
|
|
2888
|
-
}
|
|
2889
|
-
if (handsEnabled) {
|
|
2890
|
-
envFacts.cwd = taskRootFinal;
|
|
2891
|
-
if (additionalRootsCanonical.length > 0) {
|
|
2892
|
-
envFacts.additionalDirectories = [...additionalRootsCanonical];
|
|
2893
|
-
}
|
|
2894
|
-
if (resolvedReadFace !== undefined)
|
|
2895
|
-
envFacts.readFace = resolvedReadFace;
|
|
2896
|
-
if (additionalReadRootsCanonical.length > 0) {
|
|
2897
|
-
envFacts.additionalReadDirectories = [...additionalReadRootsCanonical];
|
|
2898
|
-
}
|
|
2899
|
-
try {
|
|
2900
|
-
const probe = await executionEnv.exec('uname -s; uname -r; (git rev-parse --is-inside-work-tree 2>/dev/null || echo false); (git symbolic-ref --short -q HEAD 2>/dev/null || echo "HEAD (detached)"); (git rev-parse --show-toplevel 2>/dev/null || echo); (test -n "$(git status --porcelain 2>/dev/null | head -1)" && echo dirty || echo clean); (s=$(ps -p $$ -o comm= 2>/dev/null); s=${s##*/}; echo "${s#-}"); (test "$(git rev-parse --git-dir 2>/dev/null)" != "$(git rev-parse --git-common-dir 2>/dev/null)" && echo linked || echo main); (pwd -P 2>/dev/null || pwd)', { cwd: envFacts.cwd, timeout: 10 });
|
|
2901
|
-
if (probe.ok && probe.value.exitCode === 0) {
|
|
2902
|
-
const lines = probe.value.stdout.split("\n");
|
|
2903
|
-
if (lines.length >= 6) {
|
|
2904
|
-
const [osName, osVer, git, branch, worktreeRoot, dirty] = lines;
|
|
2905
|
-
if (osName?.trim())
|
|
2906
|
-
envFacts.platform = osName.trim();
|
|
2907
|
-
if (osVer?.trim())
|
|
2908
|
-
envFacts.osVersion = osVer.trim();
|
|
2909
|
-
envFacts.isGitRepo = (git ?? "").trim() === "true";
|
|
2910
|
-
if (envFacts.isGitRepo) {
|
|
2911
|
-
const b = (branch ?? "").trim();
|
|
2912
|
-
if (b)
|
|
2913
|
-
envFacts.gitBranch = b;
|
|
2914
|
-
const root = (worktreeRoot ?? "").trim();
|
|
2915
|
-
if (root)
|
|
2916
|
-
envFacts.gitWorktreeRoot = root;
|
|
2917
|
-
const d = (dirty ?? "").trim();
|
|
2918
|
-
if (d === "dirty" || d === "clean")
|
|
2919
|
-
envFacts.gitDirty = d === "dirty";
|
|
2920
|
-
}
|
|
2921
|
-
const sh = (lines[6] ?? "").trim();
|
|
2922
|
-
if (sh)
|
|
2923
|
-
envFacts.shell = sh;
|
|
2924
|
-
if (envFacts.isGitRepo && (lines[7] ?? "").trim() === "linked")
|
|
2925
|
-
envFacts.isLinkedWorktree = true;
|
|
2926
|
-
const probedPwd = (lines[8] ?? "").trim();
|
|
2927
|
-
if (probedPwd && envFacts.cwd && probedPwd !== envFacts.cwd) {
|
|
2928
|
-
const canonOf = async (p) => {
|
|
2929
|
-
try {
|
|
2930
|
-
const c = await executionEnv.canonicalPath(p);
|
|
2931
|
-
return c.ok ? c.value : p;
|
|
2932
|
-
}
|
|
2933
|
-
catch {
|
|
2934
|
-
return p;
|
|
2935
|
-
}
|
|
2936
|
-
};
|
|
2937
|
-
const canonicalProbed = await canonOf(probedPwd);
|
|
2938
|
-
if (canonicalProbed !== (await canonOf(envFacts.cwd))) {
|
|
2939
|
-
try {
|
|
2940
|
-
deps.onError?.(new Error(`execution-env cwd misalignment: task root is "${envFacts.cwd}" but the env's shell reports pwd "${canonicalProbed}" — per-exec cwd injection may be ignored on this lane (execs/artifacts can land outside the workspace root)`), { phase: "config", sessionId });
|
|
2941
|
-
}
|
|
2942
|
-
catch {
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
|
-
}
|
|
2948
|
-
}
|
|
2949
|
-
catch {
|
|
2950
|
-
}
|
|
2951
|
-
}
|
|
2952
|
-
const gitStatusRef = await probeGitStatusLane({
|
|
2953
|
-
executionEnv,
|
|
2954
|
-
envFacts,
|
|
2955
|
-
handsEnabled,
|
|
2956
|
-
taskRoot: taskRootFinal,
|
|
2957
|
-
onDegrade: (reason) => deps.onError?.(new Error(`env git snapshot degraded — ${reason}`), { phase: "degraded", sessionId, classification: "env-git-snapshot" }),
|
|
2958
|
-
});
|
|
2959
|
-
if (toolFaceSnapshot.exclude !== undefined && toolFaceSnapshot.exclude.length > 0) {
|
|
2960
|
-
const excluded = new Set(toolFaceSnapshot.exclude);
|
|
2961
|
-
for (let i = tools.length - 1; i >= 0; i--)
|
|
2962
|
-
if (excluded.has(tools[i].name))
|
|
2963
|
-
tools.splice(i, 1);
|
|
2964
|
-
}
|
|
2965
|
-
if (handsEnabled) {
|
|
2966
|
-
const fullShellOnRoster = tools.some((t) => t.name === "Bash" && getToolContract(t, "core").contractId === FULL_SHELL_CONTRACT_ID);
|
|
2967
|
-
if (fullShellOnRoster !== fullShellReachable) {
|
|
2968
|
-
const e = new Error(`internal invariant: fullShellReachable=${String(fullShellReachable)} but the post-exclusion roster ` +
|
|
2969
|
-
`${fullShellOnRoster ? "carries" : "does not carry"} the full shell (${FULL_SHELL_CONTRACT_ID}). ` +
|
|
2970
|
-
`A new roster-affecting mechanism was added without updating the predicate.`);
|
|
2971
|
-
e.code = "internal.full_shell_reachable_mismatch";
|
|
2972
|
-
throw e;
|
|
2973
|
-
}
|
|
2974
|
-
}
|
|
2975
|
-
if (promptProfile === "classic") {
|
|
2976
|
-
for (let i = 0; i < tools.length; i++) {
|
|
2977
|
-
const t = tools[i];
|
|
2978
|
-
if (t.descriptionClassic !== undefined)
|
|
2979
|
-
tools[i] = { ...t, description: t.descriptionClassic };
|
|
2980
|
-
}
|
|
2981
|
-
}
|
|
2982
|
-
const userToolNames = (spec.tools ?? []).map((t) => t.name);
|
|
2983
|
-
const protocolToolNames = [...mcp.tools.map((t) => t.name), ...a2a.tools.map((t) => t.name)];
|
|
2984
|
-
const mcpAlwaysLoadNames = mcp.tools
|
|
2985
|
-
.filter((t) => t.mcpAlwaysLoad === true && !(toolFaceSnapshot.defer ?? []).includes(t.name))
|
|
2986
|
-
.map((t) => t.name);
|
|
2987
|
-
const classifyDeferredOverFace = (face, builtinDeferNames) => {
|
|
2988
|
-
const deferredSet = classifyDeferred({
|
|
2989
|
-
specs: spec.tools ?? [],
|
|
2990
|
-
protocolToolNames,
|
|
2991
|
-
fullTools: face.filter((t) => userToolNames.includes(t.name) || protocolToolNames.includes(t.name)),
|
|
2992
|
-
deferMode: deps.deferMode,
|
|
2993
|
-
model,
|
|
2994
|
-
deferNames: [...(toolFaceSnapshot.defer ?? []), ...builtinDeferNames].filter((n) => face.some((t) => t.name === n)),
|
|
2995
|
-
alwaysLoadNames: [
|
|
2996
|
-
ASK_USER_QUESTION_TOOL_NAME,
|
|
2997
|
-
...(toolFaceSnapshot.alwaysLoad ?? []),
|
|
2998
|
-
...mcpAlwaysLoadNames,
|
|
2999
|
-
],
|
|
3000
|
-
});
|
|
3001
|
-
for (const n of [...deferredSet]) {
|
|
3002
|
-
if (!face.some((t) => t.name === n))
|
|
3003
|
-
deferredSet.delete(n);
|
|
3004
|
-
}
|
|
3005
|
-
return deferredSet;
|
|
3006
|
-
};
|
|
3007
|
-
const sharedMemoryPair = sharedMemoryPairMounted ? SHARED_MEMORY_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n)) : [];
|
|
3008
|
-
const memoryEnginePair = explicitlyDeferredMemoryTrio(memoryEnginePairMounted, tools, toolFaceSnapshot.defer);
|
|
3009
|
-
const builtinDeferPairNames = [...sharedMemoryPair, ...memoryEnginePair];
|
|
3010
|
-
let deferred;
|
|
3011
|
-
if (builtinDeferPairNames.length > 0) {
|
|
3012
|
-
const withoutPairs = tools.filter((t) => !builtinDeferPairNames.some((n) => n === t.name));
|
|
3013
|
-
const d1 = classifyDeferredOverFace(tools, builtinDeferPairNames);
|
|
3014
|
-
const d0 = classifyDeferredOverFace(withoutPairs, []);
|
|
3015
|
-
const soleCause = d0.size === 0 && d1.size > 0;
|
|
3016
|
-
const supportNameTaken = tools.some((t) => t.name === TOOL_SEARCH_NAME || (t.aliases ?? []).includes(TOOL_SEARCH_NAME));
|
|
3017
|
-
if (soleCause && supportNameTaken) {
|
|
3018
|
-
const retractNames = memoryGroupRetractionSet(builtinDeferPairNames, memoryEnginePair.length > 0);
|
|
3019
|
-
for (let i = tools.length - 1; i >= 0; i--) {
|
|
3020
|
-
if (retractNames.has(tools[i].name))
|
|
3021
|
-
tools.splice(i, 1);
|
|
3022
|
-
}
|
|
3023
|
-
if (sharedMemoryPair.length > 0) {
|
|
3024
|
-
sharedMemoryPairMounted = false;
|
|
3025
|
-
deps.onError?.(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
|
|
3026
|
-
`the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — the pair mounts together or not at all.`), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
|
|
3027
|
-
}
|
|
3028
|
-
if (memoryEnginePair.length > 0) {
|
|
3029
|
-
memoryEnginePairMounted = false;
|
|
3030
|
-
if (memoryBlock !== undefined && memoryRecallSegment !== undefined) {
|
|
3031
|
-
const segment = memoryRecallSegment;
|
|
3032
|
-
if (memoryBlock === segment)
|
|
3033
|
-
memoryBlock = undefined;
|
|
3034
|
-
else if (memoryBlock.includes(`\n\n${segment}`))
|
|
3035
|
-
memoryBlock = memoryBlock.replace(`\n\n${segment}`, "");
|
|
3036
|
-
else if (memoryBlock.startsWith(`${segment}\n\n`))
|
|
3037
|
-
memoryBlock = memoryBlock.slice(segment.length + 2);
|
|
3038
|
-
}
|
|
3039
|
-
deps.onError?.(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
|
|
3040
|
-
`the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — these tools mount together or not at all.`), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
|
|
3041
|
-
}
|
|
3042
|
-
deferred = d0;
|
|
3043
|
-
}
|
|
3044
|
-
else {
|
|
3045
|
-
deferred = d1;
|
|
3046
|
-
}
|
|
3047
|
-
}
|
|
3048
|
-
else {
|
|
3049
|
-
deferred = classifyDeferredOverFace(tools, []);
|
|
3050
|
-
}
|
|
1096
|
+
const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, tools, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume });
|
|
1097
|
+
const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
|
|
1098
|
+
const { provider, featureFlags, userSystemPrompt, userAppendSystemPrompt, mcpInstructionsBlock, envFacts, tzValid, userTz, gitStatusRef } = await preparePromptInputs({ spec, deps, failClosedReason: selfOrchestrationFailClosedReason(spec, deps), lockedPreflight, selfOrchestrationActive, executionEnv, reminderMark, resolvedReadFace, toolFaceSnapshot, promptProfile, fableMitigations, internals, thinking, ownedEnv, delegation, resolvedRole, mcp, model, externalContentTargetActive, resume, handsEnabled, taskRootFinal, additionalRootsCanonical, additionalReadRootsCanonical, sessionId });
|
|
1099
|
+
const { deferred, memoryBlock } = prepareDeferClassify({ spec, deps, tools, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryBlock: memoryBlockWithProject, memoryRecallSegment, onceLedger, sessionId });
|
|
3051
1100
|
const delegationProvenanceChannel = internals?.delegationProvenance;
|
|
3052
1101
|
if (memoryEngineSession !== undefined || delegationProvenanceChannel !== undefined) {
|
|
3053
1102
|
const pollution = memoryEngineSession?.pollution;
|
|
@@ -3328,10 +1377,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3328
1377
|
const promptBlocks = assembled.legacyBlocks;
|
|
3329
1378
|
const collisionAudit = auditToolCollisions(tools);
|
|
3330
1379
|
for (const y of collisionAudit.yields) {
|
|
3331
|
-
|
|
1380
|
+
onceLedger.onError(new Error(`tool mount: caller tool "${y.name}" shadows another tool with the same canonical name (deliberate last-write-wins yield — the later mount serves)`), { phase: "config", sessionId });
|
|
3332
1381
|
}
|
|
3333
1382
|
for (const c of collisionAudit.aliasCollisions) {
|
|
3334
|
-
|
|
1383
|
+
onceLedger.onError(new Error(`tool mount: alias "${c.alias}" is claimed by both "${c.owners[0]}" and "${c.owners[1]}" (design/141 warn face — name resolution serves the later mount)`), { phase: "config", sessionId });
|
|
3335
1384
|
}
|
|
3336
1385
|
const saltedHash = (text) => createHash("sha256").update(PROMPT_HASH_SALT).update(text).digest("hex").slice(0, 12);
|
|
3337
1386
|
const promptManifest = {
|
|
@@ -3365,282 +1414,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3365
1414
|
});
|
|
3366
1415
|
}
|
|
3367
1416
|
}
|
|
3368
|
-
const activeTools =
|
|
3369
|
-
const
|
|
3370
|
-
const turnSnapshotRef = {};
|
|
3371
|
-
let harnessTools = tools;
|
|
3372
|
-
const failedMcpServers = mcp.statuses
|
|
3373
|
-
.filter((s) => s.status === "failed")
|
|
3374
|
-
.map((s) => ({ name: inlineUntrusted(s.name, 160), ...(s.error !== undefined ? { error: inlineUntrusted(s.error, 240) } : {}) }));
|
|
3375
|
-
let toolsDeltaRef;
|
|
3376
|
-
let toolMaterializeStatic = false;
|
|
3377
|
-
let deferDirectCall = false;
|
|
3378
|
-
const staticFaceForRef = {};
|
|
3379
|
-
if (deferred.size > 0 || failedMcpServers.length > 0) {
|
|
3380
|
-
toolsDeltaRef = { pending: [], pendingRemoved: [], pendingReadded: [], pendingFailed: failedMcpServers };
|
|
3381
|
-
}
|
|
3382
|
-
const listingRideRef = {};
|
|
3383
|
-
{
|
|
3384
|
-
const raw = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
|
|
3385
|
-
if (raw !== undefined && raw !== "swap" && raw !== "static" && deferred.size === 0) {
|
|
3386
|
-
const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(raw)} is not "swap" or "static" — inert on this task (no deferred tools), but a deferring task WITHOUT an explicit spec strategy will refuse to prepare under it (an explicit legal spec outranks and discards it, loudly). Fix or unset the flag.`;
|
|
3387
|
-
emitMaterializeEnvNotice(deps.onNotice, line, { raw });
|
|
3388
|
-
}
|
|
3389
|
-
}
|
|
3390
|
-
if (deferred.size > 0) {
|
|
3391
|
-
if (deferred.has(TOOL_SEARCH_NAME) || tools.some((t) => t.name === TOOL_SEARCH_NAME)) {
|
|
3392
|
-
const e = new Error(`Tool name "${TOOL_SEARCH_NAME}" is reserved when deferred tools are present.`);
|
|
3393
|
-
e.code = "config.reserved_tool_name";
|
|
3394
|
-
throw e;
|
|
3395
|
-
}
|
|
3396
|
-
const registry = buildDeferredRegistry(deferred, tools);
|
|
3397
|
-
const rawEnvStrategy = process.env.SEMA_TOOL_MATERIALIZE_STRATEGY;
|
|
3398
|
-
const envStrategyInvalid = rawEnvStrategy !== undefined && rawEnvStrategy !== "swap" && rawEnvStrategy !== "static";
|
|
3399
|
-
if (envStrategyInvalid && spec.toolMaterializeStrategy === undefined) {
|
|
3400
|
-
const e = new Error(`SEMA_TOOL_MATERIALIZE_STRATEGY must be "swap" or "static" (got ${JSON.stringify(rawEnvStrategy)}).`);
|
|
3401
|
-
e.code = "config.tool_materialize_invalid";
|
|
3402
|
-
throw e;
|
|
3403
|
-
}
|
|
3404
|
-
if (envStrategyInvalid) {
|
|
3405
|
-
const line = `SEMA_TOOL_MATERIALIZE_STRATEGY=${JSON.stringify(rawEnvStrategy)} was ignored — not "swap" or "static", and the task spec pins toolMaterializeStrategy=${JSON.stringify(spec.toolMaterializeStrategy)} which outranks it. Fix or unset the env flag.`;
|
|
3406
|
-
emitMaterializeEnvNotice(deps.onNotice, line, { raw: rawEnvStrategy, specStrategy: spec.toolMaterializeStrategy });
|
|
3407
|
-
}
|
|
3408
|
-
const envStrategy = envStrategyInvalid ? undefined : rawEnvStrategy;
|
|
3409
|
-
const requestedStrategy = spec.toolMaterializeStrategy ?? envStrategy ?? "swap";
|
|
3410
|
-
const laneDegrade = requestedStrategy === "static" && spec.deferSelfResolve === false;
|
|
3411
|
-
const materializeStatic = requestedStrategy === "static" && !laneDegrade;
|
|
3412
|
-
toolMaterializeStatic = materializeStatic;
|
|
3413
|
-
deferDirectCall = spec.deferSelfResolve !== false;
|
|
3414
|
-
promptManifest.toolDisclosure = {
|
|
3415
|
-
deferredTools: deferred.size,
|
|
3416
|
-
strategy: materializeStatic ? "static" : "swap",
|
|
3417
|
-
source: laneDegrade ? "degraded_no_direct_lane" : spec.toolMaterializeStrategy !== undefined ? "spec" : envStrategy !== undefined ? "env" : "default",
|
|
3418
|
-
};
|
|
3419
|
-
const everExempt = new Set();
|
|
3420
|
-
const staticFaceFor = (name) => {
|
|
3421
|
-
if (!materializeStatic)
|
|
3422
|
-
return false;
|
|
3423
|
-
if (everExempt.has(name))
|
|
3424
|
-
return false;
|
|
3425
|
-
if (staticSchemaRenderable(tools.find((t) => t.name === name)?.parameters))
|
|
3426
|
-
return true;
|
|
3427
|
-
everExempt.add(name);
|
|
3428
|
-
return false;
|
|
3429
|
-
};
|
|
3430
|
-
staticFaceForRef.current = staticFaceFor;
|
|
3431
|
-
let activationChain = Promise.resolve();
|
|
3432
|
-
const serializeActivation = (section) => {
|
|
3433
|
-
const p = activationChain.then(section);
|
|
3434
|
-
activationChain = p.then(() => undefined, () => undefined);
|
|
3435
|
-
return p;
|
|
3436
|
-
};
|
|
3437
|
-
const directCallFor = (name) => {
|
|
3438
|
-
if (spec.deferSelfResolve === false)
|
|
3439
|
-
return undefined;
|
|
3440
|
-
const executionMode = tools.find((t) => t.name === name)?.executionMode;
|
|
3441
|
-
return {
|
|
3442
|
-
resolveReal: () => {
|
|
3443
|
-
const real = tools.find((t) => t.name === name);
|
|
3444
|
-
if (real === undefined)
|
|
3445
|
-
return undefined;
|
|
3446
|
-
return {
|
|
3447
|
-
parameters: real.parameters,
|
|
3448
|
-
invoke: (toolCallId, params, signal, onUpdate) => real.execute(toolCallId, params, signal, onUpdate),
|
|
3449
|
-
};
|
|
3450
|
-
},
|
|
3451
|
-
...(executionMode !== undefined ? { executionMode } : {}),
|
|
3452
|
-
staticFace: () => staticFaceFor(name),
|
|
3453
|
-
activate: async () => serializeActivation(async () => {
|
|
3454
|
-
if (activeTools.has(name))
|
|
3455
|
-
return undefined;
|
|
3456
|
-
activeTools.add(name);
|
|
3457
|
-
try {
|
|
3458
|
-
await rematerialize(activeTools);
|
|
3459
|
-
}
|
|
3460
|
-
catch (e) {
|
|
3461
|
-
activeTools.delete(name);
|
|
3462
|
-
throw e;
|
|
3463
|
-
}
|
|
3464
|
-
return listingRideRef.current?.([name]);
|
|
3465
|
-
}),
|
|
3466
|
-
};
|
|
3467
|
-
};
|
|
3468
|
-
const placeholders = new Map([...registry.values()].map((i) => [i.name, createPlaceholderTool(i, directCallFor(i.name))]));
|
|
3469
|
-
const { messages } = await session.buildContext();
|
|
3470
|
-
for (const n of extractDiscoveredToolNames(messages, registry)) {
|
|
3471
|
-
if (deferred.has(n))
|
|
3472
|
-
activeTools.add(n);
|
|
3473
|
-
}
|
|
3474
|
-
for (const entry of await session.getBranch()) {
|
|
3475
|
-
if (entry.type !== "compaction" || entry.fromHook === true)
|
|
3476
|
-
continue;
|
|
3477
|
-
for (const n of readCompactionActiveTools(entry.details)) {
|
|
3478
|
-
if (deferred.has(n))
|
|
3479
|
-
activeTools.add(n);
|
|
3480
|
-
}
|
|
3481
|
-
}
|
|
3482
|
-
if (resume) {
|
|
3483
|
-
for (const n of resume.seed.activeTools)
|
|
3484
|
-
if (deferred.has(n))
|
|
3485
|
-
activeTools.add(n);
|
|
3486
|
-
}
|
|
3487
|
-
const callableToolNames = () => {
|
|
3488
|
-
const s = new Set(tools.map((t) => t.name));
|
|
3489
|
-
for (const n of deferred)
|
|
3490
|
-
if (!activeTools.has(n))
|
|
3491
|
-
s.delete(n);
|
|
3492
|
-
s.add(TOOL_SEARCH_NAME);
|
|
3493
|
-
return s;
|
|
3494
|
-
};
|
|
3495
|
-
offloadReachableToolsRef.current = callableToolNames;
|
|
3496
|
-
let toolSearch;
|
|
3497
|
-
const buildToolList = (active) => {
|
|
3498
|
-
const list = tools.map((t) => (deferred.has(t.name) && (staticFaceFor(t.name) || !active.has(t.name)) ? placeholders.get(t.name) : t));
|
|
3499
|
-
list.push(toolSearch);
|
|
3500
|
-
return list;
|
|
3501
|
-
};
|
|
3502
|
-
const announcedTools = new Set(activeTools);
|
|
3503
|
-
const withdrawnTools = new Set();
|
|
3504
|
-
const deltaRef = toolsDeltaRef;
|
|
3505
|
-
const rematerialize = async (active) => {
|
|
3506
|
-
contentOriginWrapRef.current?.();
|
|
3507
|
-
const list = buildToolList(active);
|
|
3508
|
-
await harnessRef.current.setTools(list, list.map((t) => t.name));
|
|
3509
|
-
if (fpRef.current)
|
|
3510
|
-
fpRef.current.tools = toolsToFingerprintInputs(list);
|
|
3511
|
-
turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
|
|
3512
|
-
const live = new Set(list.map((t) => t.name));
|
|
3513
|
-
for (const n of [...announcedTools]) {
|
|
3514
|
-
if (!live.has(n)) {
|
|
3515
|
-
announcedTools.delete(n);
|
|
3516
|
-
withdrawnTools.add(n);
|
|
3517
|
-
deltaRef.pendingRemoved.push(n);
|
|
3518
|
-
}
|
|
3519
|
-
}
|
|
3520
|
-
for (const n of active) {
|
|
3521
|
-
if (!live.has(n))
|
|
3522
|
-
continue;
|
|
3523
|
-
if (!announcedTools.has(n)) {
|
|
3524
|
-
announcedTools.add(n);
|
|
3525
|
-
if (withdrawnTools.delete(n))
|
|
3526
|
-
deltaRef.pendingReadded.push(n);
|
|
3527
|
-
else
|
|
3528
|
-
deltaRef.pending.push(n);
|
|
3529
|
-
}
|
|
3530
|
-
}
|
|
3531
|
-
};
|
|
3532
|
-
rebuildHarnessToolsRef.current = () => rematerialize(activeTools);
|
|
3533
|
-
toolSearch = createToolSearchTool({
|
|
3534
|
-
registry,
|
|
3535
|
-
active: activeTools,
|
|
3536
|
-
rematerialize,
|
|
3537
|
-
listingRide: (newly) => listingRideRef.current?.(newly),
|
|
3538
|
-
mountedNames: callableToolNames,
|
|
3539
|
-
directCallEnabled: spec.deferSelfResolve !== false,
|
|
3540
|
-
isMounted: (name) => tools.some((t) => t.name === name),
|
|
3541
|
-
...(materializeStatic
|
|
3542
|
-
? { staticSchemaFor: (name) => (staticFaceFor(name) ? tools.find((t) => t.name === name)?.parameters : undefined) }
|
|
3543
|
-
: {}),
|
|
3544
|
-
serializeActivation,
|
|
3545
|
-
});
|
|
3546
|
-
harnessTools = buildToolList(activeTools);
|
|
3547
|
-
}
|
|
3548
|
-
else {
|
|
3549
|
-
rebuildHarnessToolsRef.current = async () => {
|
|
3550
|
-
contentOriginWrapRef.current?.();
|
|
3551
|
-
const list = [...tools];
|
|
3552
|
-
await harnessRef.current.setTools(list, list.map((t) => t.name));
|
|
3553
|
-
if (fpRef.current)
|
|
3554
|
-
fpRef.current.tools = toolsToFingerprintInputs(list);
|
|
3555
|
-
turnSnapshotRef.current?.refreshTools(toolsToFingerprintInputs(list));
|
|
3556
|
-
};
|
|
3557
|
-
}
|
|
3558
|
-
if (spec.agents !== undefined && spec.agents.length > 0) {
|
|
3559
|
-
const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
3560
|
-
for (const def of spec.agents) {
|
|
3561
|
-
const unknownAllow = (def.allowTools ?? []).filter((n) => n !== "*" && !known.has(n));
|
|
3562
|
-
if (unknownAllow.length > 0) {
|
|
3563
|
-
try {
|
|
3564
|
-
deps.onError?.(new Error(`TaskSpec.agents: agent "${def.name}" allows tool(s) ${unknownAllow.join(", ")} not present in this task's assembled roster — likely a typo (the entry would be item-filtered at spawn; the agent stays usable). Advisory only: the delegation pool can differ from this roster, so a tool mounted only on the delegation tool or produced by per-spawn extraTools makes this spurious.`), { phase: "config", sessionId });
|
|
3565
|
-
}
|
|
3566
|
-
catch {
|
|
3567
|
-
}
|
|
3568
|
-
}
|
|
3569
|
-
for (const n of def.denyTools ?? []) {
|
|
3570
|
-
if (n === "*")
|
|
3571
|
-
continue;
|
|
3572
|
-
if (!known.has(n)) {
|
|
3573
|
-
const e = new Error(`TaskSpec.agents: agent "${def.name}" declares tool "${n}" in its denied tools, but no such tool exists in this deployment — fix the agent's tools list or mount the tool.`);
|
|
3574
|
-
e.code = "config.agent.unknown_tool";
|
|
3575
|
-
throw e;
|
|
3576
|
-
}
|
|
3577
|
-
}
|
|
3578
|
-
}
|
|
3579
|
-
}
|
|
3580
|
-
const rosterBearingSpec = (spec.tools ?? []).find((t) => t.agentListing !== undefined && t.agentListing.length > 0);
|
|
3581
|
-
const rosterFaceDeferred = rosterBearingSpec !== undefined &&
|
|
3582
|
-
!(toolFaceSnapshot.exclude?.includes(rosterBearingSpec.name) ?? false) &&
|
|
3583
|
-
(toolFaceSnapshot.defer?.includes(rosterBearingSpec.name) ?? false) &&
|
|
3584
|
-
!activeTools.has(rosterBearingSpec.name);
|
|
3585
|
-
const agentListingSpec = rosterBearingSpec !== undefined &&
|
|
3586
|
-
!rosterFaceDeferred &&
|
|
3587
|
-
!(toolFaceSnapshot.exclude?.includes(rosterBearingSpec.name) ?? false)
|
|
3588
|
-
? rosterBearingSpec
|
|
3589
|
-
: undefined;
|
|
3590
|
-
const agentListing = agentListingSpec
|
|
3591
|
-
? {
|
|
3592
|
-
entries: agentListingSpec.agentListing,
|
|
3593
|
-
toolName: agentListingSpec.name,
|
|
3594
|
-
seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
|
|
3595
|
-
...(agentListingSpec.agentModels !== undefined ? { models: agentListingSpec.agentModels } : {}),
|
|
3596
|
-
}
|
|
3597
|
-
:
|
|
3598
|
-
!rosterFaceDeferred && ((resume?.seed.announcedListings?.agents?.length ?? 0) > 0 || spec.sessionId !== undefined)
|
|
3599
|
-
?
|
|
3600
|
-
{
|
|
3601
|
-
entries: [],
|
|
3602
|
-
toolName: (spec.tools ?? []).find((t) => t.agentListing !== undefined)?.name ?? "Agent",
|
|
3603
|
-
seedAnnounced: true,
|
|
3604
|
-
}
|
|
3605
|
-
: undefined;
|
|
3606
|
-
if (spec.attachments?.agentListing === false && agentListing !== undefined && agentListing.entries.length > 0) {
|
|
3607
|
-
deps.onError?.(new Error(`attachments.agentListing is explicitly false but the "${agentListing.toolName}" delegation tool mounts a ${agentListing.entries.length}-type roster — the model will never see the agent-type listing its tool description points to.`), { phase: "config", sessionId });
|
|
3608
|
-
}
|
|
3609
|
-
if (spec.attachments?.skillsListing === false && skillsListing !== undefined && skillsListing.entries.length > 0) {
|
|
3610
|
-
deps.onError?.(new Error(`attachments.skillsListing is explicitly false but ${skillsListing.entries.length} skill(s) are mounted — the model will never see the skills listing the ${SKILL_TOOL_NAME} tool description points to.`), { phase: "config", sessionId });
|
|
3611
|
-
}
|
|
3612
|
-
const announcedListingsRef = {};
|
|
3613
|
-
if (rosterFaceDeferred) {
|
|
3614
|
-
if (resume?.seed.announcedListings?.agents !== undefined)
|
|
3615
|
-
announcedListingsRef.agents = [...resume.seed.announcedListings.agents];
|
|
3616
|
-
if (resume?.seed.announcedListings?.models !== undefined)
|
|
3617
|
-
announcedListingsRef.models = [...resume.seed.announcedListings.models];
|
|
3618
|
-
if (announcedListingsRef.agents === undefined && announcedListingsRef.models === undefined) {
|
|
3619
|
-
try {
|
|
3620
|
-
const entrySnap = await session.getAnnouncedListing();
|
|
3621
|
-
if (entrySnap?.agents !== undefined)
|
|
3622
|
-
announcedListingsRef.agents = [...entrySnap.agents];
|
|
3623
|
-
if (entrySnap?.models !== undefined)
|
|
3624
|
-
announcedListingsRef.models = [...entrySnap.models];
|
|
3625
|
-
}
|
|
3626
|
-
catch {
|
|
3627
|
-
}
|
|
3628
|
-
}
|
|
3629
|
-
}
|
|
3630
|
-
if (rosterFaceDeferred && rosterBearingSpec !== undefined) {
|
|
3631
|
-
const rideEntries = rosterBearingSpec.agentListing;
|
|
3632
|
-
const rideModels = rosterBearingSpec.agentModels;
|
|
3633
|
-
listingRideRef.current = (newly) => {
|
|
3634
|
-
if (!newly.includes(rosterBearingSpec.name))
|
|
3635
|
-
return undefined;
|
|
3636
|
-
if ((announcedListingsRef.agents?.length ?? 0) > 0)
|
|
3637
|
-
return undefined;
|
|
3638
|
-
return renderAgentListingDelta({ announcedAgentTypes: undefined }, rideEntries, rosterBearingSpec.name, rideModels);
|
|
3639
|
-
};
|
|
3640
|
-
}
|
|
3641
|
-
const dateChange = renderWithDate
|
|
3642
|
-
? { legDate: envFacts.date, today: () => formatLocalDate(new Date(), tzValid ? userTz : undefined) }
|
|
3643
|
-
: undefined;
|
|
1417
|
+
const { activeTools, fpRef, turnSnapshotRef, harnessTools, toolsDeltaRef, toolMaterializeStatic, deferDirectCall, staticFaceForRef, listingRideRef } = await prepareToolDisclosureMount({ spec, deps, session, resume, deferred, tools, onceLedger, mcp, promptManifest, offloadReachableToolsRef, contentOriginWrapRef, rebuildHarnessToolsRef, harnessRef });
|
|
1418
|
+
const { agentListing, announcedListingsRef, dateChange } = prepareListings({ spec, tools, onceLedger, sessionId, toolFaceSnapshot, activeTools, resume, skillsListing, listingRideRef, renderWithDate, envFacts, tzValid, userTz });
|
|
3644
1419
|
const lastBrainContextRef = {};
|
|
3645
1420
|
const requestLossyRef = { current: false };
|
|
3646
1421
|
const staleOffloadWrittenRefs = new Set();
|
|
@@ -3660,7 +1435,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3660
1435
|
};
|
|
3661
1436
|
const staleOffloadCfg = resolveStaleToolResultOffload(spec.compaction?.staleToolResultOffload);
|
|
3662
1437
|
if (staleOffloadCfg !== undefined && offloadStore === undefined) {
|
|
3663
|
-
|
|
1438
|
+
onceLedger.onError(new Error("compaction.staleToolResultOffload is set but the tool-result offload store is disabled (toolResultThresholdChars ≤ 0/∞) — the knob is inert this run; re-enable offloading or drop the knob"), { phase: "config", sessionId });
|
|
3664
1439
|
}
|
|
3665
1440
|
const staleOffload = staleOffloadCfg !== undefined && offloadStore !== undefined ? { cfg: staleOffloadCfg, store: offloadStore } : undefined;
|
|
3666
1441
|
const guardedBrain = brainCallGuardrailMs === undefined
|
|
@@ -3961,61 +1736,19 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3961
1736
|
return {};
|
|
3962
1737
|
return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
|
|
3963
1738
|
};
|
|
3964
|
-
const inheritedAskEvidence = inheritedAskRuleEvidence(deps);
|
|
3965
|
-
const permissionRuleLane = (
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
}
|
|
3976
|
-
|
|
3977
|
-
return undefined;
|
|
3978
|
-
const root = taskRootFinal;
|
|
3979
|
-
return {
|
|
3980
|
-
admits: async (req) => {
|
|
3981
|
-
const anonymous = spec.principal === undefined || spec.principal === "";
|
|
3982
|
-
if (anonymous && !localOwnerDeclared)
|
|
3983
|
-
return undefined;
|
|
3984
|
-
if (req.toolName !== PERSISTED_RULE_TOOL && req.toolName !== DIRECTORY_RULE_TOOL)
|
|
3985
|
-
return undefined;
|
|
3986
|
-
let listed;
|
|
3987
|
-
try {
|
|
3988
|
-
listed = anonymous
|
|
3989
|
-
?
|
|
3990
|
-
await provider.forLocalOwner().list()
|
|
3991
|
-
: await provider.forPrincipal(spec.principal).list();
|
|
3992
|
-
}
|
|
3993
|
-
catch (err) {
|
|
3994
|
-
emitTrace(deps.tracer, () => ({
|
|
3995
|
-
kind: "permission.rule_store_unreadable",
|
|
3996
|
-
version: 1,
|
|
3997
|
-
taskId: hostTaskId,
|
|
3998
|
-
message: err instanceof Error ? err.message : String(err),
|
|
3999
|
-
ts: Date.now(),
|
|
4000
|
-
}));
|
|
4001
|
-
return { unreadable: true };
|
|
4002
|
-
}
|
|
4003
|
-
const table = await spliceSessionOverlayRows(deps.sessionPermissionRules, sessionId, listed.rules, deps.tracer, hostTaskId);
|
|
4004
|
-
if (req.toolName === DIRECTORY_RULE_TOOL) {
|
|
4005
|
-
return directoryRuleLaneAnswer(table, req.args, { root, sessionId, liveCwd: handsCwdRef?.current });
|
|
4006
|
-
}
|
|
4007
|
-
const command = req.args?.command;
|
|
4008
|
-
if (typeof command !== "string")
|
|
4009
|
-
return undefined;
|
|
4010
|
-
const admitting = findAdmittingRule(table, { tool: req.toolName, command, cwd: root, sessionId, ...(handsCwdRef?.current !== undefined ? { execCwd: handsCwdRef.current } : {}) });
|
|
4011
|
-
if (admitting !== undefined)
|
|
4012
|
-
return persistedRuleHitOf(admitting);
|
|
4013
|
-
const coverage = segmentCoverageOf(command, { persisted: table }, { tool: req.toolName, cwd: root, sessionId, ...(handsCwdRef?.current !== undefined ? { execCwd: handsCwdRef.current } : {}) });
|
|
4014
|
-
return coverage !== undefined ? { segmentCoverage: coverage } : undefined;
|
|
4015
|
-
},
|
|
4016
|
-
};
|
|
4017
|
-
})();
|
|
4018
|
-
const permissionRuleOrgLane = makeOrgAdjudicationLane(deps.permissionRuleOrg, questionToolMounted, (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }));
|
|
1739
|
+
const inheritedAskEvidence = inheritedAskRuleEvidence(deps.permissionRuleStore);
|
|
1740
|
+
const { personal: permissionRuleLane, org: permissionRuleOrgLane } = createPermissionRuleLanes({
|
|
1741
|
+
provider: deps.permissionRuleStore,
|
|
1742
|
+
localOwnerDeclared: deps.localOwnerRules === true,
|
|
1743
|
+
principal: spec.principal,
|
|
1744
|
+
sessionId,
|
|
1745
|
+
root: taskRootFinal,
|
|
1746
|
+
liveCwd: () => handsCwdRef?.current,
|
|
1747
|
+
questionToolMounted,
|
|
1748
|
+
questionToolName: ASK_USER_QUESTION_TOOL_NAME,
|
|
1749
|
+
onRevisionDefect: (message) => deps.onError?.(new Error(message), { phase: "config", sessionId }),
|
|
1750
|
+
onDisclosure: (message) => emitTrace(deps.tracer, () => ({ kind: "permission.rule_store_unreadable", version: 1, taskId: hostTaskId, message, ts: Date.now() })),
|
|
1751
|
+
});
|
|
4019
1752
|
const ruleOffersOf = makeRuleOffersOf({
|
|
4020
1753
|
laneArmed: permissionRuleLane !== undefined,
|
|
4021
1754
|
principal: spec.principal,
|
|
@@ -4058,7 +1791,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4058
1791
|
return { action: "allow", updatedInput: editArgs };
|
|
4059
1792
|
if (judged.kind === "deny")
|
|
4060
1793
|
return judged.result;
|
|
4061
|
-
const { ask: editAsk, fallback: editFallback, mintedHere: editMintedHere } = judged;
|
|
1794
|
+
const { ask: editAsk, fallback: editFallback, mintedHere: editMintedHere, origin: editOrigin } = judged;
|
|
4062
1795
|
re = editAsk;
|
|
4063
1796
|
const rr = await resolveAsk({
|
|
4064
1797
|
toolName: creq.toolName,
|
|
@@ -4070,7 +1803,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4070
1803
|
...riskAxesOf(creq.toolName),
|
|
4071
1804
|
...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
4072
1805
|
...(re.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: re.persistedRuleShadowed } : {}),
|
|
4073
|
-
...(
|
|
1806
|
+
...inheritedAskCarry(editOrigin, editFallback, isLiveApproverSeat(onAskOf), ancestorTracker),
|
|
4074
1807
|
ruleEvidence: inheritedAskEvidence,
|
|
4075
1808
|
}, onAskOf, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
4076
1809
|
settleDenialLimitFallback({ fallback: editFallback, mintedHere: editMintedHere, tracker: ancestorTracker, resolved: rr, headless: headlessDenyAtRecheck, stop: stopForDenialLimit, toolName: creq.toolName, toolCallId: creq.toolCallId });
|
|
@@ -4131,7 +1864,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4131
1864
|
return { action: "allow" };
|
|
4132
1865
|
if (judged.kind === "deny")
|
|
4133
1866
|
return judged.result;
|
|
4134
|
-
const { ask: inheritedAsk, fallback, mintedHere } = judged;
|
|
1867
|
+
const { ask: inheritedAsk, fallback, mintedHere, origin: inheritedOrigin } = judged;
|
|
4135
1868
|
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName)) {
|
|
4136
1869
|
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
|
|
4137
1870
|
return { action: "allow" };
|
|
@@ -4148,7 +1881,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4148
1881
|
...riskAxesOf(creq.toolName),
|
|
4149
1882
|
...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
4150
1883
|
...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
|
|
4151
|
-
...(
|
|
1884
|
+
...inheritedAskCarry(inheritedOrigin, fallback, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
|
|
4152
1885
|
ruleEvidence: inheritedAskEvidence,
|
|
4153
1886
|
}, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
4154
1887
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
@@ -4211,7 +1944,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4211
1944
|
return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
|
|
4212
1945
|
if (judged.kind === "deny")
|
|
4213
1946
|
return judged.result;
|
|
4214
|
-
const { ask: inheritedAsk, fallback, mintedHere } = judged;
|
|
1947
|
+
const { ask: inheritedAsk, fallback, mintedHere, origin: inheritedOrigin } = judged;
|
|
4215
1948
|
if (fallback === undefined && sandboxAdmissionArmed && policyAskClassOf(pc.policy) === "sandbox_local" && !sandboxBoundaryCapable(creq.toolName)) {
|
|
4216
1949
|
recordAncestorSandboxAdmission(creq.toolCallId, creq.toolName);
|
|
4217
1950
|
return decision.updatedInput !== undefined ? { action: "allow", updatedInput: decision.updatedInput } : { action: "allow" };
|
|
@@ -4227,7 +1960,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4227
1960
|
...riskAxesOf(creq.toolName),
|
|
4228
1961
|
...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
4229
1962
|
...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
|
|
4230
|
-
...(
|
|
1963
|
+
...inheritedAskCarry(inheritedOrigin, fallback, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
|
|
4231
1964
|
ruleEvidence: inheritedAskEvidence,
|
|
4232
1965
|
}, pc.onAsk, csignal ?? abortController.signal, lateAskSettlementObserver({ toolName: creq.toolName, toolCallId: creq.toolCallId, sessionId, runId, ...(spec.taskId !== undefined ? { taskId: spec.taskId } : {}), onNotice: deps.onNotice, onError: deps.onError }));
|
|
4233
1966
|
const askWaitMs = Math.max(0, now() - askT0);
|
|
@@ -4337,131 +2070,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4337
2070
|
},
|
|
4338
2071
|
}
|
|
4339
2072
|
: foldedPolicy;
|
|
4340
|
-
const onAsk = spec
|
|
4341
|
-
const handWriteTools = handsEnabled && spec.handsReadOnly !== true
|
|
4342
|
-
? Object.keys(HAND_TOOL_EFFECTS).filter((name) => {
|
|
4343
|
-
const eff = HAND_TOOL_EFFECTS[name];
|
|
4344
|
-
return eff === "write" || eff === "idempotent";
|
|
4345
|
-
})
|
|
4346
|
-
: [];
|
|
4347
|
-
const hasEffectAwareGate = Boolean(policyLayers.length > 0 || (hooks?.preToolUse !== undefined && !preToolUseObservational));
|
|
4348
|
-
const destructiveMcpUngated = mcp.tools.some((t) => !irreversibleTools.has(t.name) && !egressTools.has(t.name) && (toolEffects.get(t.name) ?? "write") !== "read");
|
|
4349
|
-
const firstPartyWriteUngated = (spec.tools ?? [])
|
|
4350
|
-
.map((t) => t.name)
|
|
4351
|
-
.filter((name) => !irreversibleTools.has(name) && !egressTools.has(name) && toolEffects.get(name) === "write");
|
|
4352
|
-
if (!hasEffectAwareGate && (handWriteTools.length > 0 || destructiveMcpUngated || firstPartyWriteUngated.length > 0)) {
|
|
4353
|
-
const what = [
|
|
4354
|
-
handWriteTools.length > 0 ? `hand tools [${handWriteTools.join(", ")}]` : undefined,
|
|
4355
|
-
destructiveMcpUngated ? "MCP write tools" : undefined,
|
|
4356
|
-
firstPartyWriteUngated.length > 0 ? `write tools [${firstPartyWriteUngated.join(", ")}]` : undefined,
|
|
4357
|
-
]
|
|
4358
|
-
.filter(Boolean)
|
|
4359
|
-
.join(" + ");
|
|
4360
|
-
if (!ungatedWarnedShapes.get(deps)?.has(what)) {
|
|
4361
|
-
let shapes = ungatedWarnedShapes.get(deps);
|
|
4362
|
-
if (!shapes) {
|
|
4363
|
-
shapes = new Set();
|
|
4364
|
-
ungatedWarnedShapes.set(deps, shapes);
|
|
4365
|
-
}
|
|
4366
|
-
shapes.add(what);
|
|
4367
|
-
deps.onError?.(new Error(`write-capable ${what} are present but UNGATED — wire an effect-aware tool policy (or a PreToolUse hook). ` +
|
|
4368
|
-
`bash/MCP irreversible actions are otherwise unadjudicated; the deployment sandbox/egress boundary is the only protection (design/77 §4, design/53 §2.H). ` +
|
|
4369
|
-
`(This warning fires once per deployment per tool-set shape.)`), { phase: "config", sessionId });
|
|
4370
|
-
}
|
|
4371
|
-
}
|
|
4372
|
-
const preToolContexts = new Map();
|
|
4373
|
-
const blockedToolCalls = new Set();
|
|
4374
|
-
const approvalSettlement = new Map();
|
|
4375
|
-
const humanBareRejections = new Set();
|
|
4376
|
-
const batchHaltRef = {};
|
|
4377
|
-
const blockedTracked = Boolean(hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure || hooks?.postToolBatch);
|
|
4378
|
-
const restoreSurfaceGap = ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && ownedEnv.capabilities.suspendable ? missingRestoreSurface(ownedEnv) : [];
|
|
4379
|
-
const incompleteSuspendAdapter = restoreSurfaceGap.length > 0 ? restoreSurfaceGap : undefined;
|
|
4380
|
-
const durableSuspendInfraReady = resolveCheckpointStore(spec, deps) !== undefined &&
|
|
4381
|
-
!(offloadStore !== undefined && isVolatileOffloadStore(offloadStore)) &&
|
|
4382
|
-
(ownedEnv === undefined || isRemoteExecutionEnv(ownedEnv)) &&
|
|
4383
|
-
incompleteSuspendAdapter === undefined;
|
|
4384
|
-
const resourceSuspendEligible = spec.resourceSuspend !== undefined && durableSuspendInfraReady;
|
|
4385
|
-
if (spec.resourceSuspend !== undefined && !resourceSuspendEligible) {
|
|
4386
|
-
const why = resolveCheckpointStore(spec, deps) === undefined
|
|
4387
|
-
? "no CheckpointStore is wired"
|
|
4388
|
-
: offloadStore !== undefined && isVolatileOffloadStore(offloadStore)
|
|
4389
|
-
? "tool-result offload uses the in-memory store (a resume needs durable results)"
|
|
4390
|
-
: incompleteSuspendAdapter !== undefined
|
|
4391
|
-
? `the per-task execution env declares capabilities.suspendable but its adapter does not implement ${incompleteSuspendAdapter.join(" or ")} (a snapshot nothing can restore is worse than no snapshot)`
|
|
4392
|
-
: "the per-task execution env is not a RemoteExecutionEnv (it would be destroyed on suspend)";
|
|
4393
|
-
deps.onError?.(new Error(`resourceSuspend is set but INACTIVE: ${why}; resource limits will hard-fail, not suspend`), {
|
|
4394
|
-
phase: "config",
|
|
4395
|
-
sessionId,
|
|
4396
|
-
});
|
|
4397
|
-
}
|
|
4398
|
-
const usageGovernance = buildUsageGovernance(usageWindows, deps, spec.principal, sessionId);
|
|
4399
|
-
const platformSuspendArmed = durableSuspendInfraReady && (envLifetimeSuspendAt !== undefined || usageGovernance !== undefined);
|
|
4400
|
-
const durableApproval = spec.durableApproval ??
|
|
4401
|
-
(runtimeCaps?.forceDurableGate ? { scope: spec.principal || DEFAULT_IRREVERSIBLE_SCOPE } : undefined);
|
|
4402
|
-
const wiringQuestionSeam = resolveQuestionSeam(spec, deps);
|
|
4403
|
-
const wiringQuestionStripped = internals?.questionFaceStripped === true;
|
|
4404
|
-
const wiringAskSeam = resolveAskSeamForm(spec, deps);
|
|
4405
|
-
const wiringManifest = deriveWiringManifest({
|
|
4406
|
-
half: "effective",
|
|
4407
|
-
leg: resume !== undefined ? "resume" : internals?.isDelegatedChild === true ? "child" : "root",
|
|
4408
|
-
askForm: wiringAskSeam.form,
|
|
4409
|
-
...(wiringAskSeam.provenance !== undefined ? { askProvenance: wiringAskSeam.provenance } : {}),
|
|
4410
|
-
questionWired: wiringQuestionSeam.wired,
|
|
4411
|
-
...(wiringQuestionSeam.provenance !== undefined ? { questionProvenance: wiringQuestionSeam.provenance } : {}),
|
|
4412
|
-
...(wiringQuestionStripped ? { questionStrippedByEngine: true } : {}),
|
|
4413
|
-
...(resolvedInteractionPosture !== undefined ? { interactionPosture: resolvedInteractionPosture } : {}),
|
|
4414
|
-
...(spec.interactiveTools === true && !wiringQuestionSeam.wired && !durableQuestionFace && !wiringQuestionStripped
|
|
4415
|
-
? { interactiveToolsWithoutDeliveryFace: true }
|
|
4416
|
-
: {}),
|
|
4417
|
-
elicitSeamWired: resolveElicitSeam(deps),
|
|
4418
|
-
elicitServersOptedIn: countElicitOptIns(spec),
|
|
4419
|
-
parkCapable: checkpointStore !== undefined,
|
|
4420
|
-
parkDurableApprovalOptIn: spec.durableApproval !== undefined,
|
|
4421
|
-
parkForceDurableGate: runtimeCaps?.forceDurableGate === true,
|
|
4422
|
-
parkSafetyVocabularyArmed: irreversibleTools.size > 0 || egressTools.size > 0,
|
|
4423
|
-
...(checkpointStore !== undefined ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
|
|
4424
|
-
sessionDurability: resolveDeclaredDurability(sessions, "sessionStore"),
|
|
4425
|
-
backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
|
|
4426
|
-
subagentTranscriptTier: resolveSubagentTranscriptTier(deps.backgroundAgentStore !== undefined, sessions),
|
|
4427
|
-
permissionRuleStoreWired: deps.permissionRuleStore !== undefined,
|
|
4428
|
-
permissionRuleSyncWired: deps.permissionRuleSyncWired === true,
|
|
4429
|
-
permissionRuleOrgGoverned: deps.permissionRuleOrg !== undefined,
|
|
4430
|
-
hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
|
|
4431
|
-
lockedConfigWired: deps.lockedConfig !== undefined,
|
|
4432
|
-
complianceWired: deps.compliancePostureResolver !== undefined,
|
|
4433
|
-
memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
|
|
4434
|
-
retentionPolicyWired: deps.retentionPolicy !== undefined,
|
|
4435
|
-
...(modelGateManifest !== undefined ? { modelGate: modelGateManifest } : {}),
|
|
4436
|
-
autoMode: { armed: autoModeArmReason === "armed", reason: autoModeArmReason },
|
|
4437
|
-
});
|
|
4438
|
-
peerLaneRefs.askEffective = wiringManifest.ask.effective;
|
|
4439
|
-
if (peerLaneActive && internals?.onTaskNotification !== undefined) {
|
|
4440
|
-
bindPeerLaneDrain(harness, {
|
|
4441
|
-
deps,
|
|
4442
|
-
sessionId,
|
|
4443
|
-
runId,
|
|
4444
|
-
scope: taskScope,
|
|
4445
|
-
inject: internals.onTaskNotification,
|
|
4446
|
-
ownTokens: () => internals?.peerSelfRef?.current.ownTokens ?? [],
|
|
4447
|
-
refs: peerLaneRefs,
|
|
4448
|
-
selfName: () => internals?.explicitAgentName,
|
|
4449
|
-
parked: () => suspendRef.token !== undefined || reviewRef.token !== undefined,
|
|
4450
|
-
});
|
|
4451
|
-
}
|
|
4452
|
-
const parkLaneArmed = wiringManifest.parkLane.effective === true;
|
|
4453
|
-
announceDurableGateUnavailable({ onNotice: deps.onNotice, forceDurableGate: runtimeCaps?.forceDurableGate === true, storeWired: checkpointStore !== undefined, taskStoreNull: spec.checkpointStore === null, liveApprover: isLiveApproverSeat(frozenOnAsk), liveQuestionFace: liveQuestionFace !== undefined, parentConstraints: liveInheritedGate?.parentConstraints, sessionId, runId, principal: spec.principal });
|
|
4454
|
-
const hookIdentity = mintHookInvocationIdentity({
|
|
4455
|
-
sessionId,
|
|
4456
|
-
taskId: hostTaskId,
|
|
4457
|
-
legKind: wiringManifest.leg.kind,
|
|
4458
|
-
isDelegatedChild: delegation.isDelegatedChild,
|
|
4459
|
-
...(internals?.insideFork === true ? { insideFork: true } : {}),
|
|
4460
|
-
...(internals?.agentName !== undefined ? { agentName: internals.agentName } : {}),
|
|
4461
|
-
...(internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId } : {}),
|
|
4462
|
-
});
|
|
4463
|
-
const hookContextConsumerWired = hooks?.preToolUse !== undefined || hooks?.postToolUse !== undefined || hooks?.postToolUseFailure !== undefined;
|
|
4464
|
-
const hookEnvFace = hookContextConsumerWired && (ownedEnv ?? deps.executionEnv) != null ? createHookEnvCapabilities(executionEnv) : undefined;
|
|
2073
|
+
const { onAsk, preToolContexts, blockedToolCalls, approvalSettlement, humanBareRejections, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, platformSuspendArmed, durableApproval, wiringManifest, parkLaneArmed, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, handsEnabled, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, suspendRef, reviewRef, frozenOnAsk, liveInheritedGate, delegation });
|
|
4465
2074
|
if (fileHistoryBoundary !== undefined) {
|
|
4466
2075
|
harness.on("tool_call", async () => {
|
|
4467
2076
|
await fileHistoryBoundary.settle();
|
|
@@ -4509,7 +2118,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4509
2118
|
...riskAxesOf(req.toolName),
|
|
4510
2119
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
4511
2120
|
...(decision.action === "ask" && decision.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: decision.persistedRuleShadowed } : {}),
|
|
4512
|
-
...(decision.action === "ask"
|
|
2121
|
+
...(decision.action === "ask" ? gateAskCarry(decision, isLiveApproverSeat(onAsk), autoModeDenialTracking) : {}),
|
|
4513
2122
|
...(decision.action === "ask" && decision.probeReason !== undefined ? { probeReason: decision.probeReason } : {}),
|
|
4514
2123
|
...(decision.action === "ask" && decision.probeCause !== undefined ? { probeCause: decision.probeCause } : {}),
|
|
4515
2124
|
...(decision.action === "ask" && decision.ruleEvidence !== undefined ? { ruleEvidence: decision.ruleEvidence } : {}),
|
|
@@ -4616,13 +2225,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4616
2225
|
}
|
|
4617
2226
|
: undefined;
|
|
4618
2227
|
})(),
|
|
4619
|
-
announcedListings: announcedListingsRef
|
|
4620
|
-
? {
|
|
4621
|
-
...(announcedListingsRef.agents !== undefined ? { agents: [...announcedListingsRef.agents] } : {}),
|
|
4622
|
-
...(announcedListingsRef.skills !== undefined ? { skills: [...announcedListingsRef.skills] } : {}),
|
|
4623
|
-
...(announcedListingsRef.models !== undefined ? { models: [...announcedListingsRef.models] } : {}),
|
|
4624
|
-
}
|
|
4625
|
-
: undefined,
|
|
2228
|
+
announcedListings: serializeAnnouncedListings(announcedListingsRef),
|
|
4626
2229
|
gitAnnouncement: gitStatusRef.announced !== undefined ? { ...gitStatusRef.announced } : undefined,
|
|
4627
2230
|
delegationProvenance: internals?.delegationProvenance !== undefined ? { ...internals.delegationProvenance.ref.current } : undefined,
|
|
4628
2231
|
isDelegatedChild: hookIdentity.isDelegatedChild ? true : undefined,
|
|
@@ -4999,7 +2602,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
4999
2602
|
}
|
|
5000
2603
|
};
|
|
5001
2604
|
const suspendAsk = parkLaneArmed && checkpointStore !== undefined
|
|
5002
|
-
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated,
|
|
2605
|
+
? async (req, postHookArgs, safety, liveFaceUnavailable, realApproval, shadowedRule, askDecisionReason, probeReason, probeCause, segmentCoverage, matchedAskRule, probeMandated, carry) => {
|
|
5003
2606
|
const syncFirstEligible = req.toolName === ASK_USER_QUESTION_TOOL_NAME ? contentAskRoutable(req.toolCallId) : isLiveApproverSeat(onAsk);
|
|
5004
2607
|
if (syncFirstEligible &&
|
|
5005
2608
|
runtimeCaps?.forceDurableGate !== true &&
|
|
@@ -5007,7 +2610,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
5007
2610
|
!inheritedUnavailableAsks.has(req.toolCallId)) {
|
|
5008
2611
|
return undefined;
|
|
5009
2612
|
}
|
|
5010
|
-
const cutSignal = composedCallSignal(
|
|
2613
|
+
const cutSignal = composedCallSignal(carry?.signal);
|
|
5011
2614
|
let token;
|
|
5012
2615
|
let gate;
|
|
5013
2616
|
let cp;
|
|
@@ -5179,6 +2782,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
5179
2782
|
...(bidi ? { hasBidiControls: true } : {}),
|
|
5180
2783
|
};
|
|
5181
2784
|
})(),
|
|
2785
|
+
...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
2786
|
+
...askCarryRowMembers(carry),
|
|
5182
2787
|
...ruleOffersOf(req.toolName, parkedArgs, {
|
|
5183
2788
|
...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
|
|
5184
2789
|
...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
|
|
@@ -5255,7 +2860,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
5255
2860
|
if (eff !== undefined && !toolEffects.has(t.name))
|
|
5256
2861
|
toolEffects.set(t.name, eff);
|
|
5257
2862
|
}
|
|
5258
|
-
const writeProtectionCheck = createWriteProtectionCheck(deps.writeProtectedPaths);
|
|
2863
|
+
const writeProtectionCheck = createWriteProtectionCheck(deps.writeProtectedPaths, deps.memoryEngineDir !== undefined ? { dataRoot: deps.memoryEngineDir } : undefined);
|
|
5259
2864
|
const writeProtectionArmed = writeProtectionCheck !== undefined && tools.some((t) => PATH_CONFINABLE_WRITE_TOOLS.has(t.name));
|
|
5260
2865
|
toolCallGateArmedRef.armed =
|
|
5261
2866
|
effectivePolicy !== undefined ||
|
|
@@ -5759,25 +3364,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
5759
3364
|
overheadState.promptChars = systemPrompt.length;
|
|
5760
3365
|
const effectiveReadFaceObserved = carrierReadFace();
|
|
5761
3366
|
const effectiveReadDenyObserved = readDenyAdditionsNormalized.length > 0 ? readDenyAdditionsNormalized.map((e) => ({ ...e })) : undefined;
|
|
3367
|
+
await onceLedger.settle(session, announcedListingsRef);
|
|
5762
3368
|
const preparedHolder = {};
|
|
5763
|
-
const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp
|
|
3369
|
+
const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettlement, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(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, gateStopRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, gitStatusRef, listBackgroundTasks, hookIdentity, hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
|
|
3370
|
+
rollback.commit();
|
|
5764
3371
|
const prepared = buildPrepared();
|
|
5765
3372
|
preparedHolder.current = prepared;
|
|
5766
3373
|
return prepared;
|
|
5767
3374
|
}
|
|
5768
3375
|
catch (prepareErr) {
|
|
5769
|
-
|
|
5770
|
-
const a2aHandle = a2a;
|
|
5771
|
-
await settleTeardownLeg(() => a2aHandle.dispose(), "a2a.dispose (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
5772
|
-
}
|
|
5773
|
-
if (mcp) {
|
|
5774
|
-
const mcpHandle = mcp;
|
|
5775
|
-
await settleTeardownLeg(() => mcpHandle.dispose(), "mcp.dispose (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
5776
|
-
}
|
|
5777
|
-
if (ownedEnv && hasDestroy(ownedEnv)) {
|
|
5778
|
-
const env = ownedEnv;
|
|
5779
|
-
await settleTeardownLeg(() => env.destroy(), "ownedEnv.destroy (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
5780
|
-
}
|
|
3376
|
+
await rollback.unwindAll();
|
|
5781
3377
|
await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (prepare-throw leg)", (e) => deps.onError?.(e, { phase: "config", sessionId }));
|
|
5782
3378
|
throw prepareErr;
|
|
5783
3379
|
}
|