@sema-agent/core 7.13.0 → 7.14.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 +80 -0
- package/dist/agents/subagent.d.ts +4 -9
- package/dist/agents/subagent.js +16 -12
- package/dist/agents/teacher.js +1 -1
- package/dist/brain/reasoning.d.ts +23 -0
- package/dist/brain/reasoning.js +35 -4
- package/dist/brain/stream-engine.js +35 -17
- package/dist/brain/timeout.d.ts +25 -4
- package/dist/brain/timeout.js +1 -1
- package/dist/core/ask-origin.d.ts +28 -9
- package/dist/core/ask-origin.js +10 -5
- package/dist/core/auto-mode.d.ts +3 -3
- package/dist/core/checkpoint-seat.d.ts +18 -0
- package/dist/core/checkpoint-seat.js +6 -0
- package/dist/core/checkpoint-store.d.ts +9 -16
- package/dist/core/checkpoint-store.js +7 -4
- package/dist/core/engine-notice.d.ts +3 -1
- package/dist/core/env-knob-announce.d.ts +16 -0
- package/dist/core/env-knob-announce.js +14 -0
- package/dist/core/gate-fold.js +2 -0
- package/dist/core/gate-lanes.js +21 -12
- package/dist/core/gate-outcome.d.ts +42 -3
- package/dist/core/gate-outcome.js +22 -0
- package/dist/core/governance-codes.js +1 -1
- package/dist/core/hooks.d.ts +5 -16
- package/dist/core/hooks.js +6 -4
- package/dist/core/mcp.d.ts +3 -2
- package/dist/core/mcp.js +5 -11
- package/dist/core/memory-engine/engine.d.ts +2 -2
- package/dist/core/runner/advertised-writable-dirs.d.ts +74 -0
- package/dist/core/runner/advertised-writable-dirs.js +48 -0
- package/dist/core/runner/assemble-result.d.ts +5 -0
- package/dist/core/runner/denial-limit-arms.d.ts +7 -9
- package/dist/core/runner/denial-limit-arms.js +8 -10
- package/dist/core/runner/gate-exit.d.ts +15 -3
- package/dist/core/runner/gate-exit.js +6 -4
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +34 -4
- package/dist/core/runner/prepare-caps-and-workflow.js +47 -12
- package/dist/core/runner/prepare-gate-stations.d.ts +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +10 -4
- package/dist/core/runner/prepare-hands-readface.js +5 -14
- package/dist/core/runner/prepare-inherited-gate.d.ts +5 -3
- package/dist/core/runner/prepare-inherited-gate.js +1 -1
- package/dist/core/runner/prepare-memory.d.ts +31 -34
- package/dist/core/runner/prepare-memory.js +73 -51
- package/dist/core/runner/prepare-policy-chain.js +5 -2
- package/dist/core/runner/prepare-prompt-inputs.d.ts +4 -0
- package/dist/core/runner/prepare-prompt-inputs.js +2 -2
- package/dist/core/runner/prepare-question-face.d.ts +10 -8
- package/dist/core/runner/prepare-question-face.js +1 -3
- package/dist/core/runner/prepare-safety-scan.js +1 -1
- package/dist/core/runner/prepare-task.js +57 -48
- package/dist/core/runner/prepare-wiring-manifest.d.ts +2 -2
- package/dist/core/runner/prepare-wiring-manifest.js +1 -1
- package/dist/core/runner/run-compaction-machinery.js +2 -0
- package/dist/core/runner/run-harness-handlers.js +3 -1
- package/dist/core/runner/stream-settle-backstop.js +1 -1
- package/dist/core/runtime-caps.d.ts +21 -0
- package/dist/core/runtime-caps.js +5 -1
- package/dist/core/task-event.d.ts +11 -2
- package/dist/core/task-registry-shared.js +8 -6
- package/dist/core/task-result.d.ts +15 -0
- package/dist/core/terminal-cause.d.ts +6 -2
- package/dist/core/tool-policy.d.ts +34 -28
- package/dist/core/tool-policy.js +29 -5
- package/dist/core/tool-roster.js +2 -0
- package/dist/core/tool-spec.d.ts +13 -12
- package/dist/core/types.d.ts +2 -1
- package/dist/core/types.js +1 -0
- package/dist/core/wiring-manifest.d.ts +16 -9
- package/dist/core/wiring-manifest.js +8 -3
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/orchestration/run-workflow-tool.d.ts +8 -8
- package/dist/orchestration/run-workflow-tool.js +1 -1
- package/dist/orchestration/workflow-primitives.d.ts +4 -3
- package/dist/orchestration/workflow-primitives.js +3 -3
- package/dist/orchestration/workflow-types.d.ts +14 -0
- package/dist/orchestration/workflow.d.ts +41 -4
- package/dist/orchestration/workflow.js +23 -5
- package/dist/tools/fs/fs-shared.js +9 -3
- package/dist/tools/fs/read-deny.d.ts +24 -8
- package/dist/tools/fs/read-deny.js +20 -1
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +25 -1
|
@@ -68,12 +68,14 @@ import { defaultTaskRegistry } from "../task-registry.js";
|
|
|
68
68
|
import { announcePeerLaneMount } from "../../agents/peer-session-drain.js";
|
|
69
69
|
import { createSchedulerTools } from "../../tools/scheduler-tools.js";
|
|
70
70
|
import { selfOrchestrationFailClosedReason } from "../../orchestration/workflow-script-runner.js";
|
|
71
|
-
import { remainingBudgetMicroUsd } from "../checkpoint-store.js";
|
|
71
|
+
import { remainingBudgetMicroUsd, resolveCheckpointStore } from "../checkpoint-store.js";
|
|
72
|
+
import { checkpointSeatOf } from "../checkpoint-seat.js";
|
|
72
73
|
import { durableParkGapFor } from "../park-selfcheck.js";
|
|
73
74
|
import { deliverEngineNotice } from "../types.js";
|
|
74
75
|
import { ToolRosterDeltaSeat } from "../tool-roster.js";
|
|
75
76
|
import { faceOf } from "../tool-roster.js";
|
|
76
77
|
import { catalogEntriesNamed, pathTargetOf } from "../tool-registry.js";
|
|
78
|
+
import { AdvertisedWritableDirs } from "./advertised-writable-dirs.js";
|
|
77
79
|
import { createStructuredProjector } from "./tool-output-projection.js";
|
|
78
80
|
function warnCompactionWindowHazard(tracer, spec, model, compModel, hostTaskId) {
|
|
79
81
|
if (compModel === undefined)
|
|
@@ -257,6 +259,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
257
259
|
runIdSink.runId = runId;
|
|
258
260
|
const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
|
|
259
261
|
spec = doors.spec;
|
|
262
|
+
const checkpointStore = resolveCheckpointStore(spec, deps), checkpointSeat = checkpointSeatOf(spec, checkpointStore);
|
|
260
263
|
const autoModeSeat = refuseInvalidAutoModeSeat(spec);
|
|
261
264
|
const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, memoryCapturePolicy, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
|
|
262
265
|
announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
|
|
@@ -366,8 +369,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
366
369
|
let resolvedReadFace;
|
|
367
370
|
let readDenyAdditionsNormalized = [];
|
|
368
371
|
let handsLessResolvedFace;
|
|
369
|
-
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: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
|
|
370
|
-
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, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, carrierReadFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
|
|
372
|
+
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 });
|
|
373
|
+
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 });
|
|
371
374
|
const tools = roster.tools;
|
|
372
375
|
const pausedRef = {};
|
|
373
376
|
const remoteEnvFailures = [];
|
|
@@ -431,34 +434,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
431
434
|
const callIssuedAtRef = {};
|
|
432
435
|
const workCutRef = { continuation: false, abortedBeforeTurnEnd: false };
|
|
433
436
|
const memoryWriteGateRef = {};
|
|
434
|
-
const
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
readDenyAdditionsNormalized = handsReadFace.readDenyAdditionsNormalized;
|
|
439
|
-
handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
|
|
440
|
-
shellGatedBash = handsReadFace.shellGatedBash;
|
|
441
|
-
shellGatedMonitor = handsReadFace.shellGatedMonitor;
|
|
442
|
-
const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, executionEnv, runnerSelf, backgroundTaskToolsActive, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
|
|
443
|
-
const { checkpointStore, liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, questionToolMounted, mountedQuestionTool } = prepareQuestionFace({ frozenOnQuestion, spec, deps, runtimeCaps, resolvedInteractionPosture, resume, sessionId, roster, inheritedUnavailableAsks: () => inheritedUnavailableAsks });
|
|
444
|
-
if (spec.handsReadOnly !== true) {
|
|
445
|
-
const sessionScope = sessionId ?? spec.principal ?? spec.taskId ?? "default";
|
|
446
|
-
roster.mountAll(createSchedulerTools(executionEnv, {
|
|
447
|
-
scope: sessionScope,
|
|
448
|
-
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
449
|
-
...(sessionId !== undefined ? { sessionId } : {}),
|
|
450
|
-
requestStopAfterTurn,
|
|
451
|
-
}).map((t) => (envHandToolNames.add(t.name), t)), { source: "builtin", mountedBy: "scheduler" });
|
|
452
|
-
}
|
|
453
|
-
if (internals?.insideFork !== true &&
|
|
454
|
-
runnerSelf &&
|
|
455
|
-
sessionId !== undefined &&
|
|
456
|
-
hasSessionFork(runnerSelf.sessions) &&
|
|
457
|
-
agentForkDenial === "principal") {
|
|
458
|
-
deps.onError?.(new Error(`Fork DENIED for principal "${spec.principal ?? ""}" by runtimeCaps.allowFork=false ` +
|
|
459
|
-
`(per-principal entitlement governance, not a misconfiguration — Agent(subagent_type:"fork") will refuse honestly)`), { phase: "config", sessionId });
|
|
460
|
-
}
|
|
461
|
-
const { lspDiagnostics, nudgeLspOnEdit, lspRunIdent } = prepareLsp({ spec, deps, executionEnv, taskRootFinal, handsEnabled, sessionId, handsCwdRef, roster, envHandToolNames });
|
|
437
|
+
const advertisedWritableDirs = handsEnabled
|
|
438
|
+
? new AdvertisedWritableDirs(executionEnv, (dir, reason) => deps.onError?.(new Error(`${dir.advertiser} advertised writable directory skipped (cannot canonicalize): ${dir.path} — ${reason}`), { phase: "config", sessionId }))
|
|
439
|
+
: undefined;
|
|
440
|
+
const handsWriteHalfPlanned = handsEnabled && spec.handsReadOnly !== true;
|
|
462
441
|
const memoryPairNameDomain = new Set();
|
|
463
442
|
for (const t of tools) {
|
|
464
443
|
memoryPairNameDomain.add(t.name);
|
|
@@ -477,14 +456,14 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
477
456
|
return { fault: true };
|
|
478
457
|
}
|
|
479
458
|
})();
|
|
480
|
-
const { memoryEngineSession,
|
|
459
|
+
const { memoryEngineSession, memoryTools, composeInjection: composeMemoryInjection, admittedOrgScopes: memoryAdmittedOrgScopes, ownOrgVerdict: memoryOwnOrgVerdict, effectiveMemoryScopes: memoryEffectiveScopes, } = await prepareMemory({
|
|
481
460
|
spec,
|
|
482
461
|
deps,
|
|
483
462
|
sessionId,
|
|
484
463
|
taskRootPath: taskRootFinal,
|
|
485
464
|
memoryWriteGateRef,
|
|
486
465
|
reminderMark,
|
|
487
|
-
writeToolsMounted: tools.some((t) => t.name === "Write") &&
|
|
466
|
+
writeToolsMounted: (handsWriteHalfPlanned || tools.some((t) => t.name === "Write")) &&
|
|
488
467
|
!(toolFaceSnapshot.exclude?.includes("Write") ?? false) &&
|
|
489
468
|
!(handsEnabled && isRemoteExecutionEnv(executionEnv) && spec.memoryPersistenceCapable !== true),
|
|
490
469
|
memoryProvenance,
|
|
@@ -503,20 +482,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
503
482
|
...(internals?.memoryCaptureQueryDir !== undefined ? { parentCaptureQueryDir: internals.memoryCaptureQueryDir } : {}),
|
|
504
483
|
...(internals?.memoryCaptureAncestors !== undefined ? { captureAncestors: internals.memoryCaptureAncestors } : {}),
|
|
505
484
|
memoryPersistenceDeclared: spec.memoryPersistenceCapable,
|
|
506
|
-
|
|
507
|
-
tools.some((t) => {
|
|
508
|
-
if (toolFaceSnapshot.exclude?.includes(t.name) ?? false)
|
|
509
|
-
return false;
|
|
510
|
-
const effect = (t.effect ?? toolEffects.get(t.name) ?? "write");
|
|
511
|
-
if (effect === "read")
|
|
512
|
-
return false;
|
|
513
|
-
const face = faceOf(t);
|
|
514
|
-
const pt = face.pathTarget ?? pathTargetOf(t.name);
|
|
515
|
-
const family = face.family ?? catalogEntriesNamed(t.name)[0]?.face.family;
|
|
516
|
-
if (!((pt !== undefined && pt.access !== "read") || family === "shell"))
|
|
517
|
-
return false;
|
|
518
|
-
return !(handsEnabled && isRemoteExecutionEnv(executionEnv));
|
|
519
|
-
}),
|
|
485
|
+
...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}),
|
|
520
486
|
memorySearchToolsPlanned,
|
|
521
487
|
admissionCtx: {
|
|
522
488
|
orgMemoryDenied: complianceDenies.has("org_memory_mount"),
|
|
@@ -540,6 +506,34 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
540
506
|
if (internals?.ownOrgAdmissionRef !== undefined && ownOrgVerdictRef.current !== undefined) {
|
|
541
507
|
internals.ownOrgAdmissionRef.current = ownOrgVerdictRef.current;
|
|
542
508
|
}
|
|
509
|
+
const handsReadFaceInput = { handsEnabled, executionEnv, taskRootFinal, rebaseRestoredPath, resume, session, sessionId, hostTaskId, taskScope, fullShellReachable, effectiveShellGate, toolFaceSnapshot, model, spec, deps, internals, memoryWriteGateRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), firstPartyOffload, roster, egressTools, irreversibilityTier, irreversibleTools, reversibilityProbes, reminderMark, reminderDisclosureCounts, ...(trackFileEdit !== undefined ? { trackFileEdit } : {}), onFileEdited: noteFileEdited, ...(restoredFilePaths.length > 0 ? { restoredFilePaths } : {}) };
|
|
510
|
+
const handsReadFace = handsEnabled ? await prepareHandsMount(handsReadFaceInput) : resolveHandsLessReadFace(handsReadFaceInput);
|
|
511
|
+
const { readFileStateForCheckpoint, seedContextFiles, handsCwdRef, workspaceStateSettle, wsSnapshot, rebaseWsPath, backgroundTaskToolsActive, additionalRootsCanonical, additionalReadRootsCanonical, attachmentRootCanonical, readDenyMatcher, envHandToolNames, sealReadStateSeat } = handsReadFace;
|
|
512
|
+
resolvedReadFace = handsReadFace.resolvedReadFace;
|
|
513
|
+
readDenyAdditionsNormalized = handsReadFace.readDenyAdditionsNormalized;
|
|
514
|
+
handsLessResolvedFace = handsReadFace.handsLessResolvedFace;
|
|
515
|
+
shellGatedBash = handsReadFace.shellGatedBash;
|
|
516
|
+
shellGatedMonitor = handsReadFace.shellGatedMonitor;
|
|
517
|
+
const { worktreeSessionRef } = prepareDelegationSurface({ spec, deps, internals, resume, sessionId, hostTaskId, taskScope, taskRootFinal, executionEnv, runnerSelf, backgroundTaskToolsActive, workflowToolsActive, peerLaneActive, peerSendMessageBuiltIn, peerLaneRefs, roster, toolEffects, axisExplicitNegatives, envHandToolNames, toolFaceSnapshot, firstPartyOffload, offloadStore, subagentRetain, enrichSpecToolCtx, handsCwdRef, wsSnapshot, rebaseWsPath, rebaseRestoredPath, workspaceStateSettle });
|
|
518
|
+
const { liveQuestionFace, contentAskBindings, contentAskRoutable, lateStrandedAnswers, discloseStrandedAnswers, settleContentAskBindings, durableQuestionFace, questionToolMounted, mountedQuestionTool } = prepareQuestionFace({ frozenOnQuestion, spec, deps, checkpointStore, runtimeCaps, resolvedInteractionPosture, resume, sessionId, roster, inheritedUnavailableAsks: () => inheritedUnavailableAsks });
|
|
519
|
+
if (spec.handsReadOnly !== true) {
|
|
520
|
+
const sessionScope = sessionId ?? spec.principal ?? spec.taskId ?? "default";
|
|
521
|
+
roster.mountAll(createSchedulerTools(executionEnv, {
|
|
522
|
+
scope: sessionScope,
|
|
523
|
+
...(spec.principal !== undefined ? { principal: spec.principal } : {}),
|
|
524
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
525
|
+
requestStopAfterTurn,
|
|
526
|
+
}).map((t) => (envHandToolNames.add(t.name), t)), { source: "builtin", mountedBy: "scheduler" });
|
|
527
|
+
}
|
|
528
|
+
if (internals?.insideFork !== true &&
|
|
529
|
+
runnerSelf &&
|
|
530
|
+
sessionId !== undefined &&
|
|
531
|
+
hasSessionFork(runnerSelf.sessions) &&
|
|
532
|
+
agentForkDenial === "principal") {
|
|
533
|
+
deps.onError?.(new Error(`Fork DENIED for principal "${spec.principal ?? ""}" by runtimeCaps.allowFork=false ` +
|
|
534
|
+
`(per-principal entitlement governance, not a misconfiguration — Agent(subagent_type:"fork") will refuse honestly)`), { phase: "config", sessionId });
|
|
535
|
+
}
|
|
536
|
+
const { lspDiagnostics, nudgeLspOnEdit, lspRunIdent } = prepareLsp({ spec, deps, executionEnv, taskRootFinal, handsEnabled, sessionId, handsCwdRef, roster, envHandToolNames });
|
|
543
537
|
if (internals?.delegationProvenance !== undefined && resume !== undefined) {
|
|
544
538
|
const live = internals.delegationProvenance.ref.current;
|
|
545
539
|
const seed = resume.seed.delegationProvenance;
|
|
@@ -553,6 +547,21 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
553
547
|
live.incomplete = true;
|
|
554
548
|
}
|
|
555
549
|
}
|
|
550
|
+
const rosterCanPersist = spec.memoryPersistenceCapable ??
|
|
551
|
+
tools.some((t) => {
|
|
552
|
+
if (toolFaceSnapshot.exclude?.includes(t.name) ?? false)
|
|
553
|
+
return false;
|
|
554
|
+
const effect = (t.effect ?? toolEffects.get(t.name) ?? "write");
|
|
555
|
+
if (effect === "read")
|
|
556
|
+
return false;
|
|
557
|
+
const face = faceOf(t);
|
|
558
|
+
const pt = face.pathTarget ?? pathTargetOf(t.name);
|
|
559
|
+
const family = face.family ?? catalogEntriesNamed(t.name)[0]?.face.family;
|
|
560
|
+
if (!((pt !== undefined && pt.access !== "read") || family === "shell"))
|
|
561
|
+
return false;
|
|
562
|
+
return !(handsEnabled && isRemoteExecutionEnv(executionEnv));
|
|
563
|
+
});
|
|
564
|
+
const { memoryBlock: memoryBlockFromEngine, recallDisciplineSegment: memoryRecallSegment, seedFiles: memorySeedFiles } = composeMemoryInjection({ rosterCanPersist });
|
|
556
565
|
if (memorySeedFiles?.length && seedContextFiles) {
|
|
557
566
|
try {
|
|
558
567
|
await seedContextFiles(memorySeedFiles);
|
|
@@ -563,7 +572,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
563
572
|
}
|
|
564
573
|
const projectContext = await prepareProjectContext({ deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, roster, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume });
|
|
565
574
|
const { memoryBlock: memoryBlockWithProject, instructionSources, projectInstructionContent, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted } = projectContext;
|
|
566
|
-
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 });
|
|
575
|
+
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, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}) });
|
|
567
576
|
const { deferred, memoryBlock } = prepareDeferClassify({ spec, deps, roster, toolFaceSnapshot, handsEnabled, fullShellReachable, promptProfile, model, mcp, a2a, sharedMemoryPairMounted, memoryEnginePairMounted, memoryBlock: memoryBlockWithProject, memoryRecallSegment, onceLedger, sessionId });
|
|
568
577
|
prepareMemoryEngineSession({ memoryEngineSession, internals, memoryDelegationEvidence, externalContentTargetActive, lockedPreflight, mcp, sessionId, runId, deps, provenanceForChildrenRef, delegationSettlementRef, spec, envHandToolNames, roster, contentOriginWrapRef });
|
|
569
578
|
const { systemPromptSeat, renderWithDate, systemBlocks, promptManifest, epochDeclaredSections, epochArtifactDigestForSnapshot, buildAssembleInputs, centerAdoptionRef } = await preparePromptAssembly({ session, deps, internals, resume, provider, userSystemPrompt, userAppendSystemPrompt, featureFlags, mcpInstructionsBlock, memoryBlock, envFacts, promptProfile, fableMitigations, tools, model, sessionId, reminderMark, onceLedger });
|
|
@@ -30,7 +30,7 @@ import type { GateOutcome } from "../gate-outcome.js";
|
|
|
30
30
|
import type { RunnerDeps, RuntimeCaps, TaskSpec, ToolEffect, ToolExecuteContext } from "../types.js";
|
|
31
31
|
import { type UsageWindow } from "../usage-window-store.js";
|
|
32
32
|
import type { ToolRoster } from "../tool-roster.js";
|
|
33
|
-
import { type
|
|
33
|
+
import { type AutoModeArmFact, type WiringManifest, type WiringManifestMcpEntry } from "../wiring-manifest.js";
|
|
34
34
|
import type { AnnounceOnceSink, Prepared, PrepareResume, RunInternals, UsageGovernance } from "./contracts.js";
|
|
35
35
|
export interface PrepareWiringManifestInput {
|
|
36
36
|
/** borrowed-readonly — the REBOUND spec. Read: `onAsk` (the own seat), `onQuestion` (the question seam), `handsReadOnly` / `tools` (the ungated scan),
|
|
@@ -107,7 +107,7 @@ export interface PrepareWiringManifestInput {
|
|
|
107
107
|
* source of the hand band's write-capable names for the ungated-write warning (what actually MOUNTED). */
|
|
108
108
|
toolRoster: ToolRoster;
|
|
109
109
|
/** borrowed-readonly — the arming outcome the caps phase minted (the manifest's auto-mode face). */
|
|
110
|
-
autoModeArmReason:
|
|
110
|
+
autoModeArmReason: AutoModeArmFact;
|
|
111
111
|
/** borrowed-readonly — the frozen preflight; only `mcp` (the declared servers the manifest face joins). */
|
|
112
112
|
lockedPreflight: Pick<LockedPreflight, "mcp">;
|
|
113
113
|
/** borrowed-mutable — the peer lane's late-bound facts. Writer here: `askEffective` ← the manifest's ask derivation (the
|
|
@@ -199,7 +199,7 @@ export function prepareWiringManifest(input) {
|
|
|
199
199
|
memoryAdmissionWired: deps.memoryScopeAdmission !== undefined,
|
|
200
200
|
retentionPolicyWired: deps.retentionPolicy !== undefined,
|
|
201
201
|
...(modelGateManifest !== undefined ? { modelGate: modelGateManifest } : {}),
|
|
202
|
-
autoMode: {
|
|
202
|
+
autoMode: { ...autoModeArmReason },
|
|
203
203
|
mcp: mcpManifestEntries(lockedPreflight.mcp, mcp.statuses),
|
|
204
204
|
tools: toolRoster,
|
|
205
205
|
});
|
|
@@ -11,6 +11,8 @@ export function runCompactionMachinery(input) {
|
|
|
11
11
|
const u = msg?.usage;
|
|
12
12
|
if (!u)
|
|
13
13
|
return;
|
|
14
|
+
if (msg.usageMissing === true)
|
|
15
|
+
stats.usageMissing = true;
|
|
14
16
|
const fam = cacheFamilyOf(m);
|
|
15
17
|
const price = runner.deps.pricing?.[m.id] ?? modelCostToPricing(m.cost);
|
|
16
18
|
const priced = isModelPriced(m, runner.deps.pricing);
|
|
@@ -77,8 +77,10 @@ export function createHarnessHandlers(input) {
|
|
|
77
77
|
rs.degrade.recordDegraded({ from: deg.from, to: deg.to, reason: deg.reason, atTurn: stats.turns + 1, ...(deg.chain && { chain: deg.chain }) });
|
|
78
78
|
}
|
|
79
79
|
if (m.role === "assistant" && "usage" in m && m.usage) {
|
|
80
|
-
if (m.usageMissing)
|
|
80
|
+
if (m.usageMissing) {
|
|
81
81
|
rs.turn.turnUsageMissing = true;
|
|
82
|
+
stats.usageMissing = true;
|
|
83
|
+
}
|
|
82
84
|
const u = m.usage;
|
|
83
85
|
stats.tokens += u.totalTokens || 0;
|
|
84
86
|
const { totalInputTokens: turnTotal, uncachedInputTokens: turnUncached, costMicroUsd: turnCostMicroUsd, } = usageCostMicroUsd(rs.telemetry.cacheFamily, u, rs.telemetry.pricing);
|
|
@@ -83,7 +83,7 @@ export function streamSettleBackstop(input) {
|
|
|
83
83
|
? { retryAfterMs: hinted }
|
|
84
84
|
: {};
|
|
85
85
|
})(),
|
|
86
|
-
stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costMicroUsd: 0 },
|
|
86
|
+
stats: { turns: 0, tokens: 0, toolCalls: 0, cachedTokens: 0, costMicroUsd: 0, usageMissing: true },
|
|
87
87
|
};
|
|
88
88
|
emitTrace(entryTracer.tracer, () => ({
|
|
89
89
|
kind: "task.end",
|
|
@@ -84,6 +84,19 @@ export interface WorkflowGovernanceBaseline {
|
|
|
84
84
|
* These six are the caps the ENGINE enforces; `allowUltracode`/`allowBypassPermissions` are shell-UX /
|
|
85
85
|
* service-settings-layer concerns (see {@link RunnerDeps.runtimeCapsResolver}).
|
|
86
86
|
*/
|
|
87
|
+
/**
|
|
88
|
+
* #692 C-d — WHICH plane's ratchet denied auto mode, when the resolver knows: the three settings sources
|
|
89
|
+
* CC 2.1.250's `permissions.disableAutoMode` can come from, as the resolver folds them into the one deny bit
|
|
90
|
+
* ({@link RuntimeCaps.autoMode} `=== false`). `org` = a managed / organization policy plane; `local` = the
|
|
91
|
+
* machine's local settings; `settings` = the user's / project's settings file. Read ONLY beside the deny bit
|
|
92
|
+
* (a source with no denial is a contradiction the value screen refuses as a resolver fault); projected onto
|
|
93
|
+
* the leg's manifest read face (`WiringManifest.autoMode.deniedSource`) so a shell can say "your organization
|
|
94
|
+
* turned auto mode off" vs "your settings did" instead of one word for both.
|
|
95
|
+
*/
|
|
96
|
+
export declare const AUTO_MODE_DENY_SOURCES: readonly ["org", "local", "settings"];
|
|
97
|
+
export type AutoModeDenySource = (typeof AUTO_MODE_DENY_SOURCES)[number];
|
|
98
|
+
/** Membership test for {@link AutoModeDenySource}. */
|
|
99
|
+
export declare function isAutoModeDenySource(v: unknown): v is AutoModeDenySource;
|
|
87
100
|
export interface RuntimeCaps {
|
|
88
101
|
/** `false` DENIES workflow self-orchestration for this principal server-side — even on a deployment that is
|
|
89
102
|
* capable ({@link RunnerDeps.workflowScriptRunner} + governance). The third stage of the workflows gate.
|
|
@@ -136,6 +149,14 @@ export interface RuntimeCaps {
|
|
|
136
149
|
* A denial resolves to a no-op (asks flow the original chain; no warn — the gate, not a mistake).
|
|
137
150
|
*/
|
|
138
151
|
autoMode?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* #692 C-d (additive) — WHICH source's ratchet set {@link autoMode} to `false`, when the resolver folded
|
|
154
|
+
* several ({@link AutoModeDenySource}). Optional: a resolver that does not track sources omits it and the
|
|
155
|
+
* manifest reads `denied` with no source. Present with `autoMode` not `false`, or spelled outside the set,
|
|
156
|
+
* is a resolver FAULT (announced through `onError`, the deny bit coined fail-closed, `resolver_fault` on the
|
|
157
|
+
* manifest) — a source named for a denial that did not happen is a marshalling error, never a fact.
|
|
158
|
+
*/
|
|
159
|
+
autoModeDenySource?: AutoModeDenySource;
|
|
139
160
|
/**
|
|
140
161
|
* design/383 §3.1 — may this principal declare a session memory-capture OPT-OUT
|
|
141
162
|
* (`TaskSpec.memory.capture: "off"` / the Runner flip verb)? `false` = the deployment REQUIRES
|
|
@@ -97,7 +97,16 @@ export type BrainStatusPhase = "rate_limited" | "retrying" | "reconnecting" | "c
|
|
|
97
97
|
* - `connect_refused` — the attempt got a definite negative about the target itself (nothing accepts
|
|
98
98
|
* at that address, or the name has no address). This is the class the SHORT retry lane serves.
|
|
99
99
|
* - `transport` — any other transport-level failure: a connect timeout, a reset, a mid-stream
|
|
100
|
-
* tear
|
|
100
|
+
* tear. No verdict about the target; the full ladder applies.
|
|
101
|
+
* - `stall` — no failure was observed on the link at all: THIS process cancelled its own
|
|
102
|
+
* request because one of its stall watchdogs reached its deadline while the stream produced
|
|
103
|
+
* nothing. The distinction from `transport` is WHO GAVE UP, and nothing more: a `transport` frame
|
|
104
|
+
* reports something that happened to the call, a `stall` frame reports a local deadline. It is not
|
|
105
|
+
* a verdict on the remedy — a blackholed connection (no reset, no close) produces silence, and
|
|
106
|
+
* silence is what a watchdog measures, so a `stall` can still turn out to be a network fault; what
|
|
107
|
+
* it always tells you truthfully is that the request was cancelled here, at a configured deadline,
|
|
108
|
+
* rather than lost out there. Reporting these as `transport` asserted the opposite — that the link
|
|
109
|
+
* had dropped — when nothing about the link was known at all.
|
|
101
110
|
* - `rate_limit` — the provider asked the caller to slow down.
|
|
102
111
|
* - `server` — the provider reported a failure on its own side.
|
|
103
112
|
* - `http` — a response the status predicate calls terminal, retried anyway because the
|
|
@@ -105,7 +114,7 @@ export type BrainStatusPhase = "rate_limited" | "retrying" | "reconnecting" | "c
|
|
|
105
114
|
* - `output_cap` — not a failure of the connection at all: the request is being re-sent with a
|
|
106
115
|
* lowered output cap after the provider reported the context limit exceeded (no backoff).
|
|
107
116
|
*/
|
|
108
|
-
export type BrainRetryErrClass = "connect_refused" | "transport" | "rate_limit" | "server" | "http" | "output_cap";
|
|
117
|
+
export type BrainRetryErrClass = "connect_refused" | "transport" | "stall" | "rate_limit" | "server" | "http" | "output_cap";
|
|
109
118
|
/** design/99 §E3/§E10 — the payload of a {@link TaskEvent} `status` event (and the brain→runner signal). */
|
|
110
119
|
export interface BrainStatus {
|
|
111
120
|
phase: BrainStatusPhase;
|
|
@@ -3,6 +3,7 @@ import { summarizeWorkflowRun } from "./workflow-run-store.js";
|
|
|
3
3
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
4
4
|
import { boundedRedactedSummary } from "./untrusted-egress.js";
|
|
5
5
|
import { clipWithFilePointer } from "./tool-errors.js";
|
|
6
|
+
import { announceEnvKnob } from "./env-knob-announce.js";
|
|
6
7
|
export function mintCompletionId(target) {
|
|
7
8
|
if (target.completionId === undefined)
|
|
8
9
|
target.completionId = uuidv7();
|
|
@@ -61,14 +62,15 @@ const TASK_OUTPUT_DEFAULT_CHARS = 32_000;
|
|
|
61
62
|
export const TASK_OUTPUT_MAX_CHARS = 160_000;
|
|
62
63
|
const TASK_OUTPUT_MIN_CHARS = 512;
|
|
63
64
|
function taskMaxOutputChars() {
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
65
|
+
const rawEnv = process.env.TASK_MAX_OUTPUT_LENGTH;
|
|
66
|
+
if (rawEnv === undefined || rawEnv.trim() === "")
|
|
66
67
|
return TASK_OUTPUT_DEFAULT_CHARS;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
const raw = rawEnv.trim();
|
|
69
|
+
const n = /^\d+$/.test(raw) ? parseInt(raw, 10) : Number.NaN;
|
|
70
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
71
|
+
announceEnvKnob(`TASK_MAX_OUTPUT_LENGTH=${rawEnv} was ignored — it is not a positive whole number of characters. Using ${TASK_OUTPUT_DEFAULT_CHARS} instead.`);
|
|
71
72
|
return TASK_OUTPUT_DEFAULT_CHARS;
|
|
73
|
+
}
|
|
72
74
|
return Math.min(Math.max(n, TASK_OUTPUT_MIN_CHARS), TASK_OUTPUT_MAX_CHARS);
|
|
73
75
|
}
|
|
74
76
|
export function clipTaskOutput(s, fullOutputPath) {
|
|
@@ -25,6 +25,14 @@ import type { NestedUsage } from "./tool-spec.js";
|
|
|
25
25
|
* Orchestrators that map an unexpected `suspended` should map an unexpected `needs_review` the same
|
|
26
26
|
* hard-boundary way (it is, like `suspended`, only safe at the top-level `runTask` boundary).
|
|
27
27
|
*/
|
|
28
|
+
/**
|
|
29
|
+
* Terminal vs waiting. Three words are TERMINAL — the leg is over and nobody is expected to act: `completed`,
|
|
30
|
+
* `failed`, and `blocked` (the AGENT's own report, through `report_blocked`, that it cannot finish and why —
|
|
31
|
+
* a self-report, not a pause; it waits for nobody). Two words are WAITING — a person's decision resumes the
|
|
32
|
+
* same leg through `runner.resume(token, …)`: `suspended` (a durable pre-action approval of a tool call) and
|
|
33
|
+
* `needs_review` (a durable review pause). The task-registry face spells the two waiting words as `parked`.
|
|
34
|
+
* A consumer that files `blocked` beside `suspended` is waiting on a decision that never comes.
|
|
35
|
+
*/
|
|
28
36
|
export type TaskStatus = "completed" | "blocked" | "failed" | "suspended" | "needs_review";
|
|
29
37
|
/**
|
|
30
38
|
* RB-439-a — one remote-workspace lifecycle failure, reported as data on {@link TaskResult.remoteEnvFailures}.
|
|
@@ -598,6 +606,13 @@ export interface TaskResult {
|
|
|
598
606
|
* the compaction subtotal). OBSERVE-ONLY (never gates budget). The workflow display is unaffected (its agents
|
|
599
607
|
* fail-on-suspend, never durably resume). A `spentToolCalls` ledger fold is a follow-on. */
|
|
600
608
|
toolCalls?: number;
|
|
609
|
+
/** #692 C-b (additive): present (`true`) when the usage figures on this face are a LOWER BOUND rather than a
|
|
610
|
+
* measurement — at least one model round of the leg reported no usage frame (the brain's `usageMissing`
|
|
611
|
+
* mark; the same word the per-turn `turn_end` frame carries), or the result was minted by a host / an
|
|
612
|
+
* orchestration lane that never ran a model round it could measure (a refused spawn's journal entry, a
|
|
613
|
+
* prepare-time refusal, a synthetic abort). The numeric members stay required and stay what was counted:
|
|
614
|
+
* `tokens: 0` beside this key reads "unknown", never "free". Absent ⇔ every round reported usage. */
|
|
615
|
+
usageMissing?: true;
|
|
601
616
|
/** Prompt (input) tokens that MISSED the prefix cache, summed across the task's turns and normalized
|
|
602
617
|
* across providers (Anthropic already reports `input_tokens` this way; OpenAI/vLLM include the cache
|
|
603
618
|
* in theirs, so the subsets are removed). Mutually exclusive with `cachedTokens`/`cacheWriteTokens*`;
|
|
@@ -20,7 +20,10 @@ import type { AssertAllKeysHandled } from "./ask-origin.js";
|
|
|
20
20
|
* the one cause→plane derivation, for the faces that still speak the word.
|
|
21
21
|
* - `completed` — the run finished (a person's clean halt included — see `TaskResult.haltedByUser`).
|
|
22
22
|
* - `failed` — a limit, a provider failure, an abort, an invalid output…
|
|
23
|
-
* - `blocked` — the
|
|
23
|
+
* - `blocked` — the AGENT reported, through `report_blocked`, that it cannot finish, and why. A TERMINAL
|
|
24
|
+
* cause: the leg is over and it waits for nobody (nothing to resume, no token). The waiting-on-a-person
|
|
25
|
+
* states are the `paused` cause's two status words — `suspended` (a pre-action approval of a tool
|
|
26
|
+
* call) and `needs_review` (a review pause); the task-registry face spells both as `parked`.
|
|
24
27
|
* - `paused` — a durable pause committed a checkpoint and the run is resumable.
|
|
25
28
|
* A `GateOutcome` is the record of ONE gate pass (per call); a `TerminalCause` is the record of ONE run
|
|
26
29
|
* (per leg); a paused run's `gate` and the park row's `gate.kind` are the same word, joined by the token.
|
|
@@ -73,7 +76,8 @@ export type TerminalCause = {
|
|
|
73
76
|
nestedPause?: PausedCause;
|
|
74
77
|
} | {
|
|
75
78
|
kind: "blocked";
|
|
76
|
-
/** Why the agent could not finish.
|
|
79
|
+
/** Why the agent could not finish — the agent's own words from its `report_blocked` call. Terminal: the
|
|
80
|
+
* run is over and nobody is being waited on (contrast `paused`, whose token is the resume capability). */
|
|
77
81
|
reason: string;
|
|
78
82
|
} | {
|
|
79
83
|
kind: "paused";
|
|
@@ -106,6 +106,27 @@ export interface ToolCallRequest {
|
|
|
106
106
|
* deny/ask lane and the person's own allow rules, never over a mandated ask. */
|
|
107
107
|
declare const DECISION_REASONS: readonly ["rule", "mode", "hook", "safety", "classifier", "persisted_rule", "sandbox", "org_rule", "org_unavailable", "read_only"];
|
|
108
108
|
export type DecisionReason = (typeof DECISION_REASONS)[number];
|
|
109
|
+
/** Carry every engine attestation (settlement, classifier cause) from a decision onto a reconstruction of it — the
|
|
110
|
+
* seam for the engine's OWN clone sites outside this module (the inherited chain's final-form re-check carries the
|
|
111
|
+
* post-rewrite args on a spread of an ancestor's deny). Not re-exported from `src/index.ts`. */
|
|
112
|
+
export declare function carryEngineAttestations(from: object, to: object): void;
|
|
113
|
+
/** Carry a PRECEDING decision's settlement onto a later deny that has none of its own — the veto record (a person's
|
|
114
|
+
* yes on the settlement, the refusing layer on the disposition; invariant I3) for the engine's own clone sites,
|
|
115
|
+
* where the deny is minted over a decision that already carried an attested approval. A deny that carries its own
|
|
116
|
+
* settlement keeps it (the latest resolution wins). Not re-exported from `src/index.ts`. */
|
|
117
|
+
export declare function carryPrecedingSettlement(preceding: object, to: object): void;
|
|
118
|
+
/** Attest that `d` is a deny a classifier station minted for `call` out of a round of the named FORM. */
|
|
119
|
+
export declare function attestClassifierDenyCause<T extends object>(d: T, cause: import("./gate-outcome.js").ClassifierDenyCause, call: {
|
|
120
|
+
toolCallId: string;
|
|
121
|
+
toolName: string;
|
|
122
|
+
}): T;
|
|
123
|
+
/** Read the attested classifier cause off a deny FOR the named call (the gate's fold and exit are the
|
|
124
|
+
* consumers): a binding to a different call is a replayed object and answers absence. An internal seam
|
|
125
|
+
* between engine modules — deliberately NOT re-exported from `src/index.ts`. */
|
|
126
|
+
export declare function classifierDenyCauseOf(d: unknown, call: {
|
|
127
|
+
toolCallId: string;
|
|
128
|
+
toolName: string;
|
|
129
|
+
}): import("./gate-outcome.js").ClassifierDenyCause | undefined;
|
|
109
130
|
/** Read the engine-attested settlement off a funneled decision FOR the named call (the gate's exit is the
|
|
110
131
|
* one consumer): an attestation bound to a different call is a replayed object and answers absence.
|
|
111
132
|
* Exported for the gate module only — deliberately NOT re-exported from `src/index.ts` (an internal seam
|
|
@@ -209,15 +230,6 @@ export type PermissionResult = {
|
|
|
209
230
|
* same safe direction as `matchedAskRule`); it cannot state a window of its own, because the
|
|
210
231
|
* route it would be a window for has not been chosen yet. */
|
|
211
232
|
denialLimitFallback?: import("./auto-mode.js").UnarmedDenialLimitFallback;
|
|
212
|
-
/** #616 (additive): the classifier-unavailable FACT on an ASK — `cause` is the verdict's own word
|
|
213
|
-
* ({@link import("./auto-mode.js").AutoModeUnavailableCause}). Since #661 the ENGINE no longer writes
|
|
214
|
-
* it here: an `unavailable` verdict is a DENY (the deny arm's member above), never an ask handed to a
|
|
215
|
-
* person. The member stays on the ask arm as display metadata a policy may self-declare on its own
|
|
216
|
-
* ask (nothing reads it to decide anything); the carry stations still copy it onto the approval
|
|
217
|
-
* request and the durable row when present. Retirement candidate for the next wire window. */
|
|
218
|
-
classifierUnavailable?: {
|
|
219
|
-
readonly cause: import("./auto-mode.js").AutoModeUnavailableCause;
|
|
220
|
-
};
|
|
221
233
|
/** #144 disclosure (additive): a persisted allow rule MATCHED this call but could not clear the
|
|
222
234
|
* ask, because the ask is MANDATED (operator shellGate:"always", or the tool's own
|
|
223
235
|
* egress/irreversibility marks) rather than a classifier's hesitation — "allow rules silence
|
|
@@ -287,21 +299,18 @@ export type PermissionResult = {
|
|
|
287
299
|
* exclusions; an approval surface may render the card by it. Optional on the TYPE only because a
|
|
288
300
|
* decision is caller-composable; every ask the gate hands to its resolver carries it. */
|
|
289
301
|
origin?: import("./ask-origin.js").AskOrigin;
|
|
302
|
+
/** #688 C3 (additive): WHICH mechanism stands behind an `origin: "rule_store_unavailable"` — one word from
|
|
303
|
+
* {@link import("./ask-origin.js").RULE_STORE_UNREADABLE_KINDS} (`store`: the wired store could not be read;
|
|
304
|
+
* `call`: the call could not be read against the person's rows — the lexer's sentence is in `message`).
|
|
305
|
+
* ENGINE-STAMPED at the same single stamp point as `origin`, from the gate's own lane read; a policy's
|
|
306
|
+
* self-declared value is overwritten or removed there. Present ⇔ the origin is `rule_store_unavailable`.
|
|
307
|
+
* Carried onto the approval request and the durable row beside the origin word. */
|
|
308
|
+
ruleStoreUnreadable?: import("./ask-origin.js").RuleStoreUnreadable;
|
|
290
309
|
} | {
|
|
291
310
|
action: "deny";
|
|
292
311
|
updatedInput?: unknown;
|
|
293
312
|
message?: string;
|
|
294
313
|
decisionReason?: DecisionReason;
|
|
295
|
-
/** #661 (additive): this deny is the classifier's UNAVAILABILITY — the auto-mode classifier was consulted
|
|
296
|
-
* and could not run, and the call is refused with a sentence that says so (CC 2.1.250 `Ze.unavailable` ⇒
|
|
297
|
-
* `deny`, `x1t`). `cause` is the verdict's own word ({@link import("./auto-mode.js").AutoModeUnavailableCause}).
|
|
298
|
-
* ENGINE-STAMPED beside `decisionReason: "classifier"` at the classifier stations (the gate's own and the
|
|
299
|
-
* inherited-lane arms) on an `unavailable` verdict only; a `block` and a `parse_error` deny carry nothing
|
|
300
|
-
* here. Read by the deny observer's payload ({@link import("./hooks.js").PermissionDeniedPayload}) and by
|
|
301
|
-
* nothing that decides. */
|
|
302
|
-
classifierUnavailable?: {
|
|
303
|
-
readonly cause: import("./auto-mode.js").AutoModeUnavailableCause;
|
|
304
|
-
};
|
|
305
314
|
};
|
|
306
315
|
/**
|
|
307
316
|
* design/252 G-2 — WHY a piece of rule-provenance evidence is not on an ask.
|
|
@@ -1157,15 +1166,12 @@ export interface AskRequest {
|
|
|
1157
1166
|
* that forwards a decision's member here does not compile, which is the point: the window is the
|
|
1158
1167
|
* route's fact, and forwarding it silently is how a configured window becomes a wait with none. */
|
|
1159
1168
|
readonly denialLimitFallback?: import("./auto-mode.js").DenialLimitFallback;
|
|
1160
|
-
/** #
|
|
1161
|
-
*
|
|
1162
|
-
*
|
|
1163
|
-
*
|
|
1164
|
-
*
|
|
1165
|
-
|
|
1166
|
-
readonly classifierUnavailable?: {
|
|
1167
|
-
readonly cause: import("./auto-mode.js").AutoModeUnavailableCause;
|
|
1168
|
-
};
|
|
1169
|
+
/** #688 C3 (additive) — the wire twin of the {@link PermissionResult} ask-arm member of the same name: which
|
|
1170
|
+
* mechanism stands behind an `origin: "rule_store_unavailable"` (`store` / `call`,
|
|
1171
|
+
* {@link import("./ask-origin.js").RULE_STORE_UNREADABLE_KINDS}). Present ⇔ that origin; a consumer that
|
|
1172
|
+
* renders "the rule store is unreachable" vs "this command could not be read against your rules" branches
|
|
1173
|
+
* here, never on the message text. Filled by the gate's own carry station from the engine-stamped decision. */
|
|
1174
|
+
readonly ruleStoreUnreadable?: import("./ask-origin.js").RuleStoreUnreadable;
|
|
1169
1175
|
/** (additive) WHICH AUTHORITY raised this ask — the wire twin of the {@link PermissionResult} ask-arm
|
|
1170
1176
|
* member of the same name, one word from the closed {@link import("./ask-origin.js").AskOrigin} set.
|
|
1171
1177
|
* The gate's own mint station copies the engine-stamped word; the three inherited-lane stations
|
package/dist/core/tool-policy.js
CHANGED
|
@@ -29,6 +29,32 @@ function transferEngineSettlement(from, to) {
|
|
|
29
29
|
const v = ENGINE_SETTLEMENTS.get(from);
|
|
30
30
|
if (v !== undefined)
|
|
31
31
|
ENGINE_SETTLEMENTS.set(to, v);
|
|
32
|
+
const c = CLASSIFIER_DENY_CAUSES_SEAT.get(from);
|
|
33
|
+
if (c !== undefined)
|
|
34
|
+
CLASSIFIER_DENY_CAUSES_SEAT.set(to, c);
|
|
35
|
+
}
|
|
36
|
+
export function carryEngineAttestations(from, to) {
|
|
37
|
+
transferEngineSettlement(from, to);
|
|
38
|
+
}
|
|
39
|
+
export function carryPrecedingSettlement(preceding, to) {
|
|
40
|
+
if (ENGINE_SETTLEMENTS.get(to) !== undefined)
|
|
41
|
+
return;
|
|
42
|
+
const v = ENGINE_SETTLEMENTS.get(preceding);
|
|
43
|
+
if (v !== undefined)
|
|
44
|
+
ENGINE_SETTLEMENTS.set(to, v);
|
|
45
|
+
}
|
|
46
|
+
const CLASSIFIER_DENY_CAUSES_SEAT = new WeakMap();
|
|
47
|
+
export function attestClassifierDenyCause(d, cause, call) {
|
|
48
|
+
CLASSIFIER_DENY_CAUSES_SEAT.set(d, { cause, toolCallId: call.toolCallId, toolName: call.toolName });
|
|
49
|
+
return d;
|
|
50
|
+
}
|
|
51
|
+
export function classifierDenyCauseOf(d, call) {
|
|
52
|
+
if (typeof d !== "object" || d === null)
|
|
53
|
+
return undefined;
|
|
54
|
+
const v = CLASSIFIER_DENY_CAUSES_SEAT.get(d);
|
|
55
|
+
if (v === undefined || v.toolCallId !== call.toolCallId || v.toolName !== call.toolName)
|
|
56
|
+
return undefined;
|
|
57
|
+
return v.cause;
|
|
32
58
|
}
|
|
33
59
|
export function engineSettlementOf(d, call) {
|
|
34
60
|
if (typeof d !== "object" || d === null)
|
|
@@ -400,7 +426,6 @@ export function combinePolicies(...policies) {
|
|
|
400
426
|
let ruleAskText;
|
|
401
427
|
let probeMandateSeen = false;
|
|
402
428
|
let fallbackSeen;
|
|
403
|
-
let classifierUnavailableSeen;
|
|
404
429
|
for (const p of policies) {
|
|
405
430
|
const d = refuseOutOfContractDecision(await p.check(current, signal));
|
|
406
431
|
if (d.action === "deny") {
|
|
@@ -410,6 +435,8 @@ export function combinePolicies(...policies) {
|
|
|
410
435
|
}
|
|
411
436
|
if (settledAllow !== undefined && ENGINE_SETTLEMENTS.get(out) === undefined) {
|
|
412
437
|
const owned = out === d ? { ...d } : out;
|
|
438
|
+
if (owned !== d)
|
|
439
|
+
transferEngineSettlement(d, owned);
|
|
413
440
|
transferEngineSettlement(settledAllow, owned);
|
|
414
441
|
return owned;
|
|
415
442
|
}
|
|
@@ -431,16 +458,13 @@ export function combinePolicies(...policies) {
|
|
|
431
458
|
probeMandateSeen = true;
|
|
432
459
|
if (d.action === "ask" && d.denialLimitFallback !== undefined && fallbackSeen === undefined)
|
|
433
460
|
fallbackSeen = d.denialLimitFallback;
|
|
434
|
-
if (d.action === "ask" && d.classifierUnavailable !== undefined && classifierUnavailableSeen === undefined)
|
|
435
|
-
classifierUnavailableSeen = d.classifierUnavailable;
|
|
436
461
|
}
|
|
437
462
|
if (asked) {
|
|
438
463
|
const merged = rewrite?.updatedInput;
|
|
439
464
|
const withRuleAsk = ruleAskText !== undefined && asked.matchedAskRule === undefined ? { ...asked, matchedAskRule: ruleAskText } : asked;
|
|
440
465
|
const withMark = probeMandateSeen && withRuleAsk.probeMandated !== true ? { ...withRuleAsk, probeMandated: true } : withRuleAsk;
|
|
441
466
|
const withFallback = fallbackSeen !== undefined && withMark.denialLimitFallback === undefined ? { ...withMark, denialLimitFallback: fallbackSeen, requiresRealApproval: true } : withMark;
|
|
442
|
-
|
|
443
|
-
return merged !== undefined ? { ...withFact, updatedInput: merged } : withFact;
|
|
467
|
+
return merged !== undefined ? { ...withFallback, updatedInput: merged } : withFallback;
|
|
444
468
|
}
|
|
445
469
|
const allowed = rewrite ?? ALLOW;
|
|
446
470
|
if (settledAllow === undefined)
|
package/dist/core/tool-roster.js
CHANGED
|
@@ -208,6 +208,8 @@ export function schemaKeyBoundProblem(schema) {
|
|
|
208
208
|
return undefined;
|
|
209
209
|
}
|
|
210
210
|
export function rosterMemberBoundProblem(m) {
|
|
211
|
+
if (typeof m.name !== "string")
|
|
212
|
+
return { code: "config.tool_roster_bound", message: `tool has no string name (got ${m.name === null ? "null" : typeof m.name}); every roster row is keyed by a string \`name\`` };
|
|
211
213
|
const over = (label, value, bound) => `${label} ${JSON.stringify(value.slice(0, 40) + "…")} is ${value.length} characters; the roster's bound is ${bound}`;
|
|
212
214
|
const of = (text) => `tool ${JSON.stringify(m.name.slice(0, 40))}: ${text}`;
|
|
213
215
|
if (m.name.length > TOOL_WIRE_NAME_MAX_CHARS)
|