@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,34 +1,134 @@
|
|
|
1
1
|
import { type WorkflowJournalEntry, type WorkflowJournalStore, type ResumeClaimArgs } from "../../core/workflow-journal-store.js";
|
|
2
|
+
/**
|
|
3
|
+
* SVC-2 — the shared size bound for a journaled workflow-agent result, so ALL backends DEGRADE IDENTICALLY on
|
|
4
|
+
* an oversize TaskResult (no dialect divergence). Inlined from service's `workflow-journal-limits` on
|
|
5
|
+
* onboarding — the 5 MiB figure aligns with TiDB's ~6 MiB per-row txn-entry-size-limit (a row
|
|
6
|
+
* over it is REJECTED with "entry too large"); a LOAD-BEARING `append` that hit that would THROW and kill the
|
|
7
|
+
* whole workflow. PostgreSQL TEXT has no such low limit, so without a shared guard the same large result would
|
|
8
|
+
* journal fine on PG but fail the workflow on TiDB. All backends instead skip-journal a result whose serialized
|
|
9
|
+
* size exceeds {@link MAX_JOURNAL_RESULT_BYTES} (a no-op, never an insert/throw): the agent's ordinal is simply
|
|
10
|
+
* not cached, so a resume re-runs that one agent live. Resume is an optimization — a too-big-to-cache result is
|
|
11
|
+
* a graceful degrade, not a failure. Measured in BYTES (UTF-8) — a multibyte result can be larger than its
|
|
12
|
+
* `.length` in chars.
|
|
13
|
+
*/
|
|
2
14
|
export { MAX_JOURNAL_RESULT_BYTES, oversizeJournalResult } from "../../core/workflow-journal-store.js";
|
|
15
|
+
/**
|
|
16
|
+
* RB-242 — how long a resume claim survives WITHOUT an engine release. This is the backstop for an engine that
|
|
17
|
+
* died holding one (crash / kill -9 / box reboot): it never reaches its `finally`, so nothing but expiry can
|
|
18
|
+
* ever free the row, and a source run whose resume is wedged forever is a worse failure than the fork this
|
|
19
|
+
* guards. One hour is the balance: comfortably longer than the gap between a resume's admission and its first
|
|
20
|
+
* journal write (the window in which a fork actually duplicates work), and short enough that a crashed box does
|
|
21
|
+
* not lock a run out for a working day. A LIVE engine's own release is the normal path and is prompt — this
|
|
22
|
+
* value is only ever reached by a run that is no longer running.
|
|
23
|
+
*/
|
|
3
24
|
export declare const RESUME_CLAIM_TTL_MS: number;
|
|
25
|
+
/**
|
|
26
|
+
* RB-356② — a release REFRAINS from unlinking its own claim once the claim is this close to (or past) its
|
|
27
|
+
* expiry. Rationale: a peer may legitimately take a claim over the instant it expires, and `release` is a
|
|
28
|
+
* read-then-unlink pair with no compare-and-delete syscall between them — so the one situation where the
|
|
29
|
+
* unlink could destroy a SUCCESSOR's claim is precisely the one where the record we just read is already
|
|
30
|
+
* takeable. Refusing to unlink there is free: an expired claim blocks nobody (the next acquirer takes it over)
|
|
31
|
+
* and the sweep below reaps it. The margin only has to cover the scheduling gap between this method's read and
|
|
32
|
+
* its unlink; seconds is orders of magnitude more than that, and losing the last five seconds of a one-hour
|
|
33
|
+
* lease costs nothing.
|
|
34
|
+
*/
|
|
4
35
|
export declare const RESUME_CLAIM_RELEASE_GRACE_MS = 5000;
|
|
36
|
+
/**
|
|
37
|
+
* RB-356③ — how often (per store instance) an acquisition also SWEEPS the claims directory. The header's
|
|
38
|
+
* "the TTL reaps any orphan" was an aspiration, not a mechanism: nothing ever reaped anything. An expired
|
|
39
|
+
* claim was only removed if a LATER resume off the SAME `(sourceRunId, scope)` came along to take it over —
|
|
40
|
+
* and the normal shape is exactly one resume per source run, so a crash (or any release that could not
|
|
41
|
+
* complete) leaked one small file per source run for the lifetime of the data root. The sweep makes the
|
|
42
|
+
* comment true. Throttled because it is a `readdir` on the claims directory: it is opportunistic hygiene, not
|
|
43
|
+
* a correctness path (takeover, not the sweep, is what unblocks a contended claim promptly).
|
|
44
|
+
*/
|
|
5
45
|
export declare const RESUME_CLAIM_SWEEP_INTERVAL_MS = 60000;
|
|
46
|
+
/**
|
|
47
|
+
* RB-356③ — the sweep only reaps a claim that has been expired for at least this long. A just-expired claim
|
|
48
|
+
* is the exclusive business of whoever is taking it over right now; the sweep has no deadline to meet, so it
|
|
49
|
+
* stays well clear of that handover rather than adding a second unlinker to it.
|
|
50
|
+
*/
|
|
6
51
|
export declare const RESUME_CLAIM_REAP_GRACE_MS: number;
|
|
7
52
|
export declare class FileWorkflowJournalStore implements WorkflowJournalStore {
|
|
8
53
|
private readonly fsyncEnabled;
|
|
9
54
|
private readonly dir;
|
|
55
|
+
/** RB-242: resume claims live in their own subdirectory so a claim file can never be mistaken for a run
|
|
56
|
+
* ledger by anything that enumerates `workflow-journal/` (GC, the torn-tail replay, an operator's `ls`). */
|
|
10
57
|
private readonly claimsDir;
|
|
11
58
|
private readonly shared;
|
|
12
59
|
private readonly sharedKey;
|
|
13
60
|
private closed;
|
|
61
|
+
/** RB-356③: last {@link sweepExpiredClaims} timestamp — throttles the reap to one `readdir` per interval.
|
|
62
|
+
* Per INSTANCE, not per directory: the sweep is idempotent hygiene, so a second instance doing its own
|
|
63
|
+
* first sweep is harmless (unlike the fd/scope caches above, which are authority over the directory). */
|
|
14
64
|
private lastClaimSweepAt;
|
|
65
|
+
/** RB-61: one AppendLog per run (torn-tail truncation at open) — replaces the raw fd map. */
|
|
15
66
|
private get logs();
|
|
67
|
+
/** Per-run committed scope, cached at first touch — the append-path scope guard must NOT re-read the whole
|
|
68
|
+
* ledger per append (a journaled result can be MBs; a many-agent workflow would go O(n²) on disk). One replay
|
|
69
|
+
* discovers it; every later append compares against the cache. */
|
|
16
70
|
private get scopes();
|
|
17
71
|
constructor(root: string, fsyncEnabled?: boolean);
|
|
18
72
|
private pathFor;
|
|
73
|
+
/** [1409]③ CC local form: the run's on-disk journal path for the notification diagnostics line.
|
|
74
|
+
* Traversal-shaped ids (LLM-controlled on some paths) degrade to undefined, never throw. */
|
|
19
75
|
locator(runId: string): string | undefined;
|
|
76
|
+
/** Replay a run's ledger: last-writer-wins per ordinal; a torn/unparsable line is skipped (uncommitted). */
|
|
20
77
|
private replay;
|
|
21
78
|
append(runId: string, scope: string, entry: WorkflowJournalEntry): Promise<void>;
|
|
22
79
|
load(runId: string, scope: string): Promise<WorkflowJournalEntry[]>;
|
|
80
|
+
/**
|
|
81
|
+
* RB-242 — the claim file for `(sourceRunId, scope)`. Keyed by BOTH ([1981]): a cross-scope caller already
|
|
82
|
+
* resolves to an empty journal (it cannot fork anything), so letting it share one claim key would only hand
|
|
83
|
+
* any tenant a denial-of-resume lever over another's runs. The scope is folded in as a digest rather than
|
|
84
|
+
* a sanitized literal so two distinct scopes can never collapse onto one filename by sanitization.
|
|
85
|
+
* Throws (via `sanitizePathComponent`) on a traversal-shaped runId — callers degrade, see below.
|
|
86
|
+
*/
|
|
23
87
|
private claimPathFor;
|
|
88
|
+
/**
|
|
89
|
+
* Read a claim file, KEEPING the exact bytes it was parsed from. A missing / truncated / malformed record
|
|
90
|
+
* reads as NO claim (takeable).
|
|
91
|
+
*
|
|
92
|
+
* RB-356② — the raw bytes are the record's identity token. POSIX has no compare-and-delete, so the only way
|
|
93
|
+
* this store can check "the file I decided about is still the file on disk" immediately before removing one
|
|
94
|
+
* is to re-read and compare (see {@link unlinkIfUnchanged}). Since RB-354 a claim is published whole, so
|
|
95
|
+
* "unparsable" no longer includes "a peer is mid-acquire" — it means external truncation/corruption, which
|
|
96
|
+
* genuinely is not a live claim.
|
|
97
|
+
*/
|
|
24
98
|
private readClaimRaw;
|
|
99
|
+
/**
|
|
100
|
+
* RB-356② — remove a claim ONLY if the bytes on disk are still the ones the decision to remove it was made
|
|
101
|
+
* on. This narrows the decide→delete window to two adjacent syscalls with no user-space work between them,
|
|
102
|
+
* and everything a peer can publish into that window is published atomically (RB-354), so the re-read sees
|
|
103
|
+
* either the bytes we judged (safe to unlink) or a peer's complete new claim (we back off and re-contend).
|
|
104
|
+
* `expectedRaw === undefined` = the file did not parse: there is no live claim to destroy.
|
|
105
|
+
*/
|
|
25
106
|
private unlinkIfUnchanged;
|
|
107
|
+
/**
|
|
108
|
+
* RB-356③ — reap claims nobody can be holding any more (see {@link RESUME_CLAIM_SWEEP_INTERVAL_MS}).
|
|
109
|
+
* Best-effort throughout: a sweep failure must never turn an admission into an error. Also drops the
|
|
110
|
+
* `.tmp` scratch a crash could strand mid-publish, on the same TTL horizon.
|
|
111
|
+
*/
|
|
26
112
|
private sweepExpiredClaims;
|
|
113
|
+
/**
|
|
114
|
+
* RB-242 ([1981]) — admission for a resume off `sourceRunId`. The LOCAL (TOC) twin of server's SQL
|
|
115
|
+
* `idem_key UNIQUE` + TTL: publishing the claim file is one atomic namespace operation that fails EEXIST
|
|
116
|
+
* against anyone who got there first, so two processes racing on the same box (the shape the in-process
|
|
117
|
+
* `startWorkflow` guards cannot see — a shell restart resuming a run a still-live process is already
|
|
118
|
+
* resuming) produce exactly one winner.
|
|
119
|
+
*/
|
|
27
120
|
resumeClaim(input: ResumeClaimArgs): Promise<{
|
|
28
121
|
granted: boolean;
|
|
29
122
|
holder?: string;
|
|
30
123
|
}>;
|
|
124
|
+
/** RB-242 ([1981]) — release ONLY a claim this `newRunId` still holds. A claim that is already gone (TTL-reaped
|
|
125
|
+
* and possibly taken over) is a no-op: unlinking a successor's claim would re-open the very fork this prevents. */
|
|
31
126
|
releaseResumeClaim(input: ResumeClaimArgs): Promise<void>;
|
|
127
|
+
/** GC (service extra, mirrors the SQL twins): drop the run's ledger once the run is terminal + retained. */
|
|
32
128
|
deleteByRun(runId: string): Promise<number>;
|
|
129
|
+
/** Release ledger fds (graceful shutdown; parity with the other file stores' dispose).
|
|
130
|
+
* Refcounted: the fds are shared with any other instance on the same directory, so the LAST holder
|
|
131
|
+
* closes them. Idempotent — a second dispose() on the same instance must not decrement twice and pull
|
|
132
|
+
* the ledger out from under a live peer. */
|
|
33
133
|
dispose(): void;
|
|
34
134
|
}
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { type WorkflowRunListByScopeOptions, type WorkflowRunStore, type WorkflowRunSummary } from "../../core/workflow-run-store.js";
|
|
2
2
|
import type { WorkflowRun } from "../../orchestration/workflow.js";
|
|
3
3
|
export interface FileWorkflowRunStoreOptions {
|
|
4
|
+
/** When false, a ledger append is NOT fsync'd (the observability store is non-engine-critical). Default true. */
|
|
4
5
|
fsync?: boolean;
|
|
6
|
+
/** Compact the ledger into a snapshot once it exceeds this many events (then truncate). Default 1000. */
|
|
5
7
|
compactEvery?: number;
|
|
6
8
|
}
|
|
7
9
|
export declare class FileWorkflowRunStore implements WorkflowRunStore {
|
|
8
10
|
private readonly fsyncEnabled;
|
|
9
11
|
private readonly compactEvery;
|
|
12
|
+
/** RB-59: authoritative state is SHARED per canonical directory (see the note above the class). */
|
|
10
13
|
private readonly ledger;
|
|
14
|
+
/** RB-145: this instance's own close latch — the refcount belongs to the DIRECTORY, not to callers. */
|
|
11
15
|
private closed;
|
|
12
16
|
private get runs();
|
|
13
17
|
constructor(root: string, opts?: FileWorkflowRunStoreOptions);
|
|
18
|
+
/** Serialize an op behind any in-flight op on the same id (the in-process per-id async mutex). */
|
|
14
19
|
private withLock;
|
|
20
|
+
/** Commit one ledger event (append + fsync) THEN flip the in-memory map (crash-safe ordering), then
|
|
21
|
+
* the best-effort housekeeping compaction (RB-150/RB-167) — all three steps in the base. */
|
|
15
22
|
private commit;
|
|
16
23
|
put(id: string, run: WorkflowRun): Promise<void>;
|
|
17
24
|
get(id: string): Promise<WorkflowRun | null>;
|
|
@@ -23,7 +30,11 @@ export declare class FileWorkflowRunStore implements WorkflowRunStore {
|
|
|
23
30
|
maxAgeMs?: number;
|
|
24
31
|
keep?: number;
|
|
25
32
|
}): Promise<number>;
|
|
33
|
+
/** Force a compaction now (test/inspection): rewrite the snapshot from the authoritative map and
|
|
34
|
+
* truncate the live ledger (RB-167's closeForSwap recipe lives in the base). */
|
|
26
35
|
compactNow(): void;
|
|
36
|
+
/** Test/inspection helper: number of stored runs. */
|
|
27
37
|
get size(): number;
|
|
38
|
+
/** Release the append handle (best-effort). The boot LOCK is released by the backend factory. */
|
|
28
39
|
close(): void;
|
|
29
40
|
}
|
|
@@ -1,30 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bare command names `bash_readonly` permits out of the box, ALSO the default reversible set for the
|
|
3
|
+
* design/80 D-2 {@link import("./fs-bash.js").bashReversibilityProbe} classifier. **Coarse first filter, NOT a security
|
|
4
|
+
* boundary**: {@link coarseReadonlyCheck} matches only the command NAME, so a listed command with a
|
|
5
|
+
* writing flag (e.g. `find -delete`, `sort -o`, `tee`) would still write. Such commands are therefore
|
|
6
|
+
* kept OFF this default — the list is curated to commands with NO write/mutation mode under ANY args, so
|
|
7
|
+
* the `effect:"read"` declaration (relied on by wake/resume reconcile + the verifier read-only boundary)
|
|
8
|
+
* AND the classifier's "reversible" promise both stay truthful. The deployment's tool-policy gate is the
|
|
9
|
+
* authoritative control (design/44 §5, council blocker #1); anything that can mutate state or run arbitrary
|
|
10
|
+
* code belongs on the full `bash` (effect:write, gated), not here. A deployment may widen this list,
|
|
11
|
+
* accepting that responsibility.
|
|
12
|
+
*
|
|
13
|
+
* design/80 D-2 final-council MAJOR: `date` (`-s`/`--set` → CLOCK_SETTIME), `hostname` (`<name>` → kernel
|
|
14
|
+
* hostname), and `file` (`-C -m` → compiles/writes a magic file) were REMOVED — each is read-only by NAME
|
|
15
|
+
* but state-MUTATING with args, which an argv[0]-only filter cannot tell apart. Leaving them in defeated
|
|
16
|
+
* both the `effect:read` truthfulness here and the classifier's irreversibility promise (a `date -s` would
|
|
17
|
+
* auto-allow an irreversible clock jump under `shellGate:"classify"`).
|
|
18
|
+
*/
|
|
19
|
+
/** The verdict every unresolvable / out-of-root / unbounded finding ends on. The findings differ in what
|
|
20
|
+
* they found; the consequence is one consequence, and a copy that drifts reads as a second rule. */
|
|
1
21
|
export declare const NOT_AUTO_ALLOWED = "\u2014 not auto-allowed";
|
|
2
22
|
export declare const BASH_READONLY_DEFAULT_ALLOW: readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* G2 — options for {@link parseLeadingCommandName}. Omitting them keeps the parser byte-identical to
|
|
25
|
+
* what every existing caller (the `bash_readonly` declaration face, the coarse command-name policy, the
|
|
26
|
+
* permission-rule floor, the skill tool specifier) has always received.
|
|
27
|
+
*/
|
|
3
28
|
export interface LeadingCommandNameOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Read a shell operator that sits inside a QUOTED region as ordinary argument TEXT rather than as an
|
|
31
|
+
* operator, so `grep -E "a|b" f.log` parses as the single simple command it is.
|
|
32
|
+
*
|
|
33
|
+
* OPT-IN, and used by ONE caller: the compound classify face ({@link classifyCompoundReadonlyDetailed}),
|
|
34
|
+
* which has already rejected the whole command for backslashes / `$` / backticks / subshells / line
|
|
35
|
+
* breaks and so is inside {@link quoteMask}'s exactness envelope. The declaration and rule faces do
|
|
36
|
+
* NOT set it — their contract is "a single simple command with no operator characters at all", which
|
|
37
|
+
* is a spelling rule about what a person can read at a glance, not a claim about bash's grammar.
|
|
38
|
+
*
|
|
39
|
+
* Fail-closed on unbalanced quoting: with a region left open, the mask is not trusted and every
|
|
40
|
+
* operator character counts, exactly as without this option.
|
|
41
|
+
*/
|
|
4
42
|
quotedOperatorsAreText?: boolean;
|
|
5
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The SINGLE fail-closed simple-command parser shared by `bash_readonly` ({@link coarseReadonlyCheck}), the
|
|
46
|
+
* `bash` reversibility classifier ({@link import("./fs-bash.js").bashReversibilityProbe}), and the coarse command-name policy
|
|
47
|
+
* ({@link import("../../core/tool-policy.js").createCoarseCommandNamePolicy}). It extracts the leading
|
|
48
|
+
* `argv[0]` command NAME of a SINGLE simple command, rejecting anything that could chain past or escape an
|
|
49
|
+
* argv[0]-name filter: shell operators (pipes / redirects / `;` / `&&` / `$(…)` / subshells / backticks /
|
|
50
|
+
* newlines / backslash), a path-prefixed command (`/usr/bin/foo`), or a leading env-assignment (`FOO=bar cmd`).
|
|
51
|
+
*
|
|
52
|
+
* Returns `{ name }` for a parseable single bare command, or `{ reject }` with a human reason otherwise. It
|
|
53
|
+
* does NOT inspect ARGUMENTS for write flags or consult any allowlist — that is the caller's job (the
|
|
54
|
+
* allowlist for `bash_readonly`, the allow/deny lists for the coarse policy). Keeping ONE parser is the whole
|
|
55
|
+
* point: a second argv[0] parser would drift from this one and silently open a bypass.
|
|
56
|
+
*/
|
|
6
57
|
export declare function parseLeadingCommandName(command: string, options?: LeadingCommandNameOptions): {
|
|
7
58
|
name: string;
|
|
8
59
|
} | {
|
|
9
60
|
reject: string;
|
|
10
61
|
};
|
|
62
|
+
/** Coarse pre-execution check for `bash_readonly`: the command must be a single bare allowlisted command
|
|
63
|
+
* with no shell operators. It validates the NAME only (+ blocks operators/path-prefix/env-assignment); it
|
|
64
|
+
* does NOT inspect arguments for write flags — keeping the allowlist free of write-capable commands and
|
|
65
|
+
* the policy gate are what make `effect:"read"` truthful. Returns a reason when rejected, else undefined.
|
|
66
|
+
* design/154: this `effect:"read"` DECLARATION face deliberately stays strict-single-command; only the
|
|
67
|
+
* classify face ({@link classifyCompoundReadonly} via {@link import("./fs-bash.js").bashReversibilityProbe}) segments compounds. */
|
|
11
68
|
export declare function coarseReadonlyCheck(command: string, allow: ReadonlySet<string>, options?: LeadingCommandNameOptions): string | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* RB-412 — the READ-BOUNDARY face of the read-only classification (opt-in).
|
|
71
|
+
*
|
|
72
|
+
* The allowlist vets `argv[0]` only, so until this face existed every listed reader could name ANY
|
|
73
|
+
* path: `ls /etc` and `cat /etc/passwd` classified read-only, and a deployment whose manual-mode shell
|
|
74
|
+
* gate auto-allows the read-only verdict served the whole filesystem with no approval prompt. CC 2.1.220
|
|
75
|
+
* demotes exactly these shapes (its per-command path extractor + working-directory containment check,
|
|
76
|
+
* pretty220 @457568) and renders an approval option naming the directory (@905642).
|
|
77
|
+
*
|
|
78
|
+
* Supplying this object turns the check ON; omitting it leaves the classifier byte-identical to its
|
|
79
|
+
* pre-RB-412 behaviour, so a consumer pinned to an older calling convention is unaffected.
|
|
80
|
+
*/
|
|
12
81
|
export interface BashReadonlyRootBoundary {
|
|
82
|
+
/** Canonical containment roots — the primary root plus any additional allowed directories. An EMPTY
|
|
83
|
+
* array means "no directory is allowed", so every path-shaped operand demotes (a bare `cat a.txt`
|
|
84
|
+
* still does not: a name with no separator can only reach the working directory, which is the
|
|
85
|
+
* caller's to govern). A caller that wants the check OFF omits the whole object instead. */
|
|
13
86
|
roots: readonly string[];
|
|
87
|
+
/** Base for resolving relative operands. Defaults to `roots[0]`. Callers tracking an observable `cd`
|
|
88
|
+
* should pass the LIVE working directory, or a `..` operand will be judged against the wrong base. */
|
|
14
89
|
cwd?: string;
|
|
90
|
+
/** Expansion for a leading `~`. Without it a `~`-rooted operand is unresolvable and demotes (with no
|
|
91
|
+
* out-of-root signal — the classifier does not know where it points, which is a different verdict
|
|
92
|
+
* from knowing it points outside). */
|
|
15
93
|
homeDir?: string;
|
|
16
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* RB-412 — the structured verdict of {@link classifyCompoundReadonlyDetailed}. `reason === undefined`
|
|
97
|
+
* is the read-only verdict; everything else is a demotion.
|
|
98
|
+
*/
|
|
17
99
|
export interface CompoundReadonlyVerdict {
|
|
100
|
+
/** Demotion reason (the same string {@link classifyCompoundReadonly} returns), or undefined. */
|
|
18
101
|
reason?: string;
|
|
102
|
+
/** Set only when the SOLE obstacle to the read-only verdict was the root boundary: every segment is
|
|
103
|
+
* an allowlisted reader, no blocking/device/backgrounding arm fired, and the offending arguments are
|
|
104
|
+
* statically resolved paths outside the roots. A gate can therefore offer the narrow "allow reading
|
|
105
|
+
* from <dir>" approval instead of a generic shell prompt. Absent for every other demotion — an
|
|
106
|
+
* unresolvable path deliberately does NOT carry it (nothing concrete to grant). */
|
|
19
107
|
outOfRootRead?: true;
|
|
108
|
+
/** The resolved out-of-root paths, de-duplicated, in first-seen order. Present iff `outOfRootRead`. */
|
|
20
109
|
outOfRootPaths?: readonly string[];
|
|
110
|
+
/**
|
|
111
|
+
* RB-451 — the paths this scan resolved INSIDE the roots (de-duplicated, first-seen order), i.e. the
|
|
112
|
+
* candidates a caller holding a filesystem should re-check with symlinks resolved. Absent when there
|
|
113
|
+
* are none.
|
|
114
|
+
*
|
|
115
|
+
* Why the ALLOW side is reported at all: containment here is LEXICAL (see
|
|
116
|
+
* {@link resolveOperandLexically}), so "inside the roots" means the written spelling has an in-root
|
|
117
|
+
* prefix — a symlink inside a root that points out of it (`<root>/vendor` → `/etc`) passes. Closing
|
|
118
|
+
* that needs `realpath`, which this function deliberately cannot do (synchronous, pure, no
|
|
119
|
+
* ExecutionEnv). Carrying the candidates is the seam: the ENFORCING caller canonicalizes them and
|
|
120
|
+
* re-compares (`createBashReadonlyTool` does exactly this — a target that resolves out of root is
|
|
121
|
+
* refused there). Typically 0-2 entries per command.
|
|
122
|
+
*
|
|
123
|
+
* Present on BOTH arms, not only the allow arm: a command can name one out-of-root path AND one
|
|
124
|
+
* in-root candidate (`cat /etc/passwd inroot.txt`), and a caller that EXEMPTS the out-of-root path
|
|
125
|
+
* (the engine's own overflow spool, or a non-canonical spelling that resolves back inside) then goes
|
|
126
|
+
* on to execute — with the other candidate still unverified. The field is what that caller checks
|
|
127
|
+
* before running, whichever way the lexical verdict came out.
|
|
128
|
+
*
|
|
129
|
+
* Includes bare-word operands (`cat linkfile.txt`), which are NOT lexical out-of-root candidates —
|
|
130
|
+
* the "a name with no separator can only reach the working directory" argument is true lexically and
|
|
131
|
+
* false under symlinks, so those are exactly the entries a filesystem-holding caller must resolve.
|
|
132
|
+
* They never affect {@link reason}: a bare word cannot demote and does not here either.
|
|
133
|
+
*/
|
|
21
134
|
checkedPaths?: readonly string[];
|
|
135
|
+
/**
|
|
136
|
+
* RB-474 — operands whose spelling carries an UNEXPANDED glob (`*`, `?`, `[…]`, unquoted). Lexical
|
|
137
|
+
* containment is not decidable for them: the shell expands the pattern AFTER this verdict is made, and
|
|
138
|
+
* inside a root an expansion can land on a symlink that points out of it (`cat *` where the directory
|
|
139
|
+
* holds a link to an out-of-root file — reproduced end-to-end, not a theoretical shape).
|
|
140
|
+
*
|
|
141
|
+
* They are NOT in {@link checkedPaths}: a filesystem-holding resolver answers the unexpanded spelling
|
|
142
|
+
* with its deepest EXISTING ancestor — the directory itself — and reports nothing escaping, which is
|
|
143
|
+
* precisely the false "resolved, inside" answer this field exists to prevent.
|
|
144
|
+
*
|
|
145
|
+
* CONSUMER CONTRACT. A gate that auto-allows on containment must treat a verdict carrying this field as
|
|
146
|
+
* UNDECIDED: ask, or expand the patterns itself against the filesystem and judge every match. The
|
|
147
|
+
* read-only judgement itself is unaffected — `ls *` genuinely writes nothing, and {@link reason} stays
|
|
148
|
+
* absent — because a hard refusal here would take a routine idiom away from every caller, including the
|
|
149
|
+
* ones that CAN resolve it. The engine's own `bash_readonly` leg is such a caller: it expands and
|
|
150
|
+
* resolves each match before executing, so it keeps running these commands unprompted.
|
|
151
|
+
*
|
|
152
|
+
* `cd` is the one exception, and it fails closed: a glob there cannot be expanded into the single
|
|
153
|
+
* directory the compound face must track as the new working directory, so it demotes (see {@link reason}).
|
|
154
|
+
*/
|
|
22
155
|
undecidedPaths?: readonly string[];
|
|
23
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* RB-412 — the single minting point for the out-of-root-read approval option text, so a gate rendering
|
|
159
|
+
* the prompt and any other consumer cannot drift apart. Shape follows CC 2.1.220 (@905642): the option
|
|
160
|
+
* shows the LAST segment of the grantable directory followed by a separator, not the full path.
|
|
161
|
+
*
|
|
162
|
+
* `directory` is the directory to be granted — a caller holding a filesystem (this classifier does not)
|
|
163
|
+
* resolves a file operand to its parent first; CC does the same with a stat-then-dirname step.
|
|
164
|
+
* The separator is always `/`: this classifier's path handling is POSIX-lexical throughout, where CC
|
|
165
|
+
* uses the host's native separator.
|
|
166
|
+
*/
|
|
24
167
|
export declare function formatOutOfRootReadApprovalOption(directory: string): string;
|
|
168
|
+
/**
|
|
169
|
+
* RB-412 — {@link classifyCompoundReadonly} with the structured verdict. The name/operator/blocking arms
|
|
170
|
+
* run FIRST and unchanged, so an existing demotion keeps its exact reason and never acquires the
|
|
171
|
+
* out-of-root signal; the boundary scan below only ever converts a would-be READ-ONLY verdict into a
|
|
172
|
+
* demotion, which is why omitting `boundary` reproduces the pre-RB-412 behaviour exactly.
|
|
173
|
+
*/
|
|
25
174
|
export declare function classifyCompoundReadonlyDetailed(command: string, allow: ReadonlySet<string>, boundary?: BashReadonlyRootBoundary): CompoundReadonlyVerdict;
|
|
175
|
+
/**
|
|
176
|
+
* RB-413 — the read boundary ALONE, for the `bash_readonly` face.
|
|
177
|
+
*
|
|
178
|
+
* `bash_readonly` declares `effect:"read"` and is admitted by {@link coarseReadonlyCheck}, an argv[0]-NAME
|
|
179
|
+
* filter: path operands were invisible to it, so an allowlisted reader could name any file on the host
|
|
180
|
+
* (`cat /etc/passwd`) and — with no approval gate on the read effect — get it. The compound face above
|
|
181
|
+
* answers a different question for a different consumer (a gate that can ASK); this face answers only
|
|
182
|
+
* "does this command read outside the roots", leaving the availability arms (blocking stdin, `tail -f`,
|
|
183
|
+
* device reads) untouched so a caller wiring it gains the boundary and nothing else.
|
|
184
|
+
*
|
|
185
|
+
* PRECONDITION: `command` has already passed {@link coarseReadonlyCheck}, i.e. it is ONE simple command
|
|
186
|
+
* with no connectors, redirects, substitutions or escapes anywhere. That is what makes a whitespace split
|
|
187
|
+
* exact here. Feeding it an unvetted compound is not unsafe (the connector ends up glued to a token, which
|
|
188
|
+
* only ever produces MORE path candidates and a stricter verdict) but the reason text would name a
|
|
189
|
+
* nonsense command, so callers vet first.
|
|
190
|
+
*
|
|
191
|
+
* Returns the same {@link CompoundReadonlyVerdict} the compound face returns: `reason === undefined` means
|
|
192
|
+
* every path operand resolved inside the roots. A caller with no approval channel treats BOTH demotion
|
|
193
|
+
* kinds — resolved-and-outside (`outOfRootRead`, with `outOfRootPaths`) and could-not-resolve (reason
|
|
194
|
+
* only) — as a refusal; there is nowhere to degrade to.
|
|
195
|
+
*
|
|
196
|
+
* SCOPE OF THE GUARANTEE (RB-448, both directions): "inside the roots" is LEXICAL containment — see
|
|
197
|
+
* {@link resolveOperandLexically}. A symlink inside a root that points out of it reads as in-root here,
|
|
198
|
+
* so this face bounds where a command may NAME a file, not where the kernel will ultimately take it.
|
|
199
|
+
* RB-451 — a deployment that needs filesystem-true containment resolves the operands itself, and this
|
|
200
|
+
* verdict hands it the list to resolve ({@link CompoundReadonlyVerdict.checkedPaths}, on both arms).
|
|
201
|
+
* `createBashReadonlyTool` is that caller for the `bash_readonly` leg and does it on a local env.
|
|
202
|
+
*/
|
|
26
203
|
export declare function classifySimpleCommandReadBoundary(command: string, boundary: BashReadonlyRootBoundary): CompoundReadonlyVerdict;
|
|
204
|
+
/**
|
|
205
|
+
* design/154 — compound read-only classification, reason-only face. Returns the demotion reason, or
|
|
206
|
+
* undefined when the command classifies read-only. RB-412 added the optional `boundary`: with it, an
|
|
207
|
+
* allowlisted reader whose path arguments leave the allowed directories is demoted too (use
|
|
208
|
+
* {@link classifyCompoundReadonlyDetailed} when the caller wants to know that WHY, e.g. to offer the
|
|
209
|
+
* narrow "allow reading from <dir>" approval); without it the verdict is exactly what it always was.
|
|
210
|
+
*/
|
|
27
211
|
export declare function classifyCompoundReadonly(command: string, allow: ReadonlySet<string>, boundary?: BashReadonlyRootBoundary): string | undefined;
|
|
212
|
+
/**
|
|
213
|
+
* Iteration cap for {@link classifyBoundedReadonlyPollLoop}. The bound must be a LITERAL in the
|
|
214
|
+
* command text (that is what makes "bounded" provable without executing anything); this cap keeps a
|
|
215
|
+
* literal-but-huge bound (`$(seq 1 999999)`) from turning "bounded" into "unbounded in practice".
|
|
216
|
+
* 120 beats × the sleep cap below ≈ 20h worst case — the walltime axis governs actual runtime; this
|
|
217
|
+
* arm's job is only that termination is structurally guaranteed.
|
|
218
|
+
*/
|
|
28
219
|
export declare const POLL_LOOP_MAX_BEATS = 120;
|
|
220
|
+
/**
|
|
221
|
+
* Per-`sleep` cap (seconds) for {@link classifyBoundedReadonlyPollLoop}. A literal `sleep 99999999`
|
|
222
|
+
* is technically bounded but practically a hang — the same hazard class the RB-41 availability arms
|
|
223
|
+
* demote (a green verdict that blocks until the tool timeout is green in name only).
|
|
224
|
+
*/
|
|
29
225
|
export declare const POLL_LOOP_MAX_SLEEP_SECONDS = 600;
|
|
226
|
+
/**
|
|
227
|
+
* The BOUNDED READ-ONLY POLL LOOP arm of the classify face. The compound classifier above rejects any
|
|
228
|
+
* control structure (`for` is not an allowlisted name), which makes `shellGate:"classify"` behave like
|
|
229
|
+
* `"always"` for a monitoring consumer whose whole idiom is `for i in $(seq 1 8); do tail -n 5 x.log;
|
|
230
|
+
* sleep 2; done`. This arm accepts EXACTLY that grammar and nothing else. Returns the rejection
|
|
231
|
+
* reason, or undefined when the command matches the grammar (⇒ safe to treat as reversible).
|
|
232
|
+
*
|
|
233
|
+
* It is ADDITIVE and consulted only after {@link classifyCompoundReadonly} rejected (see
|
|
234
|
+
* {@link import("./fs-bash.js").bashReversibilityProbe}): it can never change a verdict the plain face
|
|
235
|
+
* already gives, and every unparseable shape falls through to a rejection — the default path is
|
|
236
|
+
* untouched.
|
|
237
|
+
*
|
|
238
|
+
* SAFETY ARGUMENT — why this allowance does not increase irreversibility/egress risk. Each pillar
|
|
239
|
+
* maps to a test group in test/poll-loop-classifier.test.ts (G1-G6):
|
|
240
|
+
* G1 BOUNDED TERMINATION. The loop head must be one of three LITERAL forms with a computable
|
|
241
|
+
* iteration count ≤ {@link POLL_LOOP_MAX_BEATS} ({@link pollLoopBeatsOf}); `while`/`until` never
|
|
242
|
+
* match the skeleton. Every body command terminates: non-`sleep` segments inherit the RB-41/84/
|
|
243
|
+
* 108/153 availability arms via {@link classifyCompoundReadonlyDetailed} (blocking stdin, `tail
|
|
244
|
+
* -f`, unbounded devices all reject), and `sleep` must be a literal ≤
|
|
245
|
+
* {@link POLL_LOOP_MAX_SLEEP_SECONDS}. Bounded beats × terminating body ⇒ the loop halts.
|
|
246
|
+
* G2 NO WRITE OR EGRESS CHANNEL. Every non-`sleep` body segment must pass the SAME
|
|
247
|
+
* {@link coarseReadonlyCheck} parser + allowlist as the plain face (one parser, no drift);
|
|
248
|
+
* `sleep` touches neither filesystem nor network. No redirection (`>` `<`), no `$`/backtick
|
|
249
|
+
* substitution, no subshell exists anywhere: the body bans them wholesale
|
|
250
|
+
* ({@link POLL_BODY_HARD_REJECT}), and the head's single `$(seq <int> <int>)` exemption is an
|
|
251
|
+
* exact-spelling match whose two arguments are literal digits.
|
|
252
|
+
* G3 CONNECTOR CLOSURE. `;` is the only connector in the body; `|`/`&`/`&&`/`||` are banned there
|
|
253
|
+
* outright, and a nested control structure surfaces as a `for`/`do`/`done` head token that fails
|
|
254
|
+
* the allowlist. Nothing can chain past the vetted segment heads.
|
|
255
|
+
* G4 READ BOUNDARY UNCHANGED. The reassembled body goes through the boundary scan of
|
|
256
|
+
* {@link classifyCompoundReadonlyDetailed} with the caller's boundary — out-of-root operands
|
|
257
|
+
* reject exactly as on the plain face (RB-412/413, not relaxed by one rule). STRICTER here: a
|
|
258
|
+
* verdict carrying {@link CompoundReadonlyVerdict.undecidedPaths} (an unexpanded glob) rejects,
|
|
259
|
+
* because the probe face is reason-only and a repeated read of a runtime-decided target must not
|
|
260
|
+
* auto-allow.
|
|
261
|
+
* G5 STRUCTURE SOUNDNESS (the parser sees what bash executes). Whitespace is normalized first: any
|
|
262
|
+
* non-ASCII / control whitespace ({@link NON_ASCII_WHITESPACE}) rejects the whole command, so this
|
|
263
|
+
* grammar's `[ \t]`/`\s` word split and bash's `$IFS` split cannot diverge (an NBSP-joined
|
|
264
|
+
* `ls x` never reads as head `ls` here while bash runs one word). Quote pairing is then checked
|
|
265
|
+
* whole-string ({@link pollLoopQuotesBalanced}); the var/iterable region admits no quote character
|
|
266
|
+
* at all; so the `for … ; do … ; done` skeleton the anchored regex matches is exactly the one bash
|
|
267
|
+
* parses — a `done` hidden in quotes either unbalances (reject) or leaves a body token that fails
|
|
268
|
+
* the allowlist. Within the body, the quote-blind `;` split inherits the compound face's
|
|
269
|
+
* soundness argument: with all expansion metacharacters banned, quotes only ever HIDE connectors,
|
|
270
|
+
* so mis-splits are over-fine and the error direction is reject, never widen.
|
|
271
|
+
* G6 ENVIRONMENT INTEGRITY. The loop variable must be a SINGLE LOWERCASE LETTER: the `for`
|
|
272
|
+
* assignment mutates a shell variable, and a name like `PATH`/`IFS`/`LD_PRELOAD`/`BASH_ENV`
|
|
273
|
+
* would change how the body's commands RESOLVE (`for PATH in dir; do cat x; done` would run
|
|
274
|
+
* `dir/cat` — arbitrary code under an auto-allow). No known resolution-affecting variable is a
|
|
275
|
+
* single lowercase letter, and the body cannot read the variable anyway (`$` is banned there),
|
|
276
|
+
* so the counter is write-only and inert.
|
|
277
|
+
*
|
|
278
|
+
* ACCEPTED-COST NOTES (the residuals, recorded rather than silently tolerated — same posture as the
|
|
279
|
+
* RB-84 `head` note above):
|
|
280
|
+
* · `$(seq …)` RESOLVES A COMMAND. This is the ONE command-substitution form the ruling exempts, and
|
|
281
|
+
* it does run whatever `seq` resolves to on PATH (or a shell function/alias of that name). Two
|
|
282
|
+
* facts bound the exposure. FIRST, the trust assumption is ISOMORPHIC to the allowlist itself:
|
|
283
|
+
* `ls`/`cat`/`grep` are equally PATH-resolved, so a party that can shadow `seq` can already shadow
|
|
284
|
+
* `cat` — this arm adds no trust assumption the plain face does not already make, and the file's
|
|
285
|
+
* standing posture is that the tool-policy gate is the authoritative control and command-name
|
|
286
|
+
* resolution to the real coreutils binaries is assumed (see {@link BASH_READONLY_DEFAULT_ALLOW}).
|
|
287
|
+
* SECOND, the beat count is taken from the LITERAL digits, not from seq's output, so a shadowed
|
|
288
|
+
* `seq` emitting more items only loosens the ITERATION bound (never the read-only / no-write /
|
|
289
|
+
* no-egress envelope — every beat still runs allowlisted readers + literal `sleep`), and the
|
|
290
|
+
* walltime axis cascades a hard kill regardless. A deployment wanting NO command-substitution at
|
|
291
|
+
* all can prefer `{<lo>..<hi>}` or a literal list, which bash expands internally with no command
|
|
292
|
+
* run. (Codex adversarial review flagged the substitution as [high] and recommended dropping it;
|
|
293
|
+
* kept per the ruling that names `$(seq <int> <int>)` as the one exemption — recorded for a
|
|
294
|
+
* tightening decision, since the brace form is a drop-in equivalent.)
|
|
295
|
+
* · STDIN-BLOCKING BODY (an availability residual, not a safety one). CLOSED for the shape this note
|
|
296
|
+
* used to record: a quoted multiword operand like `grep "x y"` read, to the then whitespace-blind
|
|
297
|
+
* tokenizer, as two operands meeting grep's file floor, while bash passes ONE pattern and no file and
|
|
298
|
+
* grep blocks on stdin. {@link splitWordsQuoteAware} now counts the operand bash actually passes, so
|
|
299
|
+
* that command demotes on both this arm and the plain face. What REMAINS residual is the general
|
|
300
|
+
* statement: this arm inherits whatever the shared availability arms can see, they are best-effort
|
|
301
|
+
* (an operand that only turns out to be unreadable at run time is not in the text), and a hang is
|
|
302
|
+
* bounded by the tool timeout — outside the irreversibility / egress envelope this arm governs. The
|
|
303
|
+
* G1 termination guarantee is therefore stated for the NON-stdin-blocking body.
|
|
304
|
+
*
|
|
305
|
+
* NON-RESIDUAL (a divergence this arm CLOSES rather than accepts): the JS-`\s`-vs-bash-`$IFS` gap.
|
|
306
|
+
* Because the shared parser splits on JS `\s` (which includes NBSP/U+2000-block/U+3000/form-feed/
|
|
307
|
+
* vertical-tab) but bash's default `$IFS` does not, `ls x` reads as head `ls` here yet is ONE
|
|
308
|
+
* command word to bash. Rather than reason about the (harmless — nonexistent program, exit 127)
|
|
309
|
+
* consequence case by case, the whole command is rejected up front if it carries any such character
|
|
310
|
+
* ({@link NON_ASCII_WHITESPACE}), so the grammar's tokenization and bash's stay identical.
|
|
311
|
+
*/
|
|
30
312
|
export declare function classifyBoundedReadonlyPollLoop(command: string, allow: ReadonlySet<string>, boundary?: BashReadonlyRootBoundary): string | undefined;
|
|
@@ -1,20 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 编码 parity 批(docs/CC-TOOL-PARITY-SWEEP-2026-07.md P0)— file encoding + line-ending detection
|
|
3
|
+
* for the Read/Edit/Write band, ported from CC's `utils/fileRead.ts` mechanism and hardened where CC
|
|
4
|
+
* is lossy:
|
|
5
|
+
*
|
|
6
|
+
* - BOM detection: `FF FE` → utf16le, `EF BB BF` → utf8-with-BOM, else plain utf8. (CC 88→2.1.187
|
|
7
|
+
* dynamic-verified: detection is BOM-only; UTF-16BE / heuristic sniffing deliberately NOT done —
|
|
8
|
+
* a wrong guess silently corrupts, and the BOM-less case stays a refusal at the Read layer.)
|
|
9
|
+
* - The MODEL-FACING text is BOM-stripped and CRLF-normalized (what the model quotes in `old_string`
|
|
10
|
+
* must match what Read showed it).
|
|
11
|
+
* RB-285 (encoding LOW, 归因收窄 — CC 220 亲核): the blanket "(CC parity)" that used to sit on this
|
|
12
|
+
* line was half wrong, because CC has TWO read lanes and they disagree with each other.
|
|
13
|
+
* · CRLF-normalize — parity on both lanes (`Zr_` @276877 and `Mqe` @58197 both `replaceAll`).
|
|
14
|
+
* · BOM-strip — parity on CC's READ lane only (`Zr_` @276877: `charCodeAt(0) === 65279 → slice(1)`).
|
|
15
|
+
* CC's EDIT/WRITE lane (`Mqe`) does NOT strip: it hands the model text with the leading U+FEFF
|
|
16
|
+
* still in it, and only the staleness/hash coordinate (`X9` = `CTe` + CRLF, @365630-365635)
|
|
17
|
+
* strips. sema strips in ONE shared decoder for every lane, so the model-facing view and the
|
|
18
|
+
* hash coordinate cannot drift apart — an improvement over CC's split, recorded as such rather
|
|
19
|
+
* than claimed as parity.
|
|
20
|
+
* - Write-back re-encodes with the ORIGINAL encoding, PRESERVES the BOM (CC drops it on Write — its
|
|
21
|
+
* lossy face, dynamic-verified W1; we keep it, recorded as a deliberate improvement), and restores
|
|
22
|
+
* the file's dominant line endings for Edit (Write keeps the model's endings as-is, CC decision).
|
|
23
|
+
*/
|
|
24
|
+
/** Detected byte-level encoding of a text file (BOM-driven; utf8 is the no-BOM default). */
|
|
1
25
|
export interface DetectedFileEncoding {
|
|
2
26
|
encoding: "utf8" | "utf16le";
|
|
3
27
|
hadBom: boolean;
|
|
4
28
|
}
|
|
5
29
|
export type DetectedLineEndings = "CRLF" | "LF";
|
|
6
30
|
export interface DecodedTextFile {
|
|
31
|
+
/** Model-facing text: BOM stripped, `\r\n` normalized to `\n`. The staleness-hash coordinate. */
|
|
7
32
|
text: string;
|
|
8
33
|
encoding: DetectedFileEncoding;
|
|
34
|
+
/** Dominant line ending of the ORIGINAL bytes (majority vote — CC `CKt` @58184 同算法).
|
|
35
|
+
* Deliberately NOT per-line lossless: a mixed-endings file is unified to the dominant ending on
|
|
36
|
+
* the first edit (codex 镜头1/2 LOW, accepted non-goal — CC behaves the same way).
|
|
37
|
+
* RB-285 (encoding LOW): the COUNTING RULE is CC's; the SAMPLE is not. CC votes on the first 4096
|
|
38
|
+
* characters only (`CKt(s.slice(0, 4096))` @58201) while {@link detectLineEndings} below votes on
|
|
39
|
+
* the whole file — so a file whose first 4KB is LF and whose bulk is CRLF gets opposite verdicts.
|
|
40
|
+
* sema's whole-file vote is kept on purpose: the verdict decides what an Edit WRITES BACK across
|
|
41
|
+
* the entire file, so sampling a prefix is exactly the case where the cheap answer flips the
|
|
42
|
+
* remaining 99% of the lines. Registered as a deliberate delta, not parity. */
|
|
9
43
|
endings: DetectedLineEndings;
|
|
44
|
+
/** codex 双镜头 MED: a utf16le body with an ODD byte count is TRUNCATED/corrupt — decoding would
|
|
45
|
+
* silently drop the dangling byte and a later write-back would destroy it permanently. Flagged so
|
|
46
|
+
* the tool layer fails closed instead of "repairing" the file. */
|
|
10
47
|
malformed?: true;
|
|
11
48
|
}
|
|
49
|
+
/** BOM-only encoding detection (CC `detectEncodingForResolvedPath` parity — no content heuristics). */
|
|
12
50
|
export declare function detectFileEncoding(bytes: Uint8Array): DetectedFileEncoding;
|
|
51
|
+
/** Decode file bytes into the model-facing normalized form + the metadata needed to write back losslessly. */
|
|
13
52
|
export declare function decodeTextBytes(bytes: Uint8Array): DecodedTextFile;
|
|
53
|
+
/**
|
|
54
|
+
* Re-encode text for writing back to a file that was decoded with {@link decodeTextBytes}.
|
|
55
|
+
*
|
|
56
|
+
* @param text normalized (`\n`) text when `endings` is CRLF/LF (Edit path — restore the file's
|
|
57
|
+
* dominant endings); pass `endings:"preserve"` to write the text's OWN line endings untouched
|
|
58
|
+
* (Write path — CC decision: the model sent explicit endings in `content` and meant them).
|
|
59
|
+
* @returns a plain string for the no-BOM utf8 + LF-or-preserve fast path (byte-compatible with the
|
|
60
|
+
* pre-batch writer), else the exact bytes (BOM + encoded body).
|
|
61
|
+
*/
|
|
14
62
|
export declare function encodeTextForFile(text: string, encoding: DetectedFileEncoding, endings: DetectedLineEndings | "preserve"): string | Uint8Array;
|
|
63
|
+
/** Normalize model-supplied match/replacement text the same way the file text was normalized
|
|
64
|
+
* (CC FileEditTool parity: `old_string`/`new_string` are CRLF-normalized before matching).
|
|
65
|
+
* Deliberately does NOT strip a leading U+FEFF: an `old_string` starting with one may be quoting
|
|
66
|
+
* genuine file content — use {@link normalizeFileText} only where the string stands for a whole file. */
|
|
15
67
|
export declare function normalizeEditText(s: string): string;
|
|
68
|
+
/** RB-222 ([1937] W2) — split a leading U+FEFF off MODEL-SUPPLIED whole-file text (CC `CTe` parity,
|
|
69
|
+
* 220 @365630). The BOM is a file-level byte marker, not text: decodeTextBytes strips it on the way
|
|
70
|
+
* in, so it can never live in the normalized coordinate. Only for strings that stand for a WHOLE
|
|
71
|
+
* file (Write `content`, readFileState seeding) — a decoded file text's leading U+FEFF is genuine
|
|
72
|
+
* content and must stay. */
|
|
16
73
|
export declare function splitLeadingBom(text: string): {
|
|
17
74
|
hadBom: boolean;
|
|
18
75
|
text: string;
|
|
19
76
|
};
|
|
77
|
+
/** RB-222 ([1937] W2) — CC `X9` parity (220 @365635 = BOM-strip + CRLF-normalize): THE coordinate
|
|
78
|
+
* every readFileState hash lives in (Read/Edit/Write staleness all hash `decodeTextBytes(...).text`).
|
|
79
|
+
* A hash taken on raw model content with a leading U+FEFF can never agree with any later re-read. */
|
|
20
80
|
export declare function normalizeFileText(s: string): string;
|