@sema-agent/core 5.20.0 → 5.21.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 +9 -0
- package/dist/agents/agent-definition.d.ts +7 -0
- package/dist/agents/agent-transcript-tool.d.ts +30 -0
- package/dist/agents/builtin-agents.d.ts +60 -0
- package/dist/agents/cascade.d.ts +107 -0
- package/dist/agents/cumulative-stats.d.ts +56 -0
- package/dist/agents/observer.d.ts +162 -0
- package/dist/agents/peer-admission.d.ts +95 -0
- package/dist/agents/repair-loop.d.ts +226 -0
- package/dist/agents/retain-ledger.d.ts +141 -0
- package/dist/agents/roster-store.d.ts +105 -0
- package/dist/agents/send-message-tool.d.ts +92 -0
- package/dist/agents/session-util.d.ts +5 -0
- package/dist/agents/subagent-steps.d.ts +66 -0
- package/dist/agents/subagent.d.ts +600 -0
- package/dist/agents/suspend-guard.d.ts +29 -0
- package/dist/agents/teacher.d.ts +75 -0
- package/dist/agents/team.d.ts +120 -1
- package/dist/agents/tool-filter.d.ts +34 -0
- package/dist/agents/verify.d.ts +198 -0
- package/dist/bench/metrics.d.ts +455 -0
- package/dist/brain/anthropic.d.ts +30 -0
- package/dist/brain/circuit-breaker.d.ts +33 -0
- package/dist/brain/context-overflow.d.ts +60 -3
- package/dist/brain/degrading.d.ts +67 -0
- package/dist/brain/errors.d.ts +42 -0
- package/dist/brain/failover.d.ts +15 -0
- package/dist/brain/media-degrade.d.ts +39 -0
- package/dist/brain/model-presets.d.ts +31 -0
- package/dist/brain/open-responses.d.ts +19 -0
- package/dist/brain/openai.d.ts +46 -0
- package/dist/brain/reasoning.d.ts +106 -1
- package/dist/brain/repetition.d.ts +83 -0
- package/dist/brain/request-params.d.ts +56 -0
- package/dist/brain/retry.d.ts +46 -0
- package/dist/brain/routing.d.ts +12 -0
- package/dist/brain/status-sink.d.ts +30 -1
- package/dist/brain/stream-engine.d.ts +147 -0
- package/dist/brain/stream-shared.d.ts +34 -0
- package/dist/brain/terminal-cause.d.ts +31 -0
- package/dist/brain/timeout.d.ts +107 -0
- package/dist/brain/tool-call-id.d.ts +20 -0
- package/dist/brain/tool-call-repair.d.ts +13 -0
- package/dist/config/catalog.d.ts +47 -0
- package/dist/config/defaults.d.ts +33 -0
- package/dist/core/a2a-task-state.d.ts +53 -0
- package/dist/core/a2a.d.ts +51 -0
- package/dist/core/arg-summary.d.ts +62 -0
- package/dist/core/ask-question.d.ts +272 -2
- package/dist/core/auto-compaction.d.ts +467 -0
- package/dist/core/auto-compaction.js +20 -6
- package/dist/core/auto-mode-prompt-assets.d.ts +8 -0
- package/dist/core/auto-mode-prompt.d.ts +27 -0
- package/dist/core/auto-mode.d.ts +54 -3
- package/dist/core/auto-promote.d.ts +100 -0
- package/dist/core/background-agent-store.d.ts +292 -0
- package/dist/core/background-shell.d.ts +110 -0
- package/dist/core/cache-break-detector.d.ts +34 -0
- package/dist/core/canonical-json.d.ts +57 -0
- package/dist/core/checkpoint-store.d.ts +1574 -16
- package/dist/core/compliance.d.ts +30 -0
- package/dist/core/consolidate-scope.d.ts +75 -0
- package/dist/core/context-edit.d.ts +99 -0
- package/dist/core/context-guard.d.ts +46 -0
- package/dist/core/exec-gate.d.ts +44 -0
- package/dist/core/exec-output-tail.d.ts +61 -0
- package/dist/core/file-snapshot-store.d.ts +104 -0
- package/dist/core/fs-write-gate-policy.d.ts +36 -0
- package/dist/core/git-worktree-env.d.ts +31 -0
- package/dist/core/governance-codes.d.ts +19 -0
- package/dist/core/hooks.d.ts +608 -1
- package/dist/core/human-input-projection.d.ts +37 -0
- package/dist/core/human-input-projection.js +13 -0
- package/dist/core/image-downsample.d.ts +74 -0
- package/dist/core/locked-config.d.ts +37 -0
- package/dist/core/lsp-diagnostics.d.ts +77 -0
- package/dist/core/lsp-protocol.d.ts +29 -0
- package/dist/core/lsp-session.d.ts +60 -1
- package/dist/core/lsp.d.ts +150 -1
- package/dist/core/mailbox-store.d.ts +57 -0
- package/dist/core/mcp.d.ts +381 -0
- package/dist/core/media-byte-cap.d.ts +21 -0
- package/dist/core/memory-admission.d.ts +71 -0
- package/dist/core/memory-engine/content-origin.d.ts +64 -0
- package/dist/core/memory-engine/data-plane.d.ts +43 -0
- package/dist/core/memory-engine/dual-root.d.ts +24 -0
- package/dist/core/memory-engine/engine.d.ts +230 -0
- package/dist/core/memory-engine/engine.js +103 -35
- package/dist/core/memory-engine/file-backend.d.ts +155 -0
- package/dist/core/memory-engine/frontmatter.d.ts +16 -0
- package/dist/core/memory-engine/layout.d.ts +217 -0
- package/dist/core/memory-engine/memory-backend-contract.d.ts +12 -0
- package/dist/core/memory-engine/migrate.d.ts +9 -0
- package/dist/core/memory-engine/scan.d.ts +12 -0
- package/dist/core/memory-engine/scope-contract.d.ts +61 -1
- package/dist/core/memory-engine/sync-client.d.ts +87 -0
- package/dist/core/memory-engine/sync.d.ts +60 -0
- package/dist/core/memory-engine/tools.d.ts +41 -0
- package/dist/core/memory-engine/types.d.ts +188 -0
- package/dist/core/memory-recall.d.ts +141 -0
- package/dist/core/memory-vector.d.ts +20 -0
- package/dist/core/memory.d.ts +458 -0
- package/dist/core/message-utils.d.ts +6 -0
- package/dist/core/oracle-isolation.d.ts +69 -0
- package/dist/core/permission-rule-consent.d.ts +138 -0
- package/dist/core/permission-rule-model.d.ts +122 -0
- package/dist/core/permission-rule-store.d.ts +119 -3
- package/dist/core/permission-rules.d.ts +87 -1
- package/dist/core/present-plan-tool.d.ts +20 -0
- package/dist/core/pricing.d.ts +26 -0
- package/dist/core/property-harness.d.ts +86 -0
- package/dist/core/protocol-naming.d.ts +38 -0
- package/dist/core/protocol-table.d.ts +61 -0
- package/dist/core/push-queue.d.ts +1 -0
- package/dist/core/remote-env.d.ts +383 -1
- package/dist/core/retention-policy.d.ts +33 -0
- package/dist/core/retention.d.ts +51 -0
- package/dist/core/roles.d.ts +59 -0
- package/dist/core/runner/active-skill-scope.d.ts +34 -0
- package/dist/core/runner/assemble-result.d.ts +134 -0
- package/dist/core/runner/compaction-call-options.d.ts +97 -1
- package/dist/core/runner/grounding-signal.d.ts +10 -0
- package/dist/core/runner/image.d.ts +17 -0
- package/dist/core/runner/image.js +29 -15
- package/dist/core/runner/memory-consolidation.d.ts +60 -0
- package/dist/core/runner/prepare-memory.d.ts +59 -0
- package/dist/core/runner/prepare-task.d.ts +1011 -2
- package/dist/core/runner/prepare-task.js +12 -11
- package/dist/core/runner/prompt-suggestions.d.ts +7 -0
- package/dist/core/runner/runtask.d.ts +304 -3
- package/dist/core/runner/session-file-state-replay.d.ts +49 -0
- package/dist/core/runner/session-rule-policy.d.ts +58 -0
- package/dist/core/runner/strict-output-schema.d.ts +18 -3
- package/dist/core/runner/synthetic-tools.d.ts +113 -0
- package/dist/core/runner/teardown-bounded.d.ts +34 -0
- package/dist/core/runner/tool-disclosure.d.ts +227 -0
- package/dist/core/runner/tool-output-projection.d.ts +6 -0
- package/dist/core/runner/turn-attachments.d.ts +634 -0
- package/dist/core/runner/usage-accounting.d.ts +32 -0
- package/dist/core/runtime.d.ts +9 -0
- package/dist/core/safe-notify.d.ts +64 -0
- package/dist/core/safety-axis-vocab.d.ts +23 -0
- package/dist/core/safety-merge-corpus.d.ts +37 -0
- package/dist/core/scheduler.d.ts +121 -0
- package/dist/core/secret-env.d.ts +32 -0
- package/dist/core/select-model.d.ts +15 -0
- package/dist/core/sensitive-path-policy.d.ts +42 -0
- package/dist/core/session-policy-store.d.ts +94 -0
- package/dist/core/session-reconcile.d.ts +80 -0
- package/dist/core/session-store.d.ts +85 -0
- package/dist/core/session.d.ts +153 -0
- package/dist/core/shared-memory/contract.d.ts +22 -0
- package/dist/core/shared-memory/normalize.d.ts +123 -2
- package/dist/core/shared-memory/tools.d.ts +14 -0
- package/dist/core/shared-memory/types.d.ts +105 -0
- package/dist/core/shutdown-debug.d.ts +6 -0
- package/dist/core/side-query.d.ts +38 -0
- package/dist/core/side-query.js +6 -1
- package/dist/core/skill-tool-specifier.d.ts +72 -0
- package/dist/core/skills-directory.d.ts +100 -1
- package/dist/core/spec-contract.d.ts +89 -0
- package/dist/core/store-contracts/background-agent-store-contract.d.ts +31 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +26 -0
- package/dist/core/store-contracts/contract-harness.d.ts +20 -0
- package/dist/core/store-contracts/contract-kit-version.d.ts +27 -0
- package/dist/core/store-contracts/file-snapshot-store-contract.d.ts +8 -0
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +42 -0
- package/dist/core/store-contracts/tool-result-store-contract.d.ts +13 -0
- package/dist/core/strategy-store.d.ts +37 -0
- package/dist/core/stub-env.d.ts +7 -0
- package/dist/core/surrogate-safe-slice.d.ts +35 -0
- package/dist/core/task-notification.d.ts +202 -0
- package/dist/core/task-outcome.d.ts +53 -0
- package/dist/core/task-registry-agent.d.ts +337 -1
- package/dist/core/task-registry-monitor.d.ts +12 -0
- package/dist/core/task-registry-shared.d.ts +540 -0
- package/dist/core/task-registry.d.ts +343 -0
- package/dist/core/task-registry.js +13 -2
- package/dist/core/task-tool-shape.d.ts +44 -0
- package/dist/core/tighten-task-spec.d.ts +21 -0
- package/dist/core/tool-detach.d.ts +21 -0
- package/dist/core/tool-errors.d.ts +131 -0
- package/dist/core/tool-errors.js +4 -0
- package/dist/core/tool-name-aliases.d.ts +27 -0
- package/dist/core/tool-policy.d.ts +555 -0
- package/dist/core/tool-result-budget.d.ts +32 -0
- package/dist/core/tool-result-store.d.ts +174 -1
- package/dist/core/tools.d.ts +45 -0
- package/dist/core/trace.d.ts +323 -0
- package/dist/core/types.d.ts +3859 -2
- package/dist/core/untrusted-egress.d.ts +8 -0
- package/dist/core/untrusted-text.d.ts +156 -0
- package/dist/core/usage-window-store.d.ts +95 -0
- package/dist/core/version.d.ts +1 -0
- package/dist/core/warm-resume.d.ts +17 -0
- package/dist/core/wiring-manifest.d.ts +169 -0
- package/dist/core/with-retry.d.ts +24 -0
- package/dist/core/workflow-journal-store.d.ts +160 -0
- package/dist/core/workflow-run-store-contract.d.ts +25 -0
- package/dist/core/workflow-run-store.d.ts +119 -0
- package/dist/engine/compaction/compaction.d.ts +256 -1
- package/dist/engine/compaction/utils.d.ts +94 -0
- package/dist/engine/execution-env/kill-tree.d.ts +29 -0
- package/dist/engine/execution-env/node-execution-env.d.ts +94 -0
- package/dist/engine/harness/agent-harness.d.ts +116 -0
- package/dist/engine/harness/agent-harness.js +3 -14
- package/dist/engine/harness/messages.d.ts +15 -0
- package/dist/engine/harness/types.d.ts +464 -2
- package/dist/engine/llm/diagnostics.d.ts +4 -0
- package/dist/engine/llm/event-stream.d.ts +3 -0
- package/dist/engine/llm/index.d.ts +7 -0
- package/dist/engine/llm/types.d.ts +500 -3
- package/dist/engine/llm/validation.d.ts +3 -0
- package/dist/engine/loop/agent-loop.d.ts +87 -2
- package/dist/engine/loop/runtime-deps.d.ts +7 -0
- package/dist/engine/loop/types.d.ts +424 -0
- package/dist/engine/lsp/frame-decoder.d.ts +13 -0
- package/dist/engine/lsp/node-lsp-manager.d.ts +107 -0
- package/dist/engine/lsp/stdio-lsp-transport.d.ts +7 -0
- package/dist/engine/session/import-validate.d.ts +27 -0
- package/dist/engine/session/log-digest.d.ts +93 -0
- package/dist/engine/session/memory-repo.d.ts +6 -0
- package/dist/engine/session/memory-storage.d.ts +2 -0
- package/dist/engine/session/session.d.ts +75 -0
- package/dist/engine/session/storage-base.d.ts +8 -0
- package/dist/fixtures/index.d.ts +36 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.js +0 -1
- package/dist/internal/harness-types.d.ts +6 -0
- package/dist/internal/harness.d.ts +11 -0
- package/dist/internal/llm.d.ts +6 -0
- package/dist/orchestration/builtin-workflows.d.ts +53 -0
- package/dist/orchestration/dev-vm-script-runner.d.ts +19 -0
- package/dist/orchestration/goal.d.ts +57 -0
- package/dist/orchestration/goal.js +3 -0
- package/dist/orchestration/run-spec.d.ts +42 -0
- package/dist/orchestration/run-workflow-tool.d.ts +169 -0
- package/dist/orchestration/workflow-governance.d.ts +61 -0
- package/dist/orchestration/workflow-meta.d.ts +28 -0
- package/dist/orchestration/workflow-observe.d.ts +60 -0
- package/dist/orchestration/workflow-primitives.d.ts +23 -1
- package/dist/orchestration/workflow-sandbox-conformance.d.ts +38 -0
- package/dist/orchestration/workflow-script-runner.d.ts +88 -0
- package/dist/orchestration/workflow-script-store.d.ts +98 -0
- package/dist/orchestration/workflow-size-guideline.d.ts +67 -0
- package/dist/orchestration/workflow-types.d.ts +169 -2
- package/dist/orchestration/workflow.d.ts +358 -0
- package/dist/prompt-assembly/artifact-store.d.ts +33 -0
- package/dist/prompt-assembly/artifact.d.ts +25 -0
- package/dist/prompt-assembly/assemble.d.ts +20 -0
- package/dist/prompt-assembly/composer.d.ts +29 -0
- package/dist/prompt-assembly/epoch.d.ts +55 -1
- package/dist/prompt-assembly/event-registry.d.ts +35 -0
- package/dist/prompt-assembly/explain.d.ts +12 -0
- package/dist/prompt-assembly/tool-catalog.d.ts +38 -0
- package/dist/prompt-assembly/turn-snapshot.d.ts +40 -0
- package/dist/prompt-assembly/types.d.ts +115 -0
- package/dist/prompts/coordinator.d.ts +27 -0
- package/dist/prompts/default.d.ts +539 -0
- package/dist/prompts/simple-sections.d.ts +45 -0
- package/dist/prompts/supervisor.d.ts +66 -0
- package/dist/scenarios/env.d.ts +28 -0
- package/dist/scenarios/full-body.d.ts +50 -0
- package/dist/scenarios/scenario-registry.d.ts +60 -0
- package/dist/scenarios/teacher-quickstart.d.ts +27 -0
- package/dist/server/http.d.ts +17 -0
- package/dist/stores/cc/lockfile.d.ts +6 -0
- package/dist/stores/cc/mailbox-store.d.ts +8 -0
- package/dist/stores/cc/roster-adapter.d.ts +4 -0
- package/dist/stores/cc/sidecar-transcript.d.ts +66 -0
- package/dist/stores/file/background-agent-store.d.ts +24 -0
- package/dist/stores/file/checkpoint-store.d.ts +38 -0
- package/dist/stores/file/file-snapshot-store.d.ts +25 -0
- package/dist/stores/file/fs-atomic.d.ts +155 -0
- package/dist/stores/file/index.d.ts +89 -0
- package/dist/stores/file/mailbox-store.d.ts +36 -0
- package/dist/stores/file/memory-store.d.ts +82 -0
- package/dist/stores/file/permission-rule-store.d.ts +79 -0
- package/dist/stores/file/session-policy-store.d.ts +28 -0
- package/dist/stores/file/session-store.d.ts +40 -0
- package/dist/stores/file/shared-ledger.d.ts +83 -0
- package/dist/stores/file/tool-result-store.d.ts +11 -0
- package/dist/stores/file/usage-window-store.d.ts +18 -0
- package/dist/stores/file/workflow-journal-store.d.ts +100 -0
- package/dist/stores/file/workflow-run-store.d.ts +11 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +282 -0
- package/dist/tools/fs/encoding.d.ts +60 -0
- package/dist/tools/fs/fs-bash.d.ts +133 -0
- package/dist/tools/fs/fs-pdf.d.ts +28 -0
- package/dist/tools/fs/fs-search-tools.d.ts +5 -0
- package/dist/tools/fs/fs-shared.d.ts +360 -0
- package/dist/tools/fs/fs-write.d.ts +16 -0
- package/dist/tools/fs/gh-rate-limit.d.ts +22 -0
- package/dist/tools/fs/index.d.ts +79 -0
- package/dist/tools/fs/notebook.d.ts +56 -0
- package/dist/tools/fs/pdf.d.ts +70 -0
- package/dist/tools/fs/pdf.js +8 -2
- package/dist/tools/fs/repo-map.d.ts +10 -0
- package/dist/tools/fs/safety.d.ts +376 -0
- package/dist/tools/fs/search.d.ts +131 -0
- package/dist/tools/loop-tick.d.ts +24 -0
- package/dist/tools/monitor.d.ts +55 -0
- package/dist/tools/scheduler-tools.d.ts +51 -0
- package/dist/tools/sql-adapters.d.ts +18 -0
- package/dist/tools/sql.d.ts +25 -0
- package/dist/tools/task-list.d.ts +77 -0
- package/dist/tools/todo.d.ts +8 -0
- package/dist/tools/web.d.ts +184 -0
- package/dist/tools/worktree.d.ts +81 -0
- package/package.json +2 -2
- package/dist/tools/gitea-issue.d.ts +0 -13
- package/dist/tools/gitea-issue.js +0 -75
|
@@ -88,17 +88,18 @@ import { countElicitOptIns, deriveAskEffective, deriveWiringManifest, resolveAsk
|
|
|
88
88
|
import { GLOBAL_USAGE_KEY, resolveUsageWindows, usageRetryAfterMs } from "../usage-window-store.js";
|
|
89
89
|
const RECONCILE_MAX_RETRIES = 3;
|
|
90
90
|
const DEFAULT_MAX_SUSPENDS = 5;
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
91
|
+
const TASK_LIMIT_KEY_DICT = {
|
|
92
|
+
maxTokens: true,
|
|
93
|
+
maxCostUsd: true,
|
|
94
|
+
maxTurns: true,
|
|
95
|
+
maxWalltimeMs: true,
|
|
96
|
+
maxOutputTokens: true,
|
|
97
|
+
approachNotice: true,
|
|
98
|
+
budgetStreamCancel: true,
|
|
99
|
+
degrade: true,
|
|
100
|
+
brainCallGuardrailMs: true,
|
|
101
|
+
};
|
|
102
|
+
const TASK_LIMIT_KEYS = Object.keys(TASK_LIMIT_KEY_DICT);
|
|
102
103
|
const NUMERIC_TASK_LIMIT_KEYS = ["maxTokens", "maxCostUsd", "maxTurns", "maxWalltimeMs", "maxOutputTokens"];
|
|
103
104
|
const RETIRED_TASK_LIMIT_KEYS = {
|
|
104
105
|
timeoutSec: "maxWalltimeMs (milliseconds, not seconds)",
|
|
@@ -2,6 +2,7 @@ import type { Model } from "../../internal/llm.js";
|
|
|
2
2
|
import type { AgentMessage, ThinkingLevel } from "../../internal/harness-types.js";
|
|
3
3
|
import type { Brain } from "../types.js";
|
|
4
4
|
import type { ModelPricing } from "../pricing.js";
|
|
5
|
+
/** The LLM handle for the suggestion pass (mirrors `ConsolidationLLM`). */
|
|
5
6
|
export interface SuggestionLLM {
|
|
6
7
|
brain: Brain;
|
|
7
8
|
model: Model;
|
|
@@ -21,5 +22,11 @@ export interface SuggestionResult {
|
|
|
21
22
|
tokens: number;
|
|
22
23
|
costMicroUsd: number;
|
|
23
24
|
}
|
|
25
|
+
/** Parse the model's reply into ≤count clean suggestion strings. Returns [] on anything non-conforming. */
|
|
24
26
|
export declare function parseSuggestions(text: string, count: number): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Run ONE suggestion inference pass. May throw on a hard LLM/transport error — the caller
|
|
29
|
+
* (`Runner.suggestNextPrompts`) is fire-and-forget and swallows, so a failure just yields no suggestions.
|
|
30
|
+
* Returns `{ suggestions: [], tokens: 0, costMicroUsd: 0 }` when the transcript is empty.
|
|
31
|
+
*/
|
|
25
32
|
export declare function generatePromptSuggestions(llm: SuggestionLLM, transcript: AgentMessage[], count: number): Promise<SuggestionResult>;
|
|
@@ -7,18 +7,80 @@ import type { SessionStore } from "../session.js";
|
|
|
7
7
|
import { type RecoveredOrphan } from "../session-reconcile.js";
|
|
8
8
|
import { type ApprovalSettledBy } from "../tool-policy.js";
|
|
9
9
|
import type { AgentDefinition, RunnerDeps, TaskEvent, TaskResult, TaskSpec, TaskStream } from "../types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Config re-supplied to {@link Runner.resume} (design/45). A suspended task's tools / model / policy /
|
|
12
|
+
* hooks cannot be reconstructed from a checkpoint token (the session stores neither tool implementations
|
|
13
|
+
* nor the hand band), so the caller's trusted control plane re-supplies the same {@link TaskSpec} it ran
|
|
14
|
+
* with — minus the conversation bits: `sessionId` comes from the checkpoint and `objective` is replaced by
|
|
15
|
+
* an internally-generated continuation, so both are omitted.
|
|
16
|
+
*/
|
|
10
17
|
export type ResumeTaskConfig = Omit<TaskSpec, "objective" | "sessionId">;
|
|
18
|
+
/** design/45 resume plan threaded from {@link Runner.resume} into the shared run loop. */
|
|
11
19
|
interface ResumeRun {
|
|
12
20
|
cp: Checkpoint;
|
|
21
|
+
/**
|
|
22
|
+
* RB-152 (2026-07-25, 按面收口): did the APPROVED ACTION actually begin executing?
|
|
23
|
+
*
|
|
24
|
+
* The reopen compensation used to key on an error-code whitelist (`resume.env_failed` /
|
|
25
|
+
* `resume.session_not_found` / `resume.tool_unavailable`). That is a proxy for the thing that actually
|
|
26
|
+
* matters, and every time a NEW way to exit without running the action appeared, the whitelist did not
|
|
27
|
+
* know about it: a caller's abort landing anywhere between the CAS and `tool.execute` — across
|
|
28
|
+
* `SessionStore.acquire` (pluggable, cross-network on a durable backend), the MCP reconnect, the env
|
|
29
|
+
* rebuild, `resumeVM`, tool materialization — produced a `failed` result with NO errorCode, no reopen,
|
|
30
|
+
* and a human approval consumed for work that never happened. Two rounds of fixes (RB-77, RB-109) each
|
|
31
|
+
* moved that window rather than closing it, because both extended the whitelist.
|
|
32
|
+
*
|
|
33
|
+
* This binds the compensation to the FACT instead: set the instant before `tool.execute` for the gated
|
|
34
|
+
* call (the same `onExecuteStart` signal the orphan-reconcile split already trusts). Every terminal —
|
|
35
|
+
* throw OR returned result — asks one question: was the checkpoint consumed while this stayed false?
|
|
36
|
+
*/
|
|
13
37
|
pendingActionStarted?: boolean;
|
|
38
|
+
/** Validated against `cp.gate.kind` at the resume entry: human/irreversible_ask→`policy_ask`,
|
|
39
|
+
* resource_limit→`resource_limit` (design/74), needs_review→`dry_run_review` (design/76 §2.5),
|
|
40
|
+
* plan_review→`plan_review` (design/80 D-B). The gate-match guard in `resumeStream` enforces the
|
|
41
|
+
* correlation. design/144 §3: `wake` is the NON-GATE arm — only a checkpoint awaiting NO gate
|
|
42
|
+
* decision passes the resume entry with it (gate purity, `wake.gate_pending`); the run loop skips
|
|
43
|
+
* `applyResumeDecision` for it (no pending action to resolve) and re-enters via the continuation +
|
|
44
|
+
* pendingSteer tail alone. */
|
|
14
45
|
outcome: Extract<ResumeOutcome, {
|
|
15
46
|
gate: "policy_ask" | "resource_limit" | "dry_run_review" | "plan_review" | "wake";
|
|
16
47
|
}>;
|
|
48
|
+
/** design/144 §3 (X5) — the wake's own operator message, validated (`validatePendingSteer`) at the
|
|
49
|
+
* resume entry and carried SEPARATELY from the checkpoint's parked `pendingSteer`: a message-bearing
|
|
50
|
+
* wake of a checkpoint that ALSO holds a parked steer must deliver BOTH (park order: parked first,
|
|
51
|
+
* wake message second), each under its own trusted framing — the old merge-into-the-slot shape
|
|
52
|
+
* silently DISPLACED the parked (undelivered) supervisor steer. Wake outcomes only. */
|
|
17
53
|
wakeMessage?: Omit<PendingSteerEntry, "seq">;
|
|
54
|
+
/** Compensation hook (design/45/49): called iff the resumed run fails with `resume.env_failed` (post-CAS
|
|
55
|
+
* workspace `resumeVM` failed) OR `resume.tool_unavailable` (P-7: the approved tool vanished) — in both
|
|
56
|
+
* the CAS already consumed the checkpoint but the pending action never ran. `resumeStream` supplies a
|
|
57
|
+
* closure that reopens the checkpoint (`resolved → pending`) so a retry re-resumes the SAME suspended work
|
|
58
|
+
* instead of losing it to a forced "re-initiate". design/80 D-1 (reopen-by-reason): the `reason` is
|
|
59
|
+
* recorded on the reopened row so the next re-resume validates per reason — an `env_failed` reopen must
|
|
60
|
+
* replay the persisted winner (a system retry of the approved action), while a `tool_unavailable` reopen
|
|
61
|
+
* lets a human re-decide with the tool present (a fresh decision is allowed — preserves P-7). */
|
|
18
62
|
onEnvRestoreFailed?: (reason: ReopenReason) => Promise<void>;
|
|
63
|
+
/** RB-471/FR-C1 — set in the run body right after `applyResumeDecision` completes: the negative-
|
|
64
|
+
* decision twin of `pendingActionStarted`. A reject/deny consumes its gate BY BEING DELIVERED, and
|
|
65
|
+
* this bit is the delivery fact — every throw-arm cause (prepare failure, `session_not_found`, a
|
|
66
|
+
* pre-delivery abort) and the walltime-exhausted settle fire BEFORE it is set, so an undelivered
|
|
67
|
+
* negative decision still reopens as `env_failed` (the retry replays the persisted decision; it
|
|
68
|
+
* never re-asks — design/80 D-1), closing the RB-152/RB-70 loss class the first RB-471 cut reopened. */
|
|
19
69
|
decisionDelivered?: boolean;
|
|
20
70
|
}
|
|
21
|
-
|
|
71
|
+
/** The `tool_end` body fields projected from a harness tool result — output/truncated/totalChars via
|
|
72
|
+
* {@link toolOutputFrom} and the CC card via {@link structuredFrom}. Single construction point for BOTH
|
|
73
|
+
* the live loop's frames and the resumed batch's frames (`resolvePendingCall` + the deferred-sibling
|
|
74
|
+
* close): the resumed frames used to carry only `isError`, so a client rendering tool output from frames
|
|
75
|
+
* showed an empty body for every durable-approved call. Same projection = same source as the transcript.
|
|
76
|
+
* Also the one place `settledBy` reaches a frame — see the parameter. */
|
|
77
|
+
declare function toolEndBodyFrom(result: unknown, isError: boolean,
|
|
78
|
+
/** What ended the approval this call was waiting on, supplied by the CALLER of this projection — the
|
|
79
|
+
* live loop reads it off the gate's per-call sideband, the resumed leg off the decide. Deliberately
|
|
80
|
+
* a parameter and never derived from `result`: a tool's own `details` (which post-tool hooks may
|
|
81
|
+
* also replace) is writable by layers that adjudicate nothing, so reading provenance out of it would
|
|
82
|
+
* let a failing tool claim a person approved it. Omitted ⇒ this call settled no approval. */
|
|
83
|
+
settledBy?: ApprovalSettledBy): {
|
|
22
84
|
output?: unknown;
|
|
23
85
|
truncated?: boolean;
|
|
24
86
|
totalChars?: number;
|
|
@@ -26,45 +88,284 @@ declare function toolEndBodyFrom(result: unknown, isError: boolean, settledBy?:
|
|
|
26
88
|
errorCode?: string;
|
|
27
89
|
settledBy?: ApprovalSettledBy;
|
|
28
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* scan-1/A1 — the BODY of the synthetic `tool_end` that closes a reconcile-recovered orphan. ONE
|
|
93
|
+
* construction point for BOTH minting legs (the live-abort loop at the end of a run, and the wake/crash
|
|
94
|
+
* leg's replay at run open), so the two can never disagree about the shape of the same event.
|
|
95
|
+
*
|
|
96
|
+
* Both frames used to carry `isError:true` and NOTHING else: a consumer rendering tool output from the
|
|
97
|
+
* event stream showed an EMPTY body for every interrupted call, even though the persisted transcript
|
|
98
|
+
* (which the model reads) carried the full `[INTERRUPTED]` explanation — the two faces of one call
|
|
99
|
+
* disagreed. The projection goes through the same {@link toolEndBodyFrom} every live tool result uses, so
|
|
100
|
+
* `output` = the persisted model-facing text and `errorCode` = the persisted `details.errorKind`
|
|
101
|
+
* (`interrupted_never_started` / `interrupted_outcome_unknown`) — a consumer discriminates on the code
|
|
102
|
+
* instead of prose-matching. No `structured`: the reconcile mints no CC card (no `details.type`), which
|
|
103
|
+
* `structuredFrom`'s allowlist already enforces.
|
|
104
|
+
*/
|
|
29
105
|
export declare function reconciledToolEndBody(orphan: Pick<RecoveredOrphan, "text" | "errorKind">): ReturnType<typeof toolEndBodyFrom>;
|
|
30
106
|
export declare const GOVERNANCE_READ_STALLED: unique symbol;
|
|
107
|
+
/** Await a ledger charge, firing `onSlow` ONCE if it has not settled after
|
|
108
|
+
* {@link CHARGE_SETTLE_DISCLOSE_MS}. The charge itself is always awaited to completion. */
|
|
31
109
|
export declare function awaitChargeWithSlowDisclosure<T>(charge: Promise<T>, onSlow: () => void, discloseAfterMs?: number): Promise<T>;
|
|
110
|
+
/**
|
|
111
|
+
* Await `p` until the absolute epoch `deadline`, then give up with {@link GOVERNANCE_READ_STALLED}.
|
|
112
|
+
*
|
|
113
|
+
* Chunked, and deliberately so: the deadline is derived from a caller-declared environment lifetime, so
|
|
114
|
+
* the distance to it can exceed {@link MAX_TIMER_DELAY_MS} — a single timer armed for that distance
|
|
115
|
+
* would fire at once and abandon a perfectly healthy read. Each chunk re-reads the wall clock, so the
|
|
116
|
+
* decision is always made against the deadline itself rather than against an allowance computed once.
|
|
117
|
+
*
|
|
118
|
+
* Exported for its own unit pin (the same posture as the brain-call guardrail primitive): the
|
|
119
|
+
* behaviors below are clock behaviors, and only a virtual clock can assert them without spending the
|
|
120
|
+
* wall-clock time they describe. Not re-exported from the package index.
|
|
121
|
+
*
|
|
122
|
+
* An already-overdue deadline still gets ONE zero-delay pass: a promise that is settled (or settles in a
|
|
123
|
+
* microtask, which is every in-process store) must be allowed to win, because giving up on an answer we
|
|
124
|
+
* already hold would be a fabricated stall. The second pass is what makes the loop terminate.
|
|
125
|
+
*/
|
|
32
126
|
export declare function raceUntilDeadline<T>(p: Promise<T>, deadline: number): Promise<T | typeof GOVERNANCE_READ_STALLED>;
|
|
33
127
|
export declare class Runner {
|
|
34
128
|
private deps;
|
|
35
129
|
readonly sessions: SessionStore;
|
|
130
|
+
/** Per-sessionId serialization so two tasks never mutate one session concurrently. */
|
|
36
131
|
private sessionLocks;
|
|
132
|
+
/** service [403](a) — roots whose rewind-files snapshot hit `too_large`, with the refusal time.
|
|
133
|
+
* A 20G workspace otherwise pays a full bounded tree-walk (stat tens of thousands of files) EVERY
|
|
134
|
+
* task just to re-discover the same refusal; once too_large, skip further snapshots for that root
|
|
135
|
+
* (announced once via onError). E19 semantics unchanged — rewind for that root was already
|
|
136
|
+
* impossible. codex 131 审 C: TTL'd (30min), not permanent — a long-lived Runner over a tree the
|
|
137
|
+
* user later SHRINKS re-probes instead of being locked out until restart.
|
|
138
|
+
* RB-430-b: `skipAnnounced` makes the SKIP itself observable — one announcement per cooldown window,
|
|
139
|
+
* so an operator can tell "this turn produced no snapshot" from "snapshots are working", without the
|
|
140
|
+
* per-turn log spam the latch exists to prevent. Cleared with the entry when the window elapses. */
|
|
37
141
|
private snapshotTooLargeRoots;
|
|
142
|
+
/** 飞轮 [492]② — task notifications born BETWEEN turns (run torn down / harness already idle), parked
|
|
143
|
+
* per session and drained into the session's next run at its first boundary. Runner-lived (outlives any
|
|
144
|
+
* single run, like the registry handles that produce into it); bounded + drop-disclosing, see
|
|
145
|
+
* {@link PendingSessionNotifications}. Not checkpointed — the producing handles are process-local. */
|
|
38
146
|
private readonly pendingSessionNotifications;
|
|
147
|
+
/** Parent effective-policy inheritance (same-process resume convenience): checkpoint token → the OPAQUE
|
|
148
|
+
* `parentConstraints` the suspended leg ran under. Registered by `runTaskStream`'s result hook whenever a
|
|
149
|
+
* run spawned WITH `internals.inheritedGate.parentConstraints` ends `suspended` (process-local closures
|
|
150
|
+
* parked while the park is live — the same lifecycle posture as the subagent retain ledger's
|
|
151
|
+
* `internalsSnapshot`). `resumeStream` consults it when the caller supplied no constraints, so a
|
|
152
|
+
* same-process `runner.resume()` on a delegated child's checkpoint auto re-supplies the chain; a
|
|
153
|
+
* CROSS-process resume (registry miss — a different Runner/process) still requires an explicit
|
|
154
|
+
* `resumeStream(..., internals)` and fail-louds otherwise (`resume.parent_constraint_missing`).
|
|
155
|
+
* Retention: entries are kept across an `env_failed` reopen (the same token is retried), evicted on an
|
|
156
|
+
* observed `already_resolved` and by the FIFO cap — an evicted entry only ever degrades to the explicit
|
|
157
|
+
* re-supply requirement (fail-loud), never fail-open. */
|
|
39
158
|
private readonly parentConstraintRegistry;
|
|
40
159
|
private static readonly PARENT_CONSTRAINT_REGISTRY_CAP;
|
|
41
160
|
constructor(deps: RunnerDeps);
|
|
161
|
+
/** design/73 §1 v1 — fire `RunnerDeps.onTaskOutcome` through the single swallow-guarded chokepoint
|
|
162
|
+
* ({@link emitTaskOutcome}: mechanical-tier only, throwing sink never breaks the caller). Public so
|
|
163
|
+
* thin compositions OVER the Runner (`runGoal`) can emit at their terminal state without reaching
|
|
164
|
+
* into private deps. */
|
|
42
165
|
emitTaskOutcome(outcome: TaskOutcome): void;
|
|
166
|
+
/** design/141 件A — the deployment agent catalog (RunnerDeps.agents/builtinAgents), exposed READ-ONLY
|
|
167
|
+
* so the Agent-tool lane (`createSubagentTool`) defaults to the SAME registry the workflow lane
|
|
168
|
+
* consumes (single source; the types.ts "pass the same array" follow-on made structural). The array
|
|
169
|
+
* is a fresh copy — a caller mutating it never rewrites this Runner's own deps; the definitions
|
|
170
|
+
* themselves are the shared objects (frozen by convention, same as gateBaseline's posture). */
|
|
43
171
|
get agentCatalog(): {
|
|
44
172
|
agents?: AgentDefinition[];
|
|
45
173
|
builtinAgents?: boolean;
|
|
46
174
|
models?: Record<string, Model>;
|
|
47
175
|
};
|
|
176
|
+
/** design/125 (codex 实现审 B1) — the deployment-level gating baseline, exposed READ-ONLY so thin
|
|
177
|
+
* assemblers over the Runner (`runSpec`) can COMPOSE with it. The task-level engine semantic is a
|
|
178
|
+
* WHOLE-SLOT override (`spec.toolPolicy ?? deps.toolPolicy`, same for hooks — prepare-task), so any
|
|
179
|
+
* assembler that sets `spec.toolPolicy`/`spec.hooks` without folding these in silently shadows the
|
|
180
|
+
* deployment baseline. Getter only — nothing here is writable from outside. */
|
|
48
181
|
get gateBaseline(): {
|
|
49
182
|
toolPolicy?: RunnerDeps["toolPolicy"];
|
|
50
183
|
hooks?: RunnerDeps["hooks"];
|
|
51
184
|
};
|
|
185
|
+
/** Acquire the lock for a sessionId; returns a release fn. New (undefined) sessions need no lock. */
|
|
52
186
|
private acquireSessionLock;
|
|
187
|
+
/** [1463]① — one-shot brain-routed utility query (see {@link runSideQuery} for the full contract):
|
|
188
|
+
* preserves system/multi-turn messages/tool DEFINITIONS, routes through the deployment's brain with
|
|
189
|
+
* the same model resolution as tasks, returns real usage/model. No session, no tool execution, no
|
|
190
|
+
* policy gate, no accounting — caller-trusted. */
|
|
53
191
|
sideQuery(spec: SideQuerySpec): Promise<SideQueryResult>;
|
|
192
|
+
/** Run a task and stream live events; await `.result()` for the final TaskResult. `resume` (internal)
|
|
193
|
+
* drives a design/45 durable resume through the same loop instead of a fresh objective prompt.
|
|
194
|
+
* `internals` (internal, design/78 Slice-1) is a TRUSTED run-scoped channel for live per-task state the
|
|
195
|
+
* Runner cannot see from `spec` (today: a `runRepairLoop` attempt's live `repairBundle`) — NOT a
|
|
196
|
+
* `TaskSpec` field; undefined on the public path.
|
|
197
|
+
* **Eager:** the task starts executing the moment this stream is constructed (fire-and-forget), NOT lazily
|
|
198
|
+
* on first iteration — you do NOT need to pull events for the run (and its `finish()`/env teardown) to
|
|
199
|
+
* happen. Iterating just observes events (the buffer is backpressure-free); `.result()` awaits completion. */
|
|
54
200
|
runTaskStream(spec: TaskSpec, resume?: ResumeRun, internals?: RunInternals): TaskStream;
|
|
55
201
|
private runLocked;
|
|
202
|
+
/**
|
|
203
|
+
* design/100 §E12 — the post-completion prompt-suggestion pass. Never throws (failures route to
|
|
204
|
+
* `onError(phase:"suggestions")` and resolve to `[]`), so it is fire-and-forget safe and `suggestions()`
|
|
205
|
+
* never rejects. Also fills the budget-excluded `stats.suggestions` line.
|
|
206
|
+
*/
|
|
56
207
|
private suggestNextPrompts;
|
|
208
|
+
/**
|
|
209
|
+
* design/101 §E19 — capture a working-tree snapshot of the just-finished turn, keyed by the leaf
|
|
210
|
+
* `SessionTreeEntry.id`. Best-effort + never throws (errors route to `onError(phase:"rewind")`). Runs against
|
|
211
|
+
* ANY ExecutionEnv (local or remote) when a `fileSnapshotStore` is wired (GATE-SPLIT, service [263]) — the
|
|
212
|
+
* snapshot is portable (env FileSystem ops). A deployment wanting VM-snapshot instead wires a VM-backed store.
|
|
213
|
+
*/
|
|
57
214
|
private snapshotTurn;
|
|
215
|
+
/** Run a task to completion and return a machine-readable result. */
|
|
58
216
|
runTask(spec: TaskSpec, internals?: RunInternals): Promise<TaskResult>;
|
|
59
|
-
|
|
60
|
-
|
|
217
|
+
/**
|
|
218
|
+
* Resume a task suspended at a durable approval gate (design/45 F4). The token came back as
|
|
219
|
+
* `TaskResult.checkpointToken` from a `status:"suspended"` run; `outcome` carries the human/external
|
|
220
|
+
* decision (`allow`/`deny`, with an optional `updatedInput` arg rewrite or `reason`); `taskConfig`
|
|
221
|
+
* **re-supplies** the same tools / model / policy the task ran with (a token cannot reconstruct tool
|
|
222
|
+
* implementations or the hand band — the session stores neither).
|
|
223
|
+
*
|
|
224
|
+
* Flow (§2.1): `get` the checkpoint → validate the outcome arm matches the gate (council #3) → **atomic
|
|
225
|
+
* CAS `resolve`**. *Winning the CAS is the sole trigger to execute the pending action* — so a double /
|
|
226
|
+
* concurrent resume of the same token loses the CAS and is rejected (`checkpoint.already_resolved`),
|
|
227
|
+
* never re-running the side-effecting tool. The winner rewinds the session to the suspension point,
|
|
228
|
+
* resolves the gated call (execute on `allow`, inject a denial on `deny`), closes the rest of the
|
|
229
|
+
* suspended batch as deferred, and re-enters the run loop with a continuation — returning a normal
|
|
230
|
+
* {@link TaskResult} (which may itself be `suspended` again with a fresh token).
|
|
231
|
+
*
|
|
232
|
+
* v1 serves only the `human`/`policy_ask` gate (F4). A `task_done` checkpoint (1C Path A) is not
|
|
233
|
+
* resumable here — the caller orchestrates that one and reads its handle directly.
|
|
234
|
+
*
|
|
235
|
+
* **🔒 Authorization boundary (design/80 D-2 / [122] invariant #1) — token-as-auth, scope is store-level
|
|
236
|
+
* isolation, NOT caller authorization.** `resume` is TOKEN-AS-AUTH: whoever presents a valid, still-`pending`
|
|
237
|
+
* checkpoint token resolves it. The scope passed to the store CAS is the checkpoint's OWN `cp.scope` (read
|
|
238
|
+
* off the row), so the store's scope-WHERE is store-level multi-tenant DATA isolation (one tenant's reaper /
|
|
239
|
+
* mis-scoped resolve can't touch another's row) — it does **not** verify that THIS CALLER is authorized for
|
|
240
|
+
* that scope. Authorizing the operator's principal against the checkpoint (the "tenant-B holding tenant-A's
|
|
241
|
+
* token must not resolve it" property) is the **caller's** responsibility: the BFF / supervisor service mints
|
|
242
|
+
* the principal, scopes the approval inbox to it, and only calls `resume` for a checkpoint that principal
|
|
243
|
+
* owns. The token's secrecy + single-use HMAC binding (D-G) is what keeps it from reaching the wrong tenant.
|
|
244
|
+
* A direct (non-BFF) client is BLOCKED until D-G's cryptographic principal binding — never trust a
|
|
245
|
+
* client-supplied principal/scope here.
|
|
246
|
+
*
|
|
247
|
+
* @throws {@link CheckpointError} `not_found` (no store / unknown token), `gate_mismatch` (outcome arm
|
|
248
|
+
* ≠ gate, or an unsupported gate), `invalid_outcome` (decision-action binding failed — boundCallId/
|
|
249
|
+
* boundInputHash mismatch, or a deny reason with a `</system-reminder>` tag), `reopen_revote` (an
|
|
250
|
+
* env_failed re-resume supplied a decision ≠ the persisted winner), `reopened_concurrently` (the
|
|
251
|
+
* optimistic-concurrency `rev` changed under a concurrent resolve/reopen — re-resume against current
|
|
252
|
+
* state), `unsupported_version` (checkpoint newer than this worker / remote handle with no factory),
|
|
253
|
+
* `already_resolved` (lost the CAS — idempotent no-op).
|
|
254
|
+
*/
|
|
255
|
+
resume(token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig,
|
|
256
|
+
/** RB-48② (1.404): same TRUSTED run-internals seam as {@link resumeStream} — the convenience wrapper
|
|
257
|
+
* FORWARDS it (it used to silently drop a 4th arg, so a cross-process re-supply through resume()
|
|
258
|
+
* looked wired but always failed `resume.parent_constraint_missing`). */
|
|
259
|
+
internals?: RunInternals): Promise<TaskResult>;
|
|
260
|
+
/**
|
|
261
|
+
* Streaming form of {@link resume}: runs the identical pre-CAS guards + atomic CAS, then returns the live
|
|
262
|
+
* {@link TaskStream} of the resumed run (its events + `result()`) instead of draining it to a final
|
|
263
|
+
* {@link TaskResult}. Use this when the resumed segment's tool/turn events must reach an event sink for
|
|
264
|
+
* observability parity with the original `runInBackground` stream (service [15]); `resume()` is the
|
|
265
|
+
* convenience wrapper that drains it. The guards + CAS run FIRST, so this rejects with the same
|
|
266
|
+
* {@link CheckpointError}s as `resume` *before* any stream is returned (winning the CAS is still the sole
|
|
267
|
+
* trigger to execute the pending action — a lost CAS rejects, never returns a re-running stream).
|
|
268
|
+
*
|
|
269
|
+
* **Eager (like {@link runTaskStream}):** once this resolves, the resumed run is already executing — the
|
|
270
|
+
* pending action runs exactly once and the run-loop tail's `teardownOwnedEnv` (service [325]) tears down the
|
|
271
|
+
* rebuilt remote env even if you never iterate the stream. So abandoning the returned stream is safe (no leak,
|
|
272
|
+
* no hang); it only means you
|
|
273
|
+
* don't observe the resumed segment's events.
|
|
274
|
+
*/
|
|
275
|
+
resumeStream(token: CheckpointToken, outcome: ResumeOutcome, taskConfig: ResumeTaskConfig,
|
|
276
|
+
/** service [371]①: the TRUSTED run-internals seam, symmetric with {@link runTaskStream} — a resume leg
|
|
277
|
+
* otherwise has no `onForwardEvent`席位, so a subagent's task_progress ticks (child-isolated stream,
|
|
278
|
+
* only exit = the forward sink) were unreachable on resume. Deployment-owned, never a TaskSpec field.
|
|
279
|
+
* Parent effective-policy inheritance CONTRACT: a checkpoint whose `state.inheritedGate.
|
|
280
|
+
* requiresParentConstraint` is true was suspended under inherited OPAQUE parent-policy constraints
|
|
281
|
+
* (live ToolPolicy/onAsk closures — non-serializable); this is the ONLY seam that re-supplies them
|
|
282
|
+
* (`internals.inheritedGate.parentConstraints`). Omitting them for such a checkpoint rejects pre-CAS
|
|
283
|
+
* with `resume.parent_constraint_missing` (fail-loud; the checkpoint stays pending) — never a silent
|
|
284
|
+
* resume with the ancestors' gate dropped — unless this SAME Runner spawned the leg (the spawn-time
|
|
285
|
+
* registry auto re-supplies them; see `parentConstraintRegistry`). The re-supplied chain's LENGTH must
|
|
286
|
+
* match the checkpoint's recorded `parentConstraintCount` (`resume.parent_constraint_mismatch`
|
|
287
|
+
* otherwise). Most entries are policies the deployment authored and can hand straight back; a
|
|
288
|
+
* hook-wired ancestor also contributes a PreToolUse SCREENING entry (issue #33) whose policy the
|
|
289
|
+
* engine mints — rebuild that one with the exported `createPreToolUseConstraintPolicy(hook, env)`. The DATA half (ancestorRules/shellGate) rides the checkpoint itself and needs no
|
|
290
|
+
* re-supply; on the resumed leg the CHECKPOINT snapshot is the trust floor — live internals may only
|
|
291
|
+
* TIGHTEN it (per-entry rev-monotonic ancestorRules replacement, max-rank shellGate fold), never
|
|
292
|
+
* displace it wholesale. TRUST BOUNDARY: `internals` is the DEPLOYMENT trust channel (the same tier as
|
|
293
|
+
* `inheritedManifestScope` and every other RunInternals field) — the library caller is trusted to
|
|
294
|
+
* re-supply the genuine chain; a caller able to invoke resumeStream already holds full Runner
|
|
295
|
+
* authority, so constraint substitution here is outside the enforced threat model (the count check
|
|
296
|
+
* catches shape MISTAKES, not a malicious trusted caller). */
|
|
297
|
+
internals?: RunInternals): Promise<TaskStream>;
|
|
298
|
+
/**
|
|
299
|
+
* design/45 resume step: with the harness idle and the branch rewound to the suspension leaf, resolve
|
|
300
|
+
* the gated tool call and close the rest of the suspended batch, all by appending `toolResult`s to the
|
|
301
|
+
* session (the next `harness.prompt(continuation)` replays them as context).
|
|
302
|
+
* - `allow` → execute the pending tool ONCE (re-validating `updatedInput`/captured post-hook args),
|
|
303
|
+
* append its real result. This deliberately bypasses the tool gate (the human already adjudicated)
|
|
304
|
+
* and the PostToolUse hook (documented v1 gap — rare, acceptable).
|
|
305
|
+
* - `deny` → append a model-readable denial result instead.
|
|
306
|
+
* - the remaining batch siblings (#k+1..N) → deferred-reissue results (v1 doesn't blind-run them).
|
|
307
|
+
*/
|
|
61
308
|
private applyResumeDecision;
|
|
309
|
+
/** Resolve the single gated call of a resumed batch (design/45): execute it once on `allow`, or inject
|
|
310
|
+
* a model-readable denial on `deny`. Appends exactly one `toolResult` for `pendingAction.toolCallId`.
|
|
311
|
+
* `emit` streams `tool_start`/`tool_end` for the resolved call so a `resumeStream` observer sees the
|
|
312
|
+
* approved tool actually execute (API#2 observability parity — service [15]/[37]; the gated call never
|
|
313
|
+
* emitted execution events in the original run, it suspended at the gate before running).
|
|
314
|
+
*
|
|
315
|
+
* RB-22 RULING (2026-07-18, deliberate — the $ counterpart of RB-21's walltime question): a HUMAN-
|
|
316
|
+
* APPROVED call executes even when the $ budget is already exhausted (`maxCostMicroUsd` ≤ spent).
|
|
317
|
+
* The tool execution itself consumes no model spend — the budget's coordinate is LLM cost, and the
|
|
318
|
+
* very next turn boundary's budget gate stops the run — so the overrun is ≈0 while refusing would
|
|
319
|
+
* WASTE the human approval (the review effort is already sunk). This deliberately differs from the
|
|
320
|
+
* walltime ruling (RB-21: NOT exempt) on principle: wall-clock exhaustion endangers the write-out
|
|
321
|
+
* window (time cannot be clawed back), $ exhaustion after an approved no-spend action endangers
|
|
322
|
+
* nothing. Pinned in resume tests; do not "fix" this into a pre-execution budget check. */
|
|
62
323
|
private resolvePendingCall;
|
|
324
|
+
/**
|
|
325
|
+
* design/84 Seam C — the cost-optimization compaction options threaded into BOTH `maybeCompact` call
|
|
326
|
+
* sites (within-task turn boundary + `finish()`). All three fields come from `RunnerDeps` (a trusted
|
|
327
|
+
* FUNCTION seam — never `TaskSpec`, which is serializable/durable-resumable/untrusted-caller). The
|
|
328
|
+
* Runner OWNS the consecutive-reuse counter (`prepared.compactionReuseRef`) so the
|
|
329
|
+
* `maxConsecutiveProviderReuse` drift guard spans the whole task across both sites: it FEEDS the current
|
|
330
|
+
* count in as `consecutiveProviderReuse`, and {@link recordCompactionReuse} updates it from the result.
|
|
331
|
+
* Returns `undefined` when no provider is wired (so the call site spreads nothing → byte-identical to
|
|
332
|
+
* the pre-design/84 behavior).
|
|
333
|
+
*/
|
|
63
334
|
private seamCCompactionOptions;
|
|
335
|
+
/** design/134 §3.2 — resolve the pre/postCompact lifecycle callbacks (whole-slot `spec.hooks ??
|
|
336
|
+
* deps.hooks`, same resolution as the stop hook) and wrap each in a SWALLOW+TRACE shell before
|
|
337
|
+
* threading them into maybeCompact. The wrapper owns the observability half of the R3 MED contract
|
|
338
|
+
* (maybeCompact swallows defensively too, but has no sink): a throwing callback is reported via
|
|
339
|
+
* `onError(phase:"hook")` and treated as absent; a `block` returned under a "forced" trigger is
|
|
340
|
+
* reported as ignored (maybeCompact enforces the ignore — blocking a compaction the provider/trim
|
|
341
|
+
* layer already demanded would kill the run). */
|
|
64
342
|
private compactionHookOptions;
|
|
343
|
+
/** design/84 Seam C — fold a finished compaction's `reused` flag into the run-scoped consecutive-reuse
|
|
344
|
+
* counter: a reused (provider) summary increments it, a real (LLM) summary resets it to 0. A no-op
|
|
345
|
+
* compaction (`compacted:false`) leaves the counter untouched. No-op when no provider is wired. */
|
|
65
346
|
private recordCompactionReuse;
|
|
66
347
|
private finish;
|
|
348
|
+
/**
|
|
349
|
+
* design/48 remote seam + service [325]: tear down a per-task env minted by `executionEnvFactory` (e.g. a
|
|
350
|
+
* remote container, or a `withWorktreeIsolation` worktree) — this task owned its lifetime. Best-effort, like
|
|
351
|
+
* `mcp.dispose`: a `destroy()` failure must not break the run. Only `ownedEnv` (factory-produced) is destroyed;
|
|
352
|
+
* a caller-owned static `deps.executionEnv` outlives the task and is left untouched. The service control plane's
|
|
353
|
+
* reaper is the backstop for an env orphaned by a rare pre-teardown throw (same posture as `mcp`).
|
|
354
|
+
*
|
|
355
|
+
* Runs from the run-loop tail AFTER every env-reader (finish()'s compaction `attachWorkingFiles` +
|
|
356
|
+
* `snapshotTurn`'s rewind-files capture), so the working tree is still readable when they run. This ORDER is the
|
|
357
|
+
* fix for service [325]: the old in-`finish()` destroy ran BEFORE `snapshotTurn`, so a factory-minted
|
|
358
|
+
* remote/worktree lane snapshotted an already-destroyed env (plain host/static lanes were false-green).
|
|
359
|
+
*
|
|
360
|
+
* design/49 v1.5 / design/76 §2.5: a SUSPENDED (`suspendRef.token`) or `needs_review` (`reviewRef.token`) pause
|
|
361
|
+
* `suspendVM`-paused the env and persisted its `workspaceHandle` into the checkpoint — destroying it would
|
|
362
|
+
* discard the paused VM and make resume fail to restore. Skip teardown; `resume()` rebuilds + `resumeVM`s it. A
|
|
363
|
+
* cancelled pause is reaped via `TaskStream.destroy()` (design/51); an abandoned one is the service container
|
|
364
|
+
* reaper's backstop. The gate here MIRRORS the tail's reap-stash (same durable fact
|
|
365
|
+
* `suspendRef.token===undefined && reviewRef.token===undefined`), so exactly one path owns the env's fate.
|
|
366
|
+
*/
|
|
67
367
|
private teardownOwnedEnv;
|
|
68
368
|
}
|
|
369
|
+
/** Convenience: one-shot run with explicit deps (creates a throwaway Runner). */
|
|
69
370
|
export declare function runTask(spec: TaskSpec, deps: RunnerDeps): Promise<TaskResult>;
|
|
70
371
|
export type { TaskEvent, TaskStream };
|
|
@@ -1,7 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rebuild the hands' read-before-write state from a session's own transcript (ruled 2026-08-05).
|
|
3
|
+
*
|
|
4
|
+
* `readFileState` is minted per TASK and, until this module existed, was only ever re-seeded from a
|
|
5
|
+
* durable-resume checkpoint. An ordinary session CONTINUATION — a new `runTask` carrying the previous
|
|
6
|
+
* `sessionId` — therefore started blind: turn 1 Read the file, turn 2 was told "File has not been
|
|
7
|
+
* read yet", and every file cost a re-read round-trip on every turn of a long conversation.
|
|
8
|
+
*
|
|
9
|
+
* The transcript already records what is needed, so no new persisted surface is minted here: the
|
|
10
|
+
* tool results carry the file's whole text in their `details` card. This module walks those records
|
|
11
|
+
* and reports, per file, the LAST content the transcript can prove — the caller turns each into a
|
|
12
|
+
* read-state entry whose staleness hash is computed from THAT text.
|
|
13
|
+
*
|
|
14
|
+
* Two rules make the reconstruction safe rather than merely convenient:
|
|
15
|
+
*
|
|
16
|
+
* 1. **The content never comes from disk.** Every hash the caller derives from these records is a
|
|
17
|
+
* hash of what the transcript recorded. A file changed between the turns (by the user, a linter,
|
|
18
|
+
* a `git checkout`, a bash command — anything this transcript does not record) therefore fails
|
|
19
|
+
* the staleness comparison and the edit is refused, exactly as it is inside a single task. A
|
|
20
|
+
* reconstruction that re-read the file to build its hash would instead wave the dirty edit
|
|
21
|
+
* through; that is the one thing this module must never do.
|
|
22
|
+
* 2. **Only WHOLE-file records qualify.** A partial read, a notebook projection or an `Edit` result
|
|
23
|
+
* does not carry the file's full post-condition text, so there is nothing to hash honestly.
|
|
24
|
+
* Those files are simply not reported: the gate then behaves exactly as it does today (the model
|
|
25
|
+
* re-reads), which is the pre-existing cost, not a new failure.
|
|
26
|
+
*/
|
|
1
27
|
import type { AgentMessage } from "../../internal/harness.js";
|
|
28
|
+
/** The whole text of one file at the moment the transcript recorded it. */
|
|
2
29
|
export interface TranscriptFileRecord {
|
|
30
|
+
/** The path as the tool recorded it — absolute (see {@link wholeFileRecordsFromTranscript}). */
|
|
3
31
|
path: string;
|
|
32
|
+
/** The file's ENTIRE text at that moment, in the decoded coordinate the fs tools hash. */
|
|
4
33
|
content: string;
|
|
34
|
+
/** The recording tool result's own timestamp — the most honest "last read at" available. */
|
|
5
35
|
at: number;
|
|
6
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Per-file, the last whole-file content the transcript proves — in transcript order, last record wins.
|
|
39
|
+
*
|
|
40
|
+
* Records are read only from SUCCESSFUL results (an `isError` result changed nothing and proves
|
|
41
|
+
* nothing). A `Write` proves the post-condition it just persisted; a whole-file `Read` proves what it
|
|
42
|
+
* just served. An `Edit` result proves that the file CHANGED without recording its new text, so it
|
|
43
|
+
* retracts whatever an earlier record said — leaving a stale entry behind would blame the user for
|
|
44
|
+
* the agent's own edit ("modified since read") the next turn. Anything else (partial reads, dedup
|
|
45
|
+
* stubs, notebook projections, image/document reads, foreign tools) is inert: it neither proves nor
|
|
46
|
+
* retracts, because reading a file does not change it.
|
|
47
|
+
*
|
|
48
|
+
* The parameter is the session's own message lane (`AgentMessage`, custom lanes included) rather than
|
|
49
|
+
* the narrowed model-facing union: this reads the branch as the session stores it, and every lane that
|
|
50
|
+
* is not a tool result falls out on the first line of the loop.
|
|
51
|
+
*
|
|
52
|
+
* Paths are reported only when the tool recorded an ABSOLUTE one. A relative spelling was resolved
|
|
53
|
+
* against the tracked working directory of that moment, which this task cannot reconstruct; re-
|
|
54
|
+
* resolving it here could key the entry to a different file.
|
|
55
|
+
*/
|
|
7
56
|
export declare function wholeFileRecordsFromTranscript(messages: readonly AgentMessage[]): TranscriptFileRecord[];
|
|
@@ -2,9 +2,67 @@ import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
|
2
2
|
import type { ToolEffect } from "../types.js";
|
|
3
3
|
import { type NamedToolPolicy } from "../tool-policy.js";
|
|
4
4
|
import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
5
|
+
/** Tool names whose write target is the `file_path`/`path` arg ({@link fileArgPath}).
|
|
6
|
+
*
|
|
7
|
+
* REF-B3 — the ONE definition of that narrower set. Production consumer today:
|
|
8
|
+
* `createActiveSkillScopePolicy` (runner/active-skill-scope.ts, manifest `allowPaths`). The
|
|
9
|
+
* `allowDirs`/fs-write faces consume the superset {@link PATH_CONFINABLE_WRITE_TOOLS} instead.
|
|
10
|
+
*
|
|
11
|
+
* BOUNDARY: this set stays `file_path`-keyed — its members' write target is read by {@link fileArgPath}.
|
|
12
|
+
* `NotebookEdit` (whose target is `notebook_path`) lives in the superset
|
|
13
|
+
* {@link PATH_CONFINABLE_WRITE_TOOLS} below, NOT here: `createActiveSkillScopePolicy` reads THIS set and
|
|
14
|
+
* deliberately keeps fail-closed-denying notebooks under a manifest `allowPaths` fence (widening the
|
|
15
|
+
* skill face is a separate capability decision with its own review). */
|
|
5
16
|
export declare const PATH_WRITE_TOOLS: ReadonlySet<string>;
|
|
17
|
+
/** The FULL path-confinable write-tool set: {@link PATH_WRITE_TOOLS} plus `NotebookEdit`, whose write
|
|
18
|
+
* target is `notebook_path` and is extracted by the shared tool-aware {@link writeTargetPath} ([886]①b —
|
|
19
|
+
* notebook_path outranks a decoy `file_path` for that tool).
|
|
20
|
+
*
|
|
21
|
+
* REF-B3 — the ONE spelling of "tools this codebase can confine by path". Consumers:
|
|
22
|
+
* {@link createSessionRulePolicy} (below, `allowDirs`) and `createFsWriteGatePolicy`
|
|
23
|
+
* (core/fs-write-gate-policy.ts), which used to rebuild `[...PATH_WRITE_TOOLS, "NotebookEdit"]` inline.
|
|
24
|
+
* A fourth spelling survives as a LITERAL in core/tool-policy.ts (the transcript-dir gate) because THIS
|
|
25
|
+
* module imports tool-policy.ts — that one is a documented coordinate held in sync by a source-scan pin
|
|
26
|
+
* (SW-10), not a silent copy.
|
|
27
|
+
*
|
|
28
|
+
* SW-10 relax (ruled 2026-08-04): `NotebookEdit` was previously absent from the session-rule face's
|
|
29
|
+
* covered set, so an `allowDirs` confinement denied it OUTRIGHT via the "mutating but not path-confinable"
|
|
30
|
+
* arm — a capability GAP relative to the fs-write gate, which has always proven the tool confinable.
|
|
31
|
+
* Both faces now share this set and the same target extractor, so the session face confines notebooks by
|
|
32
|
+
* path instead of refusing them. The fail-closed deny for every OTHER non-confinable mutating tool
|
|
33
|
+
* (bash, deployment-authored write tools, unknown effects) is unchanged. */
|
|
6
34
|
export declare const PATH_CONFINABLE_WRITE_TOOLS: ReadonlySet<string>;
|
|
35
|
+
/** Path-segment-aware containment, FAMILY-AWARE ([K-PLATFORM-SWEEP] MAJOR#2: canonical keys are
|
|
36
|
+
* backslash-form on a win32 env — the "/"-hardcoded suffix rejected every legitimate write there).
|
|
37
|
+
* Compare in "/" form with drive-letter case folded; never keyed on process.platform (the env may be
|
|
38
|
+
* remote and of the OTHER family than the host).
|
|
39
|
+
*
|
|
40
|
+
* REF-B3 — exported as the single source for the policy modules that fence WRITES by an allowlist of
|
|
41
|
+
* directories: active-skill-scope.ts (manifest `allowPaths`) and fs-write-gate-policy.ts (`acceptDirs`
|
|
42
|
+
* /`exemptDirs`), plus the K-PLATFORM sweep tests. The line here used to read "exported ONLY for the
|
|
43
|
+
* K-PLATFORM sweep tests", which was already false when written (fs-write-gate-policy.ts imports it in
|
|
44
|
+
* production), and active-skill-scope.ts carried a byte-identical private twin — comment included — so
|
|
45
|
+
* a platform-family fix had to be remembered at two coordinates.
|
|
46
|
+
*
|
|
47
|
+
* The sibling implementation is `within()` in tools/fs/safety.ts (the fs-tool root fence). Those stay
|
|
48
|
+
* separate functions on purpose — that one is private to the fence and keyed on canonical roots — but
|
|
49
|
+
* they must never disagree on a verdict; the empty-root arm below is that alignment. */
|
|
7
50
|
export declare function isWithin(root: string, p: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Build the per-session deny-narrowing policy. For each tool call:
|
|
53
|
+
* - `toolDeny` → deny the listed tools.
|
|
54
|
+
* - `toolAllow` (if set) → deny any tool NOT listed (a narrowing allowlist).
|
|
55
|
+
* - `commandAllow`/`commandDeny` → delegate to {@link createCoarseCommandNamePolicy} (argv[0] names; only
|
|
56
|
+
* speaks about shell tools). allowlist mode denies the un-listed/un-parseable; blocklist mode asks on the
|
|
57
|
+
* un-parseable bypass surface (the gate decides).
|
|
58
|
+
* - `allowDirs` (if set) → a WRITE tool must land inside one of the dirs (resolved via `canonicalizeTarget`,
|
|
59
|
+
* so a symlink can't smuggle a write out); a write-capable tool that cannot be path-confined (e.g. `bash`)
|
|
60
|
+
* is DENIED while `allowDirs` is set (fail-closed, mirrors active-skill-scope MAJOR-2).
|
|
61
|
+
* Returns `allow` when nothing constrains the call. `env`/`rootPath` are the task's real exec env + root, so
|
|
62
|
+
* matching sees the actual filesystem (a remote/E2B task resolves in its container). RB-108: a relative
|
|
63
|
+
* TARGET prefers the call's live tracked cwd ({@link ToolCallRequest.cwd}) when the engine stamps one;
|
|
64
|
+
* `allowDirs` entries always resolve against `rootPath`.
|
|
65
|
+
*/
|
|
8
66
|
export declare function createSessionRulePolicy(rules: SessionPermissionRules, opts: {
|
|
9
67
|
env: ExecutionEnv;
|
|
10
68
|
rootPath?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { TSchema } from "typebox";
|
|
2
|
+
/** Port of 204 `Lqn` — derive the strict form of a whole schema (root must be `type:"object"`). */
|
|
2
3
|
export declare function deriveStrictSchema(schema: unknown): {
|
|
3
4
|
ok: true;
|
|
4
5
|
schema: Record<string, unknown>;
|
|
@@ -6,15 +7,29 @@ export declare function deriveStrictSchema(schema: unknown): {
|
|
|
6
7
|
ok: false;
|
|
7
8
|
reason: string;
|
|
8
9
|
};
|
|
10
|
+
/** Exposed for callers that only need validity (no derivation). Runs the size guard first. */
|
|
9
11
|
export declare function validateJsonSchemaShape(schema: unknown): string | null;
|
|
10
|
-
export type CompiledOutputSchema =
|
|
12
|
+
export type CompiledOutputSchema =
|
|
13
|
+
/** The schema is not a valid JSON Schema (or too large) — callers must FAIL FAST, never run silently. */
|
|
14
|
+
{
|
|
11
15
|
error: string;
|
|
12
|
-
}
|
|
16
|
+
}
|
|
17
|
+
/** Strict derivation succeeded: `modelSchema` is the tightened form to hand the model. */
|
|
18
|
+
| {
|
|
13
19
|
modelSchema: TSchema;
|
|
14
20
|
strict: true;
|
|
15
|
-
}
|
|
21
|
+
}
|
|
22
|
+
/** Valid but outside the strict subset: serve the schema as-is; `fallbackReason` is the observable why. */
|
|
23
|
+
| {
|
|
16
24
|
modelSchema: TSchema;
|
|
17
25
|
strict: false;
|
|
18
26
|
fallbackReason: string;
|
|
19
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Validate + strict-derive a structured-output schema (CC 2.1.205 semantics):
|
|
30
|
+
* invalid / oversized schema → `{error}` (the caller throws — no silent acceptance);
|
|
31
|
+
* in-subset schema → `{modelSchema: strictForm, strict:true}` (`additionalProperties:false` stamped);
|
|
32
|
+
* valid-but-unsupported schema → `{modelSchema: original, strict:false, fallbackReason}` (observable).
|
|
33
|
+
* A derivation CRASH also falls back non-strict (CC's inner try/catch), never fails the task.
|
|
34
|
+
*/
|
|
20
35
|
export declare function compileOutputSchema(schema: TSchema): CompiledOutputSchema;
|