@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
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `withRetry` (design/61 §9, core thin helper) — the consumer side of the typed-retryable error contract
|
|
3
|
+
* (`remote-env.ts`). Retries a Result-returning op, but **only** on a whitelisted typed-retryable code, up to
|
|
4
|
+
* `maxAttempts`, with optional backoff. All remote adapters (E2B / SSH / ADB) reuse it so they don't each
|
|
5
|
+
* re-implement "retry by typed code + backoff" (and risk getting the contract wrong).
|
|
6
|
+
*
|
|
7
|
+
* 🔴 **Apply ONLY to IDEMPOTENT establishment ops** — `connect` / `reconnect` (design/61 §2(A)). The error
|
|
8
|
+
* contract is "the caller decides retry per idempotency; the adapter MUST NOT blind-retry"; this helper IS that
|
|
9
|
+
* caller-side decision, scoped to the safe (idempotent) layer. **Never wrap a side-effecting operation** — and
|
|
10
|
+
* never put a permanent code (`"auth_failed"`, `"unsupported"`) in `retryableCodes` (retrying a rejected key
|
|
11
|
+
* burns the budget / can lock the account).
|
|
12
|
+
*
|
|
13
|
+
* Determinism / fault injection (council §9 F): the `op(attempt)` closure is the injection point (a test
|
|
14
|
+
* scripts a per-attempt error sequence), and `opts.sleep` overrides the real backoff delay — so
|
|
15
|
+
* "first attempt fails, second succeeds" is tested deterministically without a real device/network.
|
|
16
|
+
*/
|
|
1
17
|
export type RetryResult<T, E> = {
|
|
2
18
|
ok: true;
|
|
3
19
|
value: T;
|
|
@@ -6,10 +22,18 @@ export type RetryResult<T, E> = {
|
|
|
6
22
|
error: E;
|
|
7
23
|
};
|
|
8
24
|
export interface RetryPolicy {
|
|
25
|
+
/** Error codes that warrant a retry (a typed-retryable whitelist). A code not in this list returns immediately. */
|
|
9
26
|
retryableCodes: readonly string[];
|
|
27
|
+
/** Total attempts, `>= 1`. (e.g. ADB first-auth = 2; SSH connect-drop = a few.) */
|
|
10
28
|
maxAttempts: number;
|
|
29
|
+
/** Backoff before attempt N (1-based; called with the attempt that just FAILED, so N≥1). Default: no wait. */
|
|
11
30
|
backoffMs?: (failedAttempt: number) => number;
|
|
12
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Run `op` until it succeeds, a non-retryable error surfaces, or `maxAttempts` is exhausted. `op` receives the
|
|
34
|
+
* 1-based attempt number. Returns the success, or the LAST failure (so an exhausted-but-still-failing op surfaces
|
|
35
|
+
* its final typed error to the caller).
|
|
36
|
+
*/
|
|
13
37
|
export declare function withRetry<T, E extends {
|
|
14
38
|
code: string;
|
|
15
39
|
}>(op: (attempt: number) => Promise<RetryResult<T, E>>, policy: RetryPolicy, opts?: {
|
|
@@ -1,37 +1,197 @@
|
|
|
1
1
|
import type { TaskResult } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* design/97 CORE-7 — one recorded `ctx.agent` result in a workflow's RESUME journal. Unlike the observation-only
|
|
4
|
+
* {@link WorkflowRunStore} (best-effort, swallowed on throw), the journal is LOAD-BEARING: a resume replays the
|
|
5
|
+
* longest unchanged PREFIX of these (keyed by the deterministic {@link workflowAgentCallKey}) and runs only the
|
|
6
|
+
* first changed/new call + everything after it live. Same script + same args → 100% cache hit.
|
|
7
|
+
*/
|
|
2
8
|
export interface WorkflowJournalEntry {
|
|
9
|
+
/** The agent's deterministic call key (`ordinal:specIdentityHash`) — the replay match key. */
|
|
3
10
|
callKey: string;
|
|
11
|
+
/** The agent's TaskResult, replayed verbatim when a resume's call key matches at the same ordinal. */
|
|
4
12
|
result: TaskResult;
|
|
5
13
|
}
|
|
14
|
+
/** REF-D5: the resume-claim key triple — ONE shape for the interface pair and every implementation
|
|
15
|
+
* (it was re-inlined seven times; a key-field rename must red every leg at once). */
|
|
6
16
|
export interface ResumeClaimArgs {
|
|
7
17
|
sourceRunId: string;
|
|
8
18
|
newRunId: string;
|
|
9
19
|
scope: string;
|
|
10
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* The seam a resume reads/writes its journal through. Core ships {@link InMemoryWorkflowJournalStore}; a durable
|
|
23
|
+
* deployment provides a Postgres-backed one (service SVC-2) so a workflow survives a replica crash. The owner of
|
|
24
|
+
* a run is the sole writer of its journal.
|
|
25
|
+
*
|
|
26
|
+
* 🔐 `scope` (the TENANT/principal of the resuming run) is LOAD-BEARING for multi-tenant isolation (CORE-9 audit
|
|
27
|
+
* BLOCKER): an LLM controls the `resumeFromRunId`, so `load` MUST be constrained to the caller's scope — exactly
|
|
28
|
+
* as {@link WorkflowRunStore} puts scope in the WHERE of every method. A store implementation MUST return ONLY a
|
|
29
|
+
* run's journal whose recorded scope equals the requested `scope` (a cross-scope `resumeFromRunId` resolves to an
|
|
30
|
+
* EMPTY journal → the resume safely diverges to a live re-run, never disclosing another tenant's results).
|
|
31
|
+
*
|
|
32
|
+
* SESSION-axis RULING (clay 裁定 2026-07-22, B-2 会话轴扫描案; FINAL form settled in the γ batch):
|
|
33
|
+
* `load` is deliberately SCOPE-only, PERMANENTLY — same-scope callers from a different session may
|
|
34
|
+
* replay a run's journal. Rationale: (a) same-scope = same trust domain (the task-list store
|
|
35
|
+
* records the same posture) and a runId is engine-minted, not enumerable; (b) cross-session resume
|
|
36
|
+
* is `resumeFromRunId`'s CORE legitimate use — a fresh session picking up yesterday's run has a new
|
|
37
|
+
* taskId AND a new sessionId by construction, so ANY engine-side session/owner clamp on this face
|
|
38
|
+
* refuses exactly the contract the tool teaches (the [1455] prefix-replay pins are the regression
|
|
39
|
+
* proof; a γ-batch recheck attempt was reverted on them). Layering: the ENGINE owns the scope
|
|
40
|
+
* hard-wall (this WHERE); the SESSION acceptance arm belongs to the SERVING layer, which has a
|
|
41
|
+
* session-bound connection to adjudicate against (server 1.247's wire-face soft acceptance). This
|
|
42
|
+
* face diverges from poll/stop (scope+owner/session positive-match) BY DESIGN: they gate on the
|
|
43
|
+
* caller's CURRENT identity, resume asserts continuity with a PRIOR leg.
|
|
44
|
+
*/
|
|
11
45
|
export interface WorkflowJournalStore {
|
|
46
|
+
/** Entries for `runId` IF its recorded scope === `scope`, ASCENDING by ordinal; otherwise EMPTY (cross-scope). */
|
|
12
47
|
load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
|
|
48
|
+
/** Record one agent's result under `(runId, scope)`. MUST be idempotent per `(runId, ordinal)` — a resumed run
|
|
49
|
+
* re-appends the cached results of its replayed prefix, so a second append for the same ordinal overwrites. */
|
|
13
50
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
51
|
+
/** [1409]③ (CC diagnostics parity, additive): a HUMAN/MODEL-readable coordinate for this run's journal —
|
|
52
|
+
* the file store returns the on-disk jsonl path (CC "Read journal.jsonl" local form); a service store
|
|
53
|
+
* returns its API route (cloud form). `undefined`/absent ⇒ the completion notification's diagnostics
|
|
54
|
+
* line teaches only the TaskOutput route (which is always live). Engine-facing string, never parsed. */
|
|
14
55
|
locator?(runId: string, scope: string): string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* RB-242 ([1980] server → [1981] core ruling, 2026-07-30) — ADMISSION for a resume, so two concurrent
|
|
58
|
+
* `resumeFromRunId` legs off ONE source run cannot fork its execution.
|
|
59
|
+
*
|
|
60
|
+
* Why a store seam at all: the engine's own mutex is in-process only (`startWorkflow` refuses a duplicate
|
|
61
|
+
* `runId` and refuses resuming from a source still ACTIVE here). A resume mints a FRESH runId, so a second
|
|
62
|
+
* resume off the same source — from another process, replica, or a later session — trips neither guard: both
|
|
63
|
+
* legs replay the same prefix and then re-run the whole suffix LIVE, duplicating every side effect after it,
|
|
64
|
+
* each writing its own journal, neither ever seeing the other. The only place that fork is visible is the
|
|
65
|
+
* shared durable layer, i.e. here.
|
|
66
|
+
*
|
|
67
|
+
* OPTIONAL, and absence is a first-class state: a store that does NOT implement this has NO STORE-LEVEL lease
|
|
68
|
+
* semantics. RB-367 ([2054] test AI dual-process probe): a store without the hook is no longer completely
|
|
69
|
+
* unguarded, though — `runWorkflow`'s engine falls back to a process-local admission table for exactly this
|
|
70
|
+
* shape, so a same-process double `resumeFromRunId` off one source is still refused even against a store
|
|
71
|
+
* that never implements this member. That fallback cannot see a SECOND process, which is the one thing a
|
|
72
|
+
* real implementation is for — core's own {@link InMemoryWorkflowJournalStore} DOES implement this member
|
|
73
|
+
* (with in-process-only semantics; a single-process store cannot promise more), so the engine's fallback
|
|
74
|
+
* table is, in practice, only ever exercised by some OTHER store that omits the hook.
|
|
75
|
+
*
|
|
76
|
+
* Contract:
|
|
77
|
+
* - `granted: true` ⇒ the caller (`newRunId`) now HOLDS the claim on `(sourceRunId, scope)` and owes exactly
|
|
78
|
+
* one {@link releaseResumeClaim} for it. The engine calls that at the run's terminal boundary — completed,
|
|
79
|
+
* failed, cancelled/aborted, or abandoned by a total-timeout — in a `finally`.
|
|
80
|
+
* - `granted: false` ⇒ someone else holds it. The engine REFUSES the resume with an explicit error rather
|
|
81
|
+
* than forking; `holder` (the holding leg's `newRunId`, when the store can attribute it) goes into that
|
|
82
|
+
* error text so an operator can find the run that is actually still going. `holder` is store-supplied text
|
|
83
|
+
* and is charset-clamped before it lands in a message.
|
|
84
|
+
* - IDEMPOTENT for the same holder: a re-claim by the SAME `newRunId` on a claim it already holds MUST be
|
|
85
|
+
* granted (a retry must not deadlock a run against itself).
|
|
86
|
+
* - `scope` is the journal scope passed through VERBATIM ([1981]). A claim is keyed by `(sourceRunId, scope)`,
|
|
87
|
+
* NOT by `sourceRunId` alone: a cross-scope caller already resolves to an EMPTY journal (the CORE-9 wall
|
|
88
|
+
* above) so it cannot fork anything, and letting it take a global claim would hand any tenant a
|
|
89
|
+
* denial-of-resume lever over another's runs.
|
|
90
|
+
* - THROWING is a store failure, not a refusal — it propagates and fails the resume. Return `{granted:false}`
|
|
91
|
+
* to refuse.
|
|
92
|
+
* - A GRANT MEANS POSSESSION (RB-356①, codex 异源复审 2026-07-29). `granted: true` may only be returned once
|
|
93
|
+
* the claim is RECORDED where every other contender will see it; a store that cannot tell whether it holds
|
|
94
|
+
* the claim must refuse, with `holder` omitted if it cannot attribute the winner. The asymmetry is the
|
|
95
|
+
* reason: an unearned refusal fails one resume loudly and is recoverable, an unearned grant forks a run
|
|
96
|
+
* and its duplicated side effects are not.
|
|
97
|
+
* - ACQUISITION MUST BE ATOMIC (RB-354). Contenders must never be able to observe a claim MID-ACQUISITION
|
|
98
|
+
* and read that state as "no claim" — an implementation that reserves the key first and fills the record
|
|
99
|
+
* in a second step needs the reservation itself to be the exclusion (SQL: one INSERT; core's file store:
|
|
100
|
+
* write a temp, then `link` it into place). Note this interacts with crash recovery: any rule of the form
|
|
101
|
+
* "an incomplete record is dead, take it over" is only safe if an in-flight acquisition can never look
|
|
102
|
+
* incomplete.
|
|
103
|
+
*
|
|
104
|
+
* TWO INVALIDATION DOMAINS, each covering what the other cannot ([1981], settled with server):
|
|
105
|
+
* 1. ENGINE release — the normal path, prompt, covers every terminal outcome of a live engine.
|
|
106
|
+
* 2. STORE TTL — the backstop for an engine that DIED holding a claim (crash/kill/replica loss): it never
|
|
107
|
+
* reaches its `finally`, so only a store-side expiry can ever free that row. Server's SQL twins land this
|
|
108
|
+
* as the bake-store `idem_key UNIQUE` + TTL shape; core's file store carries the same TTL semantics.
|
|
109
|
+
* A store implementing neither expiry nor release would wedge the source run's resume permanently.
|
|
110
|
+
*/
|
|
15
111
|
resumeClaim?(input: ResumeClaimArgs): Promise<{
|
|
16
112
|
granted: boolean;
|
|
17
113
|
holder?: string;
|
|
18
114
|
}>;
|
|
115
|
+
/**
|
|
116
|
+
* RB-242 ([1981]) — release the claim {@link resumeClaim} granted to `newRunId` on `(sourceRunId, scope)`.
|
|
117
|
+
* Called by the engine at the resuming run's TERMINAL boundary (in a `finally`, so abort/error/abandon are
|
|
118
|
+
* covered), and ONLY when the matching claim was actually granted.
|
|
119
|
+
*
|
|
120
|
+
* MUST be idempotent and MUST NOT release a claim the caller does not hold: the claim may already be gone
|
|
121
|
+
* (TTL-reaped after a stall, then taken over by another leg), and unlinking the successor's claim would
|
|
122
|
+
* re-open exactly the fork this seam exists to prevent. A missing claim is a no-op, never an error.
|
|
123
|
+
*
|
|
124
|
+
* A throw here is swallowed by the engine — a release failure must never replace the run's real outcome, and
|
|
125
|
+
* the store's TTL is the standing backstop for the row it left behind.
|
|
126
|
+
*
|
|
127
|
+
* RB-355 (codex 异源复审 2026-07-29): the engine's terminal boundary now BOUNDS this call (and its wait on
|
|
128
|
+
* the {@link resumeClaim} verdict) by a timeout — a store that hangs would otherwise keep the run's `done`
|
|
129
|
+
* promise pending forever, after the outcome is already decided. A release that outlives that bound is
|
|
130
|
+
* treated exactly like one that throws: swallowed, with the TTL as the backstop. Implementations should not
|
|
131
|
+
* rely on the engine waiting indefinitely for cleanup.
|
|
132
|
+
*/
|
|
19
133
|
releaseResumeClaim?(input: ResumeClaimArgs): Promise<void>;
|
|
20
134
|
}
|
|
135
|
+
/** The ordinal a callKey (`ordinal:hash`) was minted at — the journal's positional index. The sole producer
|
|
136
|
+
* ({@link workflowAgentCallKey}) always emits a well-formed `<int>:<hash>`; a MALFORMED key (no leading int)
|
|
137
|
+
* falls back to `0` rather than throwing — callers must not feed it arbitrary strings (audit note). */
|
|
21
138
|
export declare const MAX_JOURNAL_RESULT_BYTES: number;
|
|
139
|
+
/** True when a serialized journal result is too large to store (UTF-8 byte length over {@link MAX_JOURNAL_RESULT_BYTES}). */
|
|
22
140
|
export declare function oversizeJournalResult(serialized: string): boolean;
|
|
23
141
|
export declare function callKeyOrdinal(callKey: string): number;
|
|
142
|
+
/** RB-243 ([1937]): the errorCode a journal OVERSIZE TOMBSTONE carries — the resume path recognizes it to
|
|
143
|
+
* say WHY an ordinal never replays. `failed` + this code lands on the T2A-4 no-replay arm, so replay
|
|
144
|
+
* behavior is identical to the entry simply being absent (the pre-tombstone shape). */
|
|
24
145
|
export declare const JOURNAL_OVERSIZE_ERROR_CODE = "workflow.journal_oversize";
|
|
146
|
+
/** RB-243 ([1937]): mint the tiny FAILED entry the ENGINE journals in place of an oversize result. The
|
|
147
|
+
* tombstone is minted in the engine, NOT in a store — the stores' skip semantics are unchanged (the
|
|
148
|
+
* §4.1 backend-parity pin depends on that layering; a store-side tombstone would re-create the RB-168
|
|
149
|
+
* dialect divergence). Never copies `result.result`/`structuredOutput` (the oversize payload); keeps
|
|
150
|
+
* taskId/sessionId/stats so the journal read-face still shows the session handle and the paid spend. */
|
|
25
151
|
export declare function journalOversizeTombstone(result: TaskResult, bytes: number): TaskResult;
|
|
152
|
+
/**
|
|
153
|
+
* RB-367 ([2054] test AI dual-process probe, core-side judgment) — the TTL a resume claim {@link
|
|
154
|
+
* InMemoryWorkflowJournalStore} grants survives WITHOUT an engine release: the backstop for an engine that
|
|
155
|
+
* abandoned a granted claim without ever reaching the terminal `finally` that releases it (a bug, not the
|
|
156
|
+
* normal path — the normal path releases promptly). Same VALUE as the file store's `RESUME_CLAIM_TTL_MS`
|
|
157
|
+
* (`src/stores/file/workflow-journal-store.ts`) — kept as an independent constant rather than imported from
|
|
158
|
+
* it, because `core/` is upstream of `stores/file/` (the file store already imports FROM this module; the
|
|
159
|
+
* reverse would invert that layering) and an in-memory store's claims die with the process regardless, so the
|
|
160
|
+
* two constants have no shared-drift risk to guard against by literally sharing one declaration.
|
|
161
|
+
*/
|
|
26
162
|
export declare const IN_MEMORY_RESUME_CLAIM_TTL_MS: number;
|
|
163
|
+
/** In-process journal store (core default). A single-replica run; durable resume across crashes needs the
|
|
164
|
+
* service's Postgres store (SVC-2). Records each run's `scope` and enforces it on load (CORE-9 audit BLOCKER:
|
|
165
|
+
* a cross-scope resumeFromRunId resolves to an empty journal — no cross-tenant disclosure).
|
|
166
|
+
*
|
|
167
|
+
* RB-367: also carries a REAL {@link WorkflowJournalStore.resumeClaim}/{@link
|
|
168
|
+
* WorkflowJournalStore.releaseResumeClaim} implementation (this store used to be the canonical example of a
|
|
169
|
+
* store WITHOUT the seam — its own header said so). Left unimplemented, the DEFAULT deployment (no
|
|
170
|
+
* `journalStore` supplied, or this store supplied explicitly) had NO protection against two same-process
|
|
171
|
+
* `resumeFromRunId` calls off one source forking its execution — the class of bug RB-242 exists to close, and
|
|
172
|
+
* a test AI's dual-process probe found it wide open on the default path while the file store's half already
|
|
173
|
+
* worked. A single-process store's promise is necessarily single-process: this implementation gives no
|
|
174
|
+
* cross-process guarantee (there is no shared medium to publish a claim on), so a multi-process deployment
|
|
175
|
+
* still needs a store that IS durable/shared (the file or SQL stores) for the seam to mean anything beyond
|
|
176
|
+
* this one process. */
|
|
27
177
|
export declare class InMemoryWorkflowJournalStore implements WorkflowJournalStore {
|
|
28
178
|
private readonly runs;
|
|
179
|
+
/** RB-367 resume claims, nested by scope then sourceRunId (rather than a single string-concatenated key) so
|
|
180
|
+
* untrusted-length `scope`/`sourceRunId` text (`sourceRunId` is `resumeFromRunId` — LLM-controlled) can never
|
|
181
|
+
* collide across the boundary between the two fields. */
|
|
29
182
|
private readonly resumeClaims;
|
|
30
183
|
private claimsForScope;
|
|
184
|
+
/** RB-367 — contract parity with the file store: keyed by `(sourceRunId, scope)`; idempotent re-grant to the
|
|
185
|
+
* SAME holder; an unexpired claim held by someone else refuses (naming the holder); an expired or absent
|
|
186
|
+
* claim is free to take. Acquisition is a single synchronous Map read-then-write with no `await` between
|
|
187
|
+
* the check and the publish — there is no window (matching the atomicity RB-354 requires of a durable
|
|
188
|
+
* implementation) in which a concurrent caller could observe the key as free mid-acquire. */
|
|
31
189
|
resumeClaim(input: ResumeClaimArgs): Promise<{
|
|
32
190
|
granted: boolean;
|
|
33
191
|
holder?: string;
|
|
34
192
|
}>;
|
|
193
|
+
/** RB-367 — release ONLY a claim `newRunId` still holds (a missing/foreign/already-taken-over claim is a
|
|
194
|
+
* no-op, never an error — matches the file store's contract). */
|
|
35
195
|
releaseResumeClaim(input: ResumeClaimArgs): Promise<void>;
|
|
36
196
|
load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
|
|
37
197
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { type WorkflowRunStore } from "./workflow-run-store.js";
|
|
2
2
|
import type { WorkflowRun, WorkflowRunStatus } from "../orchestration/workflow.js";
|
|
3
|
+
/**
|
|
4
|
+
* design/97 S1b/§D.8 — the **runner-agnostic** cross-backend contract for {@link WorkflowRunStore}, so the
|
|
5
|
+
* SAME assertions run over every backend AND **service can import it to verify its own PG implementation**
|
|
6
|
+
* (§D.8: "contract 函数须 service 可复用,放可被 import 的位置,非纯 test/ 私有").
|
|
7
|
+
*
|
|
8
|
+
* It lives in `src/` (compiled into `dist/`, re-exported from the package barrel) and uses Node's built-in
|
|
9
|
+
* `assert` — NO vitest dependency — so it runs under any harness (vitest, node:test, a bare script). Core's
|
|
10
|
+
* `test/cross-backend-shared.ts` wraps {@link workflowRunStoreContract} in a single vitest `it()` to bind it
|
|
11
|
+
* to the InMemory + File backends; service binds it to its PG store.
|
|
12
|
+
*
|
|
13
|
+
* The fixtures pin `createdAt`/`endedAt` so the `listByScope` ordering + `reap` retention are deterministic
|
|
14
|
+
* (no wall clock). `make()` must return a FRESH, empty store each call.
|
|
15
|
+
*/
|
|
16
|
+
/** Build a {@link WorkflowRun} fixture (terminal `completed` by default). All fields explicit + deterministic. */
|
|
3
17
|
export declare function createWorkflowRun(over?: Partial<WorkflowRun>): WorkflowRun;
|
|
18
|
+
/**
|
|
19
|
+
* The full cross-backend contract for {@link WorkflowRunStore}. `make` returns a fresh store; `runAssertion`
|
|
20
|
+
* lets a harness label/await each case (a vitest binding passes `it`; a bare runner can pass a sequential
|
|
21
|
+
* runner). The DEFAULT runs the cases inline and sequentially (so `service` can call
|
|
22
|
+
* `await workflowRunStoreContract(make)` with no harness at all).
|
|
23
|
+
*
|
|
24
|
+
* ⚠️ Binding note (service [390]): the cases are REGISTERED eagerly and awaited via `Promise.all` — an async
|
|
25
|
+
* `runAssertion` that schedules work concurrently will interleave cases. Bindings whose stores share state
|
|
26
|
+
* (e.g. SQL twins on one table) must CHAIN the assertions themselves (serialize inside `runAssertion`).
|
|
27
|
+
*/
|
|
4
28
|
export declare function workflowRunStoreContract(make: () => WorkflowRunStore, runAssertion?: (name: string, fn: () => Promise<void>) => void): Promise<void>;
|
|
29
|
+
/** Re-export so a contract caller can label runs by status without re-importing the run module. */
|
|
5
30
|
export type { WorkflowRunStatus };
|
|
@@ -1,47 +1,165 @@
|
|
|
1
1
|
import type { WorkflowRun, WorkflowRunStatus } from "../orchestration/workflow.js";
|
|
2
|
+
/**
|
|
3
|
+
* design/97 S1b — the **WorkflowRunStore** persistence seam for the workflow-mode primitive
|
|
4
|
+
* (`src/orchestration/workflow.ts`, S1a). It is the durable home for a {@link WorkflowRun} so the
|
|
5
|
+
* `/workflows` observability layer (S1c) can list runs IN PROGRESS *and the history* (completed / failed),
|
|
6
|
+
* across replicas — the "看之前的" core seam (design/97 §A/§D.1).
|
|
7
|
+
*
|
|
8
|
+
* **Distinct from the four load-bearing durable seams** (session / checkpoint / memory / tool-result): those
|
|
9
|
+
* are the engine's suspend/resume命脉, so core ships PG adapters for them. A WorkflowRunStore is an
|
|
10
|
+
* **observation layer — opt-in, non-engine-critical** — so per design/97 §D.8 (clay 2026-06-22 拍) core
|
|
11
|
+
* ships ONLY: this interface + {@link InMemoryWorkflowRunStore} (process-local / default / tests) +
|
|
12
|
+
* `FileWorkflowRunStore` (zero-dependency, TOC-local persistence — `src/stores/file/workflow-run-store.ts`).
|
|
13
|
+
* **The PG backend lives in service (TOB), implementing THIS interface** when it needs cross-replica
|
|
14
|
+
* `/workflows` history. `WorkflowRun` / `WorkflowRunStatus` are imported type-only — this module is purely
|
|
15
|
+
* the store contract + projection, it does NOT re-home the run model (that stays in `workflow.ts`).
|
|
16
|
+
*
|
|
17
|
+
* Mirrors the {@link import("./checkpoint-store.js").CheckpointStore} shape: create-once `put`, `get`, an
|
|
18
|
+
* **atomic CAS `update`** (scope + `rev` optimistic-concurrency — concurrent agents racing to record progress
|
|
19
|
+
* on the same run), a `listByScope` returning lightweight {@link WorkflowRunSummary} projections (history
|
|
20
|
+
* included, NOT pending-only like CheckpointStore.listByScope), and a `reap` retention sweep that only ever
|
|
21
|
+
* deletes TERMINAL runs (never a running one). The {@link summarizeWorkflowRun} projection is SHARED across
|
|
22
|
+
* all backends (anti-drift, exactly like `summarizeCheckpoint`).
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* A lightweight, read-only projection of one {@link WorkflowRun} returned in bulk by
|
|
26
|
+
* {@link WorkflowRunStore.listByScope} so the `/workflows` list view (CLI design/92 / client design/90) can
|
|
27
|
+
* enumerate a scope's runs in ONE call — no N+1 `get`s, no full run payloads. Every field is DERIVED from
|
|
28
|
+
* the persisted run (nothing here is a new source of truth or a gate input). Built by the shared
|
|
29
|
+
* {@link summarizeWorkflowRun} so the projection is IDENTICAL across the in-memory, file, and (service) PG
|
|
30
|
+
* backends.
|
|
31
|
+
*/
|
|
2
32
|
export interface WorkflowRunSummary {
|
|
33
|
+
/** The run id ({@link WorkflowRun.id}). */
|
|
3
34
|
id: string;
|
|
35
|
+
/** The tenant / grouping key this run lives in ({@link WorkflowRun.scope}). */
|
|
4
36
|
scope: string;
|
|
37
|
+
/** γ 批 ([1510] server request): the ORIGINATING session id ({@link WorkflowRun.originatingSessionId}),
|
|
38
|
+
* projected so a serving layer's LIST face can apply its session acceptance/filter arm without an
|
|
39
|
+
* N+1 `get` of the full run. Absent for a direct `runWorkflow` call / sessionless deployment. */
|
|
5
40
|
originatingSessionId?: string;
|
|
41
|
+
/** design/99 MF-W: the workflow's display name from the script's `export const meta` ({@link WorkflowRun.name}).
|
|
42
|
+
* Projected here so the `/workflows` list view labels each row WITHOUT an N+1 `get` of the full run.
|
|
43
|
+
* Absent for a direct `runWorkflow` call that supplied no name. */
|
|
6
44
|
name?: string;
|
|
45
|
+
/** One-line description from the script's meta ({@link WorkflowRun.description}); absent if none. */
|
|
7
46
|
description?: string;
|
|
47
|
+
/** Lifecycle status ({@link WorkflowRun.status}): running / completed / failed. */
|
|
8
48
|
status: WorkflowRunStatus;
|
|
49
|
+
/** Count of agent-runs that ended failed ({@link WorkflowRun.agentFailures}) — present only when > 0.
|
|
50
|
+
* Lets a list view flag a "completed, with failures" run without an N+1 `get` of the full run. */
|
|
9
51
|
agentFailures?: number;
|
|
52
|
+
/** Title of the latest phase recorded ({@link WorkflowRun.phases}`.at(-1).title`) — what the run is on RIGHT
|
|
53
|
+
* NOW for a `running` row (so the `/workflows` list shows the live phase without subscribing to the event
|
|
54
|
+
* stream). Absent when no phase has started yet. */
|
|
10
55
|
currentPhase?: string;
|
|
56
|
+
/** Number of phases recorded so far ({@link WorkflowRun.phases}`.length`). */
|
|
11
57
|
phaseCount: number;
|
|
58
|
+
/** Number of agent-runs recorded so far ({@link WorkflowRun.agents}`.length`). */
|
|
12
59
|
agentCount: number;
|
|
60
|
+
/** Total tokens spent = own + nested (`stats.tokens + stats.nested.tokens`) — the figure a triage view
|
|
61
|
+
* sorts/compares by. own/nested stay SEPARATE on the full run (R-5); the summary folds them for display. */
|
|
13
62
|
tokens: number;
|
|
63
|
+
/** When the run started ({@link WorkflowRun.startedAt}, epoch ms). */
|
|
14
64
|
startedAt: number;
|
|
65
|
+
/** When the run finished ({@link WorkflowRun.endedAt}, epoch ms) — absent while still running. */
|
|
15
66
|
endedAt?: number;
|
|
67
|
+
/** When the run record was created ({@link WorkflowRun.createdAt}, epoch ms) — the `listByScope` sort key. */
|
|
16
68
|
createdAt: number;
|
|
17
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* design/97 S1b: project a {@link WorkflowRun} to its lightweight {@link WorkflowRunSummary}. SHARED by every
|
|
72
|
+
* {@link WorkflowRunStore} impl so the projection is IDENTICAL across the in-memory, file, and (service) PG
|
|
73
|
+
* backends (the anti-drift guard — exactly like `summarizeCheckpoint`). Pure; reads no clock/random. Total
|
|
74
|
+
* spend folds own + nested (`stats.tokens + stats.nested.tokens`), the one place the two are summed.
|
|
75
|
+
*/
|
|
18
76
|
export declare function summarizeWorkflowRun(run: WorkflowRun): WorkflowRunSummary;
|
|
77
|
+
/** Whether a run is in a TERMINAL state — the ONLY states {@link WorkflowRunStore.reap} may delete (a
|
|
78
|
+
* `running` run is never reaped, so an in-flight workflow's record can't vanish out from under it). */
|
|
19
79
|
export declare function isTerminalWorkflowStatus(status: WorkflowRunStatus): boolean;
|
|
80
|
+
/** A typed workflow-run-store error so callers branch on `code` (mirrors `CheckpointError`). */
|
|
20
81
|
export declare class WorkflowRunStoreError extends Error {
|
|
21
82
|
readonly code: "workflow_run.already_exists";
|
|
22
83
|
constructor(code: "workflow_run.already_exists", message: string);
|
|
23
84
|
}
|
|
85
|
+
/** REF-D18: `listByScope`'s query options — the SINGLE shape the interface method and both the
|
|
86
|
+
* In-Memory and File implementations reference. */
|
|
24
87
|
export interface WorkflowRunListByScopeOptions {
|
|
25
88
|
status?: WorkflowRunStatus;
|
|
26
89
|
limit?: number;
|
|
27
90
|
session?: string;
|
|
28
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* The pluggable persistence seam for workflow runs (design/97 S1b). Symmetric with `CheckpointStore`:
|
|
94
|
+
* create-once `put`, `get`, an atomic CAS `update`, a `listByScope` query (history included), and a `reap`
|
|
95
|
+
* retention sweep. A durable backend (service's PG) makes `/workflows` history cross-replica; the default
|
|
96
|
+
* {@link InMemoryWorkflowRunStore} is process-only (single instance / tests).
|
|
97
|
+
*/
|
|
29
98
|
export interface WorkflowRunStore {
|
|
99
|
+
/** Create-once. Throws {@link WorkflowRunStoreError} `already_exists` on a token collision (never a silent
|
|
100
|
+
* overwrite — a reused id would clobber a live run's record). */
|
|
30
101
|
put(id: string, run: WorkflowRun): Promise<void>;
|
|
31
102
|
get(id: string): Promise<WorkflowRun | null>;
|
|
103
|
+
/**
|
|
104
|
+
* Atomic CAS update of an EXISTING run (`UPDATE … SET … WHERE id=? AND scope=?` [`AND rev=?`]). Returns
|
|
105
|
+
* `true` for the winner, `false` when the row is missing, the `scope` does not match (multi-tenant
|
|
106
|
+
* isolation — a wrong-scope update must not win), or `expect.rev` does not match the live row's `rev`
|
|
107
|
+
* (an intervening update bumped it). On success the store bumps the row's `rev` (monotonic) and persists
|
|
108
|
+
* the new `run`. The workflow owner is the sole writer (best-effort progress recording — design/97 §F),
|
|
109
|
+
* so `expect.rev` lets it detect a lost write rather than silently clobber.
|
|
110
|
+
*/
|
|
32
111
|
update(id: string, scope: string, run: WorkflowRun, expect?: {
|
|
33
112
|
rev: number;
|
|
34
113
|
}): Promise<boolean>;
|
|
114
|
+
/**
|
|
115
|
+
* List lightweight {@link WorkflowRunSummary} projections for runs in `scope` — **history included** (the
|
|
116
|
+
* completed / failed runs, NOT pending-only like `CheckpointStore.listByScope`), so this is the "看之前的"
|
|
117
|
+
* core query. Newest first (`createdAt` DESC). `opts.status` filters to one lifecycle state; `opts.limit`
|
|
118
|
+
* caps the result count (applied AFTER the sort, so it keeps the newest N). An empty scope returns `[]`.
|
|
119
|
+
*/
|
|
120
|
+
/** `opts.session` ([1513] server ask, δ 尾件): STORE-side push-down filter on
|
|
121
|
+
* `originatingSessionId` — strict equality, rows WITHOUT the field excluded (filter semantics,
|
|
122
|
+
* deliberately unlike the per-id acceptance arm's absent-pass). Applied BEFORE limit, so the
|
|
123
|
+
* serving layer's "this session's workflows" is FULL-history, not page-window ([1513] cli note). */
|
|
35
124
|
listByScope(scope: string, opts?: WorkflowRunListByScopeOptions): Promise<WorkflowRunSummary[]>;
|
|
125
|
+
/**
|
|
126
|
+
* Retention sweep: delete OLD **terminal** runs in `scope` (a `running` run is NEVER deleted — design/97
|
|
127
|
+
* §D.1/§F). Returns the count deleted (for metrics). Retention is by either policy (apply BOTH when both
|
|
128
|
+
* given — a run is reaped if it fails either bound):
|
|
129
|
+
* - `opts.maxAgeMs`: delete a terminal run whose `endedAt` (else `createdAt`) is older than `now -
|
|
130
|
+
* maxAgeMs`.
|
|
131
|
+
* - `opts.keep`: keep only the newest `keep` terminal runs (by `createdAt` DESC); delete the rest.
|
|
132
|
+
* With NEITHER option set, nothing is deleted (a no-op `0`) — retention is always an explicit policy.
|
|
133
|
+
*/
|
|
36
134
|
reap(scope: string, now: number, opts?: {
|
|
37
135
|
maxAgeMs?: number;
|
|
38
136
|
keep?: number;
|
|
39
137
|
}): Promise<number>;
|
|
40
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* REF-A5 — THE optimistic-concurrency guard for {@link WorkflowRunStore.update}, shared by every backend:
|
|
141
|
+
* scope in the WHERE (multi-tenant isolation — a wrong-scope update never wins), row must exist, and an
|
|
142
|
+
* `expect.rev` that no longer matches the LIVE row loses (an intervening update bumped it). Returns the
|
|
143
|
+
* row to persist, or `null` when the CAS lost; PURE — it neither reads nor writes any store state, so
|
|
144
|
+
* each backend keeps its own persistence mechanism (in-memory `Map.set` vs ledger commit) around it.
|
|
145
|
+
* RB-59 (a cross-instance CAS both instances won) landed on this predicate.
|
|
146
|
+
*/
|
|
41
147
|
export declare function nextWorkflowRunOnUpdate(cur: WorkflowRun | undefined, id: string, scope: string, run: WorkflowRun, expect?: {
|
|
42
148
|
rev: number;
|
|
43
149
|
}): WorkflowRun | null;
|
|
150
|
+
/**
|
|
151
|
+
* REF-A5 — THE `listByScope` query projection, shared by every backend: scope/status/session filter →
|
|
152
|
+
* newest-first by `createdAt` → truncate → {@link summarizeWorkflowRun}. Pure over any row iterable, so
|
|
153
|
+
* the in-memory backend passes its `Map` values and the file backend passes the replayed ledger's.
|
|
154
|
+
*/
|
|
44
155
|
export declare function queryWorkflowRuns(runs: Iterable<WorkflowRun>, scope: string, opts?: WorkflowRunListByScopeOptions): WorkflowRunSummary[];
|
|
156
|
+
/**
|
|
157
|
+
* Default in-process {@link WorkflowRunStore}. Single-instance / tests only — it does NOT survive a restart
|
|
158
|
+
* or span replicas. Single-threaded JS already serializes `update`, so the CAS is trivially atomic here;
|
|
159
|
+
* `structuredClone` on the boundaries prevents aliasing (a later mutation of the caller's object — the live
|
|
160
|
+
* `WorkflowRun` the workflow keeps mutating — must not corrupt the stored row, and a returned run must not
|
|
161
|
+
* be a live reference the caller can mutate back into the store).
|
|
162
|
+
*/
|
|
45
163
|
export declare class InMemoryWorkflowRunStore implements WorkflowRunStore {
|
|
46
164
|
private runs;
|
|
47
165
|
put(id: string, run: WorkflowRun): Promise<void>;
|
|
@@ -54,5 +172,6 @@ export declare class InMemoryWorkflowRunStore implements WorkflowRunStore {
|
|
|
54
172
|
maxAgeMs?: number;
|
|
55
173
|
keep?: number;
|
|
56
174
|
}): Promise<number>;
|
|
175
|
+
/** Test/inspection helper: number of stored runs. */
|
|
57
176
|
get size(): number;
|
|
58
177
|
}
|