@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,12 +1,10 @@
|
|
|
1
1
|
import { pgSafeJsonStringify, pgProtocolJsonStringify } from "./pg-safe-json.js";
|
|
2
|
-
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
3
|
-
/** Table names (single source). SAME names as the TiDB twin (the two dialects never share one DB). */
|
|
2
|
+
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
4
3
|
export const PG_MEMORY_SYNC_TABLES = {
|
|
5
4
|
syncCursor: "agent_memory_engine_sync_cursor",
|
|
6
5
|
pushQueue: "agent_memory_engine_push_queue",
|
|
7
6
|
history: "agent_memory_engine_history",
|
|
8
7
|
};
|
|
9
|
-
/** Idempotent DDL for the sync plane (PG dialect). Call once at startup. */
|
|
10
8
|
export async function ensurePgMemorySyncSchema(query) {
|
|
11
9
|
await query(`CREATE TABLE IF NOT EXISTS ${PG_MEMORY_SYNC_TABLES.syncCursor} (
|
|
12
10
|
scope varchar(190) COLLATE "C" NOT NULL,
|
|
@@ -24,18 +22,13 @@ export async function ensurePgMemorySyncSchema(query) {
|
|
|
24
22
|
next_attempt_at_ms bigint NOT NULL,
|
|
25
23
|
last_error text COLLATE "C" NULL
|
|
26
24
|
)`);
|
|
27
|
-
// Scan face (§1.2): the reaper/consumer sweeps by due time. Perf add-on, never correctness.
|
|
28
25
|
await query(`CREATE INDEX IF NOT EXISTS idx_${PG_MEMORY_SYNC_TABLES.pushQueue}_next_attempt_ms ON ${PG_MEMORY_SYNC_TABLES.pushQueue} (next_attempt_at_ms)`);
|
|
29
26
|
}
|
|
30
|
-
/** jsonb defense: node-pg hands back parsed objects, but a text-typed round-trip (or a foreign
|
|
31
|
-
* writer) may yield a string — same posture as parseJson in run-store-sql.ts. */
|
|
32
27
|
function fromJsonb(v) {
|
|
33
28
|
if (typeof v === "string")
|
|
34
29
|
return JSON.parse(v);
|
|
35
30
|
return v;
|
|
36
31
|
}
|
|
37
|
-
/** node-pg returns bigint columns as strings (int8 overflows JS numbers in general; ours are epoch
|
|
38
|
-
* ms, safely within 2^53) — normalize defensively. */
|
|
39
32
|
function toMs(v) {
|
|
40
33
|
return typeof v === "number" ? v : Number(v);
|
|
41
34
|
}
|
|
@@ -50,7 +43,6 @@ export class PgMemorySyncStore {
|
|
|
50
43
|
}
|
|
51
44
|
this.claimLeaseMs = lease;
|
|
52
45
|
}
|
|
53
|
-
// ── cursors ────────────────────────────────────────────────────────────────
|
|
54
46
|
async getCursor(scope, peer) {
|
|
55
47
|
const res = await this.query(`SELECT base_revs, updated_at_ms FROM ${PG_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = $1 AND peer = $2`, [scope, peer]);
|
|
56
48
|
const row = res.rows[0];
|
|
@@ -64,44 +56,20 @@ export class PgMemorySyncStore {
|
|
|
64
56
|
};
|
|
65
57
|
}
|
|
66
58
|
async putCursor(cursor) {
|
|
67
|
-
// #271 件1:scope 是 PK 的一半(varchar(190))——超宽此前落不可分类的裸 22001,而 MySQL-protocol
|
|
68
|
-
// twin 上同一份数据会**静默截断**成另一个盘的基线。两方言同点、同文案拒(engine 两表同守)。
|
|
69
59
|
assertMemoryScopeWidth(cursor.scope);
|
|
70
|
-
// PK (scope,peer) is the table's ONLY unique key ⇒ ON CONFLICT has exactly one target, no
|
|
71
|
-
// hijack face (纪律:the entries-table ban on blind upserts does NOT apply here).
|
|
72
60
|
await this.query(`INSERT INTO ${PG_MEMORY_SYNC_TABLES.syncCursor} (scope, peer, base_revs, updated_at_ms)
|
|
73
61
|
VALUES ($1, $2, $3::jsonb, $4)
|
|
74
|
-
ON CONFLICT (scope, peer) DO UPDATE SET base_revs = EXCLUDED.base_revs, updated_at_ms = EXCLUDED.updated_at_ms`,
|
|
75
|
-
// codex R11:baseRevs=对账基线(entry id→rev,协议数据)——有损改写=peer 收到的与库里存的
|
|
76
|
-
// 是两份 map,对账决策对着被改写的身份/rev 做。拒绝式(与 outbox/add/update 同族)。
|
|
77
|
-
[cursor.scope, cursor.peer, pgProtocolJsonStringify(cursor.baseRevs, "memory sync cursor baseRevs"), cursor.updatedAtMs]);
|
|
62
|
+
ON CONFLICT (scope, peer) DO UPDATE SET base_revs = EXCLUDED.base_revs, updated_at_ms = EXCLUDED.updated_at_ms`, [cursor.scope, cursor.peer, pgProtocolJsonStringify(cursor.baseRevs, "memory sync cursor baseRevs"), cursor.updatedAtMs]);
|
|
78
63
|
}
|
|
79
|
-
// ── push queue ─────────────────────────────────────────────────────────────
|
|
80
64
|
async enqueue(item) {
|
|
81
|
-
assertMemoryScopeWidth(item.scope);
|
|
82
|
-
// Plain INSERT, fail-loud on a duplicate id: the caller mints uuidv7 per enqueue, so a dup is a
|
|
83
|
-
// caller bug (or a replay the caller must decide about) — never silently absorbed here.
|
|
65
|
+
assertMemoryScopeWidth(item.scope);
|
|
84
66
|
await this.query(`INSERT INTO ${PG_MEMORY_SYNC_TABLES.pushQueue} (id, scope, entry_id, payload, attempts, next_attempt_at_ms, last_error)
|
|
85
|
-
VALUES ($1, $2, $3, $4::jsonb, 0, $5, NULL)`,
|
|
86
|
-
// codex R10:payload=协议数据(文档明言 verbatim;rev=内容哈希)——jsonb 列存不了脏值,但有损
|
|
87
|
-
// 改写=payload 与 rev 内部矛盾(消费端永拒/应用脏内容)。协议纪律:拒绝式(与 memory add/update
|
|
88
|
-
// 的 unstorable_bytes 同族;上游 add/update 已拒,此处=纵深防御)。
|
|
89
|
-
[item.id, item.scope, item.entryId, pgProtocolJsonStringify(item.payload, "memory outbox payload"), item.nextAttemptAtMs]);
|
|
67
|
+
VALUES ($1, $2, $3, $4::jsonb, 0, $5, NULL)`, [item.id, item.scope, item.entryId, pgProtocolJsonStringify(item.payload, "memory outbox payload"), item.nextAttemptAtMs]);
|
|
90
68
|
}
|
|
91
69
|
async claimDue(nowMs, limit) {
|
|
92
70
|
if (limit <= 0)
|
|
93
71
|
return [];
|
|
94
72
|
const leasedUntilMs = nowMs + this.claimLeaseMs;
|
|
95
|
-
// ONE statement = one implicit transaction: SKIP LOCKED excludes claimers overlapping in time,
|
|
96
|
-
// the next_attempt_at_ms bump (lease) excludes everyone arriving after we commit. RETURNING gives
|
|
97
|
-
// the post-update row, so attempts/last_error are the pre-claim values (fail() is the only
|
|
98
|
-
// incrementer) and next_attempt_at_ms is the lease we just wrote.
|
|
99
|
-
// ⚠️ MATERIALIZED CTE, NOT `WHERE id IN (SELECT … LIMIT n FOR UPDATE SKIP LOCKED)`: the IN-subquery
|
|
100
|
-
// form lets the planner RESCAN the locking subquery (e.g. as a rescanned semi-join inner), and each
|
|
101
|
-
// rescan skips the rows the previous pass locked and picks the NEXT due ones — an over-claim beyond
|
|
102
|
-
// LIMIT. Plan-dependent ⇒ intermittent: caught live on the build-host test DB (claimDue(limit=2) returned
|
|
103
|
-
// 3 rows, serial single-claimer run — the "sync-store env flake" real root cause). AS MATERIALIZED
|
|
104
|
-
// (PG12+) pins one evaluation; UPDATE … FROM joins the pinned id set.
|
|
105
73
|
const res = await this.query(`WITH due AS MATERIALIZED (
|
|
106
74
|
SELECT id FROM ${PG_MEMORY_SYNC_TABLES.pushQueue}
|
|
107
75
|
WHERE next_attempt_at_ms <= $1
|
|
@@ -126,19 +94,12 @@ export class PgMemorySyncStore {
|
|
|
126
94
|
await this.query(`DELETE FROM ${PG_MEMORY_SYNC_TABLES.pushQueue} WHERE id = $1`, [id]);
|
|
127
95
|
}
|
|
128
96
|
async fail(id, error, nextAttemptAtMs) {
|
|
129
|
-
// attempts = attempts + 1 is SQL-atomic — a read-modify-write in JS would race a concurrent
|
|
130
|
-
// failer and lose increments (the attempt cap is the fail-loud stop, it must count honestly).
|
|
131
97
|
await this.query(`UPDATE ${PG_MEMORY_SYNC_TABLES.pushQueue}
|
|
132
98
|
SET attempts = attempts + 1, last_error = $2, next_attempt_at_ms = $3
|
|
133
99
|
WHERE id = $1`, [id, error, nextAttemptAtMs]);
|
|
134
100
|
}
|
|
135
101
|
}
|
|
136
|
-
/** Idempotent DDL for the history table (PG dialect). Separate from the sync-plane ensure so S5.1
|
|
137
|
-
* can wire the write point independently of the S4 sync wiring. */
|
|
138
102
|
export async function ensurePgMemoryHistorySchema(query) {
|
|
139
|
-
// scope column = design-doc amendment (see MemoryHistoryAppendRow.scope). written_at_ms BIGINT
|
|
140
|
-
// (caller-injected epoch ms) instead of the draft's TIMESTAMP — the family's clock discipline
|
|
141
|
-
// (sync_cursors.updated_at_ms precedent; DB clocks never judge anything).
|
|
142
103
|
await query(`CREATE TABLE IF NOT EXISTS ${PG_MEMORY_SYNC_TABLES.history} (
|
|
143
104
|
id char(36) COLLATE "C" NOT NULL,
|
|
144
105
|
rev varchar(128) COLLATE "C" NOT NULL,
|
|
@@ -149,7 +110,6 @@ export async function ensurePgMemoryHistorySchema(query) {
|
|
|
149
110
|
written_at_ms bigint NOT NULL,
|
|
150
111
|
PRIMARY KEY (id, rev)
|
|
151
112
|
)`);
|
|
152
|
-
// countByScope / future per-scope export-with-history scan face. Perf add-on, never correctness.
|
|
153
113
|
await query(`CREATE INDEX IF NOT EXISTS idx_${PG_MEMORY_SYNC_TABLES.history}_scope ON ${PG_MEMORY_SYNC_TABLES.history} (scope)`);
|
|
154
114
|
}
|
|
155
115
|
export class PgMemoryHistoryStore {
|
|
@@ -160,9 +120,6 @@ export class PgMemoryHistoryStore {
|
|
|
160
120
|
async appendHistory(rows) {
|
|
161
121
|
if (rows.length === 0)
|
|
162
122
|
return;
|
|
163
|
-
// ONE multi-row INSERT (the batch is "one applyPatches commit's worth" — small). ON CONFLICT
|
|
164
|
-
// (id, rev) DO NOTHING = idempotent replay; PK is the table's ONLY unique key, so the conflict
|
|
165
|
-
// target is unambiguous (no hijack face — append-only, nothing is ever updated).
|
|
166
123
|
const params = [];
|
|
167
124
|
const tuples = rows.map((r, i) => {
|
|
168
125
|
params.push(r.id, r.rev, r.prevRev ?? null, r.op, r.scope, r.snapshot === undefined || r.snapshot === null ? null : pgSafeJsonStringify(r.snapshot), r.writtenAtMs);
|
|
@@ -181,12 +138,10 @@ export class PgMemoryHistoryStore {
|
|
|
181
138
|
return res.rows.map((r) => historyRowFrom(r));
|
|
182
139
|
}
|
|
183
140
|
async countByScope(scope) {
|
|
184
|
-
// COUNT(*) comes back as a bigint-string from node-pg — normalize (toMs posture).
|
|
185
141
|
const res = await this.query(`SELECT COUNT(*) AS n FROM ${PG_MEMORY_SYNC_TABLES.history} WHERE scope = $1`, [scope]);
|
|
186
142
|
return toMs(res.rows[0]?.n ?? 0);
|
|
187
143
|
}
|
|
188
144
|
}
|
|
189
|
-
/** Shared row mapper (both dialects' SELECTs project the same column set). */
|
|
190
145
|
export function historyRowFrom(r) {
|
|
191
146
|
return {
|
|
192
147
|
id: String(r.id),
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { historyRowFrom } from "./memory-sync-store-pg.js";
|
|
2
|
-
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
3
|
-
/** Table names (single source) — SAME names as PG_MEMORY_SYNC_TABLES (the two dialects never share
|
|
4
|
-
* one database). */
|
|
2
|
+
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
5
3
|
export const TIDB_MEMORY_SYNC_TABLES = {
|
|
6
4
|
syncCursor: "agent_memory_engine_sync_cursor",
|
|
7
5
|
pushQueue: "agent_memory_engine_push_queue",
|
|
8
6
|
history: "agent_memory_engine_history",
|
|
9
7
|
};
|
|
10
|
-
/** Idempotent DDL for the sync plane (TiDB dialect). Call once at startup.
|
|
11
|
-
* Key budget: utf8mb4 = 4 bytes/char, index cap 3072 bytes ⇒ PK(scope,peer) = 380 chars = 1520 B ✓. */
|
|
12
8
|
export async function ensureTiDBMemorySyncSchema(pool) {
|
|
13
9
|
await pool.query(`CREATE TABLE IF NOT EXISTS ${TIDB_MEMORY_SYNC_TABLES.syncCursor} (
|
|
14
10
|
scope VARCHAR(190) NOT NULL,
|
|
@@ -29,14 +25,11 @@ export async function ensureTiDBMemorySyncSchema(pool) {
|
|
|
29
25
|
KEY idx_agent_memory_engine_push_queue_next_attempt_ms (next_attempt_at_ms)
|
|
30
26
|
) COLLATE utf8mb4_bin`);
|
|
31
27
|
}
|
|
32
|
-
/** JSON-column defense: mysql2 normally hands back parsed objects for JSON columns, but string
|
|
33
|
-
* shapes leak through (connection flags/proxies) — same posture as memory-engine-tidb.ts. */
|
|
34
28
|
function fromJson(v) {
|
|
35
29
|
if (typeof v === "string")
|
|
36
30
|
return JSON.parse(v);
|
|
37
31
|
return v;
|
|
38
32
|
}
|
|
39
|
-
/** BIGINT columns may arrive as string depending on driver flags — normalize defensively. */
|
|
40
33
|
function toMs(v) {
|
|
41
34
|
return typeof v === "number" ? v : Number(v);
|
|
42
35
|
}
|
|
@@ -51,7 +44,6 @@ export class TiDBMemorySyncStore {
|
|
|
51
44
|
}
|
|
52
45
|
this.claimLeaseMs = lease;
|
|
53
46
|
}
|
|
54
|
-
// ── cursors ────────────────────────────────────────────────────────────────
|
|
55
47
|
async getCursor(scope, peer) {
|
|
56
48
|
const [rows] = await this.pool.query(`SELECT base_revs, updated_at_ms FROM ${TIDB_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = ? AND peer = ?`, [scope, peer]);
|
|
57
49
|
const row = rows[0];
|
|
@@ -65,20 +57,13 @@ export class TiDBMemorySyncStore {
|
|
|
65
57
|
};
|
|
66
58
|
}
|
|
67
59
|
async putCursor(cursor) {
|
|
68
|
-
// #271 件1:scope 是 PK 的一半(VARCHAR(190))——非严格 MySQL 的静默截断会把两个盘的同步基线
|
|
69
|
-
// 折叠成一行(对账基线互串),写前响亮拒(engine 两表同守;Pg twin 同注)。
|
|
70
60
|
assertMemoryScopeWidth(cursor.scope);
|
|
71
|
-
// The table's ONLY unique key is PK(scope,peer) ⇒ ON DUPLICATE KEY UPDATE has exactly one
|
|
72
|
-
// possible trigger — no hijack face (the entries-table ban does NOT apply; see header).
|
|
73
61
|
await this.pool.query(`INSERT INTO ${TIDB_MEMORY_SYNC_TABLES.syncCursor} (scope, peer, base_revs, updated_at_ms)
|
|
74
62
|
VALUES (?, ?, ?, ?)
|
|
75
63
|
ON DUPLICATE KEY UPDATE base_revs = VALUES(base_revs), updated_at_ms = VALUES(updated_at_ms)`, [cursor.scope, cursor.peer, JSON.stringify(cursor.baseRevs), cursor.updatedAtMs]);
|
|
76
64
|
}
|
|
77
|
-
// ── push queue ─────────────────────────────────────────────────────────────
|
|
78
65
|
async enqueue(item) {
|
|
79
|
-
assertMemoryScopeWidth(item.scope);
|
|
80
|
-
// Plain INSERT, fail-loud on a duplicate id (ER_DUP_ENTRY 1062) — caller mints uuidv7 per
|
|
81
|
-
// enqueue, a dup is a caller bug, never silently absorbed. Same posture as the PG twin.
|
|
66
|
+
assertMemoryScopeWidth(item.scope);
|
|
82
67
|
await this.pool.query(`INSERT INTO ${TIDB_MEMORY_SYNC_TABLES.pushQueue} (id, scope, entry_id, payload, attempts, next_attempt_at_ms, last_error)
|
|
83
68
|
VALUES (?, ?, ?, ?, 0, ?, NULL)`, [item.id, item.scope, item.entryId, JSON.stringify(item.payload), item.nextAttemptAtMs]);
|
|
84
69
|
}
|
|
@@ -86,10 +71,6 @@ export class TiDBMemorySyncStore {
|
|
|
86
71
|
if (limit <= 0)
|
|
87
72
|
return [];
|
|
88
73
|
const leasedUntilMs = nowMs + this.claimLeaseMs;
|
|
89
|
-
// Optimistic CAS claim (header rationale): read the due candidates, then win each row with a
|
|
90
|
-
// conditional UPDATE on the exact next_attempt_at_ms we read. Losers (another worker claimed, or
|
|
91
|
-
// fail()/a re-schedule moved the row) see affectedRows=0 and are skipped — the SKIP LOCKED
|
|
92
|
-
// equivalent, without pessimistic locks or a held connection.
|
|
93
74
|
const [candidates] = await this.pool.query(`SELECT id, scope, entry_id, payload, attempts, next_attempt_at_ms, last_error
|
|
94
75
|
FROM ${TIDB_MEMORY_SYNC_TABLES.pushQueue}
|
|
95
76
|
WHERE next_attempt_at_ms <= ?
|
|
@@ -97,13 +78,11 @@ export class TiDBMemorySyncStore {
|
|
|
97
78
|
LIMIT ?`, [nowMs, limit]);
|
|
98
79
|
const claimed = [];
|
|
99
80
|
for (const row of candidates) {
|
|
100
|
-
// affectedRows=CHANGED 语义坑防御:lease = nowMs + claimLeaseMs > nowMs ≥ 读到的 next_attempt_at_ms
|
|
101
|
-
// (WHERE 已保证),新值严格大于旧值 ⇒ 匹配必 change ⇒ affectedRows=1 恒等于「真的赢了」。
|
|
102
81
|
const [res] = await this.pool.query(`UPDATE ${TIDB_MEMORY_SYNC_TABLES.pushQueue}
|
|
103
82
|
SET next_attempt_at_ms = ?
|
|
104
83
|
WHERE id = ? AND next_attempt_at_ms = ?`, [leasedUntilMs, row.id, toMs(row.next_attempt_at_ms)]);
|
|
105
84
|
if (res.affectedRows !== 1)
|
|
106
|
-
continue;
|
|
85
|
+
continue;
|
|
107
86
|
claimed.push({
|
|
108
87
|
id: row.id,
|
|
109
88
|
scope: row.scope,
|
|
@@ -120,20 +99,12 @@ export class TiDBMemorySyncStore {
|
|
|
120
99
|
await this.pool.query(`DELETE FROM ${TIDB_MEMORY_SYNC_TABLES.pushQueue} WHERE id = ?`, [id]);
|
|
121
100
|
}
|
|
122
101
|
async fail(id, error, nextAttemptAtMs) {
|
|
123
|
-
// attempts = attempts + 1 is SQL-atomic (never read-modify-write in JS); the increment also
|
|
124
|
-
// guarantees the row CHANGES — the claim verdict is honest under BOTH affectedRows semantics
|
|
125
|
-
// (mysql2 default = FOUND_ROWS/matched; changed-only would also work here).
|
|
126
102
|
await this.pool.query(`UPDATE ${TIDB_MEMORY_SYNC_TABLES.pushQueue}
|
|
127
103
|
SET attempts = attempts + 1, last_error = ?, next_attempt_at_ms = ?
|
|
128
104
|
WHERE id = ?`, [error, nextAttemptAtMs, id]);
|
|
129
105
|
}
|
|
130
106
|
}
|
|
131
|
-
// ── 142-S5 §1.3 — history table (tombstone + rev chain), TiDB dialect ───────────────────────────
|
|
132
|
-
/** Idempotent DDL for the history table (TiDB dialect). Key budget: PK(id,rev) = CHAR(36)+
|
|
133
|
-
* VARCHAR(128) = 164 chars ≤ 3072-byte utf8mb4 index cap ✓; KEY(scope) = 190 chars ✓. */
|
|
134
107
|
export async function ensureTiDBMemoryHistorySchema(pool) {
|
|
135
|
-
// scope column + written_at_ms BIGINT = the same design-doc amendments as the Pg twin (see
|
|
136
|
-
// MemoryHistoryAppendRow.scope / ensurePgMemoryHistorySchema).
|
|
137
108
|
await pool.query(`CREATE TABLE IF NOT EXISTS ${TIDB_MEMORY_SYNC_TABLES.history} (
|
|
138
109
|
id CHAR(36) NOT NULL,
|
|
139
110
|
rev VARCHAR(128) NOT NULL,
|
|
@@ -154,9 +125,6 @@ export class TiDBMemoryHistoryStore {
|
|
|
154
125
|
async appendHistory(rows) {
|
|
155
126
|
if (rows.length === 0)
|
|
156
127
|
return;
|
|
157
|
-
// ONE multi-row INSERT; dup (id,rev) replay = no-op via `ON DUPLICATE KEY UPDATE id = id`
|
|
158
|
-
// (header rationale: NOT INSERT IGNORE — IGNORE swallows non-dup errors too). PK(id,rev) is the
|
|
159
|
-
// only unique key ⇒ the ODKU trigger is unambiguous, and the "update" writes nothing.
|
|
160
128
|
const params = [];
|
|
161
129
|
for (const r of rows) {
|
|
162
130
|
params.push(r.id, r.rev, r.prevRev ?? null, r.op, r.scope, r.snapshot === undefined || r.snapshot === null ? null : JSON.stringify(r.snapshot), r.writtenAtMs);
|
|
@@ -1,41 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Outcome ledger (design/73 §7) — the read-only first step of grounded self-evolution. SINGLE-FILE
|
|
3
|
-
* DUAL-DIALECT (design/158 A12 定型半场): ONE implementation, TWO dialects; the historical
|
|
4
|
-
* `TiDBOutcomeLedger` / `PgOutcomeLedger` class names survive as thin ctor subclasses so every consumer
|
|
5
|
-
* (store-backend.ts, pg-pool.ts, the unit + real-DB suites) is untouched.
|
|
6
|
-
*
|
|
7
|
-
* One row per graded task run, keyed by (taskSignature, model). v1 is STRICTLY READ-ONLY: it records
|
|
8
|
-
* outcomes for a dashboard + as the dataset for an offline backtest (bandit vs fixed routing). It drives
|
|
9
|
-
* NO automatic policy change — that is flag-gated and must first pass the offline-backtest gate
|
|
10
|
-
* (council blocker 3 / major 7).
|
|
11
|
-
*
|
|
12
|
-
* 🔴 Signal-hardness split is the load-bearing invariant (council blocker 1): only `mechanical` fields
|
|
13
|
-
* (oracle pass/total, build exits, mergeOk, cost, turns, route, replan) may ever drive an automatic
|
|
14
|
-
* policy change. `llmAssisted` (stuck counts, verifier verdict+confidence) is dashboard-only, carried in
|
|
15
|
-
* its own blob, never co-weighted with mechanical. `outcome` is DERIVED from mechanical fields ONLY.
|
|
16
|
-
*
|
|
17
|
-
* ── Dialect deltas, kept EXPLICIT ────────────────────────────────────────────────────────────────────
|
|
18
|
-
* - `?` placeholders vs `$n` - JSON columns bound `CAST(? AS JSON)`-free (TiDB
|
|
19
|
-
* stores the string verbatim) vs `$n::jsonb`
|
|
20
|
-
* - JSON encode: TiDB `JSON.stringify` verbatim vs PG `pgSafeJsonStringify` (deep NUL/lone-surrogate
|
|
21
|
-
* sanitize — a raw tool_result/core blob can carry bytes PG's text/jsonb columns reject, ERRCODE 22P05)
|
|
22
|
-
* - affected/SELECT rows via `result.rowCount` / `result.rows` (not RowDataPacket/ResultSetHeader) — via SqlDriver
|
|
23
|
-
* - BIGINT (cost_micro_usd) read back as a STRING by node-pg → wrapped in `Number(...)`
|
|
24
|
-
* - `AVG(outcome = 'pass')` (MySQL boolean→1/0) vs `AVG((outcome = 'pass')::int)` (PG has no boolean→int coercion)
|
|
25
|
-
* - merge_ok TINYINT(1) vs SMALLINT (0/1 in/out) - DATETIME(3) vs TIMESTAMPTZ(3) (Date in/out)
|
|
26
|
-
* - schema ownership: TiDB DDL lives in tidb-pool.ts SCHEMA_STATEMENTS; the PG DDL is exported from HERE
|
|
27
|
-
* (`PG_OUTCOME_LEDGER_SCHEMA` + `ensurePgOutcomeLedgerSchema`) for pg-pool.ts + the integration test.
|
|
28
|
-
*
|
|
29
|
-
* Kept in lock-step across dialects by the shared real-DB integration suite
|
|
30
|
-
* (test/pg-outcome-ledger-integration.test.ts, both engines run the SAME scenarios in parallel).
|
|
31
|
-
*/
|
|
32
1
|
import { randomUUID, createHash } from "node:crypto";
|
|
33
2
|
import { mysqlDriver, pgDriver, dialectJsonEncoder } from "./sql-driver.js";
|
|
34
|
-
/**
|
|
35
|
-
* v1 task-signature (council Q11, recommended candidate (a)): a deterministic structural hash — zero
|
|
36
|
-
* embedder, coarse on purpose (routing is a coarse decision). An explicit `tag` short-circuits to the
|
|
37
|
-
* tag itself (candidate (c)).
|
|
38
|
-
*/
|
|
39
3
|
export function taskSignature(s) {
|
|
40
4
|
if (s.tag)
|
|
41
5
|
return `tag:${s.tag}`;
|
|
@@ -47,14 +11,7 @@ export function taskSignature(s) {
|
|
|
47
11
|
});
|
|
48
12
|
return `h:${createHash("sha256").update(canon).digest("hex").slice(0, 16)}`;
|
|
49
13
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Derive the coarse outcome from MECHANICAL fields only (council blocker 1: llmAssisted never participates).
|
|
52
|
-
* pass = all hidden tests green AND both build exits 0 AND merge ok; fail = nothing graded green; else partial.
|
|
53
|
-
*/
|
|
54
14
|
export function deriveOutcome(m, runStatus = "completed-graded") {
|
|
55
|
-
// An infra-aborted run never reached the grader — its 0/total is infra, not task quality. Don't let it
|
|
56
|
-
// read as "fail" (which a self-scheduler would mistake for task difficulty); mark it "aborted" so the
|
|
57
|
-
// backtest can exclude it (council blocker 3).
|
|
58
15
|
if (runStatus === "infra-aborted")
|
|
59
16
|
return "aborted";
|
|
60
17
|
const oracleGreen = m.oracleTotal != null && m.oracleTotal > 0 && m.oraclePassed === m.oracleTotal;
|
|
@@ -62,24 +19,9 @@ export function deriveOutcome(m, runStatus = "completed-graded") {
|
|
|
62
19
|
const mergeGreen = m.mergeOk !== false;
|
|
63
20
|
if (oracleGreen && buildGreen && mergeGreen)
|
|
64
21
|
return "pass";
|
|
65
|
-
// "partial" needs POSITIVE green evidence. An OMITTED mergeOk is non-evidence — counting it green (the
|
|
66
|
-
// pre-2026-07 behavior) made "fail" unreachable for any row without an explicit mergeOk:false, so a
|
|
67
|
-
// fully-red oracle read as "partial" (caught wiring the design/73 §1 bridge, whose rows carry no mergeOk).
|
|
68
22
|
const anyGreen = oracleGreen || (m.oraclePassed != null && m.oraclePassed > 0) || m.mergeOk === true;
|
|
69
23
|
return anyGreen ? "partial" : "fail";
|
|
70
24
|
}
|
|
71
|
-
/**
|
|
72
|
-
* design/73 §1 → §7 bridge: project one CORE `TaskOutcome` fact (the `RunnerDeps.onTaskOutcome` seam,
|
|
73
|
-
* core 1.226) onto a v1 ledger row. Lossless: the verbatim fact rides `core` → the `core_outcome` JSON
|
|
74
|
-
* column (red-line ② `oracleHadRedRun` + the free-form oracle blob have no v1 columns); the mapped
|
|
75
|
-
* mechanical fields keep the existing aggregate surface working. Mapping rules:
|
|
76
|
-
* - `signatureInputs.tag = o.taskSignature` — core already emits THE aggregation key, so the ledger's
|
|
77
|
-
* tag short-circuit adopts it verbatim (no re-derivation drift);
|
|
78
|
-
* - oracle counts: `oracle.oraclePassed/oracleTotal` when the harness supplied numeric counts, else the
|
|
79
|
-
* required `green` bit as 1/0 over 1 (an honest degenerate count, not a fabrication);
|
|
80
|
-
* - `runStatus` stays "completed-graded": core's red line ③ means emission only happens at a REAL
|
|
81
|
-
* mechanical-oracle terminal (an infra-aborted run never reaches the emit point).
|
|
82
|
-
*/
|
|
83
25
|
export function coreOutcomeToLedgerRow(o) {
|
|
84
26
|
const passed = typeof o.oracle?.oraclePassed === "number" ? o.oracle.oraclePassed : o.oracle?.green ? 1 : 0;
|
|
85
27
|
const total = typeof o.oracle?.oracleTotal === "number" ? o.oracle.oracleTotal : 1;
|
|
@@ -87,8 +29,6 @@ export function coreOutcomeToLedgerRow(o) {
|
|
|
87
29
|
taskId: o.runId.slice(0, 64),
|
|
88
30
|
model: (o.model ?? "unknown").slice(0, 190),
|
|
89
31
|
signatureInputs: {
|
|
90
|
-
// 🔴 `taskSignature()` prepends "tag:" → the final task_signature must fit VARCHAR(190).
|
|
91
|
-
// Cap the raw tag at 190 − len("tag:") = 186 so the prefixed key never overflows/truncates the column.
|
|
92
32
|
tag: o.taskSignature.slice(0, 186),
|
|
93
33
|
...(o.signatureInputs?.toolset ? { toolset: o.signatureInputs.toolset } : {}),
|
|
94
34
|
...(o.signatureInputs?.targetLang ? { targetLang: o.signatureInputs.targetLang } : {}),
|
|
@@ -103,14 +43,6 @@ export function coreOutcomeToLedgerRow(o) {
|
|
|
103
43
|
core: o,
|
|
104
44
|
};
|
|
105
45
|
}
|
|
106
|
-
/**
|
|
107
|
-
* PG translation of the `outcome_ledger` DDL in tidb-pool.ts SCHEMA_STATEMENTS:
|
|
108
|
-
* JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), TINYINT(1)→SMALLINT, INT→INTEGER, inline KEY→separate CREATE INDEX.
|
|
109
|
-
* The table is DISJOINT from every other store's tables, so this self-contained set is safe (no shadowing).
|
|
110
|
-
*
|
|
111
|
-
* SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
|
|
112
|
-
* drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead.
|
|
113
|
-
*/
|
|
114
46
|
export const PG_OUTCOME_LEDGER_SCHEMA = [
|
|
115
47
|
`CREATE TABLE IF NOT EXISTS outcome_ledger (
|
|
116
48
|
id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
@@ -142,44 +74,31 @@ export const PG_OUTCOME_LEDGER_SCHEMA = [
|
|
|
142
74
|
`CREATE INDEX IF NOT EXISTS idx_outcome_ledger_outcome ON outcome_ledger (outcome)`,
|
|
143
75
|
`CREATE INDEX IF NOT EXISTS idx_outcome_ledger_created ON outcome_ledger (created_at)`,
|
|
144
76
|
];
|
|
145
|
-
/** Idempotent self-contained PG schema apply (for the integration test; central aggregation via pg-pool.ts). */
|
|
146
77
|
export async function ensurePgOutcomeLedgerSchema(pool) {
|
|
147
78
|
for (const stmt of PG_OUTCOME_LEDGER_SCHEMA)
|
|
148
79
|
await pool.query(stmt);
|
|
149
80
|
}
|
|
150
|
-
/** Dual-dialect outcome ledger. See the file header for the dialect-delta ledger. */
|
|
151
81
|
export class SqlOutcomeLedger {
|
|
152
82
|
db;
|
|
153
83
|
constructor(db) {
|
|
154
84
|
this.db = db;
|
|
155
85
|
}
|
|
156
|
-
/** Pick the dialect's SQL text. Both statements stay written out at the call site ON PURPOSE. */
|
|
157
86
|
q(tidb, pg) {
|
|
158
87
|
return this.db.dialect === "tidb" ? tidb : pg;
|
|
159
88
|
}
|
|
160
|
-
/** JSON column encode: TiDB stores `JSON.stringify` verbatim; PG runs the deep NUL/lone-surrogate
|
|
161
|
-
* sanitize (a raw tool_result/core blob can carry bytes PG's jsonb column rejects, ERRCODE 22P05) — this
|
|
162
|
-
* store's blobs are observability/dashboard data, so the lossy (U+FFFD) sanitize is the right tier
|
|
163
|
-
* (not the lossless envelope checkpoint-store-sql.ts uses for the approval trust boundary). */
|
|
164
89
|
json(value) {
|
|
165
90
|
return dialectJsonEncoder(this.db.dialect)(value);
|
|
166
91
|
}
|
|
167
|
-
/** Record one graded task run. Idempotent only by row id; callers pass distinct runs. */
|
|
168
92
|
async record(o) {
|
|
169
93
|
const id = randomUUID().replace(/-/g, "").slice(0, 32);
|
|
170
94
|
const sig = taskSignature(o.signatureInputs);
|
|
171
95
|
const m = o.mechanical;
|
|
172
96
|
const runStatus = o.runStatus ?? "completed-graded";
|
|
173
|
-
await this.db.query(this.q(
|
|
174
|
-
// 🔴 byte-for-byte preserved from tidb-outcome-ledger.ts (pre-collapse) — the parity harness
|
|
175
|
-
// diffs this literally, so its indentation is NOT reflowed to the new call-site nesting.
|
|
176
|
-
`INSERT INTO outcome_ledger
|
|
97
|
+
await this.db.query(this.q(`INSERT INTO outcome_ledger
|
|
177
98
|
(id, task_signature, model, task_id, run_status, route, worker_count, oracle_passed, oracle_total,
|
|
178
99
|
assemble_exit, lint_exit, merge_ok, replan_triggers, cost_micro_usd, turns, outcome,
|
|
179
100
|
llm_assisted, signature_inputs, core_outcome, created_at)
|
|
180
|
-
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
181
|
-
// 🔴 byte-for-byte preserved from pg-outcome-ledger.ts (pre-collapse).
|
|
182
|
-
`INSERT INTO outcome_ledger
|
|
101
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`, `INSERT INTO outcome_ledger
|
|
183
102
|
(id, task_signature, model, task_id, run_status, route, worker_count, oracle_passed, oracle_total,
|
|
184
103
|
assemble_exit, lint_exit, merge_ok, replan_triggers, cost_micro_usd, turns, outcome,
|
|
185
104
|
llm_assisted, signature_inputs, core_outcome, created_at)
|
|
@@ -194,16 +113,9 @@ export class SqlOutcomeLedger {
|
|
|
194
113
|
]);
|
|
195
114
|
return id;
|
|
196
115
|
}
|
|
197
|
-
/** design/73 §1 seam consumption: record one CORE outcome fact (see {@link coreOutcomeToLedgerRow}). */
|
|
198
116
|
async recordCore(o) {
|
|
199
117
|
await this.record(coreOutcomeToLedgerRow(o));
|
|
200
118
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Read-only aggregate for the dashboard / offline backtest: pass-rate + mean cost per (signature, model).
|
|
203
|
-
* 🔴 Defaults to `completed-graded` rows ONLY — the route/budget-learning signal must exclude infra-aborted
|
|
204
|
-
* runs (council blocker 3: a basework death is not a task-difficulty signal). Pass
|
|
205
|
-
* `includeAborted: true` only for the infra-health view, never for training.
|
|
206
|
-
*/
|
|
207
119
|
async summary(opts = {}) {
|
|
208
120
|
const clauses = [];
|
|
209
121
|
const params = [];
|
|
@@ -211,14 +123,10 @@ export class SqlOutcomeLedger {
|
|
|
211
123
|
clauses.push("run_status = 'completed-graded'");
|
|
212
124
|
if (opts.taskSignature) {
|
|
213
125
|
params.push(opts.taskSignature);
|
|
214
|
-
// 🔴 placeholder ARITY differs, not just the token: TiDB's `?` is positional-anonymous (always just
|
|
215
|
-
// "?"), while PG's `$n` must name the actual ordinal — `params.length` after the push IS that ordinal.
|
|
216
126
|
clauses.push(this.q("task_signature = ?", `task_signature = $${params.length}`));
|
|
217
127
|
}
|
|
218
128
|
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
219
|
-
const { rows } = await this.db.query(this.q(
|
|
220
|
-
// 🔴 byte-for-byte preserved (both statements) — not reflowed to the new call-site nesting.
|
|
221
|
-
`SELECT task_signature, model, COUNT(*) n,
|
|
129
|
+
const { rows } = await this.db.query(this.q(`SELECT task_signature, model, COUNT(*) n,
|
|
222
130
|
AVG(outcome = 'pass') pass_rate, AVG(cost_micro_usd) mean_cost
|
|
223
131
|
FROM outcome_ledger ${where}
|
|
224
132
|
GROUP BY task_signature, model
|
|
@@ -236,13 +144,11 @@ export class SqlOutcomeLedger {
|
|
|
236
144
|
}));
|
|
237
145
|
}
|
|
238
146
|
}
|
|
239
|
-
/** MySQL-protocol (TiDB) binding — historical class name + ctor shape preserved. */
|
|
240
147
|
export class TiDBOutcomeLedger extends SqlOutcomeLedger {
|
|
241
148
|
constructor(pool) {
|
|
242
149
|
super(mysqlDriver(pool));
|
|
243
150
|
}
|
|
244
151
|
}
|
|
245
|
-
/** PostgreSQL binding — historical class name + ctor shape preserved. */
|
|
246
152
|
export class PgOutcomeLedger extends SqlOutcomeLedger {
|
|
247
153
|
constructor(pool) {
|
|
248
154
|
super(pgDriver(pool));
|