@sema-agent/core 7.16.0 → 7.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +173 -0
- package/dist/agents/builtin-agents.d.ts +3 -3
- package/dist/agents/cross-session-envelope.d.ts +0 -1
- package/dist/agents/cross-session-envelope.js +0 -1
- package/dist/agents/peer-notices.d.ts +0 -9
- package/dist/agents/peer-notices.js +0 -63
- package/dist/agents/subagent.js +7 -15
- package/dist/brain/request-params.d.ts +2 -2
- package/dist/config/defaults.d.ts +0 -7
- package/dist/config/defaults.js +0 -2
- package/dist/core/artifact-host.d.ts +218 -0
- package/dist/core/artifact-host.js +92 -0
- package/dist/core/ask-origin.d.ts +29 -3
- package/dist/core/ask-origin.js +17 -9
- package/dist/core/ask-question.d.ts +25 -0
- package/dist/core/ask-question.js +4 -0
- package/dist/core/ask-unresolvable-notice.d.ts +30 -21
- package/dist/core/ask-unresolvable-notice.js +16 -8
- package/dist/core/auto-compaction.d.ts +10 -10
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/background-agent-store.d.ts +5 -5
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-execution-record.d.ts +0 -3
- package/dist/core/checkpoint-execution-record.js +0 -4
- package/dist/core/checkpoint-store.d.ts +71 -99
- package/dist/core/checkpoint-store.js +9 -16
- package/dist/core/context-edit.d.ts +36 -104
- package/dist/core/context-edit.js +37 -60
- package/dist/core/effective-path-target.d.ts +68 -8
- package/dist/core/effective-path-target.js +42 -30
- package/dist/core/engine-notice.d.ts +46 -32
- package/dist/core/env-budget.d.ts +31 -0
- package/dist/core/env-budget.js +39 -0
- package/dist/core/exec-output-tail.d.ts +5 -5
- package/dist/core/file-history-store.js +3 -6
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.js +3 -3
- package/dist/core/gate-lanes.js +9 -15
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +18 -17
- package/dist/core/lsp-session.d.ts +1 -1
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/lsp.js +5 -4
- package/dist/core/mcp.d.ts +5 -5
- package/dist/core/mcp.js +4 -48
- package/dist/core/memory-recall.d.ts +0 -8
- package/dist/core/memory.d.ts +1 -1
- package/dist/core/oracle-isolation.js +2 -2
- package/dist/core/parent-spec-seat.d.ts +66 -0
- package/dist/core/parent-spec-seat.js +47 -0
- package/dist/core/permission-rule-model.d.ts +11 -6
- package/dist/core/permission-rule-model.js +53 -37
- package/dist/core/permission-rules.js +8 -3
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +19 -0
- package/dist/core/retired-keys.js +15 -0
- package/dist/core/runner/active-skill-scope.js +3 -3
- package/dist/core/runner/assemble-result.d.ts +15 -13
- package/dist/core/runner/assemble-result.js +5 -4
- package/dist/core/runner/compaction-call-options.d.ts +25 -89
- package/dist/core/runner/contracts.d.ts +10 -9
- package/dist/core/runner/gate-exit.js +15 -1
- package/dist/core/runner/halt-attribution.d.ts +35 -0
- package/dist/core/runner/halt-attribution.js +16 -0
- package/dist/core/runner/memory-consolidation.d.ts +0 -2
- package/dist/core/runner/memory-consolidation.js +0 -1
- package/dist/core/runner/prepare-artifact.d.ts +53 -0
- package/dist/core/runner/prepare-artifact.js +77 -0
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
- package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
- package/dist/core/runner/prepare-config-doors.d.ts +3 -3
- package/dist/core/runner/prepare-config-doors.js +5 -2
- package/dist/core/runner/prepare-context-lane.d.ts +2 -2
- package/dist/core/runner/prepare-context-lane.js +2 -13
- package/dist/core/runner/prepare-file-history.d.ts +1 -1
- package/dist/core/runner/prepare-file-history.js +3 -52
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -7
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +5 -2
- package/dist/core/runner/prepare-inherited-gate.js +7 -8
- package/dist/core/runner/prepare-memory.d.ts +4 -6
- package/dist/core/runner/prepare-memory.js +4 -2
- package/dist/core/runner/prepare-policy-chain.d.ts +3 -3
- package/dist/core/runner/prepare-policy-chain.js +15 -30
- package/dist/core/runner/prepare-prompt-assembly.js +1 -1
- package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
- package/dist/core/runner/prepare-suspend-saga.js +0 -2
- package/dist/core/runner/prepare-task.js +10 -9
- package/dist/core/runner/prepare-wiring-manifest.js +9 -7
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-internals-and-config.js +4 -0
- package/dist/core/runner/retired-runner-deps.d.ts +3 -0
- package/dist/core/runner/retired-runner-deps.js +7 -0
- package/dist/core/runner/run-harness-handlers.js +2 -3
- package/dist/core/runner/run-settle-and-teardown.d.ts +1 -1
- package/dist/core/runner/run-settle-and-teardown.js +3 -1
- package/dist/core/runner/session-file-state-replay.js +3 -3
- package/dist/core/runner/session-rule-policy.js +3 -4
- package/dist/core/runner/stream-halt-verbs.js +1 -1
- package/dist/core/runner/stream-steer-verb.js +1 -4
- package/dist/core/runner/workspace-path.js +3 -0
- package/dist/core/runner-deps.d.ts +30 -49
- package/dist/core/safe-notify.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +4 -4
- package/dist/core/session.d.ts +2 -2
- package/dist/core/skill-tool-specifier.d.ts +1 -1
- package/dist/core/skills-directory.d.ts +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/strategy-store.d.ts +8 -11
- package/dist/core/task-event.d.ts +20 -17
- package/dist/core/task-limits.d.ts +1 -1
- package/dist/core/task-notification.d.ts +5 -5
- package/dist/core/task-registry-agent.d.ts +5 -5
- package/dist/core/task-registry-monitor.d.ts +2 -2
- package/dist/core/task-registry-shared.d.ts +27 -27
- package/dist/core/task-registry-shared.js +3 -13
- package/dist/core/task-registry.d.ts +8 -8
- package/dist/core/task-result.d.ts +37 -37
- package/dist/core/task-spec.d.ts +5 -5
- package/dist/core/task-stream.d.ts +16 -11
- package/dist/core/tool-catalog-entries.d.ts +1 -0
- package/dist/core/tool-catalog-entries.js +5 -0
- package/dist/core/tool-conformance.d.ts +0 -2
- package/dist/core/tool-conformance.js +1 -3
- package/dist/core/tool-errors.d.ts +5 -0
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +51 -22
- package/dist/core/tool-policy.js +28 -5
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +11 -11
- package/dist/core/tool-spec.d.ts +34 -38
- package/dist/core/tools.d.ts +2 -2
- package/dist/core/trace.d.ts +12 -15
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +4 -3
- package/dist/core/workflow-journal-store.d.ts +11 -11
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
- package/dist/engine/execution-env/node-execution-env.js +4 -2
- package/dist/engine/harness/agent-harness.d.ts +47 -4
- package/dist/engine/harness/agent-harness.js +52 -9
- package/dist/engine/loop/agent-loop.js +15 -8
- package/dist/engine/loop/types.d.ts +5 -0
- package/dist/index.d.ts +19 -14
- package/dist/index.js +15 -10
- package/dist/internal/harness.d.ts +1 -0
- package/dist/orchestration/run-spec.d.ts +3 -0
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +12 -6
- package/dist/orchestration/run-workflow-tool.js +23 -5
- package/dist/orchestration/workflow-primitives.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +1 -4
- package/dist/orchestration/workflow-types.d.ts +34 -4
- package/dist/orchestration/workflow-types.js +2 -0
- package/dist/orchestration/workflow.d.ts +39 -7
- package/dist/orchestration/workflow.js +234 -113
- package/dist/prompt-assembly/assemble.d.ts +2 -2
- package/dist/prompt-assembly/assemble.js +9 -9
- package/dist/prompt-assembly/composer.d.ts +6 -7
- package/dist/prompt-assembly/composer.js +10 -10
- package/dist/prompt-assembly/epoch.js +1 -1
- package/dist/prompt-assembly/event-registry.d.ts +0 -21
- package/dist/prompt-assembly/packs/sema-default.js +30 -30
- package/dist/prompt-assembly/types.d.ts +14 -14
- package/dist/prompts/coordinator.d.ts +0 -13
- package/dist/prompts/coordinator.js +0 -3
- package/dist/stores/file/checkpoint-store.js +1 -5
- package/dist/stores/file/file-history-store.js +2 -2
- package/dist/stores/file/permission-rule-store.d.ts +4 -35
- package/dist/stores/file/permission-rule-store.js +4 -148
- package/dist/tools/artifact/artifact-text.d.ts +62 -0
- package/dist/tools/artifact/artifact-text.js +68 -0
- package/dist/tools/artifact/artifact-tool.d.ts +52 -0
- package/dist/tools/artifact/artifact-tool.js +351 -0
- package/dist/tools/artifact/local-stub.d.ts +26 -0
- package/dist/tools/artifact/local-stub.js +296 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +74 -8
- package/dist/tools/fs/bash-readonly-classifier.js +171 -60
- package/dist/tools/fs/fs-bash.d.ts +14 -10
- package/dist/tools/fs/fs-bash.js +19 -27
- package/dist/tools/fs/fs-search-tools.js +5 -2
- package/dist/tools/fs/fs-shared.d.ts +2 -0
- package/dist/tools/fs/fs-shared.js +3 -11
- package/dist/tools/fs/index.d.ts +4 -0
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/safety.d.ts +117 -1
- package/dist/tools/fs/safety.js +131 -31
- package/dist/tools/fs/search.js +8 -6
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +115 -39
|
@@ -58,6 +58,7 @@ import { derivedRouteFallsBack } from "./derived-route-fallback.js";
|
|
|
58
58
|
import { prepareProtocolTools } from "./prepare-protocol-tools.js";
|
|
59
59
|
import { prepareQuestionFace } from "./prepare-question-face.js";
|
|
60
60
|
import { prepareLsp } from "./prepare-lsp.js";
|
|
61
|
+
import { prepareArtifact } from "./prepare-artifact.js";
|
|
61
62
|
import { createRollbackStack } from "./rollback-stack.js";
|
|
62
63
|
export { __resetMaterializeEnvAnnouncements } from "./prepare-tool-disclosure-mount.js";
|
|
63
64
|
export { fileHistoryFilesystemIdentity, resolveFileHistoryScope } from "./prepare-file-history.js";
|
|
@@ -70,6 +71,7 @@ import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
|
70
71
|
import { selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
|
|
71
72
|
import { remainingBudgetMicroUsd, resolveCheckpointStore } from "../checkpoint-store.js";
|
|
72
73
|
import { checkpointSeatOf } from "../checkpoint-seat.js";
|
|
74
|
+
import { handsWriteHalfMounts } from "../../tools/fs/index.js";
|
|
73
75
|
import { durableParkGapFor } from "../park-selfcheck.js";
|
|
74
76
|
import { deliverEngineNotice } from "../types.js";
|
|
75
77
|
import { ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
@@ -264,7 +266,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
264
266
|
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, memoryCapturePolicy, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
265
267
|
announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
|
|
266
268
|
const modelGateManifest = toolModelGateManifest(doors.modelGate);
|
|
267
|
-
const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes
|
|
269
|
+
const { toolEffects, egressTools, irreversibleTools, irreversibilityTier, axisExplicitNegatives, reversibilityProbes } = prepareSafetyScan({ spec, deps });
|
|
268
270
|
let shellGatedTools = new Set();
|
|
269
271
|
refuseRequireExistingWithoutSession(spec);
|
|
270
272
|
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 } : {}; })() });
|
|
@@ -368,8 +370,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
368
370
|
let resolvedReadFace;
|
|
369
371
|
let readDenyAdditionsNormalized = [];
|
|
370
372
|
let handsLessResolvedFace;
|
|
371
|
-
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, checkpointSeat, 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: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
372
|
-
const { roster, 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, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
|
|
373
|
+
const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, 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: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
374
|
+
const { roster, 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, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, parentSpecSeat, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
|
|
373
375
|
const tools = roster.tools;
|
|
374
376
|
const pausedRef = {};
|
|
375
377
|
const remoteEnvFailures = [];
|
|
@@ -432,11 +434,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
432
434
|
const { mcp, a2a, rebuildHarnessToolsRef, contentOriginWrapRef, toolCallGateArmedRef } = await prepareProtocolTools({ lockedPreflight, spec, deps, reminderMark, reminderDisclosureCounts, runId, sessionId, onceLedger, roster, rosterSeat, remoteToolOffload, toolFaceSnapshot, toolEffects, axisExplicitNegatives, irreversibilityTier, irreversibleTools, egressTools, abortController, rollback });
|
|
433
435
|
const callIssuedAtRef = {};
|
|
434
436
|
const workCutRef = { continuation: false, abortedBeforeTurnEnd: false };
|
|
435
|
-
const memoryWriteGateRef = {};
|
|
436
437
|
const advertisedWritableDirs = handsEnabled
|
|
437
438
|
? new AdvertisedWritableDirs(executionEnv, (dir, reason) => deps.onError?.(new Error(`${dir.advertiser} advertised writable directory skipped (cannot canonicalize): ${dir.path} — ${reason}`), { phase: "config", sessionId }))
|
|
438
439
|
: undefined;
|
|
439
|
-
const handsWriteHalfPlanned = handsEnabled && spec.handsReadOnly
|
|
440
|
+
const handsWriteHalfPlanned = handsEnabled && handsWriteHalfMounts({ readOnly: spec.handsReadOnly === true });
|
|
440
441
|
const memoryPairNameDomain = new Set();
|
|
441
442
|
for (const t of tools) {
|
|
442
443
|
memoryPairNameDomain.add(t.name);
|
|
@@ -455,12 +456,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
455
456
|
return { fault: true };
|
|
456
457
|
}
|
|
457
458
|
})();
|
|
458
|
-
const { memoryEngineSession, memoryTools, composeInjection: composeMemoryInjection, admittedOrgScopes: memoryAdmittedOrgScopes, ownOrgVerdict: memoryOwnOrgVerdict, effectiveMemoryScopes: memoryEffectiveScopes, } = await prepareMemory({
|
|
459
|
+
const { memoryEngineSession, memoryTools, memoryWriteGate, composeInjection: composeMemoryInjection, admittedOrgScopes: memoryAdmittedOrgScopes, ownOrgVerdict: memoryOwnOrgVerdict, effectiveMemoryScopes: memoryEffectiveScopes, } = await prepareMemory({
|
|
459
460
|
spec,
|
|
460
461
|
deps,
|
|
461
462
|
sessionId,
|
|
462
463
|
taskRootPath: taskRootFinal,
|
|
463
|
-
memoryWriteGateRef,
|
|
464
464
|
reminderMark,
|
|
465
465
|
writeToolsMounted: (handsWriteHalfPlanned || tools.some((t) => t.name === "Write")) &&
|
|
466
466
|
!(toolFaceSnapshot.exclude?.includes("Write") ?? false) &&
|
|
@@ -505,7 +505,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
505
505
|
if (internals?.ownOrgAdmissionRef !== undefined && ownOrgVerdictRef.current !== undefined) {
|
|
506
506
|
internals.ownOrgAdmissionRef.current = ownOrgVerdictRef.current;
|
|
507
507
|
}
|
|
508
|
-
const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals,
|
|
508
|
+
const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGate, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), firstPartyOffload, roster, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts, ...(trackFileEdit !== undefined ? { trackFileEdit } : {}), onFileEdited: noteFileEdited, ...(restoredFilePaths.length > 0 ? { restoredFilePaths } : {}) };
|
|
509
509
|
const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
|
|
510
510
|
const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
|
|
511
511
|
resolvedReadFace = handsReadFace.resolvedReadFace;
|
|
@@ -532,6 +532,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
532
532
|
`(per-principal entitlement governance, not a misconfiguration — Agent(subagent_type:"fork") will refuse honestly)`), { phase: "config", sessionId });
|
|
533
533
|
}
|
|
534
534
|
const { lspDiagnostics, nudgeLspOnEdit, lspRunIdent } = prepareLsp({ spec, deps, executionEnv, taskRootFinal, handsEnabled, sessionId, handsCwdRef, roster, envHandToolNames });
|
|
535
|
+
prepareArtifact({ deps, runId, taskId: hostTaskId, sessionId, agentName: internals?.explicitAgentName, executionEnv, rootCanonical: attachmentRootCanonical, additionalRootsCanonical: [...additionalRootsCanonical, ...additionalReadRootsCanonical], readDenyMatcher, resolvedReadFace, handsCwdRef, roster, firstPartyOffload, envHandToolNames });
|
|
535
536
|
if (internals?.delegationProvenance !== undefined && resume !== undefined) {
|
|
536
537
|
const live = internals.delegationProvenance.ref.current;
|
|
537
538
|
const seed = resume.seed.delegationProvenance;
|
|
@@ -681,7 +682,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
681
682
|
releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
|
|
682
683
|
}
|
|
683
684
|
}
|
|
684
|
-
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives,
|
|
685
|
+
const { hooks, hookTimeoutMs, preToolUseObservational, ownGatePreToolUse, basePolicyForResumeEdit, denyNarrowingPolicy, policyLayers, effectivePolicy, inheritedUnavailableAsks, inheritedAskGrants, askSourceIdentity, riskAxesOf, ruleOffersOf, permissionRuleLane, permissionRuleOrgLane, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, foldAskClasses, ancestorSandboxAdmissions } = await preparePolicyChain({ spec, deps, internals, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, lockedPreflight, tools, harnessTools, toolEffects, egressTools, irreversibilityTier, axisExplicitNegatives, shellGatedTools, envHandToolNames, handsCwdRef, readDenyMatcher, questionToolMounted, runtimeCaps, inheritedAncestorRules, inheritedParentConstraints, seedInheritedGate, hasSkillManifest, skillScope, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, autoModeDecider, stopForDenialLimit, delegation, abortController, now, ownSessionRulesRef });
|
|
685
686
|
const { onAsk, preToolContexts, blockedToolCalls, gateOutcomes, batchHaltRef, blockedTracked, incompleteSuspendAdapter, durableSuspendInfraReady, resourceSuspendEligible, usageGovernance, durableApproval, wiringManifest, parkLaneArmed, gateMachineryActive, hookIdentity, hookEnvFace } = prepareWiringManifest({ spec, deps, internals, resume, sessionId, runId, hostTaskId, taskScope, hooks, preToolUseObservational, policyLayers, effectivePolicy, handsEnabled, mcp, irreversibleTools, egressTools, toolEffects, ownedEnv, executionEnv, offloadStore, onceLedger, usageWindows, envLifetimeSuspendAt, runtimeCaps, resolvedInteractionPosture, durableQuestionFace, liveQuestionFace, checkpointStore, sessions, modelGateManifest, autoModeArmReason, lockedPreflight, peerLaneRefs, peerLaneActive, harness, pausedRef, frozenOnAsk, liveInheritedGate, delegation, toolRoster });
|
|
686
687
|
if (fileHistoryBoundary !== undefined) {
|
|
687
688
|
harness.on("tool_call", async () => {
|
|
@@ -3,7 +3,7 @@ import { resolveCheckpointStore } from "../checkpoint-store.js";
|
|
|
3
3
|
import { createHookEnvCapabilities, mintHookInvocationIdentity } from "../hooks.js";
|
|
4
4
|
import { isRemoteExecutionEnv, missingRestoreSurface } from "../remote-env.js";
|
|
5
5
|
import { isVolatileOffloadStore } from "../tool-result-store.js";
|
|
6
|
-
import { isLiveApproverSeat } from "../tool-policy.js";
|
|
6
|
+
import { durableMandateSourceOf, isLiveApproverSeat } from "../tool-policy.js";
|
|
7
7
|
import { deliverEngineNotice } from "../types.js";
|
|
8
8
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
9
9
|
import { GLOBAL_USAGE_KEY, usageRetryAfterMs, windowsGovernCost } from "../usage-window-store.js";
|
|
@@ -84,24 +84,26 @@ function buildUsageGovernance(windows, deps, principal, sessionId) {
|
|
|
84
84
|
}
|
|
85
85
|
const ungatedWarnedShapes = new WeakMap();
|
|
86
86
|
function announceDurableGateUnavailable(args) {
|
|
87
|
-
if (
|
|
87
|
+
if (args.mandate === undefined || args.storeWired)
|
|
88
88
|
return;
|
|
89
|
+
const source = args.mandate;
|
|
89
90
|
const cause = args.taskStoreDisabled ? "task_store_disabled" : "no_deployment_store";
|
|
90
91
|
const inheritedContentMandate = (args.parentConstraints ?? []).some((pc) => pc.contentMandate === true);
|
|
91
92
|
const liveQuestionFace = args.liveQuestionFace && !inheritedContentMandate;
|
|
92
93
|
deliverEngineNotice(args.onNotice, {
|
|
93
94
|
code: "config.durable_gate_unavailable",
|
|
94
|
-
message: `Durable approval gate mandated but unavailable: the runtime entitlement forceDurableGate is in force for this ` +
|
|
95
|
-
`
|
|
95
|
+
message: `Durable approval gate mandated but unavailable: ${source === "force_durable_gate" ? "the runtime entitlement forceDurableGate is in force for this principal" : "this task opted into durable approval (durableApproval) with no live approver seat"}, ` +
|
|
96
|
+
`but ${cause === "task_store_disabled" ? 'this task disabled the checkpoint store (checkpointStore: "disabled")' : "this deployment wired no checkpoint store"}, ` +
|
|
96
97
|
`so no interactive ask of this task can be parked for a durable answer. Asks resolve on the live chain instead ` +
|
|
97
|
-
`(${args.liveApprover ? "a live approver answers permission asks in-stream" : "permission asks are denied fail-closed — no live approver is wired"}; ` +
|
|
98
|
+
`(${args.liveApprover ? "a live approver answers permission asks in-stream" : args.blanketAllow ? 'a blanket "allow" posture answers ordinary permission asks without judgment (a mandated ask is refused)' : "permission asks are denied fail-closed — no live approver is wired"}; ` +
|
|
98
99
|
`${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"}) ` +
|
|
99
|
-
`and NO durable approval record is written. ${cause === "task_store_disabled" ? "Run this task with its store
|
|
100
|
+
`and NO durable approval record is written. ${cause === "task_store_disabled" ? "Run this task with its store" : "Wire a checkpoint store on this deployment"}, or ${source === "force_durable_gate" ? "withdraw the entitlement for principals served here" : "give the task a live approver seat (or drop durableApproval)"}.`,
|
|
100
101
|
detail: {
|
|
101
102
|
sessionId: args.sessionId,
|
|
102
103
|
runId: args.runId,
|
|
103
104
|
...(args.principal ? { principal: args.principal } : {}),
|
|
104
105
|
cause,
|
|
106
|
+
source,
|
|
105
107
|
liveApprover: args.liveApprover,
|
|
106
108
|
liveQuestionFace,
|
|
107
109
|
},
|
|
@@ -225,7 +227,7 @@ export function prepareWiringManifest(input) {
|
|
|
225
227
|
resourceSuspendEligible ||
|
|
226
228
|
platformSuspendArmed ||
|
|
227
229
|
spec.enablePlanMode === true;
|
|
228
|
-
announceDurableGateUnavailable({ onNotice: deps.onNotice, forceDurableGate: runtimeCaps?.forceDurableGate === true, storeWired: checkpointStore !== undefined, taskStoreDisabled: spec.checkpointStore === "disabled", liveApprover: isLiveApproverSeat(frozenOnAsk), liveQuestionFace: liveQuestionFace !== undefined, parentConstraints: liveInheritedGate?.parentConstraints, sessionId, runId, principal: spec.principal });
|
|
230
|
+
announceDurableGateUnavailable({ onNotice: deps.onNotice, mandate: durableMandateSourceOf({ forceDurableGate: runtimeCaps?.forceDurableGate === true, durableApprovalDeclared: spec.durableApproval !== undefined, onAsk: frozenOnAsk }), storeWired: checkpointStore !== undefined, taskStoreDisabled: spec.checkpointStore === "disabled", liveApprover: isLiveApproverSeat(frozenOnAsk), blanketAllow: frozenOnAsk === "allow", liveQuestionFace: liveQuestionFace !== undefined, parentConstraints: liveInheritedGate?.parentConstraints, sessionId, runId, principal: spec.principal });
|
|
229
231
|
const hookIdentity = mintHookInvocationIdentity({
|
|
230
232
|
sessionId,
|
|
231
233
|
taskId: hostTaskId,
|
|
@@ -36,6 +36,7 @@ export async function resumeAdmission(input) {
|
|
|
36
36
|
const outcomeGate = outcome.gate;
|
|
37
37
|
const resumeSignal = config.signal;
|
|
38
38
|
let suppliedMessage;
|
|
39
|
+
const parkedSteers = readPendingSteerQueue(cp.state);
|
|
39
40
|
if (outcomeGate === "wake") {
|
|
40
41
|
suppliedMessage = outcome.message;
|
|
41
42
|
const gk = cp.gate.kind;
|
|
@@ -74,7 +75,7 @@ export async function resumeAdmission(input) {
|
|
|
74
75
|
if (suppliedMessage !== undefined) {
|
|
75
76
|
wakeMessage = validatePendingSteer(suppliedMessage);
|
|
76
77
|
}
|
|
77
|
-
else if (
|
|
78
|
+
else if (parkedSteers.length === 0) {
|
|
78
79
|
throw new CheckpointError("wake.nothing_to_deliver", "cannot wake: no message was supplied and the checkpoint holds no parked pendingSteer — an empty " +
|
|
79
80
|
"wake would burn the checkpoint on a blank continuation; supply `message` or park a steer first");
|
|
80
81
|
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { CheckpointError, readPendingSteerQueue } from "../checkpoint-store.js";
|
|
2
2
|
import { GLOBAL_USAGE_KEY, resolveUsageWindows, usageRetryAfterMs } from "../usage-window-store.js";
|
|
3
3
|
import { placementValueOrAbsent } from "./checkpoint-scope.js";
|
|
4
|
+
import { assertNoRetiredRunnerDeps } from "./retired-runner-deps.js";
|
|
5
|
+
import { resolveMicroCompactKnob } from "./prepare-config-doors.js";
|
|
4
6
|
export function resumeInternalsAndConfig(input) {
|
|
5
7
|
const { cp, config, internals, outcomeGate, runner } = input;
|
|
6
8
|
let windowEntrance;
|
|
9
|
+
assertNoRetiredRunnerDeps(runner.deps);
|
|
10
|
+
resolveMicroCompactKnob(runner.deps.microCompact);
|
|
7
11
|
if (runner.deps.lockedConfig?.keys?.includes("toolPolicy") === true && config.basePolicyForResumeEdit !== undefined) {
|
|
8
12
|
throw new CheckpointError("checkpoint.invalid_outcome", "TaskSpec.basePolicyForResumeEdit is administratively locked by this deployment (locked key \"toolPolicy\") — a task-supplied " +
|
|
9
13
|
"resume-edit policy is refused pre-CAS (the checkpoint stays pending); remove the field or change the deployment's lock configuration");
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { assertNoRetiredKeys } from "../retired-keys.js";
|
|
2
|
+
const RETIRED_RUNNER_DEPS_KEYS = {
|
|
3
|
+
legacyRewindBoundaryProbe: "the rewind refusal always names both possibilities (never existed / retired whole-tree era); a deployment that holds that era's table answers the question on its own surface",
|
|
4
|
+
};
|
|
5
|
+
export function assertNoRetiredRunnerDeps(deps) {
|
|
6
|
+
assertNoRetiredKeys(deps, RETIRED_RUNNER_DEPS_KEYS, "RunnerDeps");
|
|
7
|
+
}
|
|
@@ -286,9 +286,8 @@ export function createHarnessHandlers(input) {
|
|
|
286
286
|
if (prepared.abortController.signal.aborted)
|
|
287
287
|
prepared.workCutRef.abortedBeforeTurnEnd = true;
|
|
288
288
|
const stopExtra = rs.turn.turnStopReason !== undefined ? { stopReason: rs.turn.turnStopReason } : {};
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
: { type: "turn_end", usageMissing: true, ...stopExtra, ...ident() });
|
|
289
|
+
const usage = rs.turn.turnUsage ?? { inputTokens: 0, totalInputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, costMicroUsd: 0 };
|
|
290
|
+
queue.push({ type: "turn_end", usage, ...(rs.turn.turnUsage === undefined || rs.turn.turnUsageMissing ? { usageMissing: true } : {}), ...stopExtra, ...ident() });
|
|
292
291
|
rs.turn.turnUsageMissing = false;
|
|
293
292
|
rs.turn.turnStopReason = undefined;
|
|
294
293
|
if (parentToolCallId !== undefined) {
|
|
@@ -51,7 +51,7 @@ export interface RunSettleAndTeardownInput {
|
|
|
51
51
|
parentToolCallId: string | undefined;
|
|
52
52
|
/** borrowed-readonly — the sub-agent display name for the terminal tick. */
|
|
53
53
|
subagentName: string | undefined;
|
|
54
|
-
/** borrowed-readonly — the loop-liveness latch: `userHalted`
|
|
54
|
+
/** borrowed-readonly — the loop-liveness latch: `userHalted` = the halt verb was ACCEPTED (the unconsumed-halt notice's input; it does not sign the seat). */
|
|
55
55
|
loopLatch: {
|
|
56
56
|
readonly userHalted: boolean;
|
|
57
57
|
};
|
|
@@ -11,6 +11,7 @@ import { defaultTaskRegistry } from "../task-registry.js";
|
|
|
11
11
|
import { emitTrace } from "../trace.js";
|
|
12
12
|
import { deliverEngineNotice } from "../types.js";
|
|
13
13
|
import { assembleResult } from "./assemble-result.js";
|
|
14
|
+
import { discloseUnconsumedHalt } from "./halt-attribution.js";
|
|
14
15
|
import { TIMER_LATENESS_REPORT_MS } from "./clock-and-limits.js";
|
|
15
16
|
import { resumeDecisionWasNegative } from "./decide-continuation.js";
|
|
16
17
|
import { drainForwardedFramesBeforeDone, forwardsSubagentEvents } from "./prepare-run-refs.js";
|
|
@@ -102,7 +103,7 @@ export async function runSettleAndTeardown(input) {
|
|
|
102
103
|
rewindNotes: prepared.rewindNotes,
|
|
103
104
|
editedFiles: prepared.editedFilesSnapshot(),
|
|
104
105
|
haltedOnUserRejection: prepared.batchHaltRef.current !== undefined,
|
|
105
|
-
|
|
106
|
+
haltConsumption: prepared.harness.haltConsumption(),
|
|
106
107
|
strandedHumanAnswers,
|
|
107
108
|
remoteEnvFailures: prepared.remoteEnvFailures,
|
|
108
109
|
effectiveReadFace: prepared.effectiveReadFace,
|
|
@@ -136,6 +137,7 @@ export async function runSettleAndTeardown(input) {
|
|
|
136
137
|
},
|
|
137
138
|
});
|
|
138
139
|
}
|
|
140
|
+
discloseUnconsumedHalt({ accepted: loopLatch.userHalted, consumption: prepared.harness.haltConsumption(), onNotice: runner.deps.onNotice, sessionId: prepared.sessionId, runId: rs.telemetry.runId, taskId: spec.taskId });
|
|
139
141
|
if (resume !== undefined &&
|
|
140
142
|
resume.pendingActionStarted !== true &&
|
|
141
143
|
result.terminal.kind !== "completed" &&
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isAbsoluteForFamily, pathFamilyOf } from "../../tools/fs/safety.js";
|
|
2
2
|
import { applyRecordedEdit } from "../../tools/fs/fs-shared.js";
|
|
3
3
|
import { normalizeFileText } from "../../tools/fs/encoding.js";
|
|
4
4
|
import { isOffloadedDetailReplacement } from "../tool-result-store.js";
|
|
@@ -75,7 +75,7 @@ export function wholeFileRecordsFromTranscript(messages) {
|
|
|
75
75
|
if (typeof changed !== "string")
|
|
76
76
|
continue;
|
|
77
77
|
const replayed = replayedEditContent(rest);
|
|
78
|
-
if (replayed !== undefined &&
|
|
78
|
+
if (replayed !== undefined && isAbsoluteForFamily(pathFamilyOf({ root: changed }), changed)) {
|
|
79
79
|
byPath.set(changed, { path: changed, content: replayed, at: m.timestamp });
|
|
80
80
|
}
|
|
81
81
|
else {
|
|
@@ -97,7 +97,7 @@ export function wholeFileRecordsFromTranscript(messages) {
|
|
|
97
97
|
const nested = rest.file;
|
|
98
98
|
const holder = isRead ? (typeof nested === "object" && nested !== null ? nested : undefined) : rest;
|
|
99
99
|
const filePath = holder?.filePath;
|
|
100
|
-
if (typeof filePath !== "string" || !
|
|
100
|
+
if (typeof filePath !== "string" || !isAbsoluteForFamily(pathFamilyOf({ root: filePath }), filePath))
|
|
101
101
|
continue;
|
|
102
102
|
const content = isRead ? wholeFileFromReadCard(rest) : typeof rest.content === "string" ? rest.content : undefined;
|
|
103
103
|
if (content === undefined)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { canonicalizeTarget, writeTargetPath } from "../../tools/fs/safety.js";
|
|
2
|
-
import { isWinFormPath } from "../../tools/fs/safety.js";
|
|
1
|
+
import { canonicalizeTarget, isWinFormPath, pathFamilyOf, writeTargetPath } from "../../tools/fs/safety.js";
|
|
3
2
|
import { createCoarseCommandNamePolicy, namespacedCoveringEntries, namespacedCoveringHit } from "../tool-policy.js";
|
|
4
3
|
import { declaredPathTargetOf, isProtectedWrite, pathConfinableWriteToolNames, skillScopeWriteToolNames } from "../tool-registry.js";
|
|
5
4
|
import { pathTargetBaseOf } from "../effective-path-target.js";
|
|
@@ -66,13 +65,13 @@ export function createSessionRulePolicy(rules, opts) {
|
|
|
66
65
|
if (typeof path !== "string" || path.length === 0) {
|
|
67
66
|
return deny(`write tool "${req.toolName}" denied: session rule confines writes to allowDirs but the call has no resolvable path`);
|
|
68
67
|
}
|
|
69
|
-
const canon = await canonicalizeTarget(env, path, signal, pathTargetBaseOf(declared, { root: rootPath, cwd: req.cwd }));
|
|
68
|
+
const canon = await canonicalizeTarget(env, path, signal, pathFamilyOf({ root: rootPath, cwd: req.cwd ?? env.cwd }), pathTargetBaseOf(declared, { root: rootPath, cwd: req.cwd }));
|
|
70
69
|
if (!canon.ok) {
|
|
71
70
|
return deny(`write to "${path}" denied: its real target could not be resolved against the session-rule allowDirs`);
|
|
72
71
|
}
|
|
73
72
|
const roots = [];
|
|
74
73
|
for (const raw of allowDirs) {
|
|
75
|
-
const r = await canonicalizeTarget(env, raw, signal, rootPath);
|
|
74
|
+
const r = await canonicalizeTarget(env, raw, signal, pathFamilyOf({ root: rootPath ?? env.cwd }), rootPath);
|
|
76
75
|
if (r.ok)
|
|
77
76
|
roots.push(r.key);
|
|
78
77
|
}
|
|
@@ -19,7 +19,7 @@ export function streamHaltVerbs(input) {
|
|
|
19
19
|
if (receipt.accepted && !abortOwnedBeforeHalt) {
|
|
20
20
|
h.loop.userHalted = true;
|
|
21
21
|
}
|
|
22
|
-
else {
|
|
22
|
+
else if (!h.loop.userHalted) {
|
|
23
23
|
deliverEngineNotice(runner.deps.onNotice, {
|
|
24
24
|
code: "task.halt_unconsumed",
|
|
25
25
|
message: "a user halt arrived while the run was already ending for its own reason: nothing was cut or stopped " +
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { uuidv7 } from "../../internal/harness.js";
|
|
2
2
|
import { snapshotActorAssertion } from "../../internal/llm.js";
|
|
3
|
-
import {
|
|
3
|
+
import { MAX_STEER_INPUT_ID_CHARS } from "../checkpoint-store.js";
|
|
4
4
|
import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
5
5
|
import { buildHumanInputEvent, frameMidTurnUserInput, projectHumanInput } from "../human-input-projection.js";
|
|
6
6
|
import { isSystemInjectionPriority, SYSTEM_INJECTION_PRIORITIES } from "../task-notification.js";
|
|
@@ -23,9 +23,6 @@ export function streamSteerVerb(input) {
|
|
|
23
23
|
if (inputId === "" || inputId.length > MAX_STEER_INPUT_ID_CHARS) {
|
|
24
24
|
throw steeringError(`inputId must be a non-empty string of at most ${MAX_STEER_INPUT_ID_CHARS} characters`, "steering.invalid_content");
|
|
25
25
|
}
|
|
26
|
-
if (inputId === LEGACY_PENDING_STEER_INPUT_ID) {
|
|
27
|
-
throw steeringError(`inputId "${LEGACY_PENDING_STEER_INPUT_ID}" is reserved for a pre-queue parked steer and cannot be supplied by a caller`, "steering.invalid_content");
|
|
28
|
-
}
|
|
29
26
|
}
|
|
30
27
|
const priorityIn = options?.priority;
|
|
31
28
|
if (priorityIn !== undefined && !isSystemInjectionPriority(priorityIn)) {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { isWinFormPath, pathFamilyOf } from "../../tools/fs/safety.js";
|
|
1
2
|
export function rebaseWorkspacePath(p, fromRaw, toRaw) {
|
|
2
3
|
if (p.includes("\\") || fromRaw.includes("\\") || toRaw.includes("\\"))
|
|
3
4
|
return p;
|
|
5
|
+
if (pathFamilyOf({ root: fromRaw }) !== "posix" || pathFamilyOf({ root: toRaw }) !== "posix" || isWinFormPath(p))
|
|
6
|
+
return p;
|
|
4
7
|
const stripTrail = (s) => (s.length > 1 && s.endsWith("/") ? stripTrail(s.slice(0, -1)) : s);
|
|
5
8
|
const from = stripTrail(fromRaw);
|
|
6
9
|
const to = stripTrail(toRaw);
|
|
@@ -187,7 +187,7 @@ export interface RunnerDeps {
|
|
|
187
187
|
*/
|
|
188
188
|
readDenyPatterns?: readonly import("../tools/fs/read-deny.js").ReadDenyEntry[];
|
|
189
189
|
/**
|
|
190
|
-
*
|
|
190
|
+
* The deployment's built-in deny-table TIER selection:
|
|
191
191
|
* EXACTLY the listed tiers of
|
|
192
192
|
* {@link import("../tools/fs/read-deny.js").READ_FACE_BUILTIN_DENY_TABLE} are active (`[]` = none —
|
|
193
193
|
* explicit and legal); absent = the default selection
|
|
@@ -203,7 +203,7 @@ export interface RunnerDeps {
|
|
|
203
203
|
*/
|
|
204
204
|
readDenyBuiltinTiers?: readonly string[];
|
|
205
205
|
/**
|
|
206
|
-
*
|
|
206
|
+
* Per-row removal from the built-in deny table by STABLE NAME (= the row's canonical
|
|
207
207
|
* pattern text, e.g. `".ssh"`, `".config/gcloud"`; the admin delete channel), applied after
|
|
208
208
|
* {@link readDenyBuiltinTiers} selection. Naming a row of an inactive tier is a satisfied intent
|
|
209
209
|
* (no-op); a name matching NO table row refuses loudly at prepare (#123 — never silently
|
|
@@ -490,30 +490,6 @@ export interface RunnerDeps {
|
|
|
490
490
|
* in its own audit surface. Values outside the two-member set refuse prepare loudly.
|
|
491
491
|
*/
|
|
492
492
|
onTrackFailure?: "refuse" | "proceed-unprotected";
|
|
493
|
-
/**
|
|
494
|
-
* design/381 §5.3① / §10 S-1 — the ADDITIVE discriminant for the rewind refusal that this engine
|
|
495
|
-
* cannot decide on its own. When a restore finds no boundary for the requested entry, there are
|
|
496
|
-
* two very different worlds: the boundary NEVER existed (those turns ran with no history store, or
|
|
497
|
-
* it was reaped), or it exists in the RETIRED whole-tree snapshot epoch, which the per-edited-file
|
|
498
|
-
* seat deliberately does not restore. The engine holds no whole-tree state, so absent this probe
|
|
499
|
-
* the refusal honestly names BOTH possibilities rather than picking one — the default, and the
|
|
500
|
-
* only honest wording for a deployment that never had a whole-tree epoch at all.
|
|
501
|
-
*
|
|
502
|
-
* A deployment that DOES hold the retired epoch (its assembly layer can ask the old table whether
|
|
503
|
-
* that entry has a legacy row) supplies the bit here, and the refusal splits into the two precise
|
|
504
|
-
* messages. `true` = a whole-tree-era boundary exists for that entry; `false` = it definitively
|
|
505
|
-
* does not; `undefined` = the deployment cannot tell, which reads exactly like an absent probe.
|
|
506
|
-
*
|
|
507
|
-
* Consulted ONLY on the refusal path (never on a successful restore), so a slow probe costs
|
|
508
|
-
* nothing in the ordinary case. It is a DIAGNOSTIC input, never a control input: a probe that
|
|
509
|
-
* throws, hangs past the caller's own patience, or answers a non-boolean cannot change the outcome
|
|
510
|
-
* — the refusal still happens, the wording falls back to the honest both-possibilities form, and
|
|
511
|
-
* the bad value is disclosed through `onError` (phase "rewind") rather than silently absorbed.
|
|
512
|
-
*/
|
|
513
|
-
legacyRewindBoundaryProbe?: (query: {
|
|
514
|
-
sessionId: string;
|
|
515
|
-
entryId: string;
|
|
516
|
-
}) => boolean | undefined | Promise<boolean | undefined>;
|
|
517
493
|
/**
|
|
518
494
|
* design/99 §E6 — DURABLE per-session permission rules (a deny-narrowing folded into the task-time tool
|
|
519
495
|
* policy). Omitted ⇒ the feature is OFF (no rules read, zero behavior change). A deployment supplies a
|
|
@@ -713,7 +689,7 @@ export interface RunnerDeps {
|
|
|
713
689
|
*/
|
|
714
690
|
denialLimit?: import("./auto-mode.js").AutoModeDenialLimitOptions;
|
|
715
691
|
/**
|
|
716
|
-
*
|
|
692
|
+
* OPT IN to recording this arming's serializable criteria (an
|
|
717
693
|
* {@link import("./auto-mode-arming.js").AutoModeArmingRecipe}) on the constraint-chain entries a
|
|
718
694
|
* durable park freezes, so a CROSS-PROCESS redemption can rebuild an equivalent classifier over its
|
|
719
695
|
* own model leg ({@link import("./auto-mode-rebuild.js").rebuildAutoModeDecider}) instead of
|
|
@@ -733,7 +709,7 @@ export interface RunnerDeps {
|
|
|
733
709
|
*/
|
|
734
710
|
persistArming?: boolean;
|
|
735
711
|
/**
|
|
736
|
-
*
|
|
712
|
+
* The deployment's auto-mode SETTINGS GENERATION (a config revision, a settings hash, a
|
|
737
713
|
* publish id — the string is opaque to the engine). Recorded in the arming recipe and compared at
|
|
738
714
|
* rebuild time against the REDEEMING deployment's own: it is a declaration axis that can only make
|
|
739
715
|
* the rebuild stricter (see {@link import("./auto-mode-arming.js").foldAutoModeArming}). Absent =
|
|
@@ -947,7 +923,7 @@ export interface RunnerDeps {
|
|
|
947
923
|
/** design/72 §2.2 (B): deployment default for the suspend-loop cap (see `TaskSpec.maxSuspends`).
|
|
948
924
|
* Default 5 (loose — human-approval flows re-suspend rarely). A task's own `maxSuspends` overrides. */
|
|
949
925
|
maxSuspends?: number;
|
|
950
|
-
/**
|
|
926
|
+
/** Deployment default for the outer per-brain-call guardrail — see
|
|
951
927
|
* `TaskSpec.limits.brainCallGuardrailMs` for the semantics (default one hour; `false`/`0` = off;
|
|
952
928
|
* an invalid value is a fail-loud config error). A task's own `limits.brainCallGuardrailMs`
|
|
953
929
|
* overrides it. Set it here to cover EVERY run of this Runner, delegated children included — a
|
|
@@ -1156,6 +1132,18 @@ export interface RunnerDeps {
|
|
|
1156
1132
|
* `undefined` for a file whose language has no server, and the tool degrades to "fall back to grep". A
|
|
1157
1133
|
* task's own `lspManager` overrides this. Unset ⇒ no `lsp` tool. */
|
|
1158
1134
|
lspManager?: import("./lsp.js").LspServerManager;
|
|
1135
|
+
/**
|
|
1136
|
+
* The Artifacts hosting seat — a storage/service-tier seam of the same family as
|
|
1137
|
+
* {@link checkpointStore} and {@link rosterStore}. Three states, judged once at prepare: ABSENT ⇒ no
|
|
1138
|
+
* `Artifact` tool and no announcement (the default: a library deployment has no hosting); a host
|
|
1139
|
+
* whose `capabilities()` satisfies the declaration invariants ⇒ ONE `Artifact` tool assembled from
|
|
1140
|
+
* that declaration (`plane: "hosted"` mounts the hosted face over the declared action subset,
|
|
1141
|
+
* `plane: "local-stub"` — `localArtifactStub({ dir })` — mounts the publish-only local face with
|
|
1142
|
+
* `eval-stub://` links); a host whose declaration fails an invariant ⇒ no tool and a loud
|
|
1143
|
+
* `config.artifact_host_invalid` notice. Filling the seat IS the authorization to publish: there is
|
|
1144
|
+
* no second switch. `TaskSpec.excludeTools` unmounts the tool as it does any other.
|
|
1145
|
+
*/
|
|
1146
|
+
artifactHost?: import("./artifact-host.js").ArtifactHost;
|
|
1159
1147
|
/**
|
|
1160
1148
|
* Default in-process hooks for all tasks (design/37) — the FULL lifecycle seam of the `Hooks`
|
|
1161
1149
|
* interface, not just the tool-call trio: `preToolUse` (rewrite/restrict args + inject context),
|
|
@@ -1309,27 +1297,20 @@ export interface RunnerDeps {
|
|
|
1309
1297
|
*/
|
|
1310
1298
|
toolResultThresholdChars?: number;
|
|
1311
1299
|
/**
|
|
1312
|
-
* design/374 — microCompact
|
|
1313
|
-
* default ON (`machine: "cc"`, `clearOnRejection: true`); the pre-374 behavior is the explicit
|
|
1314
|
-
* opt-out `{ machine: "legacy", clearOnRejection: false }`.
|
|
1300
|
+
* design/374 — the microCompact knobs. Both default ON (`machine: "cc"`, `clearOnRejection: true`).
|
|
1315
1301
|
*
|
|
1316
|
-
* - `machine`:
|
|
1317
|
-
*
|
|
1318
|
-
*
|
|
1319
|
-
*
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1322
|
-
*
|
|
1323
|
-
*
|
|
1324
|
-
* fallback re-ordering shipped with this default (blocking-point re-run → in-turn forced
|
|
1325
|
-
* compaction behind the adopt seam → trim demoted to the disaster-only last resort), so an
|
|
1326
|
-
* under-20k refusal no longer falls straight into a message-dropping trim.
|
|
1302
|
+
* - `machine`: `"cc"` (default: the CC 2.1.223 rejection-leg form — keep 5, ≥20k minimum-savings
|
|
1303
|
+
* gate, one deep clear beyond the keep window, CC marker bytes) or `"off"` (no proactive
|
|
1304
|
+
* frontier clearing at all — the D-2 off switch; the machine then gets its ONE shot at the
|
|
1305
|
+
* blocking point instead, the slice-3 arm A, trace trigger `"blocking"`). The vocabulary is
|
|
1306
|
+
* {@link import("./context-edit.js").MICRO_COMPACT_MACHINES}; the guard chain behind either
|
|
1307
|
+
* value is the same (blocking-point run → in-turn forced compaction behind the adopt seam →
|
|
1308
|
+
* trim demoted to the disaster-only last resort), so an under-20k refusal never falls straight
|
|
1309
|
+
* into a message-dropping trim.
|
|
1327
1310
|
* - `clearOnRejection` (MC-R, slice 2): on a provider input-too-long rejection, run ONE cheap
|
|
1328
|
-
* deterministic clear over the rejected projection (same
|
|
1329
|
-
* and retry inside the turn BEFORE the forced-compaction recovery. Default true
|
|
1330
|
-
*
|
|
1331
|
-
* `machine` — an enabled MC-R always clears in the cc form (the rejection arm has no
|
|
1332
|
-
* budget coordinate for the legacy incremental form to stop at). BUDGET ACCOUNTING (design/374
|
|
1311
|
+
* deterministic clear over the rejected projection (the same machine, savings ≥20k or nothing)
|
|
1312
|
+
* and retry inside the turn BEFORE the forced-compaction recovery. Default true. Independent of
|
|
1313
|
+
* `machine` — an enabled MC-R clears even with the frontier pass `"off"`. BUDGET ACCOUNTING (design/374
|
|
1333
1314
|
* §3.2.1, stated here because it is otherwise invisible to a deployment): a successful MC-R
|
|
1334
1315
|
* clear-and-retry SPENDS one attempt of the shared prompt-too-long recovery budget (default 2
|
|
1335
1316
|
* attempts per chain), so a chain that clears and is rejected AGAIN has one forced-compaction
|
|
@@ -1344,7 +1325,7 @@ export interface RunnerDeps {
|
|
|
1344
1325
|
* effect.
|
|
1345
1326
|
*/
|
|
1346
1327
|
microCompact?: {
|
|
1347
|
-
machine?: "
|
|
1328
|
+
machine?: import("./context-edit.js").MicroCompactMachine;
|
|
1348
1329
|
clearOnRejection?: boolean;
|
|
1349
1330
|
};
|
|
1350
1331
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Host-callback isolation, one primitive.
|
|
3
3
|
*
|
|
4
4
|
* The invariant "a callback the HOST handed us must never take the engine down" was implemented by hand at
|
|
5
5
|
* ~20 sites across 10 modules (observer taps, cascade/verify observability hooks, roster eviction sinks, the
|
|
@@ -60,7 +60,7 @@ export declare function dataRootSpellings(explicit: string | undefined): string[
|
|
|
60
60
|
* `env` is the same ExecutionEnv the hands run against (inject the one you pass to the Runner), so
|
|
61
61
|
* canonicalization sees the task's real filesystem (a remote/E2B task resolves in its container).
|
|
62
62
|
* `rootPath` is the FALLBACK base a relative path resolves against (omit = paths resolve as given).
|
|
63
|
-
*
|
|
63
|
+
* When the engine stamps a live cwd onto the request (`ToolCallRequest.cwd` — the tracked
|
|
64
64
|
* directory the shell's `cd` moves and the fs tools resolve with), THAT wins for the call's target, so
|
|
65
65
|
* the guard judges the file the tool will actually write. `rootPath` still serves every deployment that
|
|
66
66
|
* calls this policy outside a Runner.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { isAbsolute, join, resolve } from "node:path";
|
|
3
|
-
import { canonicalizeTarget, expandHomeTilde,
|
|
3
|
+
import { canonicalizeTarget, expandHomeTilde, isAbsoluteForFamily, isWinFormPath, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
|
|
4
4
|
import { compileSegmentPattern, matchSegmentPatterns } from "../tools/fs/read-deny.js";
|
|
5
5
|
import { isProtectedWrite, protectivePathTargetOf } from "./tool-registry.js";
|
|
6
6
|
import { pathTargetBaseOf } from "./effective-path-target.js";
|
|
@@ -40,7 +40,7 @@ export function dataRootSpellings(explicit) {
|
|
|
40
40
|
const engineForm = isAbsolute(raw) ? raw : resolve(raw);
|
|
41
41
|
const out = [];
|
|
42
42
|
for (const v of [raw, tilde, engineForm]) {
|
|
43
|
-
const anchored =
|
|
43
|
+
const anchored = isAbsoluteForFamily(pathFamilyOf({ root: v }), v) ? v : resolve(v);
|
|
44
44
|
if (!out.includes(anchored))
|
|
45
45
|
out.push(anchored);
|
|
46
46
|
}
|
|
@@ -82,7 +82,7 @@ export function createSensitivePathPolicy(opts) {
|
|
|
82
82
|
const keys = [];
|
|
83
83
|
for (const spelling of dataRootPaths) {
|
|
84
84
|
try {
|
|
85
|
-
const r = await canonicalizeTarget(opts.env, spelling, signal);
|
|
85
|
+
const r = await canonicalizeTarget(opts.env, spelling, signal, pathFamilyOf({ root: spelling }));
|
|
86
86
|
if (r.ok && !keys.includes(r.key))
|
|
87
87
|
keys.push(r.key);
|
|
88
88
|
}
|
|
@@ -102,7 +102,7 @@ export function createSensitivePathPolicy(opts) {
|
|
|
102
102
|
const path = writeTargetPath(req, protective);
|
|
103
103
|
if (typeof path !== "string" || path.length === 0)
|
|
104
104
|
return { action: "allow" };
|
|
105
|
-
const canon = await canonicalizeTarget(opts.env, path, signal, pathTargetBaseOf(protective, { root: opts.rootPath, cwd: req.cwd }));
|
|
105
|
+
const canon = await canonicalizeTarget(opts.env, path, signal, pathFamilyOf({ root: opts.rootPath, cwd: req.cwd ?? opts.env.cwd }), pathTargetBaseOf(protective, { root: opts.rootPath, cwd: req.cwd }));
|
|
106
106
|
if (!canon.ok) {
|
|
107
107
|
if (canon.unresolvedSymlink) {
|
|
108
108
|
return {
|
package/dist/core/session.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export interface SessionStoreSummary {
|
|
|
74
74
|
* Absent until a task has run on the session (recorded via {@link SessionStore.noteTaskRun}). */
|
|
75
75
|
lastTaskId?: string;
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
77
|
+
* An ENGINE-minted run identity for this session ({@link import("./types.js").TaskResult.runId}),
|
|
78
78
|
* recorded by the most recent {@link SessionStore.noteTaskRun} write to LAND.
|
|
79
79
|
* {@link lastTaskId} above is `spec.taskId ?? sessionId`, so on the ordinary deployment that supplies no
|
|
80
80
|
* host task id it answers the SESSION's own id: a "last run" handle that names the session it hangs off
|
|
@@ -317,7 +317,7 @@ export declare function boundedTail(pathEntries: readonly SessionTreeEntry[]): {
|
|
|
317
317
|
* times in one prepare (project-memory phase, design/148 center adoption, the epoch-pin
|
|
318
318
|
* discriminator), and an entry-COUNT proxy at ANY of them re-creates the same lie: a host may write
|
|
319
319
|
* session_info / model_change / … rows before the first turn (naming a new chat, recording a model
|
|
320
|
-
* pick), and none of that is evidence of a prior session (
|
|
320
|
+
* pick), and none of that is evidence of a prior session (the count proxy
|
|
321
321
|
* survived at the adoption arm after the pin arm was fixed, silently costing a pre-named session
|
|
322
322
|
* its published center prompt for life). Conversation content — and only that — is the evidence.
|
|
323
323
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Pattern specifiers inside a {@link import("./types.js").SkillManifest}'s `allowTools`.
|
|
3
3
|
*
|
|
4
4
|
* An entry may be a bare tool name (`Bash`) or a name carrying a specifier (`Bash(git:*)`). A bare name
|
|
5
5
|
* claims the whole tool; a specifier claims a NARROWER slice of it. Both directions of that sentence are
|
|
@@ -42,7 +42,7 @@ export type SkillsDirectoryWarningCode =
|
|
|
42
42
|
| "name_mismatch"
|
|
43
43
|
/** Required `description` absent. */
|
|
44
44
|
| "missing_description"
|
|
45
|
-
/**
|
|
45
|
+
/** An `allowed-tools` entry carries a pattern specifier (`Bash(git:*)`) — the scope policy
|
|
46
46
|
* matches names, not patterns, so the entry cannot match and the tool is DISABLED in this skill
|
|
47
47
|
* frame. Disclosed loudly until pattern enforcement lands (the entry is deliberately NOT widened to
|
|
48
48
|
* its base name: that would turn "only git" into "all of Bash"). */
|
|
@@ -185,7 +185,7 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
185
185
|
assert.equal(q[0].text, "go");
|
|
186
186
|
assert.equal(q[0].trusted, false);
|
|
187
187
|
assert.equal(q[0].seq, 1);
|
|
188
|
-
assert.equal((await store.get(cp.token)).state
|
|
188
|
+
assert.equal("pendingSteer" in (await store.get(cp.token)).state, false);
|
|
189
189
|
});
|
|
190
190
|
run("setPendingSteer APPENDS: two steers both survive in issue order, each keeping its frozen trust", async () => {
|
|
191
191
|
const store = make();
|