@sema-agent/server 7.43.0 → 7.44.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/USAGE.md +18 -0
- package/dist/adoption/plan.js +31 -192
- package/dist/adoption/quiesce.js +3 -86
- package/dist/adoption/runner.js +3 -140
- package/dist/adoption/sql.js +0 -74
- package/dist/adoption/wire.js +0 -80
- package/dist/approval-ask-machine.js +0 -75
- package/dist/approval-card.js +0 -323
- package/dist/approval-deny-reasons.js +0 -42
- package/dist/approval-hmac.js +0 -35
- package/dist/approval-reconciler.js +8 -166
- package/dist/approval.js +4 -67
- package/dist/audit.js +1 -44
- package/dist/auth-bridge.js +4 -47
- package/dist/auth-keys.js +0 -23
- package/dist/bake-runner/main.js +4 -65
- package/dist/bake-runner/protocol.js +3 -58
- package/dist/bake-runner/runner.js +5 -91
- package/dist/bench/l8/artifact.js +2 -61
- package/dist/bench/l8/escape.js +0 -25
- package/dist/bench/l8/index.js +0 -14
- package/dist/bench/l8/probes.js +0 -60
- package/dist/bench/l8/run-probes.js +2 -45
- package/dist/bench/s1/arms.js +14 -189
- package/dist/bench/s1/live-deps.js +11 -253
- package/dist/bench/s1/oracle.js +1 -36
- package/dist/bench/s1/repair-oracle-adapter.js +0 -30
- package/dist/bench/s1/reviewer.js +0 -34
- package/dist/bench/s1/row.js +0 -62
- package/dist/bench/s1/run-firm.js +4 -70
- package/dist/bench/s1/runner-ctx.js +0 -40
- package/dist/bench/s1/tasks.js +0 -115
- package/dist/boot/adoption.js +0 -20
- package/dist/boot/budget-tracing.js +3 -55
- package/dist/boot/config-center.js +43 -551
- package/dist/boot/coordinators.js +1 -69
- package/dist/boot/crash-last.js +0 -18
- package/dist/boot/deferred-sandbox-path-env.js +3 -119
- package/dist/boot/execution-env.js +5 -171
- package/dist/boot/governance-seams.js +0 -92
- package/dist/boot/leader.js +0 -69
- package/dist/boot/limit-sync.js +0 -2
- package/dist/boot/memory-boundary.js +3 -91
- package/dist/boot/org-memory.js +1 -25
- package/dist/boot/parked-revive-gate.js +0 -150
- package/dist/boot/permission-rules-audit.js +1 -56
- package/dist/boot/reapers.js +15 -273
- package/dist/boot/resolve-spec.js +9 -768
- package/dist/boot/retention-lane.js +4 -87
- package/dist/boot/runner-deps.js +3 -297
- package/dist/boot/runtime-caps.js +0 -40
- package/dist/boot/session-faces.js +2 -133
- package/dist/boot/shutdown.js +9 -83
- package/dist/boot/side-query-lane.js +2 -137
- package/dist/boot/stores.js +15 -215
- package/dist/boot/task-list-lane.js +0 -18
- package/dist/boot/webfetch-summarize-lane.js +0 -47
- package/dist/boot/workflow-orchestration.js +3 -99
- package/dist/boot-reclaim.js +0 -20
- package/dist/bounded-session-map.js +0 -19
- package/dist/brain.js +2 -139
- package/dist/budget.js +9 -244
- package/dist/capabilities/builtin-tools.js +0 -3
- package/dist/capabilities/center-plugins.js +6 -51
- package/dist/capabilities/center-prompts.js +6 -71
- package/dist/capabilities/code-review-council.js +7 -40
- package/dist/capabilities/collab-workflows.js +1 -44
- package/dist/capabilities/hands-lane.js +0 -65
- package/dist/capabilities/memory-notice.js +0 -70
- package/dist/capabilities/prompt.js +0 -14
- package/dist/capabilities/prompts/code-review.js +0 -14
- package/dist/capabilities/prompts/identity.js +0 -6
- package/dist/capabilities/prompts/team.js +0 -4
- package/dist/capabilities/repo-tools.js +0 -34
- package/dist/capabilities/sandbox-file-send.js +3 -92
- package/dist/capabilities/scenarios.d.ts +0 -1
- package/dist/capabilities/scenarios.js +18 -279
- package/dist/capabilities/select-environment-tool.js +0 -32
- package/dist/capabilities/send-user-file-tool.js +2 -66
- package/dist/capabilities/skills.d.ts +1 -2
- package/dist/capabilities/skills.js +3 -21
- package/dist/capabilities/team.d.ts +3 -11
- package/dist/capabilities/team.js +3 -51
- package/dist/capabilities/tool-defer.js +0 -3
- package/dist/config-center/apply-effective.js +15 -330
- package/dist/config-center/apply-ledger.js +5 -38
- package/dist/config-center/facade.js +0 -41
- package/dist/config-center/hot-keys-registry.js +0 -20
- package/dist/config-center/http-client.js +2 -125
- package/dist/config-center/mcp-revocation.js +2 -34
- package/dist/config-center/read-face.js +0 -59
- package/dist/config-center/restart-signal.js +1 -85
- package/dist/config-center/skills-mcp.d.ts +1 -1
- package/dist/config-center/skills-mcp.js +9 -76
- package/dist/config-center/stage-limits.js +8 -35
- package/dist/config-invariants.js +0 -16
- package/dist/config-lkg.js +0 -42
- package/dist/config-provider.js +3 -186
- package/dist/config-types.js +0 -5
- package/dist/config.js +107 -1145
- package/dist/degenerate-instrument.js +3 -67
- package/dist/deployment-governance.js +0 -124
- package/dist/digest-form.js +0 -12
- package/dist/elicitation.js +3 -86
- package/dist/env-facts.js +7 -75
- package/dist/fleet/fleet-bus.js +34 -507
- package/dist/fleet/fleet-reconciler.js +9 -149
- package/dist/fleet/fleet-terminal-window.js +10 -178
- package/dist/fleet/subagent-tail-bus.js +3 -72
- package/dist/fleet-client.js +10 -70
- package/dist/fleet-lease.js +5 -79
- package/dist/git-api-kind.js +0 -3
- package/dist/governance-ask-marks.js +2 -78
- package/dist/hooks/branch-transcript.js +0 -74
- package/dist/hooks/cc-agent-hook-prompt.js +0 -29
- package/dist/hooks/cc-stop-prompt.js +1 -46
- package/dist/hooks/hook-llm.js +1 -53
- package/dist/hooks/hook-runner.js +20 -414
- package/dist/http/active-run-conflict.js +4 -129
- package/dist/http/cursor-fingerprint.d.ts +5 -0
- package/dist/http/cursor-fingerprint.js +5 -0
- package/dist/http/idempotency.js +0 -37
- package/dist/http/principal-gate.js +3 -40
- package/dist/http/route-ctx.js +0 -9
- package/dist/http/routes/a2a-serve.js +7 -319
- package/dist/http/routes/admin-config-refresh.js +0 -4
- package/dist/http/routes/admin-drain.js +0 -10
- package/dist/http/routes/adoption.js +1 -29
- package/dist/http/routes/agents-roster.js +1 -47
- package/dist/http/routes/approvals-assistant.js +32 -395
- package/dist/http/routes/attachments.js +4 -22
- package/dist/http/routes/capabilities.js +3 -420
- package/dist/http/routes/diagnostics.js +1 -81
- package/dist/http/routes/fleet.js +9 -185
- package/dist/http/routes/images.js +14 -239
- package/dist/http/routes/leader.js +0 -13
- package/dist/http/routes/memory-bundle.js +1 -61
- package/dist/http/routes/memory-policy.js +9 -99
- package/dist/http/routes/notify-wake.js +3 -37
- package/dist/http/routes/observability.js +2 -19
- package/dist/http/routes/retention-ops.js +2 -34
- package/dist/http/routes/rules.js +1 -93
- package/dist/http/routes/runs.js +64 -873
- package/dist/http/routes/session-sync.js +19 -258
- package/dist/http/routes/sessions-list.js +17 -43
- package/dist/http/routes/sessions.js +34 -226
- package/dist/http/routes/shared-memory.js +5 -36
- package/dist/http/routes/side-query.js +1 -87
- package/dist/http/routes/tasks.js +52 -722
- package/dist/http/routes/trace-usage.js +32 -195
- package/dist/http/routes/workflows.js +19 -195
- package/dist/http/run-meta.js +0 -6
- package/dist/http/send.js +0 -32
- package/dist/http/server.js +92 -1597
- package/dist/http/sse-lifecycle.js +2 -13
- package/dist/http/sse-log.js +3 -48
- package/dist/http/tar.js +5 -21
- package/dist/http/verify-rounds.js +0 -5
- package/dist/http/wire-gate.js +0 -9
- package/dist/http/workspace-content.js +0 -10
- package/dist/images/bake-validate.js +1 -70
- package/dist/images/manifest.js +1 -6
- package/dist/index.js +0 -21
- package/dist/key-resolver.js +2 -17
- package/dist/leader/diffout.js +1 -20
- package/dist/leader/diffup.js +0 -47
- package/dist/leader/endpoint.js +2 -57
- package/dist/leader/fanout.js +3 -45
- package/dist/leader/grader-env-factory.js +3 -72
- package/dist/leader/leader.js +5 -156
- package/dist/leader/merge.js +8 -115
- package/dist/leader/planner.js +3 -54
- package/dist/leader/repair-oracle.js +1 -60
- package/dist/leader/repair-wire.js +2 -79
- package/dist/leader/wire.js +8 -307
- package/dist/lsp/e2b-bridge.js +4 -64
- package/dist/lsp/e2b-manager.js +6 -94
- package/dist/lsp/lsp-frames.js +0 -12
- package/dist/lsp/manager.js +4 -96
- package/dist/lsp/ws-transport.js +5 -55
- package/dist/lsp-evict.js +1 -15
- package/dist/main.js +57 -765
- package/dist/memory-bundle-engine.js +0 -55
- package/dist/memory-export.js +0 -4
- package/dist/memory-posture.js +1 -15
- package/dist/memory-scope.js +11 -148
- package/dist/memory-sync-client.js +2 -44
- package/dist/memory-sync.js +1 -80
- package/dist/model-select.js +3 -80
- package/dist/observability/cost-quota.js +1 -17
- package/dist/observability/cost-taxonomy.js +0 -34
- package/dist/observability/fail-open.js +7 -86
- package/dist/observability/logger.js +0 -6
- package/dist/observability/metrics.js +0 -94
- package/dist/observability/otel-exporter.js +3 -13
- package/dist/observability/principal-context.js +0 -9
- package/dist/observability/prompt-manifest.js +1 -37
- package/dist/observability/rate-limit.js +0 -4
- package/dist/observability/secret-env-scrub.js +2 -56
- package/dist/observability/tool-trace.js +1 -70
- package/dist/orchestration/hardened-vm-runner.js +4 -118
- package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
- package/dist/orchestration/hardened-vm-worker.js +0 -27
- package/dist/orchestration/subagent-steer.js +1 -45
- package/dist/orchestration/workflow-agent-steer.js +1 -80
- package/dist/orchestration/workflow-completion-inbox.js +32 -285
- package/dist/orchestration/workflow-notify-journal.js +16 -259
- package/dist/org-memory-admission.js +3 -47
- package/dist/parent-watch.js +2 -48
- package/dist/parked-decide.js +1 -109
- package/dist/per-task-image.js +0 -57
- package/dist/plan-cache-probe.js +3 -27
- package/dist/plugins/adoption-log-sql.js +2 -119
- package/dist/plugins/approval-ask-store-memory.js +3 -38
- package/dist/plugins/approval-ask-store-sql.js +6 -188
- package/dist/plugins/approval-exemption-store.js +2 -28
- package/dist/plugins/background-agent-store-sql.js +4 -105
- package/dist/plugins/background-shell-support.js +14 -122
- package/dist/plugins/blob-backend.js +6 -169
- package/dist/plugins/breaker-state-sql.js +8 -46
- package/dist/plugins/caching-session-store.js +4 -106
- package/dist/plugins/checkpoint-store-sql.js +18 -505
- package/dist/plugins/e2b-orphan-reclaim.js +0 -45
- package/dist/plugins/file-outcome-sink.js +0 -9
- package/dist/plugins/file-resume-anchor-store.js +4 -43
- package/dist/plugins/file-run-store.js +26 -364
- package/dist/plugins/file-snapshot-store-sql.js +10 -181
- package/dist/plugins/fork-routing-session-store.js +8 -111
- package/dist/plugins/host-platform.js +2 -91
- package/dist/plugins/image-bake-store-sql.js +7 -250
- package/dist/plugins/image-index-sql.js +4 -123
- package/dist/plugins/k8s-bg-scripts.js +4 -88
- package/dist/plugins/k8s-exec-protocol.js +0 -41
- package/dist/plugins/leader-run-store-sql.js +0 -101
- package/dist/plugins/local-checkpoint-store.js +8 -128
- package/dist/plugins/local-session-store.js +34 -296
- package/dist/plugins/local-task-attachment-store.js +2 -16
- package/dist/plugins/mailbox-store-sql.js +8 -65
- package/dist/plugins/memory-embedder-fingerprint.js +5 -166
- package/dist/plugins/memory-embedder.js +4 -70
- package/dist/plugins/memory-engine-pg.js +6 -170
- package/dist/plugins/memory-engine-tidb.js +7 -155
- package/dist/plugins/memory-engine-vector-util.js +0 -10
- package/dist/plugins/memory-key-guards.js +0 -34
- package/dist/plugins/memory-origin-law.js +0 -187
- package/dist/plugins/memory-resume-anchor-store.js +0 -17
- package/dist/plugins/memory-run-store.js +12 -89
- package/dist/plugins/memory-session-policy-store.js +0 -17
- package/dist/plugins/memory-sync-store-pg.js +4 -49
- package/dist/plugins/memory-sync-store-tidb.js +3 -35
- package/dist/plugins/outcome-ledger-sql.js +3 -97
- package/dist/plugins/permission-rule-store-file.js +5 -133
- package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
- package/dist/plugins/permission-rule-store-sql.js +50 -307
- package/dist/plugins/pg-cost-quota.js +0 -7
- package/dist/plugins/pg-pool.js +0 -92
- package/dist/plugins/pg-rate-limiter.js +2 -13
- package/dist/plugins/pg-safe-json.js +4 -40
- package/dist/plugins/pg-session-storage.js +25 -189
- package/dist/plugins/posix-shell-fs.js +1 -31
- package/dist/plugins/remote-env-adb.js +12 -101
- package/dist/plugins/remote-env-e2b.js +40 -371
- package/dist/plugins/remote-env-file-error.js +0 -37
- package/dist/plugins/remote-env-host.js +63 -483
- package/dist/plugins/remote-env-k8s.js +32 -326
- package/dist/plugins/remote-env-local-docker.js +24 -157
- package/dist/plugins/remote-env-ssh.js +20 -128
- package/dist/plugins/remote-scratchpad.js +2 -32
- package/dist/plugins/remote-shell.js +1 -32
- package/dist/plugins/resume-anchor-store-sql.js +0 -11
- package/dist/plugins/retention-lane-store-sql.js +0 -108
- package/dist/plugins/retention-store-sql.js +4 -383
- package/dist/plugins/roster-store-sql.js +0 -55
- package/dist/plugins/run-store-sql.js +14 -251
- package/dist/plugins/s3-presign.js +2 -49
- package/dist/plugins/scheduler-support.js +3 -80
- package/dist/plugins/send-file-ledger.js +4 -53
- package/dist/plugins/send-user-file.js +4 -94
- package/dist/plugins/session-placement.js +1 -89
- package/dist/plugins/session-policy-store-sql.js +3 -81
- package/dist/plugins/session-store.js +0 -57
- package/dist/plugins/shared-memory-store-sql.js +4 -167
- package/dist/plugins/sql-driver.js +0 -17
- package/dist/plugins/sql-errors.js +0 -7
- package/dist/plugins/sql-escape.js +0 -8
- package/dist/plugins/sql-row-helpers.js +0 -25
- package/dist/plugins/store-backend.js +42 -222
- package/dist/plugins/store-contracts.js +2 -46
- package/dist/plugins/task-attachment-store.js +3 -49
- package/dist/plugins/task-list-store-sql.js +0 -79
- package/dist/plugins/tidb-cost-quota.js +1 -4
- package/dist/plugins/tidb-pool.js +1 -207
- package/dist/plugins/tidb-rate-limiter.js +3 -9
- package/dist/plugins/tidb-session-storage.js +4 -70
- package/dist/plugins/tidb-session-store.js +19 -341
- package/dist/plugins/tool-result-store-sql.js +4 -179
- package/dist/plugins/usage-window-store-sql.js +0 -8
- package/dist/plugins/web-search.js +10 -132
- package/dist/plugins/workflow-journal-store-sql.js +2 -58
- package/dist/plugins/workflow-run-store-sql.js +6 -91
- package/dist/plugins/worktree-isolation.js +6 -126
- package/dist/plugins/write-behind-counter.js +16 -75
- package/dist/principal-jwt.js +5 -60
- package/dist/project-memory.js +15 -146
- package/dist/prompts-domain-validate.js +1 -51
- package/dist/question.js +2 -111
- package/dist/resource-suspend.js +0 -18
- package/dist/router/route-orchestration.js +0 -77
- package/dist/rules-consent.d.ts +44 -5
- package/dist/rules-consent.js +75 -215
- package/dist/run-local.js +14 -381
- package/dist/runs.js +29 -617
- package/dist/runtime-caps-resolver.js +6 -132
- package/dist/runtime-governance.js +1 -232
- package/dist/sandbox-pkg-source.js +0 -37
- package/dist/sealed-key.js +3 -68
- package/dist/security.js +6 -291
- package/dist/session-leaf-bus.js +0 -32
- package/dist/session-sync-content.js +1 -69
- package/dist/session-sync-kernel.js +3 -60
- package/dist/session-sync.js +3 -66
- package/dist/session-titler.js +7 -41
- package/dist/session-watch.js +9 -73
- package/dist/shared-memory-scope-authorizer.js +0 -18
- package/dist/sighup-idle.js +1 -12
- package/dist/spec-fields.js +4 -143
- package/dist/store-live-probe.js +3 -49
- package/dist/task-a2a.js +1 -126
- package/dist/task-cwd.js +1 -103
- package/dist/task-mcp.js +1 -89
- package/dist/task-settings.js +10 -300
- package/dist/task-workflow.js +8 -75
- package/dist/tool-approval.d.ts +28 -1
- package/dist/tool-approval.js +70 -1430
- package/dist/trace/artifacts.js +5 -20
- package/dist/trace/engine-notice-wire.js +6 -138
- package/dist/trace/ledger-sink.js +7 -96
- package/dist/trace/project.js +4 -448
- package/dist/trace/redact.js +9 -77
- package/dist/turn-activity.js +1 -27
- package/dist/usage-analytics.js +4 -34
- package/dist/wall-clock-jump-guard.js +1 -68
- package/package.json +3 -3
- package/dist/capabilities/scenario-alias.d.ts +0 -27
- package/dist/capabilities/scenario-alias.js +0 -61
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable-store BACKEND seam (clay 2026-06-21 PG migration). One place that, given `config.dbBackend`, builds the
|
|
3
|
-
* SQL pool (TiDB/MySQL via mysql2 OR PostgreSQL via pg) and constructs the right store TWIN for every durable
|
|
4
|
-
* subsystem. main.ts wires through this instead of `new TiDB*(pool)` so `DB_BACKEND=pg` boots the whole service on
|
|
5
|
-
* PG with zero call-site churn. The twins are interchangeable behaviourally (one shared real-DB integration suite
|
|
6
|
-
* keeps them in lock-step); the unions below exist only because TS private fields make the concrete classes
|
|
7
|
-
* nominally distinct (run/imageIndex/imageBake have no core interface — the rest implement one).
|
|
8
|
-
*/
|
|
9
1
|
import { homedir } from "node:os";
|
|
10
2
|
import { join } from "node:path";
|
|
11
3
|
import { AdoptionError, FileStorageBackend, FileSessionRepo, DEFAULT_SNAPSHOT_BOUNDS } from "@sema-agent/core";
|
|
@@ -43,23 +35,13 @@ import { TiDBSessionStore } from "./tidb-session-store.js";
|
|
|
43
35
|
import { PgSessionStore } from "./pg-session-storage.js";
|
|
44
36
|
import { TiDBSharedMemoryStore, PgSharedMemoryStore } from "./shared-memory-store-sql.js";
|
|
45
37
|
import { TiDBAdoptionLogStore, PgAdoptionLogStore } from "./adoption-log-sql.js";
|
|
46
|
-
// #270:托管留存的两半场(破坏性三方法 = 车1;租约/hold/审计 = 车2)。SQL 后端专有,local 诚实缺席。
|
|
47
38
|
import { TiDBRetentionStore, PgRetentionStore } from "./retention-store-sql.js";
|
|
48
39
|
import { TiDBRetentionLaneStore, PgRetentionLaneStore } from "./retention-lane-store-sql.js";
|
|
49
|
-
// #154 车二:持久化权限规则店(三面一束)。`mysqlDriver`/`pgDriver` 是既有的方言中立 seam。
|
|
50
|
-
// #203 §1:local 车道的 File 三面束(core 现成 provider + 本仓 File 形审批记录/导入票)。
|
|
51
40
|
import { createSqlPermissionRuleStores } from "./permission-rule-store-sql.js";
|
|
52
41
|
import { createFilePermissionRuleStores } from "./permission-rule-store-file.js";
|
|
53
42
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
54
|
-
/** Build the snapshot-BLOB object-store backend from config, or undefined to fall back to the SqlBlobBackend default.
|
|
55
|
-
* When `config.snapshotBlobStore` (MINIO_* env) is set, the E19/2c file-snapshot stores route ONLY the blob BYTES to
|
|
56
|
-
* MinIO; the MANIFEST stays in SQL (the transcript hot read never touches MinIO). The MinioBlobBackend is given the
|
|
57
|
-
* SAME pool+dialect so it keeps the bytes-NULL `snapshot_blob` INDEX in SQL (the orphan/grace reference-tracking +
|
|
58
|
-
* presence checks read it — see blob-backend.ts). Shared by the tidb + pg backends. */
|
|
59
43
|
function snapshotBlobBackend(config, dialect, pool) {
|
|
60
44
|
const s = config.snapshotBlobStore;
|
|
61
|
-
// (a): no MinIO ⇒ construct the SQL default EXPLICITLY so the per-blob cap (env override or the tidb
|
|
62
|
-
// packet-wall default) rides along — the stores' internal `?? new SqlBlobBackend(...)` fallback would lose it.
|
|
63
45
|
if (!s)
|
|
64
46
|
return new SqlBlobBackend(dialect, pool, config.snapshotBlobSqlMaxBytes);
|
|
65
47
|
return new MinioBlobBackend({
|
|
@@ -72,11 +54,6 @@ function snapshotBlobBackend(config, dialect, pool) {
|
|
|
72
54
|
...(s.presignTtlSec ? { presignTtlSec: s.presignTtlSec } : {}),
|
|
73
55
|
}, { dialect, pool });
|
|
74
56
|
}
|
|
75
|
-
/** (c)(clay 拍 a+c,2026-07-27)云形快照 blob 姿势门——boot 时调用(main.ts),纯函数可单测。
|
|
76
|
-
* SQL 后端(mysql|pg)把 blob 字节落 `snapshot_blob` 单行的形已被两役实证撞 mysql-协议包墙
|
|
77
|
-
* (D-1 附件 → 对象存储裁定;snapshot_blob >31MiB 同墙):大字节归对象存储。云形缺 MinIO ⇒
|
|
78
|
-
* fail-loud(启动了才是假安全);`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true` = 显式接受 SQL 店 +
|
|
79
|
-
* per-blob 帽(单机/测试台形)。local 后端不经此门(file/in-memory 快照店,无 SQL 包墙)。 */
|
|
80
57
|
export function assertCloudSnapshotBlobPosture(kind, config) {
|
|
81
58
|
if (kind === "local" || config.snapshotBlobStore || config.snapshotBlobAllowSql)
|
|
82
59
|
return;
|
|
@@ -86,10 +63,6 @@ export function assertCloudSnapshotBlobPosture(kind, config) {
|
|
|
86
63
|
"same ruling as the D-1 attachment store). Single-box/test rigs may explicitly opt out with " +
|
|
87
64
|
"SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true (a per-blob cap then applies; oversized blobs are rejected 413).");
|
|
88
65
|
}
|
|
89
|
-
/** REWIND_SNAPSHOT_MAX_MB overrides ONLY the byte cap of core's DEFAULT_SNAPSHOT_BOUNDS (raise for
|
|
90
|
-
* big monorepos, lower for tight DBs). File-count / ignoreDirs stay core defaults — the knob answers the one
|
|
91
|
-
* field the 20G-workspace case needed; more knobs when a case shows up. Exported for tests
|
|
92
|
-
* (fractional-MB rounding + partial-merge semantics need a regression lock). */
|
|
93
66
|
export function snapshotBoundsFromConfig(config) {
|
|
94
67
|
const mb = config.rewindSnapshotMaxMb;
|
|
95
68
|
return mb !== undefined ? { ...DEFAULT_SNAPSHOT_BOUNDS, maxBytes: Math.round(mb * 1024 * 1024) } : DEFAULT_SNAPSHOT_BOUNDS;
|
|
@@ -107,32 +80,32 @@ class TiDBBackend {
|
|
|
107
80
|
run() { return new TiDBRunStore(this.pool); }
|
|
108
81
|
resumeAnchor() { return new TiDBResumeAnchorStore(this.pool); }
|
|
109
82
|
approvalExemption() { return new TiDBApprovalExemptionStore(this.pool); }
|
|
110
|
-
approvalAsk() { return new TiDBApprovalAskStore(this.pool); }
|
|
111
|
-
permissionRule() { return createSqlPermissionRuleStores(mysqlDriver(this.pool)); }
|
|
83
|
+
approvalAsk() { return new TiDBApprovalAskStore(this.pool); }
|
|
84
|
+
permissionRule() { return createSqlPermissionRuleStores(mysqlDriver(this.pool)); }
|
|
112
85
|
sendFileLedger() { return new TiDBSendFileLedger(this.pool); }
|
|
113
86
|
sessionPolicy() { return new TiDBSessionPolicyStore(this.pool); }
|
|
114
87
|
fileSnapshot() { return new TiDBFileSnapshotStore(this.pool, snapshotBlobBackend(this.config, "tidb", this.pool), snapshotBoundsFromConfig(this.config)); }
|
|
115
88
|
workflowJournal() { return new TiDBWorkflowJournalStore(this.pool); }
|
|
116
|
-
outcomeSink() { return new TiDBOutcomeLedger(this.pool); }
|
|
117
|
-
workflowRun(onWarn) { return new TiDBWorkflowRunStore(this.pool, onWarn); }
|
|
118
|
-
completionInbox(onWarn) { return new TiDBWorkflowCompletionInbox(this.pool, onWarn); }
|
|
119
|
-
notifyJournal() { return new TiDBWorkflowNotifyJournalStore(this.pool); }
|
|
120
|
-
sharedMemoryStore(authorizer) { return new TiDBSharedMemoryStore(this.pool, authorizer); }
|
|
89
|
+
outcomeSink() { return new TiDBOutcomeLedger(this.pool); }
|
|
90
|
+
workflowRun(onWarn) { return new TiDBWorkflowRunStore(this.pool, onWarn); }
|
|
91
|
+
completionInbox(onWarn) { return new TiDBWorkflowCompletionInbox(this.pool, onWarn); }
|
|
92
|
+
notifyJournal() { return new TiDBWorkflowNotifyJournalStore(this.pool); }
|
|
93
|
+
sharedMemoryStore(authorizer) { return new TiDBSharedMemoryStore(this.pool, authorizer); }
|
|
121
94
|
checkpoint(logger) { return new TiDBCheckpointStore(this.pool, logger); }
|
|
122
95
|
toolResult() { return new TiDBToolResultStore(this.pool); }
|
|
123
96
|
imageIndex() { return new TiDBImageIndex(this.pool); }
|
|
124
97
|
imageBake() { return new TiDBImageBake(this.pool); }
|
|
125
|
-
leaderRun() { return new TiDBLeaderRunStore(this.pool); }
|
|
126
|
-
retention(opts) { return new TiDBRetentionStore(this.pool, opts); }
|
|
127
|
-
retentionLane() { return new TiDBRetentionLaneStore(this.pool); }
|
|
98
|
+
leaderRun() { return new TiDBLeaderRunStore(this.pool); }
|
|
99
|
+
retention(opts) { return new TiDBRetentionStore(this.pool, opts); }
|
|
100
|
+
retentionLane() { return new TiDBRetentionLaneStore(this.pool); }
|
|
128
101
|
breaker(onWriteFail) { return new TiDBBreakerState(this.pool, onWriteFail); }
|
|
129
102
|
costQuota(limitMicroUsd, windowMs, onDegraded) { return new TiDBCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
|
|
130
103
|
rateLimiter(limit, onDegraded) { return new TiDBRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
|
|
131
104
|
session() { return new TiDBSessionStore(this.pool); }
|
|
132
|
-
adoptionLog() { return new TiDBAdoptionLogStore(this.pool); }
|
|
105
|
+
adoptionLog() { return new TiDBAdoptionLogStore(this.pool); }
|
|
133
106
|
mysqlPool() { return this.pool; }
|
|
134
107
|
pgPool() { return undefined; }
|
|
135
|
-
async dbNowMs() { const [rows] = await this.pool.query("SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000) AS ms"); return Number(rows[0]?.ms); }
|
|
108
|
+
async dbNowMs() { const [rows] = await this.pool.query("SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000) AS ms"); return Number(rows[0]?.ms); }
|
|
136
109
|
}
|
|
137
110
|
class PgBackend {
|
|
138
111
|
pool;
|
|
@@ -147,57 +120,33 @@ class PgBackend {
|
|
|
147
120
|
run() { return new PgRunStore(this.pool); }
|
|
148
121
|
resumeAnchor() { return new PgResumeAnchorStore(this.pool); }
|
|
149
122
|
approvalExemption() { return new PgApprovalExemptionStore(this.pool); }
|
|
150
|
-
approvalAsk() { return new PgApprovalAskStore(this.pool); }
|
|
151
|
-
permissionRule() { return createSqlPermissionRuleStores(pgDriver(this.pool)); }
|
|
123
|
+
approvalAsk() { return new PgApprovalAskStore(this.pool); }
|
|
124
|
+
permissionRule() { return createSqlPermissionRuleStores(pgDriver(this.pool)); }
|
|
152
125
|
sendFileLedger() { return new PgSendFileLedger(this.pool); }
|
|
153
126
|
sessionPolicy() { return new PgSessionPolicyStore(this.pool); }
|
|
154
127
|
fileSnapshot() { return new PgFileSnapshotStore(this.pool, snapshotBlobBackend(this.config, "pg", this.pool), snapshotBoundsFromConfig(this.config)); }
|
|
155
128
|
workflowJournal() { return new PgWorkflowJournalStore(this.pool); }
|
|
156
|
-
outcomeSink() { return new PgOutcomeLedger(this.pool); }
|
|
157
|
-
workflowRun(onWarn) { return new PgWorkflowRunStore(this.pool, onWarn); }
|
|
158
|
-
completionInbox(onWarn) { return new PgWorkflowCompletionInbox(this.pool, onWarn); }
|
|
159
|
-
notifyJournal() { return new PgWorkflowNotifyJournalStore(this.pool); }
|
|
160
|
-
sharedMemoryStore(authorizer) { return new PgSharedMemoryStore(this.pool, authorizer); }
|
|
129
|
+
outcomeSink() { return new PgOutcomeLedger(this.pool); }
|
|
130
|
+
workflowRun(onWarn) { return new PgWorkflowRunStore(this.pool, onWarn); }
|
|
131
|
+
completionInbox(onWarn) { return new PgWorkflowCompletionInbox(this.pool, onWarn); }
|
|
132
|
+
notifyJournal() { return new PgWorkflowNotifyJournalStore(this.pool); }
|
|
133
|
+
sharedMemoryStore(authorizer) { return new PgSharedMemoryStore(this.pool, authorizer); }
|
|
161
134
|
checkpoint(logger) { return new PgCheckpointStore(this.pool, logger); }
|
|
162
135
|
toolResult() { return new PgToolResultStore(this.pool); }
|
|
163
136
|
imageIndex() { return new PgImageIndex(this.pool); }
|
|
164
137
|
imageBake() { return new PgImageBake(this.pool); }
|
|
165
|
-
leaderRun() { return new PgLeaderRunStore(this.pool); }
|
|
166
|
-
retention(opts) { return new PgRetentionStore(this.pool, opts); }
|
|
167
|
-
retentionLane() { return new PgRetentionLaneStore(this.pool); }
|
|
138
|
+
leaderRun() { return new PgLeaderRunStore(this.pool); }
|
|
139
|
+
retention(opts) { return new PgRetentionStore(this.pool, opts); }
|
|
140
|
+
retentionLane() { return new PgRetentionLaneStore(this.pool); }
|
|
168
141
|
breaker(onWriteFail) { return new PgBreakerState(this.pool, onWriteFail); }
|
|
169
142
|
costQuota(limitMicroUsd, windowMs, onDegraded) { return new PgCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
|
|
170
143
|
rateLimiter(limit, onDegraded) { return new PgRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
|
|
171
144
|
session() { return new PgSessionStore(this.pool); }
|
|
172
|
-
adoptionLog() { return new PgAdoptionLogStore(this.pool); }
|
|
145
|
+
adoptionLog() { return new PgAdoptionLogStore(this.pool); }
|
|
173
146
|
mysqlPool() { return undefined; }
|
|
174
147
|
pgPool() { return this.pool; }
|
|
175
|
-
async dbNowMs() { const res = await this.pool.query("SELECT ROUND(EXTRACT(EPOCH FROM now())*1000) AS ms"); return Number(res.rows[0]?.ms); }
|
|
148
|
+
async dbNowMs() { const res = await this.pool.query("SELECT ROUND(EXTRACT(EPOCH FROM now())*1000) AS ms"); return Number(res.rows[0]?.ms); }
|
|
176
149
|
}
|
|
177
|
-
/**
|
|
178
|
-
* LOCAL backend (clay 2026-06-25 seamless local↔cloud; P0.5 variant-2 + variant-1 file-backed 2026-06-26): a DB-less StoreBackend
|
|
179
|
-
* so a locally-run HTTP service serves the SAME contract a cloud worker does (runs + trace + sessions + resume;
|
|
180
|
-
* long-term memory rides the design/138 memory ENGINE, not this backend). Sessions / per-session policy / file snapshots are
|
|
181
|
-
* FILE-BACKED via core's `FileStorageBackend` over `root`, so a session SURVIVES a process restart and the resume
|
|
182
|
-
* picker (GET /v1/sessions → LocalSessionStore.listSessions) is restart-visible — the 2c "first local, then sync"
|
|
183
|
-
* hard prerequisite.
|
|
184
|
-
*
|
|
185
|
-
* The session store stays the §0.5 owner-aware {@link LocalSessionStore} wrapper, but over a fresh `FileSessionRepo`
|
|
186
|
-
* (NOT `fileBackend.sessionStore`, which is core's bare `TtlSessionStore` lacking the §0.5 owner/list/fork/delete
|
|
187
|
-
* ops); policy/snapshot delegate to the fileBackend's own file stores. The fileBackend takes a coarse boot
|
|
188
|
-
* single-instance lock on `root/LOCK` (an `O_EXCL`+`linkSync` PIDFILE, not flock — [2557] F4; same-host only:
|
|
189
|
-
* liveness = `process.kill(pid,0)`, so a foreign PID namespace or NFS defeats it, see DEPLOY-PREREQS) —
|
|
190
|
-
* `close()` MUST `dispose()` it so a graceful restart can
|
|
191
|
-
* re-acquire (a stale lock from a dead PID is pruned by core, but graceful restart relies on the release).
|
|
192
|
-
*
|
|
193
|
-
* P0.5 variant-1 (clay 2026-06-26): `run()`/`resumeAnchor()` are now FILE-backed too (FileRunStore /
|
|
194
|
-
* FileResumeAnchorStore over core 1.138.0's crash-safe fs primitives — atomicWriteFile/writeThenLink/AppendLog), so the
|
|
195
|
-
* runs ledger + trace event log + E18 anchors SURVIVE a process restart, completing variant-2's promised follow-on. The
|
|
196
|
-
* picker still lists from the session abstraction (variant-2), but a restart now also re-reads run history + resume
|
|
197
|
-
* anchors. Owner identity is still process-local (single-user acceptable; durable owners = P1). The cloud-only stores
|
|
198
|
-
* (checkpoint/approval/toolResult/imageIndex/imageBake/breaker/costQuota/rateLimiter) are OMITTED → the consumers'
|
|
199
|
-
* `backend?.X?.()` gates degrade them exactly like a no-DB env.
|
|
200
|
-
*/
|
|
201
150
|
class LocalBackend {
|
|
202
151
|
kind = "local";
|
|
203
152
|
fileBackend;
|
|
@@ -205,51 +154,19 @@ class LocalBackend {
|
|
|
205
154
|
sessionStore;
|
|
206
155
|
resumeAnchorStore;
|
|
207
156
|
approvalExemptionStore;
|
|
208
|
-
/** #151 —— 进程内易失(见 approvalAsk() 的注);File 形落地前它就是 local 车道的占位。 */
|
|
209
157
|
approvalAskStore = new InMemoryApprovalAskStore();
|
|
210
158
|
sendFileLedgerStore;
|
|
211
|
-
// SVC-2: FILE-backed durable journal (was core's InMemory — a TOC restart silently resolved
|
|
212
|
-
// resumeFromRunId to an empty journal → full live re-run). Now restart-durable on one box; crash-safe JSONL,
|
|
213
|
-
// scope-guarded, oversize skip-journal — byte-matched degrade with the SQL twins. Singleton: fds live here.
|
|
214
159
|
workflowJournalStore;
|
|
215
|
-
/** design/73 §1 — owner-only JSONL outcome-fact sink (stateless append; no fd held). */
|
|
216
160
|
outcomeSinkInst;
|
|
217
|
-
/** boot 期的告警口(`openStoreBackendWithFallback` 把它接到 logger.warn 上)。可选:直接 `new` 一只
|
|
218
|
-
* LocalBackend 的测试/工具不必装配日志面 —— 缺席时 core 的披露仍留在 core 那侧,只是本仓不复述。 */
|
|
219
161
|
onWarn;
|
|
220
|
-
/** boot 期的 **error 级**口(F-D):`FileRunStore.hydrate` 的 readdir 失败按三处成文(本文件下方接线处 /
|
|
221
|
-
* `fail-open.ts` 的 tag note / 普查表第 29 行)承诺的是 **error 级**披露,而 warn 座满足不了那句话 ——
|
|
222
|
-
* 按 `level >= error` 配告警的运维会一条都收不到。可选:缺席时退回 {@link onWarn}(降一级好过无声)。 */
|
|
223
162
|
onError;
|
|
224
163
|
constructor(root, config, onWarn, onError) {
|
|
225
164
|
this.onWarn = onWarn;
|
|
226
165
|
this.onError = onError;
|
|
227
|
-
// The fileBackend ctor mkdirs `root` + takes the boot pidfile lock — a same-host SECOND instance FAILS FAST.
|
|
228
|
-
// Re-throw with a clear operator message (the raw core error is "another instance owns this data dir").
|
|
229
|
-
//
|
|
230
|
-
// 🔴 core 5.23.0([3372] 提货批件④):这个构造器现在有**第二个**拒绝面 —— design/183 的 I6 adoption
|
|
231
|
-
// boot 门(`dist/stores/file/index.js:45` 的 `assertAdoptionBootGate`):数据根下 `adoption.json` 是
|
|
232
|
-
// 在飞标记 ⇒ `AdoptionError("adoption_in_flight")`,标记读不出 ⇒ `"adoption_marker_corrupt"`。
|
|
233
|
-
// 原来的 catch 是 **catch-all**,会把它一并改写成下面那句 boot-lock 文案 —— 那是**误诊**:它给的两条
|
|
234
|
-
// 出路对 adoption 在飞都不对,而「把 LOCAL_DATA_ROOT 指到一个空目录」更是直接劝运维**丢下一个迁移
|
|
235
|
-
// 到一半的数据根**;`AdoptionError` 的类型身份与 typed `code` 也在改写中被抹掉。
|
|
236
|
-
// ⇒ `AdoptionError` **原样穿过**:core 的拒绝句本就是写给人读的(点名在飞的那条弧,并给出「resume
|
|
237
|
-
// adoptLocalDataRoot 到完成,或修 adoption.json」的出路),我们没有更好的话可说,包一层只会更差。
|
|
238
|
-
// 包装只留给真正的 boot-lock 类失败(那句文案的适用条件)。
|
|
239
166
|
try {
|
|
240
167
|
this.fileBackend = new FileStorageBackend({
|
|
241
168
|
root,
|
|
242
|
-
...(config.rewindSnapshotMaxMb !== undefined ? { snapshotBounds: { maxBytes: Math.round(config.rewindSnapshotMaxMb * 1024 * 1024) } } : {}),
|
|
243
|
-
// 🔴 腐读披露座(交接件④,与 `run-local.ts` 的同座同形、同 warn 名 `file_store_corrupt_read`)。
|
|
244
|
-
// core 对**读不出来的**持久文件是 documented fail-open —— 当作「缺席」继续跑(抛会让整条任务死,
|
|
245
|
-
// 也会自锁修复写)。这条 fail-open 是 core 的裁定,本仓不改它;但它**不许无声**(#157 安全轴纪律)。
|
|
246
|
-
// 此前 HTTP 服务的 local 车道**根本没接这个座**:同一份坏字节,run-local 上打一行、服务上一个字
|
|
247
|
-
// 都没有 —— 而服务形恰恰是没人盯着终端的那一个。
|
|
248
|
-
//
|
|
249
|
-
// ⚠️ 文案按脸分列后果、**不下统一结论**(run-local 那次 codex R2-medium 的教训一并吸收):一个座
|
|
250
|
-
// 覆盖 core 转发的三张脸,后果各不相同 —— 把「少了会话规则这一层」写成「整个任务无约束」会误导
|
|
251
|
-
// 事故定级,而且对另外两张脸根本不成立。共同事实只有一句:一次持久读被当成了缺席。
|
|
252
|
-
// ENOENT 不触发(那是真缺席,不是腐读)。
|
|
169
|
+
...(config.rewindSnapshotMaxMb !== undefined ? { snapshotBounds: { maxBytes: Math.round(config.rewindSnapshotMaxMb * 1024 * 1024) } } : {}),
|
|
253
170
|
onCorruptRead: (info) => this.onWarn?.("file_store_corrupt_read", {
|
|
254
171
|
path: info.path,
|
|
255
172
|
reason: info.reason,
|
|
@@ -268,21 +185,7 @@ class LocalBackend {
|
|
|
268
185
|
throw new Error(`DB_BACKEND=local cannot open data dir ${root}: ${e instanceof Error ? e.message : String(e)} ` +
|
|
269
186
|
"(another HTTP service / run-local instance may own it; stop it first or set LOCAL_DATA_ROOT to a free dir)");
|
|
270
187
|
}
|
|
271
|
-
// §0.5 owner-aware wrapper over a fresh FileSessionRepo on the SAME root (NOT fileBackend.sessionStore — that's
|
|
272
|
-
// the bare TtlSessionStore without the §0.5 owner/list/fork/delete ops). Both touch root/sessions/*.jsonl.
|
|
273
|
-
// Pass `root` as the lastActivity sidecar dir so the /resume picker's newest-first order survives a restart (TOC #10):
|
|
274
|
-
// without it the in-memory recency map starts empty on boot and every session degrades to createdAt ordering.
|
|
275
188
|
this.sessionStore = new LocalSessionStore(new FileSessionRepo(this.fileBackend.root), this.fileBackend.root);
|
|
276
|
-
// P0.5 variant-1: file-backed runs ledger + E18 anchors over the SAME canonical root (under root/runs + root/
|
|
277
|
-
// resume-anchors). Hydrate the in-memory index from disk on construction → restart-visible.
|
|
278
|
-
// 件6(#43 案 [4281]):hydrate 的 readdir 失败要**响亮**——把 boot 告警口接过去(缺席则只走 fail-open
|
|
279
|
-
// 遥测那一半)。包一层箭头而不是直接传方法引用:后者会把方法从接收者上摘下来,带 `this` 的实现会在
|
|
280
|
-
// 第一次真披露时炸,而那一刻正是最不该炸的时候(本文件 :658 同族先例)。
|
|
281
|
-
// 🔴 F-D(2026-08-17 合并码扫描):这一座必须接 **error** 口。件6 三处成文(FileRunStore 那条 catch 注 /
|
|
282
|
-
// `fail-open.ts` 的 tag note / 普查表第 29 行)承诺的都是「一行 **error 级**启动日志」,而修前它被绑到
|
|
283
|
-
// `onWarn` 上、且 `openStoreBackendWithFallback` 的 logger 参数类型里压根没有 error 通道 ⇒ 生产恒 warn,
|
|
284
|
-
// 按 `level >= error` 配告警的运维一条都收不到,而这条债的全部价值就是「恒 miss 不再被当成本来就没有」。
|
|
285
|
-
// `onError` 缺席(只装配了 warn 座的调用方)⇒ 退回 warn 座:降一级好过无声。
|
|
286
189
|
this.runStore = new FileRunStore(this.fileBackend.root, {
|
|
287
190
|
error: (msg, fields) => void (this.onError ?? this.onWarn)?.(msg, fields),
|
|
288
191
|
});
|
|
@@ -292,47 +195,32 @@ class LocalBackend {
|
|
|
292
195
|
this.workflowJournalStore = new FileWorkflowJournalStore(this.fileBackend.root);
|
|
293
196
|
this.outcomeSinkInst = new FileOutcomeSink(this.fileBackend.root);
|
|
294
197
|
}
|
|
295
|
-
async ensureSchema() { }
|
|
198
|
+
async ensureSchema() { }
|
|
296
199
|
async close() {
|
|
297
|
-
await this.sessionStore.dispose();
|
|
298
|
-
this.runStore.dispose();
|
|
299
|
-
this.resumeAnchorStore.dispose();
|
|
300
|
-
this.approvalExemptionStore.dispose();
|
|
301
|
-
this.sendFileLedgerStore.dispose();
|
|
302
|
-
this.workflowJournalStore.dispose();
|
|
303
|
-
this.checkpointStoreInst?.close();
|
|
304
|
-
// #203 §1: release core's permission-rule DIRECTORY write lock (lazy — may never have been built).
|
|
305
|
-
// Same discipline as root/LOCK below: an un-released writer lock tells the next boot that a live process
|
|
306
|
-
// owns the rule directory, and core refuses the second holder LOUDLY rather than queueing.
|
|
200
|
+
await this.sessionStore.dispose();
|
|
201
|
+
this.runStore.dispose();
|
|
202
|
+
this.resumeAnchorStore.dispose();
|
|
203
|
+
this.approvalExemptionStore.dispose();
|
|
204
|
+
this.sendFileLedgerStore.dispose();
|
|
205
|
+
this.workflowJournalStore.dispose();
|
|
206
|
+
this.checkpointStoreInst?.close();
|
|
307
207
|
this.permissionRuleStoresInst?.dispose();
|
|
308
|
-
await this.fileBackend.dispose();
|
|
208
|
+
await this.fileBackend.dispose();
|
|
309
209
|
}
|
|
310
210
|
run() { return this.runStore; }
|
|
311
|
-
resumeAnchor() { return this.resumeAnchorStore; }
|
|
312
|
-
approvalExemption() { return this.approvalExemptionStore; }
|
|
313
|
-
/** #151 流内审批协议持久层的 local 形。**必须**是 singleton:状态全在实例的两个 Map 里,每次
|
|
314
|
-
* new 一个就等于每个消费者各拿一份互不可见的账(而 SQL twin 天然共享一张表)。易失代价 +
|
|
315
|
-
* 「local 车道默认不上协议」的裁定见 StoreBackend.approvalAsk() 的接口注。 */
|
|
211
|
+
resumeAnchor() { return this.resumeAnchorStore; }
|
|
212
|
+
approvalExemption() { return this.approvalExemptionStore; }
|
|
316
213
|
approvalAsk() { return this.approvalAskStore; }
|
|
317
|
-
/** #203 §1:local 车道的 File 三面束。**必须**是 singleton —— core 的 File provider 在第一次取写面时
|
|
318
|
-
* 对规则目录取一把进程级写锁,第二个持有者被**响亮拒绝**(不是排队),所以每次 `new` 一个就等于给
|
|
319
|
-
* 自己造一次「另一个进程占着这个目录」的假象。`close()` 里 `dispose()` 释放它(与数据根 `root/LOCK`
|
|
320
|
-
* 同一条纪律:不释放 ⇒ 优雅重启被自己上一条命留下的锁挡在门外)。惰性建:没开旋钮的部署不该因为
|
|
321
|
-
* 一个从不使用的面而在数据根里长出目录、更不该白占一把写锁。 */
|
|
322
214
|
permissionRule() {
|
|
323
215
|
if (!this.permissionRuleStoresInst) {
|
|
324
216
|
this.permissionRuleStoresInst = createFilePermissionRuleStores(this.fileBackend.root, {
|
|
325
|
-
// core 规则文件的披露面(读不出 / 校验和不符 / 撞上符号链接 ⇒ 答零规则**并说出来**)。
|
|
326
|
-
// 不接住它就是一次静默的 fail-closed:用户突然被反复询问,而日志里一个字都没有。
|
|
327
217
|
onError: (message) => this.onWarn?.("permission_rule_store_disclosure", { message }),
|
|
328
218
|
});
|
|
329
219
|
}
|
|
330
220
|
return this.permissionRuleStoresInst;
|
|
331
221
|
}
|
|
332
222
|
permissionRuleStoresInst;
|
|
333
|
-
sendFileLedger() { return this.sendFileLedgerStore; }
|
|
334
|
-
/** Durable HITL / plan-review parking on the LOCAL lane. Lazy singleton (the inner core store holds
|
|
335
|
-
* an append fd). taskId join ← FileRunStore; E21 owner guard ← LocalSessionStore.ownerOf (same root). */
|
|
223
|
+
sendFileLedger() { return this.sendFileLedgerStore; }
|
|
336
224
|
checkpoint() {
|
|
337
225
|
if (!this.checkpointStoreInst) {
|
|
338
226
|
const cs = new LocalCheckpointStore(this.fileBackend.root, {
|
|
@@ -340,14 +228,6 @@ class LocalBackend {
|
|
|
340
228
|
ownerOf: (sessionId) => this.sessionStore.ownerOf(sessionId),
|
|
341
229
|
});
|
|
342
230
|
this.checkpointStoreInst = cs;
|
|
343
|
-
// [868] the reverse join: FileRunStore's suspended-run reapers cannot JOIN the checkpoint plane (separate
|
|
344
|
-
// store), so they take the two EXISTS predicates as an injected probe — wired HERE because this is the one
|
|
345
|
-
// place both stores exist over the same root. Without it the reapers stay honest NO-OPs, and an abandoned
|
|
346
|
-
// durable HITL park (approval never answered) held its task_active claim forever (session locked; the
|
|
347
|
-
// [868] incident). NB the probe only tells the reaper what the SQL twins read via JOIN — the reaper's
|
|
348
|
-
// D-D NOT-pending guard still decides; and the APPROVAL_TIMEOUT_SEC=0 default still keeps the time-based
|
|
349
|
-
// leg OFF (durable HITL waits for a human indefinitely BY DESIGN; the checkpoint-expiry leg + the cancel
|
|
350
|
-
// verb are the recovery handles).
|
|
351
231
|
this.runStore.setCheckpointProbe({
|
|
352
232
|
hasPending: async (sessionId) => (await cs.findPendingTokenBySession(sessionId)) !== null,
|
|
353
233
|
hasExpired: (sessionId) => cs.hasExpiredBySession(sessionId),
|
|
@@ -356,53 +236,24 @@ class LocalBackend {
|
|
|
356
236
|
return this.checkpointStoreInst;
|
|
357
237
|
}
|
|
358
238
|
checkpointStoreInst;
|
|
359
|
-
sessionPolicy() { return this.fileBackend.sessionPolicyStore; }
|
|
360
|
-
fileSnapshot() { return this.fileBackend.fileSnapshotStore; }
|
|
361
|
-
workflowJournal() { return this.workflowJournalStore; }
|
|
362
|
-
outcomeSink() { return this.outcomeSinkInst; }
|
|
239
|
+
sessionPolicy() { return this.fileBackend.sessionPolicyStore; }
|
|
240
|
+
fileSnapshot() { return this.fileBackend.fileSnapshotStore; }
|
|
241
|
+
workflowJournal() { return this.workflowJournalStore; }
|
|
242
|
+
outcomeSink() { return this.outcomeSinkInst; }
|
|
363
243
|
session() { return this.sessionStore; }
|
|
364
|
-
// core 1.219 (dogfood finding "ReadToolResult(ref) 恒空"): the TOC lane now has a DURABLE tool-result
|
|
365
|
-
// store — core's FileToolResultStore under the same data root (one file per ref, write-once, restart-durable).
|
|
366
|
-
// In-process cross-task deref was already fixed core-side (1.219 Runner-shared fallback); this adds the
|
|
367
|
-
// cross-RESTART half. deleteBySession:core 5.28 起 FileToolResultStore **有**(R3 复扫更正过一次、
|
|
368
|
-
// R4 复扫再纠:上一版注写成「两者都有」过头了——`reapOlderThan` 在 core 5.28.0 dist 全文零命中,
|
|
369
|
-
// TTL 腿仍是 SQL twins only,reapers.ts 的 `?.` 可选链在 local 上永久 no-op,别拆)。deleteBySession
|
|
370
|
-
// 按 .owner.json 边车判属主,无边车的存量计 unattributable 不删,与 SQL 孪生同口径。
|
|
371
244
|
toolResult() { return this.fileBackend.toolResultStore; }
|
|
372
245
|
mysqlPool() { return undefined; }
|
|
373
246
|
pgPool() { return undefined; }
|
|
374
247
|
}
|
|
375
|
-
/**
|
|
376
|
-
* 启动日志里「跨副本 or 进程内」那一格的标签。
|
|
377
|
-
*
|
|
378
|
-
* 🔴 判据必须是**那个 store 本身在不在**,不是「有没有 backend」(2026-07-31 缝合审)。
|
|
379
|
-
* `LocalBackend` 明确省略 `costQuota`/`rateLimiter`(见它类尾那行 "intentionally absent"),
|
|
380
|
-
* 而 `DB_BACKEND=local` 是**裸 boot 的默认值** —— 原来 main.ts 那两行用 `backend ? "shared()" : …` 判,
|
|
381
|
-
* 于是单机默认形配了 `RATE_LIMIT_PER_MIN` 会打出 `shared()`,实际跑的是每副本一份的进程内限流器。
|
|
382
|
-
* 运维扩到 N 副本时读这行判定"限流是跨副本的",真实额度是 N 倍。
|
|
383
|
-
* (同一个说谎病族在紧邻的 `breakerState` 上修过一次——"was hard-coded 'shared(tidb)' — lied under
|
|
384
|
-
* DB_BACKEND=pg"——`session:` 也跟着修了,这两行没跟上。顺带:原来的 `shared()` 括号是空的,
|
|
385
|
-
* 而兄弟行都写 `shared(${kind})`。)
|
|
386
|
-
*/
|
|
387
248
|
export function counterStoreLabel(store, kind) {
|
|
388
249
|
return store ? `shared(${kind ?? "unknown"})` : "in-process";
|
|
389
250
|
}
|
|
390
|
-
/** Build the durable-store backend for `config.dbBackend`. Does NOT connect (the pool connects lazily; the
|
|
391
|
-
* reachability probe is the caller's first `ensureSchema`). Returns undefined when no DB is configured.
|
|
392
|
-
*
|
|
393
|
-
* `onWarn` (additive, optional) is the boot-time warning sink the LOCAL backend needs for core's
|
|
394
|
-
* permission-rule DISCLOSURE face (#203 §1): an unreadable / checksum-mismatched / symlinked rule file
|
|
395
|
-
* answers ZERO rules and says so — swallowing that turns a fail-closed degrade into a silent one.
|
|
396
|
-
* `onError` (additive, optional; F-D) is the **error-level** twin of that seat — 件6 的
|
|
397
|
-
* `file_run_store_hydrate_unreadable` 按成文承诺是 error 级,warn 座满足不了那句话。两座都是**加项**:
|
|
398
|
-
* 既有一/二参调用方逐字不变(缺席 ⇒ 该披露退回 warn 座 / 只剩 fail-open 遥测那一半)。 */
|
|
399
251
|
export function createStoreBackend(config, onWarn, onError) {
|
|
400
252
|
if (config.dbBackend === "local")
|
|
401
253
|
return new LocalBackend(config.localDataRoot ?? join(homedir(), ".ai-agent"), config, onWarn, onError);
|
|
402
254
|
if (config.dbBackend === "pg") {
|
|
403
255
|
if (!config.pg)
|
|
404
256
|
return undefined;
|
|
405
|
-
// pgPoolOptions also arms the DB_QUERY_TIMEOUT_MS pool-wide per-query ceiling (S9 deeper fix) when set.
|
|
406
257
|
const pool = createPgPool(pgPoolOptions(config.pg, config.dbQueryTimeoutMs));
|
|
407
258
|
return new PgBackend(pool, config);
|
|
408
259
|
}
|
|
@@ -410,50 +261,19 @@ export function createStoreBackend(config, onWarn, onError) {
|
|
|
410
261
|
return undefined;
|
|
411
262
|
return new TiDBBackend(createTidbPool(config), config);
|
|
412
263
|
}
|
|
413
|
-
|
|
414
|
-
*
|
|
415
|
-
* 为什么必须把 `createStoreBackend()` 也罩进来(2026-07-28 修):clay 1.292 拍的口径是「裸 boot
|
|
416
|
-
* **默认推导**的 local 在只读文件系统/mkdir 失败时不得拒启」,但原先 try 只罩 `ensureSchema()`——
|
|
417
|
-
* 而 LocalBackend 的 `ensureSchema` 是**空实现**,真正会抛的 mkdir + 数据根 BootLock 在构造函数里、
|
|
418
|
-
* 落在 try 之外。于是那条降级臂对 local 形从来没生效过,注释宣称的行为与代码真做的事撕裂。
|
|
419
|
-
*
|
|
420
|
-
* 降级条件不变(两形同一姿势):`sessionBackend=auto`(配置的 DB 不可达,S5 原形)或**默认推导**的
|
|
421
|
-
* local(`dbBackend=local && !dbBackendExplicit`)。**显式** DB_BACKEND=local / tidb 仍 fail-loud——
|
|
422
|
-
* operator 要了 durable,静默丢=丢数据。 */
|
|
423
|
-
export async function openStoreBackendWithFallback(config,
|
|
424
|
-
// 🔴 `error` 是**可选加项**(F-D):件6 的 hydrate 披露按成文承诺是 error 级,而本参数此前只有 warn 通道
|
|
425
|
-
// ⇒ 那句承诺在生产链上结构性地兑不了(注释说 error、代码只出 warn,正是本文件下方 :659 自陈的病族)。
|
|
426
|
-
// 仍可选:只装配 warn 座的调用方(测试/工具)逐字不变,它们的 hydrate 披露退回 warn 座。
|
|
427
|
-
logger) {
|
|
428
|
-
// 判据按后端分岔(2026-07-28 同批修):原先写的是 `sessionBackend === "auto" || (local && !explicit)`,
|
|
429
|
-
// 而 `auto` 是 SESSION_BACKEND 的**默认值** ⇒ 显式 `DB_BACKEND=local` 的部署照样落进第一条臂被静默降级,
|
|
430
|
-
// 「显式仍 fail-loud」这句话同样从未生效。local 形只看 DB_BACKEND 是不是显式;SQL 形维持 S5 原义
|
|
431
|
-
// (auto=尽力而为可降级,显式 tidb/pg=fail-loud,静默丢持久化会丢 run)。
|
|
264
|
+
export async function openStoreBackendWithFallback(config, logger) {
|
|
432
265
|
const mayFallback = config.dbBackend === "local" ? !config.dbBackendExplicit : config.sessionBackend === "auto";
|
|
433
266
|
let backend;
|
|
434
267
|
try {
|
|
435
|
-
// 包一层箭头而不是直接传 `logger.warn`:后者会把方法从它的接收者上摘下来,一个带 `this` 的
|
|
436
|
-
// logger 实现会在第一次真披露时炸——而那一刻正是最不该炸的时候(本仓 fail-loud 病族先例)。
|
|
437
|
-
// error 座同款包法;调用方没有 error 通道时**不传**(LocalBackend 那侧据此退回 warn 座)。
|
|
438
268
|
backend = createStoreBackend(config, (msg, meta) => logger.warn(msg, meta), logger.error === undefined ? undefined : (msg, meta) => logger.error(msg, meta));
|
|
439
|
-
// connects + creates schema (also the reachability probe).
|
|
440
269
|
if (backend)
|
|
441
270
|
await backend.ensureSchema();
|
|
442
271
|
}
|
|
443
272
|
catch (err) {
|
|
444
|
-
// 🔴 core 5.23.0([3372] 提货批,codex 复审 high 抓获、验真后修):**I6 adoption 门不许被降级臂吃掉**。
|
|
445
|
-
// 1.292 拍的降级裁定说的是「只读 FS / mkdir 失败」那一类——它们的意思是「这台机器存不了盘」,降级到
|
|
446
|
-
// 内存是诚实的等价物。`AdoptionError` 说的是完全不同的一句话:「这份数据**有主、正在搬**」(design/183
|
|
447
|
-
// I6:数据根的 `adoption.json` 在飞,或标记读不出)。对后者降级 = **在搬家途中另开一个写者**,而那正是
|
|
448
|
-
// I6 门存在的全部理由;更坏的是它还会留下一行「DB 不可达」的 warn,把运维支去查连通性。
|
|
449
|
-
// ⇒ 与显式 DB_BACKEND 同待遇:无条件 fail-loud,穿过 `mayFallback`。
|
|
450
273
|
if (err instanceof AdoptionError)
|
|
451
274
|
throw err;
|
|
452
275
|
if (!mayFallback)
|
|
453
276
|
throw err;
|
|
454
|
-
// S5 (SILENT-FALLBACK P0-b): one boot warn was the ONLY trace of "this replica is not persisting".
|
|
455
|
-
// The caller renders the gauge + /health twin flag so a degraded replica is distinguishable from
|
|
456
|
-
// an in-memory-by-choice one.
|
|
457
277
|
logger.warn("store_db_unreachable_fallback_memory", {
|
|
458
278
|
backend: backend?.kind ?? config.dbBackend,
|
|
459
279
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
import { parseJsonOr, toIso as iso, normalizeIsoOrNull as isoOrNull } from "./sql-row-helpers.js";
|
|
2
|
-
/**
|
|
3
|
-
* 「这一行已经走到**终局**了吗」——`RunRecord["status"]`(= `"running" | TaskStatus`)的**穷举**判据,
|
|
4
|
-
* 与类型定义同住一个文件(词表的属主 = 判据的属主)。
|
|
5
|
-
*
|
|
6
|
-
* 🔴 为什么是 `switch` 而不是一张 `ReadonlySet<string>`(2026-08-08,#176 二轮扫描 finding):终局词表
|
|
7
|
-
* **真变过**(core 5.8.0 把 `"timeout"` 从 `TaskStatus` 里删了)。而集合形的门对象是 `string`,对词表
|
|
8
|
-
* 两个方向都无钉:
|
|
9
|
-
* · **增词**(core 新加一个终局态)⇒ 集合不含它 ⇒ 该行被静默判成「非终局」,收敛器会一直等一个
|
|
10
|
-
* 永远不会再动的行;
|
|
11
|
-
* · **删词** ⇒ 集合里留一个死字面量,没有任何东西会说话。
|
|
12
|
-
* 穷举 `switch` 把两个方向都变成**编译期**问题:core 加成员 ⇒ `default` 臂收不敛到 `never` ⇒ tsc 红,
|
|
13
|
-
* 加成员的人被迫当场回答「这个新态算不算终局」;删成员 ⇒ 对应 `case` 变成不可达字面量、同样红。
|
|
14
|
-
* 这条判据不许再被手抄成第二份 —— 两个消费点(`approval-reconciler.ts` 判据 2、`http/routes/runs.ts`
|
|
15
|
-
* 的取消后复读)都从这里取。
|
|
16
|
-
*
|
|
17
|
-
* 词表现状:终局 = completed / failed / blocked;非终局 = running / suspended / needs_review
|
|
18
|
-
* (suspended 与 needs_review 都还能再动,`running` 更不必说)。
|
|
19
|
-
*/
|
|
20
2
|
export function isTerminalRunStatus(status) {
|
|
21
3
|
switch (status) {
|
|
22
4
|
case "completed":
|
|
@@ -28,35 +10,12 @@ export function isTerminalRunStatus(status) {
|
|
|
28
10
|
case "needs_review":
|
|
29
11
|
return false;
|
|
30
12
|
default: {
|
|
31
|
-
// core 的 `TaskStatus` 出了新成员 ⇒ 这里收不敛到 never ⇒ tsc 红(整条判据的**编译期**执行点)。
|
|
32
13
|
const unhandled = status;
|
|
33
|
-
// 🔴 运行时**必须**照旧返回 false(#176 codex R1 真缺陷,红先复现):`never` 只活在编译期,
|
|
34
|
-
// `return unhandled` 在运行时返回的是那个字符串本身 = **truthy**。而 `plugins/run-store-sql.ts` 的
|
|
35
|
-
// rowToRun 把无约束的 SQL 文本列**裸 cast** 成本联合 ⇒ 滚动升级里一个更新的副本写入的新状态词
|
|
36
|
-
// 会被这里读到。判成「终局」的代价不可逆:approval-reconciler 判据 2 会把一张还活着的 ask 永久
|
|
37
|
-
// 打成 DENIED/routing_failure,取消路由也会把这一行当终局上报。未知 ⇒ 保守判**非终局**(落 hold,
|
|
38
|
-
// 下一轮重试),与本函数取代的那份 `ReadonlySet<string>.has()` 逐字节同行为。
|
|
39
14
|
void unhandled;
|
|
40
15
|
return false;
|
|
41
16
|
}
|
|
42
17
|
}
|
|
43
18
|
}
|
|
44
|
-
/**
|
|
45
|
-
* 「这一行**停在门上**(park)了吗」——`RunRecord["status"]` 的第二张穷举判据,与 {@link isTerminalRunStatus}
|
|
46
|
-
* 同住词表属主处(扫描P2,2026-08-12)。
|
|
47
|
-
*
|
|
48
|
-
* 🔴 为什么必须有名字:park 是**两个词**,不是一个 —— `suspended`(tool_approval / policy_ask / human /
|
|
49
|
-
* resource_limit 门)与 `needs_review`(`plan_review` / dry-run 拦截门,写点 = 各 store 的
|
|
50
|
-
* `setNeedsReview`)。两者的行为面**完全一致**:行还活着、`task_active` claim 还占着、checkpoint 还能被
|
|
51
|
-
* 决议。凡是「行是不是还停着」的判断只手抄 `=== "suspended"` 的地方,对 plan_review 腿一律给出**相反**
|
|
52
|
-
* 结论 —— 它会被当成终局:取消谎报 "already terminal — no-op"(claim 从此无人释放,正是 [868] 那个把会话
|
|
53
|
-
* 锁死的指纹)、trace 面无帧、leader 面不触发 park 反应。这条判据的存在就是为了不再有第三次手抄。
|
|
54
|
-
*
|
|
55
|
-
* 未知词的方向与 {@link isTerminalRunStatus} 同向保守:判**不是** park。理由是消费点会据此做**不可逆**
|
|
56
|
-
* 动作(cancel 的恢复把手会 CAS-expire 一张 checkpoint);对一个本进程读不懂的状态,少动一次远好过错杀。
|
|
57
|
-
* 真正的执法点是下面那条 `never`:core 的 `TaskStatus` 一加成员就 tsc 红,加成员的人必须当场回答
|
|
58
|
-
* 「这个新态算不算 park」。
|
|
59
|
-
*/
|
|
60
19
|
export function isParkedRunStatus(status) {
|
|
61
20
|
switch (status) {
|
|
62
21
|
case "suspended":
|
|
@@ -68,13 +27,12 @@ export function isParkedRunStatus(status) {
|
|
|
68
27
|
case "blocked":
|
|
69
28
|
return false;
|
|
70
29
|
default: {
|
|
71
|
-
const unhandled = status;
|
|
30
|
+
const unhandled = status;
|
|
72
31
|
void unhandled;
|
|
73
|
-
return false;
|
|
32
|
+
return false;
|
|
74
33
|
}
|
|
75
34
|
}
|
|
76
35
|
}
|
|
77
|
-
/** SELECT row → {@link BakeRecord} — shared by BOTH SQL twins (design/158 S8 归位, A3 沉底). */
|
|
78
36
|
export function mapBakeRow(r) {
|
|
79
37
|
return {
|
|
80
38
|
bakeId: r.bake_id,
|
|
@@ -106,11 +64,9 @@ export function mapBakeRow(r) {
|
|
|
106
64
|
updatedAt: iso(r.updated_at),
|
|
107
65
|
};
|
|
108
66
|
}
|
|
109
|
-
/** Column list mapBakeRow expects — shared by BOTH SQL twins (design/158 S8 归位, A3 沉底). */
|
|
110
67
|
export const BAKE_SELECT_COLS = "bake_id, profile, bands, base_ref, push, dry_run, logs, argv, status, state, digest, repo, ref, index_id, " +
|
|
111
68
|
"exit_code, error, error_code, manifest_sha, tag, idem_key, ingest_secret, runner_id, lease_until, " +
|
|
112
69
|
"cancel_requested, requested_by, created_at, updated_at";
|
|
113
|
-
/** SELECT row → {@link ImageIndexEntry} — shared by BOTH SQL twins (design/158 S8 归位, A3 沉底). */
|
|
114
70
|
export function mapImageRow(r) {
|
|
115
71
|
return {
|
|
116
72
|
id: r.id,
|