@sema-agent/server 7.138.0 → 7.140.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/MIGRATION.md +457 -7
- package/README.md +7 -4
- package/README.zh-CN.md +6 -4
- package/USAGE.md +91 -41
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +26 -26
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/live-deps.js +3 -2
- package/dist/bench/s1/row.d.ts +5 -2
- package/dist/bench/s1/row.js +2 -22
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/adoption.js +1 -1
- package/dist/boot/device-lane.d.ts +8 -3
- package/dist/boot/device-lane.js +7 -2
- package/dist/boot/execution-env.d.ts +4 -2
- package/dist/boot/execution-env.js +28 -34
- package/dist/boot/execution-lane.d.ts +9 -1
- package/dist/boot/execution-lane.js +6 -1
- package/dist/boot/governance-seams.d.ts +3 -2
- package/dist/boot/governance-seams.js +9 -11
- package/dist/boot/memory-boundary.d.ts +14 -51
- package/dist/boot/memory-boundary.js +10 -20
- package/dist/boot/memory-consolidation.d.ts +15 -11
- package/dist/boot/memory-consolidation.js +25 -14
- package/dist/boot/resolve-spec.d.ts +4 -6
- package/dist/boot/resolve-spec.js +10 -13
- package/dist/boot/retention-lane.d.ts +5 -5
- package/dist/boot/retention-lane.js +0 -6
- package/dist/boot/runner-deps.d.ts +6 -4
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.d.ts +0 -3
- package/dist/boot/session-faces.js +15 -8
- package/dist/boot/stage-02-web-admin-pull.js +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +2 -6
- package/dist/boot/stage-03-store-backend.js +2 -3
- package/dist/boot/stage-04-stores.d.ts +2 -6
- package/dist/boot/stage-04-stores.js +5 -5
- package/dist/boot/stage-05-execution-env.d.ts +2 -6
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +2 -6
- package/dist/boot/stage-06-runners.js +2 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -6
- package/dist/boot/stage-08-reapers.d.ts +2 -6
- package/dist/boot/stage-09-leader.d.ts +2 -6
- package/dist/boot/stage-09-leader.js +1 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -6
- package/dist/boot/stage-10-http-server.js +7 -6
- package/dist/boot/stores.d.ts +3 -6
- package/dist/boot/stores.js +27 -40
- package/dist/boot/task-list-lane.d.ts +1 -1
- package/dist/boot/task-list-lane.js +1 -1
- package/dist/boot/web-admin.js +37 -11
- package/dist/capabilities/execution-lane.d.ts +2 -2
- package/dist/capabilities/mcp-probe.js +1 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +15 -7
- package/dist/capability-facts.js +8 -3
- package/dist/config-catalog.js +9 -8
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +33 -37
- package/dist/config-provider.js +51 -30
- package/dist/config-types.d.ts +21 -7
- package/dist/config.d.ts +33 -0
- package/dist/config.js +46 -16
- package/dist/delegated-session-owner.d.ts +4 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/deployment-shape.d.ts +10 -9
- package/dist/deployment-shape.js +4 -4
- package/dist/device-enrollment.d.ts +41 -2
- package/dist/device-enrollment.js +27 -12
- package/dist/device-store.d.ts +58 -85
- package/dist/device-store.js +19 -49
- package/dist/device-ws-hub.js +2 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -4
- package/dist/exec-cut.d.ts +60 -0
- package/dist/exec-cut.js +36 -0
- package/dist/execution-lane-caps.d.ts +115 -105
- package/dist/execution-lane-caps.js +76 -82
- package/dist/execution-placement.d.ts +259 -0
- package/dist/execution-placement.js +199 -0
- package/dist/field-conflict.d.ts +3 -2
- package/dist/field-conflict.js +1 -0
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +8 -19
- package/dist/http/checkpoint-ctx.d.ts +21 -12
- package/dist/http/checkpoint-ctx.js +4 -2
- package/dist/http/claimed-run.d.ts +1 -1
- package/dist/http/durable-run-leg.js +9 -5
- package/dist/http/execution-context.d.ts +218 -0
- package/dist/http/execution-context.js +330 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
- package/dist/http/memory-compliance-failure-reply.js +5 -0
- package/dist/http/memory-layer-reply.d.ts +30 -0
- package/dist/http/memory-layer-reply.js +24 -0
- package/dist/http/resume-legs.js +63 -36
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/route-ctx.d.ts +14 -8
- package/dist/http/routes/adoption.js +1 -1
- package/dist/http/routes/background.d.ts +29 -1
- package/dist/http/routes/background.js +29 -2
- package/dist/http/routes/devices.d.ts +13 -19
- package/dist/http/routes/devices.js +21 -26
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-compliance.js +8 -1
- package/dist/http/routes/memory-consolidation.js +12 -1
- package/dist/http/routes/memory-origin.js +14 -2
- package/dist/http/routes/memory-policy.js +5 -0
- package/dist/http/routes/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +143 -22
- package/dist/http/routes/side-query.d.ts +12 -4
- package/dist/http/routes/side-query.js +4 -5
- package/dist/http/routes/tasks.js +41 -33
- package/dist/http/server.d.ts +40 -44
- package/dist/http/server.js +13 -2
- package/dist/http/session-inherit-env.d.ts +3 -4
- package/dist/http/session-inherit-env.js +1 -1
- package/dist/http/wire-types.d.ts +8 -12
- package/dist/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/lane-home-probe.d.ts +68 -42
- package/dist/lane-home-probe.js +53 -30
- package/dist/leader/wire.js +5 -4
- package/dist/memory-bundle-engine.d.ts +16 -9
- package/dist/memory-bundle-engine.js +29 -6
- package/dist/memory-operator-faces.d.ts +85 -30
- package/dist/memory-operator-faces.js +136 -30
- package/dist/memory-project-layer.d.ts +3 -21
- package/dist/memory-project-layer.js +11 -8
- package/dist/memory-scope-stores.d.ts +73 -0
- package/dist/memory-scope-stores.js +86 -0
- package/dist/memory-scope.d.ts +29 -5
- package/dist/memory-scope.js +16 -4
- package/dist/memory-sync-client.d.ts +70 -18
- package/dist/memory-sync-client.js +215 -117
- package/dist/memory-sync.d.ts +1 -1
- package/dist/memory-sync.js +3 -2
- package/dist/observability/cost-taxonomy.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +12 -0
- package/dist/observability/fail-open.js +12 -0
- package/dist/observability/run-terminal.js +0 -1
- package/dist/orchestration/workflow-notify-journal.js +4 -4
- package/dist/org-memory-admission.d.ts +4 -3
- package/dist/org-memory-admission.js +6 -6
- package/dist/parked-decide.d.ts +34 -9
- package/dist/parked-decide.js +12 -19
- package/dist/parked-revive-surface.d.ts +12 -15
- package/dist/parked-revive-surface.js +3 -6
- package/dist/peer-seat.d.ts +13 -7
- package/dist/peer-seat.js +37 -3
- package/dist/plugins/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-file.d.ts +7 -0
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
- package/dist/plugins/approval-ask-store-memory.js +12 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
- package/dist/plugins/approval-ask-store-sql.js +44 -26
- package/dist/plugins/approval-nonce-store.js +4 -4
- package/dist/plugins/blob-backend.d.ts +4 -4
- package/dist/plugins/blob-backend.js +7 -7
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/caching-session-store.d.ts +6 -2
- package/dist/plugins/caching-session-store.js +9 -3
- package/dist/plugins/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
- package/dist/plugins/checkpoint-store-sql.js +21 -23
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +68 -122
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +151 -0
- package/dist/plugins/execution-placement-sql.js +223 -0
- package/dist/plugins/file-history-store-sql.d.ts +9 -8
- package/dist/plugins/file-history-store-sql.js +9 -9
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +4 -0
- package/dist/plugins/fork-routing-session-store.d.ts +6 -0
- package/dist/plugins/fork-routing-session-store.js +16 -0
- package/dist/plugins/image-bake-store-sql.js +9 -9
- package/dist/plugins/image-index-sql.js +11 -11
- package/dist/plugins/leader-run-store-sql.js +4 -4
- package/dist/plugins/local-session-store.d.ts +149 -50
- package/dist/plugins/local-session-store.js +382 -107
- package/dist/plugins/mailbox-store-sql.d.ts +2 -1
- package/dist/plugins/mailbox-store-sql.js +4 -4
- package/dist/plugins/memory-engine-tidb.js +5 -5
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +117 -8
- package/dist/plugins/one-time-migrations.js +408 -38
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
- package/dist/plugins/permission-rule-store-sql.js +29 -32
- package/dist/plugins/pg-pool.d.ts +7 -3
- package/dist/plugins/pg-session-storage.d.ts +31 -20
- package/dist/plugins/pg-session-storage.js +120 -52
- package/dist/plugins/remote-env-adb.d.ts +18 -19
- package/dist/plugins/remote-env-adb.js +91 -66
- package/dist/plugins/remote-env-device.d.ts +23 -31
- package/dist/plugins/remote-env-device.js +19 -17
- package/dist/plugins/remote-env-e2b.d.ts +27 -21
- package/dist/plugins/remote-env-e2b.js +91 -45
- package/dist/plugins/remote-env-file-error.d.ts +3 -1
- package/dist/plugins/remote-env-file-error.js +9 -3
- package/dist/plugins/remote-env-host.d.ts +31 -34
- package/dist/plugins/remote-env-host.js +91 -72
- package/dist/plugins/remote-env-k8s.d.ts +13 -29
- package/dist/plugins/remote-env-k8s.js +63 -55
- package/dist/plugins/remote-env-local-docker.d.ts +24 -12
- package/dist/plugins/remote-env-local-docker.js +49 -61
- package/dist/plugins/remote-env-ssh.d.ts +11 -19
- package/dist/plugins/remote-env-ssh.js +74 -57
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/resume-anchor-store-sql.js +2 -2
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +2 -2
- package/dist/plugins/retention-store-sql.d.ts +10 -7
- package/dist/plugins/retention-store-sql.js +13 -7
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +20 -15
- package/dist/plugins/schema-consistency.d.ts +21 -11
- package/dist/plugins/schema-consistency.js +15 -9
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- package/dist/plugins/schema-ensure-legs.d.ts +2 -3
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/schema-live-columns.d.ts +1 -1
- package/dist/plugins/session-capture-record-store-sql.js +1 -1
- package/dist/plugins/session-fork.d.ts +54 -0
- package/dist/plugins/session-fork.js +38 -1
- package/dist/plugins/session-row-sql.d.ts +22 -0
- package/dist/plugins/session-row-sql.js +81 -0
- package/dist/plugins/session-rules-column.d.ts +1 -1
- package/dist/plugins/session-rules-column.js +1 -1
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.js +1 -1
- package/dist/plugins/shared-memory-store-sql.js +2 -2
- package/dist/plugins/sql-driver.d.ts +10 -5
- package/dist/plugins/sql-driver.js +6 -6
- package/dist/plugins/sql-errors.js +1 -1
- package/dist/plugins/store-backend.d.ts +5 -7
- package/dist/plugins/store-backend.js +23 -11
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +13 -7
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.d.ts +57 -26
- package/dist/plugins/tidb-session-store.js +127 -59
- package/dist/plugins/tool-result-store-sql.d.ts +5 -4
- package/dist/plugins/tool-result-store-sql.js +21 -13
- package/dist/plugins/user-input-ledger-sql.js +2 -2
- package/dist/plugins/workflow-journal-store-sql.js +5 -5
- package/dist/plugins/workflow-run-store-sql.js +19 -19
- package/dist/plugins/write-behind-counter.d.ts +2 -3
- package/dist/plugins/write-behind-counter.js +5 -5
- package/dist/project-memory.d.ts +9 -15
- package/dist/project-memory.js +5 -4
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +6 -7
- package/dist/request-value-closure.js +1 -3
- package/dist/resource-suspend.d.ts +14 -12
- package/dist/resource-suspend.js +1 -1
- package/dist/resource-window.d.ts +4 -3
- package/dist/run-local.d.ts +6 -0
- package/dist/run-local.js +34 -35
- package/dist/runs.d.ts +10 -5
- package/dist/runs.js +52 -66
- package/dist/security.d.ts +38 -12
- package/dist/security.js +8 -2
- package/dist/server-secret-env.d.ts +9 -5
- package/dist/session-children-retire.d.ts +27 -9
- package/dist/session-children-retire.js +9 -5
- package/dist/session-incarnation.d.ts +20 -10
- package/dist/session-incarnation.js +5 -3
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-row.d.ts +114 -5
- package/dist/session-row.js +55 -5
- package/dist/session-titler.d.ts +2 -2
- package/dist/sql-engine-posture.d.ts +1 -1
- package/dist/sql-engine-posture.js +1 -1
- package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
- package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
- package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
- package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
- package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-a2a.js +5 -6
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +22 -9
- package/dist/task-mcp.js +17 -9
- package/dist/terminal.d.ts +27 -0
- package/dist/terminal.js +6 -0
- package/dist/tool-approval.d.ts +6 -0
- package/dist/tool-approval.js +8 -1
- package/dist/trace/project.d.ts +1 -0
- package/dist/trace/project.js +1 -0
- package/dist/trace/redact.d.ts +3 -1
- package/dist/trace/redact.js +80 -28
- package/dist/trace/self-minted-wire.d.ts +169 -2
- package/dist/trace/self-minted-wire.js +54 -1
- package/dist/url-refusal.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +17 -3
- package/dist/web-admin-client/apply-effective.js +20 -10
- package/dist/web-admin-client/candidate-gate.d.ts +11 -7
- package/dist/web-admin-client/candidate-gate.js +1 -1
- package/dist/web-admin-client/http-client.d.ts +5 -4
- package/dist/web-admin-client/http-client.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +7 -17
- package/dist/web-admin-client/read-warnings.js +5 -9
- package/dist/web-admin-client/skills-mcp.js +3 -2
- package/dist/web-admin-client/types.d.ts +6 -0
- package/package.json +3 -3
- package/wire-changes/server@7.139.0.json +348 -0
- package/wire-changes/server@7.140.0.json +184 -0
- package/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { isExecutionLane } from "../execution-lane-caps.js";
|
|
2
|
+
import { sessionFaceFromRow } from "../session-incarnation.js";
|
|
3
|
+
import { toIso } from "./sql-row-helpers.js";
|
|
4
|
+
import { WRITABLE, assertPlaceableLane, settledMirrorOutcome, settledPlaceOutcome, } from "../execution-placement.js";
|
|
5
|
+
export const SESSION_META_PLACEMENT_COLUMNS = [
|
|
6
|
+
{ name: "execution_role", mysql: "execution_role VARCHAR(16) NULL", pg: `execution_role VARCHAR(16) COLLATE "C"` },
|
|
7
|
+
{ name: "execution_lane", mysql: "execution_lane VARCHAR(32) NULL", pg: `execution_lane VARCHAR(32) COLLATE "C"` },
|
|
8
|
+
{ name: "execution_device_id", mysql: "execution_device_id VARCHAR(64) NULL", pg: `execution_device_id VARCHAR(64) COLLATE "C"` },
|
|
9
|
+
{ name: "execution_device_owner_tenant", mysql: "execution_device_owner_tenant VARCHAR(190) NULL", pg: `execution_device_owner_tenant VARCHAR(190) COLLATE "C"` },
|
|
10
|
+
{ name: "execution_device_owner_subject", mysql: "execution_device_owner_subject VARCHAR(190) NULL", pg: `execution_device_owner_subject VARCHAR(190) COLLATE "C"` },
|
|
11
|
+
{ name: "execution_placed_at", mysql: "execution_placed_at DATETIME(3) NULL", pg: "execution_placed_at TIMESTAMPTZ(3)" },
|
|
12
|
+
{ name: "execution_rev", mysql: "execution_rev BIGINT NULL", pg: "execution_rev BIGINT" },
|
|
13
|
+
{ name: "fork_key", mysql: "fork_key VARCHAR(255) NULL", pg: `fork_key VARCHAR(255) COLLATE "C"` },
|
|
14
|
+
];
|
|
15
|
+
export function sessionMetaPlacementColumnsDdl(dialect) {
|
|
16
|
+
return SESSION_META_PLACEMENT_COLUMNS.map((c) => c[dialect]).join(",\n ");
|
|
17
|
+
}
|
|
18
|
+
const WRITER = "writer";
|
|
19
|
+
const MIRROR = "mirror";
|
|
20
|
+
const NOT_WRITER = "(execution_role IS NULL OR execution_role <> 'writer')";
|
|
21
|
+
export const MIRROR_ON_COMMIT_SET = "execution_role = 'mirror'";
|
|
22
|
+
export const MIRROR_ON_COMMIT_GUARD = NOT_WRITER;
|
|
23
|
+
export function importedRowCells(forkKey) {
|
|
24
|
+
return { columns: "fork_key, execution_role", values: [forkKey, MIRROR] };
|
|
25
|
+
}
|
|
26
|
+
export const SESSION_META_PLACEMENT_READ_COLUMNS = "owner, incarnation, placement_kind, execution_role, execution_lane, execution_device_id, execution_device_owner_tenant, execution_device_owner_subject, execution_placed_at, execution_rev";
|
|
27
|
+
function binder(dialect) {
|
|
28
|
+
const values = [];
|
|
29
|
+
return {
|
|
30
|
+
values,
|
|
31
|
+
p: (v) => {
|
|
32
|
+
values.push(v);
|
|
33
|
+
return dialect === "pg" ? `$${values.length}` : "?";
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function occupantPredicate(dialect, p, root, occupant) {
|
|
38
|
+
const nullSafeEq = dialect === "pg" ? "IS NOT DISTINCT FROM" : "<=>";
|
|
39
|
+
return `session_id = ${p(root)} AND incarnation = ${p(occupant.incarnation)} AND owner ${nullSafeEq} ${p(occupant.owner)} AND placement_kind IS NULL`;
|
|
40
|
+
}
|
|
41
|
+
export function readPlacementRowStatement(dialect, root, opts) {
|
|
42
|
+
const b = binder(dialect);
|
|
43
|
+
return { text: `SELECT ${SESSION_META_PLACEMENT_READ_COLUMNS} FROM session_meta WHERE session_id = ${b.p(root)}${opts.lock ? " FOR UPDATE" : ""}`, values: b.values };
|
|
44
|
+
}
|
|
45
|
+
export function placeWriterStatement(dialect, root, occupant, cell, now) {
|
|
46
|
+
const b = binder(dialect);
|
|
47
|
+
const set = `execution_role = '${WRITER}', execution_lane = ${b.p(cell.lane)}, execution_device_id = ${b.p(cell.device?.id ?? null)}, ` +
|
|
48
|
+
`execution_device_owner_tenant = ${b.p(cell.device?.owner.tenant ?? null)}, execution_device_owner_subject = ${b.p(cell.device?.owner.subject ?? null)}, ` +
|
|
49
|
+
`execution_placed_at = ${b.p(now)}, execution_rev = 0`;
|
|
50
|
+
return { text: `UPDATE session_meta SET ${set} WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role IS NULL`, values: b.values };
|
|
51
|
+
}
|
|
52
|
+
export function bindDeviceStatement(dialect, root, occupant, device, now) {
|
|
53
|
+
const b = binder(dialect);
|
|
54
|
+
const set = `execution_device_id = ${b.p(device.id)}, execution_device_owner_tenant = ${b.p(device.owner.tenant)}, execution_device_owner_subject = ${b.p(device.owner.subject)}, execution_placed_at = ${b.p(now)}, execution_rev = 0`;
|
|
55
|
+
return {
|
|
56
|
+
text: `UPDATE session_meta SET ${set} WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role = '${WRITER}' AND execution_lane = 'device' AND execution_device_id IS NULL`,
|
|
57
|
+
values: b.values,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function rebindDeviceStatement(dialect, root, occupant, owner, expectedRev, targetDeviceId, now) {
|
|
61
|
+
const b = binder(dialect);
|
|
62
|
+
const set = `execution_device_id = ${b.p(targetDeviceId)}, execution_placed_at = ${b.p(now)}, execution_rev = execution_rev + 1`;
|
|
63
|
+
const where = `${occupantPredicate(dialect, b.p, root, occupant)} AND execution_role = '${WRITER}' AND execution_lane = 'device' ` +
|
|
64
|
+
`AND execution_device_owner_tenant = ${b.p(owner.tenant)} AND execution_device_owner_subject = ${b.p(owner.subject)} AND execution_rev = ${b.p(expectedRev)}`;
|
|
65
|
+
return { text: `UPDATE session_meta SET ${set} WHERE ${where}`, values: b.values };
|
|
66
|
+
}
|
|
67
|
+
export function markMirrorStatement(dialect, root, occupant) {
|
|
68
|
+
const b = binder(dialect);
|
|
69
|
+
return { text: `UPDATE session_meta SET execution_role = '${MIRROR}' WHERE ${occupantPredicate(dialect, b.p, root, occupant)} AND ${NOT_WRITER}`, values: b.values };
|
|
70
|
+
}
|
|
71
|
+
export function backfillWriterStatement(dialect, lane, now) {
|
|
72
|
+
const b = binder(dialect);
|
|
73
|
+
return {
|
|
74
|
+
text: `UPDATE session_meta SET execution_role = '${WRITER}', execution_lane = ${b.p(lane)}, execution_placed_at = ${b.p(now)}, execution_rev = 0 ` +
|
|
75
|
+
"WHERE execution_role IS NULL AND placement_kind IS NULL AND EXISTS (SELECT 1 FROM task_run tr WHERE tr.session_id = session_meta.session_id)",
|
|
76
|
+
values: b.values,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function migrateDeviceBindingsStatement(dialect, sourceTable) {
|
|
80
|
+
if (dialect === "pg") {
|
|
81
|
+
return {
|
|
82
|
+
text: `UPDATE session_meta SET execution_role = '${WRITER}', execution_lane = 'device', execution_device_id = ds.device_id, ` +
|
|
83
|
+
"execution_device_owner_tenant = ds.owner_tenant, execution_device_owner_subject = ds.owner_subject, execution_placed_at = ds.bound_at, execution_rev = ds.rev " +
|
|
84
|
+
`FROM ${sourceTable} ds WHERE ds.root_session_id = session_meta.session_id AND session_meta.placement_kind IS NULL`,
|
|
85
|
+
values: [],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const str = (col) => `CONVERT(ds.${col} USING utf8mb4)`;
|
|
89
|
+
return {
|
|
90
|
+
text: `UPDATE session_meta sm JOIN ${sourceTable} ds ON ${str("root_session_id")} COLLATE utf8mb4_bin = sm.session_id ` +
|
|
91
|
+
`SET sm.execution_role = '${WRITER}', sm.execution_lane = 'device', sm.execution_device_id = ${str("device_id")}, ` +
|
|
92
|
+
`sm.execution_device_owner_tenant = ${str("owner_tenant")}, sm.execution_device_owner_subject = ${str("owner_subject")}, sm.execution_placed_at = ds.bound_at, sm.execution_rev = ds.rev ` +
|
|
93
|
+
"WHERE sm.placement_kind IS NULL",
|
|
94
|
+
values: [],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export function countOrphanBindingsStatement(dialect, sourceTable) {
|
|
98
|
+
const rootKey = dialect === "pg" ? "ds.root_session_id" : "CONVERT(ds.root_session_id USING utf8mb4) COLLATE utf8mb4_bin";
|
|
99
|
+
return {
|
|
100
|
+
text: `SELECT COUNT(*) AS orphans FROM ${sourceTable} ds WHERE NOT EXISTS (SELECT 1 FROM session_meta sm WHERE sm.session_id = ${rootKey} AND sm.placement_kind IS NULL)`,
|
|
101
|
+
values: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export class ExecutionPlacementRowError extends Error {
|
|
105
|
+
sessionId;
|
|
106
|
+
code = "execution_placement.row_unreadable";
|
|
107
|
+
constructor(sessionId, detail) {
|
|
108
|
+
super(`session ${sessionId}: the execution placement columns do not form a placement (${detail}) — refusing to read it as any placement`);
|
|
109
|
+
this.sessionId = sessionId;
|
|
110
|
+
this.name = "ExecutionPlacementRowError";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const isNull = (v) => v === null || v === undefined;
|
|
114
|
+
const text = (v) => (Buffer.isBuffer(v) ? v.toString("utf8") : String(v));
|
|
115
|
+
export function placementCellFromRow(root, row) {
|
|
116
|
+
const role = row.execution_role;
|
|
117
|
+
const deviceCols = [row.execution_device_id, row.execution_device_owner_tenant, row.execution_device_owner_subject];
|
|
118
|
+
if (isNull(role) || role === MIRROR) {
|
|
119
|
+
if (!isNull(row.execution_lane) || deviceCols.some((v) => !isNull(v)))
|
|
120
|
+
throw new ExecutionPlacementRowError(root, `role ${isNull(role) ? "NULL" : MIRROR} with a lane or device`);
|
|
121
|
+
return isNull(role) ? { role: "absent" } : { role: "mirror" };
|
|
122
|
+
}
|
|
123
|
+
if (role !== WRITER)
|
|
124
|
+
throw new ExecutionPlacementRowError(root, `role ${JSON.stringify(text(role))} is not writer / mirror / NULL`);
|
|
125
|
+
const lane = isNull(row.execution_lane) ? "" : text(row.execution_lane);
|
|
126
|
+
if (!isExecutionLane(lane))
|
|
127
|
+
throw new ExecutionPlacementRowError(root, `writer lane ${JSON.stringify(lane)} is not an execution lane`);
|
|
128
|
+
if (isNull(row.execution_device_id)) {
|
|
129
|
+
if (deviceCols.some((v) => !isNull(v)))
|
|
130
|
+
throw new ExecutionPlacementRowError(root, "device owner without a device");
|
|
131
|
+
return { role: "writer", lane };
|
|
132
|
+
}
|
|
133
|
+
if (lane !== "device")
|
|
134
|
+
throw new ExecutionPlacementRowError(root, `a device on the ${lane} lane`);
|
|
135
|
+
if (deviceCols.some(isNull) || isNull(row.execution_rev) || isNull(row.execution_placed_at))
|
|
136
|
+
throw new ExecutionPlacementRowError(root, "a device without its owner, rev or placement time");
|
|
137
|
+
return {
|
|
138
|
+
role: "writer",
|
|
139
|
+
lane,
|
|
140
|
+
device: {
|
|
141
|
+
id: text(row.execution_device_id),
|
|
142
|
+
owner: { tenant: text(row.execution_device_owner_tenant), subject: text(row.execution_device_owner_subject) },
|
|
143
|
+
rev: Number(row.execution_rev),
|
|
144
|
+
placedAt: toIso(row.execution_placed_at),
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function placementReadingFromRow(root, row) {
|
|
149
|
+
if (row === undefined || !isNull(row.placement_kind))
|
|
150
|
+
return undefined;
|
|
151
|
+
return { occupant: sessionFaceFromRow(root, isNull(row.owner) ? null : text(row.owner), row.incarnation), cell: placementCellFromRow(root, row) };
|
|
152
|
+
}
|
|
153
|
+
export function rowsOnSqlExec(exec) {
|
|
154
|
+
return async (sql, values) => (await exec.query(sql, [...values])).rows;
|
|
155
|
+
}
|
|
156
|
+
export async function lockRootAndReadClaim(conn, dialect, root) {
|
|
157
|
+
const ph = dialect === "pg" ? "$1" : "?";
|
|
158
|
+
const row = await conn(`SELECT session_id FROM session_meta WHERE session_id = ${ph} FOR UPDATE`, [root]);
|
|
159
|
+
if (row.length === 0)
|
|
160
|
+
return { rowPresent: false, activeTaskId: null };
|
|
161
|
+
const claim = await conn(`SELECT task_id FROM task_active WHERE session_id = ${ph} FOR UPDATE`, [root]);
|
|
162
|
+
return { rowPresent: true, activeTaskId: claim[0] === undefined ? null : text(claim[0].task_id) };
|
|
163
|
+
}
|
|
164
|
+
export async function readPlacementOn(conn, dialect, root) {
|
|
165
|
+
const s = readPlacementRowStatement(dialect, root, { lock: true });
|
|
166
|
+
return placementReadingFromRow(root, (await conn(s.text, s.values))[0]);
|
|
167
|
+
}
|
|
168
|
+
function placementInvariant(root, what) {
|
|
169
|
+
return new Error(`execution placement of session ${root}: ${what} matched no row while a re-read finds the cell writable — engine/driver anomaly, refusing to guess`);
|
|
170
|
+
}
|
|
171
|
+
export function createSqlExecutionPlacementSeat(db) {
|
|
172
|
+
const read = async (root) => {
|
|
173
|
+
const s = readPlacementRowStatement(db.dialect, root, { lock: false });
|
|
174
|
+
return placementReadingFromRow(root, (await db.query(s.text, [...s.values])).rows[0]);
|
|
175
|
+
};
|
|
176
|
+
return {
|
|
177
|
+
read,
|
|
178
|
+
async place(root, occupant, cell) {
|
|
179
|
+
assertPlaceableLane(root, cell);
|
|
180
|
+
const s = placeWriterStatement(db.dialect, root, occupant, { lane: cell.lane }, new Date());
|
|
181
|
+
if ((await db.query(s.text, [...s.values])).affected === 1)
|
|
182
|
+
return { outcome: "placed" };
|
|
183
|
+
const settled = settledPlaceOutcome(root, await read(root), occupant, cell);
|
|
184
|
+
if (settled === WRITABLE)
|
|
185
|
+
throw placementInvariant(root, "place");
|
|
186
|
+
return settled;
|
|
187
|
+
},
|
|
188
|
+
async markMirror(root, occupant) {
|
|
189
|
+
const conn = await db.connect();
|
|
190
|
+
try {
|
|
191
|
+
await conn.begin();
|
|
192
|
+
const rowsOn = rowsOnSqlExec(conn);
|
|
193
|
+
const claim = await lockRootAndReadClaim(rowsOn, db.dialect, root);
|
|
194
|
+
if (!claim.rowPresent) {
|
|
195
|
+
await conn.rollback();
|
|
196
|
+
return { outcome: "gone" };
|
|
197
|
+
}
|
|
198
|
+
const settled = settledMirrorOutcome(root, await readPlacementOn(rowsOn, db.dialect, root), occupant);
|
|
199
|
+
if (settled !== WRITABLE) {
|
|
200
|
+
await conn.rollback();
|
|
201
|
+
return settled;
|
|
202
|
+
}
|
|
203
|
+
if (claim.activeTaskId !== null) {
|
|
204
|
+
await conn.rollback();
|
|
205
|
+
return { outcome: "active", activeTaskId: claim.activeTaskId };
|
|
206
|
+
}
|
|
207
|
+
const s = markMirrorStatement(db.dialect, root, occupant);
|
|
208
|
+
if ((await conn.query(s.text, [...s.values])).affected !== 1)
|
|
209
|
+
throw placementInvariant(root, "markMirror under a held row lock");
|
|
210
|
+
await conn.commit();
|
|
211
|
+
return { outcome: "mirrored" };
|
|
212
|
+
}
|
|
213
|
+
catch (err) {
|
|
214
|
+
await conn.rollback().catch(() => undefined);
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
conn.release();
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=execution-placement-sql.js.map
|
|
@@ -3,7 +3,7 @@ import type { Pool as PgPool } from "pg";
|
|
|
3
3
|
import { type FileHistoryRetentionPolicy, type ExecutionEnv, type FileHistoryStore, type FileHistoryResult, type FileHistoryTrackResult, type FileHistoryRestoreResult, type FileHistoryExport, type FileHistoryExportResult } from "@sema-agent/core";
|
|
4
4
|
import { type BlobBackend } from "./blob-backend.js";
|
|
5
5
|
import type { SyncBlobPutResult } from "./store-backend.js";
|
|
6
|
-
import { type SqlDriver } from "./sql-driver.js";
|
|
6
|
+
import { type SqlDriver, type SqlDialect } from "./sql-driver.js";
|
|
7
7
|
/** The file-history blob domain's SQL table (BlobBackend `table`/MinIO index binding + the A7 assert). */
|
|
8
8
|
export declare const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
|
|
9
9
|
/** MinIO object-key prefix for this domain (snapshot epoch used "blobs/"; a separate prefix keeps the
|
|
@@ -183,17 +183,18 @@ export declare class PgFileHistoryStore extends SqlFileHistoryStore {
|
|
|
183
183
|
}
|
|
184
184
|
/**
|
|
185
185
|
* [ref] D1② — A7-recipe upgrade precondition assert (**refuse to start**, never a warn; the form and
|
|
186
|
-
* rationale follow `assertPermissionRuleTicketSchema` verbatim:
|
|
186
|
+
* rationale follow `assertPermissionRuleTicketSchema` verbatim: the boot check never alters a table, and
|
|
187
187
|
* `CREATE TABLE IF NOT EXISTS` is a no-op on an existing table whose columns predate this build).
|
|
188
188
|
*
|
|
189
189
|
* The probe is one always-empty `WHERE 1=0` read per table naming every column this build references.
|
|
190
|
-
* ONLY a provable missing-column error gets the
|
|
191
|
-
* (timeout / reset / permission / missing table) rethrows untouched:
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
190
|
+
* ONLY a provable missing-column error gets the missing-column refusal — everything else
|
|
191
|
+
* (timeout / reset / permission / missing table) rethrows untouched: diagnosing a connection blip as a
|
|
192
|
+
* schema drift is more expensive than the defect this gate exists to stop. The refusal's ways out are the
|
|
193
|
+
* closed set of three shared with the whole-database check (`describeProbedTableDrift`); none of them
|
|
194
|
+
* is destructive. Honest cost statement: the history lives in these tables (per-file REWIND history,
|
|
195
|
+
* working-tree convenience state) — session transcripts and every other durable subsystem are untouched.
|
|
195
196
|
*/
|
|
196
197
|
export declare function assertFileHistorySchema(query: (sql: string) => Promise<{
|
|
197
198
|
rows: Record<string, unknown>[];
|
|
198
|
-
}>, dialect:
|
|
199
|
+
}>, dialect: SqlDialect): Promise<void>;
|
|
199
200
|
//# sourceMappingURL=file-history-store-sql.d.ts.map
|
|
@@ -3,6 +3,7 @@ import { captureFileBackupViaEnv, applyFileRestoreViaEnv, fileHistoryModeProvenD
|
|
|
3
3
|
import { BlobTooLargeError, SqlBlobBackend } from "./blob-backend.js";
|
|
4
4
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
5
5
|
import { isMissingColumnError } from "./sql-errors.js";
|
|
6
|
+
import { describeProbedTableDrift } from "./schema-drift-sentence.js";
|
|
6
7
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
7
8
|
import { foldCaughtFailure } from "../file-history-disclosure.js";
|
|
8
9
|
export const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
|
|
@@ -32,13 +33,13 @@ export class SqlFileHistoryStore {
|
|
|
32
33
|
this.db = db;
|
|
33
34
|
this.pool = pool;
|
|
34
35
|
this.blobs = blobBackend ?? new SqlBlobBackend(this.db.dialect, this.pool, undefined, FILE_HISTORY_BLOB_TABLE);
|
|
35
|
-
this.boundaryKeep = resolveFileHistoryRetention(this.db.dialect === "
|
|
36
|
+
this.boundaryKeep = resolveFileHistoryRetention(this.db.dialect === "mysql" ? "TiDBFileHistoryStore" : "PgFileHistoryStore", opts?.retention);
|
|
36
37
|
}
|
|
37
|
-
q(
|
|
38
|
-
return this.db.dialect === "
|
|
38
|
+
q(mysql, pg) {
|
|
39
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
39
40
|
}
|
|
40
41
|
bool(v) {
|
|
41
|
-
return this.db.dialect === "
|
|
42
|
+
return this.db.dialect === "mysql" ? (v ? 1 : 0) : v;
|
|
42
43
|
}
|
|
43
44
|
leaseKey(scope, entryId) {
|
|
44
45
|
return `${scope}\x00${entryId}`;
|
|
@@ -835,11 +836,10 @@ export async function assertFileHistorySchema(query, dialect) {
|
|
|
835
836
|
throw err;
|
|
836
837
|
throw new Error(`${table} is missing required columns — refusing to start. This build's per-file rewind history (S-15) ` +
|
|
837
838
|
`references every probed column unconditionally; a partial table would fail mid-write and could publish a ` +
|
|
838
|
-
`half-shaped history graph.
|
|
839
|
-
|
|
840
|
-
`
|
|
841
|
-
|
|
842
|
-
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}`);
|
|
839
|
+
`half-shaped history graph. The file_history_* tables hold the per-file REWIND history (working-tree convenience ` +
|
|
840
|
+
`state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history.\n` +
|
|
841
|
+
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
|
|
842
|
+
(await describeProbedTableDrift(query, dialect, [table])));
|
|
843
843
|
}
|
|
844
844
|
}
|
|
845
845
|
}
|
|
@@ -152,7 +152,7 @@ export declare class FileRunStore implements InProcessClaimFence {
|
|
|
152
152
|
whileHoldingClaim(sessionId: string, taskId: string, write: () => FenceLanded): boolean;
|
|
153
153
|
getRun(taskId: string): Promise<RunRecord | undefined>;
|
|
154
154
|
/** Undo THIS submission's binding (key + body fingerprint → null) — called only where the submit surface refuses the submission
|
|
155
|
-
* before any work and terminalizes the row it just claimed (the sync leg's zero-work refusal,
|
|
155
|
+
* before any work and terminalizes the row it just claimed (the sync leg's zero-work refusal, an execution-placement reject):
|
|
156
156
|
* the error answer invites a retry, and a retry with the same key must run, not replay the refusal. The row stays (it
|
|
157
157
|
* is the refusal's record); only the binding is freed. */
|
|
158
158
|
releaseIdemKey(taskId: string): Promise<void>;
|
|
@@ -332,6 +332,11 @@ export declare class FileRunStore implements InProcessClaimFence {
|
|
|
332
332
|
* 起服时调(`LocalBackend.runOneTimeMigrations`,任何读者之前);数据根独占 ⇒ 本车道没有滚动升级窗,不需要读侧半场。幂等:第二跑零命中零写盘。
|
|
333
333
|
*/
|
|
334
334
|
migrateLegacyHolderDeathCodes(): number;
|
|
335
|
+
/**
|
|
336
|
+
* 一次性迁移 **M32 的 local 腿**(执行放置回填;窗、删法与删后谁接住:窗口见 `plugins/one-time-migrations.ts` 登记表):有 run 行的会话 id(去重)。
|
|
337
|
+
* 起服时调(`LocalBackend.runOneTimeMigrations`,任何读者之前);读内存索引(构造时已从盘上 hydrate),零写。
|
|
338
|
+
*/
|
|
339
|
+
sessionIdsWithRuns(): ReadonlySet<string>;
|
|
335
340
|
/** Release every open event-log fd (called by LocalBackend.close so a graceful restart can re-open the data dir). */
|
|
336
341
|
dispose(): void;
|
|
337
342
|
}
|
|
@@ -280,6 +280,7 @@ export class FileRunStore {
|
|
|
280
280
|
"Refusing to boot with an EMPTY single-active claim index (every session would look unclaimed, so a delete or a fresh run could race a live/parked one). " +
|
|
281
281
|
"Fix the permissions/mount on that path, point LOCAL_DATA_ROOT at a readable dir, or unset RUN_STORE_STRICT_HYDRATE to boot anyway (the pre-existing behavior).");
|
|
282
282
|
}
|
|
283
|
+
recordFailOpen("server.run-store.file-active-claims-unreadable", detail);
|
|
283
284
|
claims = [];
|
|
284
285
|
}
|
|
285
286
|
const nonTerminalBySanitizedSession = new Map();
|
|
@@ -1009,6 +1010,9 @@ export class FileRunStore {
|
|
|
1009
1010
|
}
|
|
1010
1011
|
return migrated;
|
|
1011
1012
|
}
|
|
1013
|
+
sessionIdsWithRuns() {
|
|
1014
|
+
return new Set([...this.runs.values()].map((r) => r.sessionId));
|
|
1015
|
+
}
|
|
1012
1016
|
dispose() {
|
|
1013
1017
|
for (const log of this.eventLogs.values()) {
|
|
1014
1018
|
try {
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { type SessionStore, type AcquiredSession, type SessionStoreSummary, type SessionPlacement, type PlacedSessionRow } from "@sema-agent/core";
|
|
27
27
|
import { type DelegationTree } from "../delegated-session-owner.js";
|
|
28
|
+
import type { ExecutionPlacementSeat } from "../execution-placement.js";
|
|
29
|
+
import type { ForkToFn } from "./session-fork.js";
|
|
28
30
|
/** acquire 选项形(core 5.48 SessionStore 契约):[ref] placement=受信 spawner 的分区建会话声明。 */
|
|
29
31
|
type AcquireOpts = {
|
|
30
32
|
requireExisting?: boolean;
|
|
@@ -54,6 +56,10 @@ export declare class ForkRoutingSessionStore implements SessionStore {
|
|
|
54
56
|
olderThanMs?: number;
|
|
55
57
|
scope?: string;
|
|
56
58
|
}) => Promise<PlacedSessionRow[]>;
|
|
59
|
+
/** 执行席透传(host 有才有):执行放置是**根会话行**上的一格,根会话行住 host durable 店;transient 店没有会话行可放,也就不声明席。 */
|
|
60
|
+
readonly executionPlacement?: ExecutionPlacementSeat;
|
|
61
|
+
/** fork 续写的店契约透传(host 有才有):目标在 host 店原子新建,新 id 与源一并记 host 路由(与 {@link fork} 的 host 腿同)。 */
|
|
62
|
+
readonly forkTo?: ForkToFn;
|
|
57
63
|
constructor(host: SessionStore, transient: SessionStore);
|
|
58
64
|
private remember;
|
|
59
65
|
private pick;
|
|
@@ -11,6 +11,8 @@ export class ForkRoutingSessionStore {
|
|
|
11
11
|
placed = new Set();
|
|
12
12
|
placements;
|
|
13
13
|
listPlaced;
|
|
14
|
+
executionPlacement;
|
|
15
|
+
forkTo;
|
|
14
16
|
constructor(host, transient) {
|
|
15
17
|
this.host = host;
|
|
16
18
|
this.transient = transient;
|
|
@@ -23,6 +25,20 @@ export class ForkRoutingSessionStore {
|
|
|
23
25
|
if (host.fork === undefined && transient.fork === undefined) {
|
|
24
26
|
this.fork = undefined;
|
|
25
27
|
}
|
|
28
|
+
const hostFaces = host;
|
|
29
|
+
if (hostFaces.executionPlacement !== undefined)
|
|
30
|
+
this.executionPlacement = hostFaces.executionPlacement;
|
|
31
|
+
const hostForkTo = hostFaces.forkTo;
|
|
32
|
+
if (typeof hostForkTo === "function") {
|
|
33
|
+
this.forkTo = async (sourceId, owner, target) => {
|
|
34
|
+
const out = await hostForkTo.call(host, sourceId, owner, target);
|
|
35
|
+
if (out !== null && out.outcome !== "occupied") {
|
|
36
|
+
this.remember(sourceId, "host");
|
|
37
|
+
this.remember(out.id, "host");
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
26
42
|
}
|
|
27
43
|
remember(id, where) {
|
|
28
44
|
this.route.delete(id);
|
|
@@ -145,7 +145,7 @@ export async function ensurePgImageBakeSchema(pool, poolName = "standard") {
|
|
|
145
145
|
}
|
|
146
146
|
const isDupKey = isDupKeyError;
|
|
147
147
|
function dupKeyName(dialect, err) {
|
|
148
|
-
if (dialect === "
|
|
148
|
+
if (dialect === "mysql") {
|
|
149
149
|
const msg = String(err?.sqlMessage ?? err?.message ?? "");
|
|
150
150
|
if (/uq_image_bake_event_line/i.test(msg))
|
|
151
151
|
return "uq_line";
|
|
@@ -169,8 +169,8 @@ export class SqlImageBake {
|
|
|
169
169
|
this.db = db;
|
|
170
170
|
this.poolName = poolName;
|
|
171
171
|
}
|
|
172
|
-
q(
|
|
173
|
-
return this.db.dialect === "
|
|
172
|
+
q(mysql, pg) {
|
|
173
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
174
174
|
}
|
|
175
175
|
insertParams(bakeId, input, now) {
|
|
176
176
|
return [
|
|
@@ -214,7 +214,7 @@ export class SqlImageBake {
|
|
|
214
214
|
const conn = await this.db.connect();
|
|
215
215
|
let inserted = false;
|
|
216
216
|
try {
|
|
217
|
-
if (this.db.dialect === "
|
|
217
|
+
if (this.db.dialect === "mysql") {
|
|
218
218
|
await conn.begin();
|
|
219
219
|
await conn.query("INSERT INTO image_bake_admit (pool) VALUES (?) ON DUPLICATE KEY UPDATE pool = pool", [this.poolName]);
|
|
220
220
|
await conn.query("SELECT pool FROM image_bake_admit WHERE pool = ? FOR UPDATE", [this.poolName]);
|
|
@@ -255,15 +255,15 @@ export class SqlImageBake {
|
|
|
255
255
|
return { created: true, bake };
|
|
256
256
|
}
|
|
257
257
|
async getBake(bakeId) {
|
|
258
|
-
const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("
|
|
258
|
+
const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("mysql")} FROM image_bake WHERE bake_id = ? LIMIT 1`, `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE bake_id = $1 LIMIT 1`), [bakeId]);
|
|
259
259
|
return rows[0] ? refusingBakeRow(() => mapRow(rows[0])) : null;
|
|
260
260
|
}
|
|
261
261
|
async getBakeByIdem(idemKey) {
|
|
262
|
-
const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("
|
|
262
|
+
const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("mysql")} FROM image_bake WHERE idem_key = ? LIMIT 1`, `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE idem_key = $1 LIMIT 1`), [idemKey]);
|
|
263
263
|
return rows[0] ? refusingBakeRow(() => mapRow(rows[0])) : null;
|
|
264
264
|
}
|
|
265
265
|
async findActiveByArgv(argv) {
|
|
266
|
-
const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("
|
|
266
|
+
const { rows } = await this.db.query(this.q(`SELECT ${bakeSelectCols("mysql")} FROM image_bake WHERE status IN ('queued','running') AND argv = CAST(? AS JSON) ` +
|
|
267
267
|
"ORDER BY created_at DESC LIMIT 1", `SELECT ${bakeSelectCols("pg")} FROM image_bake WHERE status IN ('queued','running') AND argv = $1::jsonb ` +
|
|
268
268
|
"ORDER BY created_at DESC LIMIT 1"), [JSON.stringify(argv)]);
|
|
269
269
|
return rows[0] ? refusingBakeRow(() => mapRow(rows[0])) : null;
|
|
@@ -380,7 +380,7 @@ export class SqlImageBake {
|
|
|
380
380
|
return m == null ? 0 : Number(m);
|
|
381
381
|
}
|
|
382
382
|
async getEvents(bakeId, afterSeq) {
|
|
383
|
-
const { rows } = await this.db.query(this.q(`SELECT seq, line_ord, kind, level, ${jsonColumnText("
|
|
383
|
+
const { rows } = await this.db.query(this.q(`SELECT seq, line_ord, kind, level, ${jsonColumnText("mysql", "data")}, ts FROM image_bake_event WHERE bake_id = ? AND seq > ? ORDER BY seq ASC`, `SELECT seq, line_ord, kind, level, ${jsonColumnText("pg", "data")}, ts FROM image_bake_event WHERE bake_id = $1 AND seq > $2 ORDER BY seq ASC`), [bakeId, afterSeq]);
|
|
384
384
|
return rows.map((r) => ({
|
|
385
385
|
seq: Number(r.seq),
|
|
386
386
|
lineOrd: r.line_ord === null ? null : Number(r.line_ord),
|
|
@@ -401,7 +401,7 @@ export class SqlImageBake {
|
|
|
401
401
|
await this.tx(async (conn) => {
|
|
402
402
|
const res = await conn.query(this.q("UPDATE image_bake SET status=?, state=?, digest=?, repo=?, ref=?, index_id=?, exit_code=?, error=?, " +
|
|
403
403
|
"error_code=?, manifest_sha=?, tag=?, ingest_secret=NULL, updated_at=? WHERE bake_id=? AND status IN ('queued','running')", "UPDATE image_bake SET status=$1, state=$2, digest=$3, repo=$4, ref=$5, index_id=$6, exit_code=$7, error=$8, " +
|
|
404
|
-
"error_code=$9, manifest_sha=$10, tag=$11, ingest_secret=NULL, updated_at=$12 WHERE bake_id=$13 AND status IN ('queued','running')"), this.db.dialect === "
|
|
404
|
+
"error_code=$9, manifest_sha=$10, tag=$11, ingest_secret=NULL, updated_at=$12 WHERE bake_id=$13 AND status IN ('queued','running')"), this.db.dialect === "mysql" ? this.terminalParamsTidb(bakeId, t) : this.terminalParamsPg(bakeId, t));
|
|
405
405
|
affectedRows = res.affected;
|
|
406
406
|
await conn.query(this.q("DELETE FROM image_bake_lease WHERE pool = ? AND bake_id = ?", "DELETE FROM image_bake_lease WHERE pool = $1 AND bake_id = $2"), [this.poolName, bakeId]);
|
|
407
407
|
});
|
|
@@ -181,8 +181,8 @@ export class SqlImageIndex {
|
|
|
181
181
|
constructor(db) {
|
|
182
182
|
this.db = db;
|
|
183
183
|
}
|
|
184
|
-
q(
|
|
185
|
-
return this.db.dialect === "
|
|
184
|
+
q(mysql, pg) {
|
|
185
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
186
186
|
}
|
|
187
187
|
static idFor(repo, digest) {
|
|
188
188
|
return createHash("sha256").update(`${repo}@${digest}`).digest("hex").slice(0, 64);
|
|
@@ -216,7 +216,7 @@ export class SqlImageIndex {
|
|
|
216
216
|
status = EXCLUDED.status, visibility = EXCLUDED.visibility, tenant_id = EXCLUDED.tenant_id,
|
|
217
217
|
manifest_sha = EXCLUDED.manifest_sha, recipe_git_sha = EXCLUDED.recipe_git_sha,
|
|
218
218
|
generator_version = EXCLUDED.generator_version, build_date = EXCLUDED.build_date,
|
|
219
|
-
supersedes = EXCLUDED.supersedes, signed = EXCLUDED.signed, updated_at = EXCLUDED.updated_at`), this.db.dialect === "
|
|
219
|
+
supersedes = EXCLUDED.supersedes, signed = EXCLUDED.signed, updated_at = EXCLUDED.updated_at`), this.db.dialect === "mysql" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
|
|
220
220
|
return id;
|
|
221
221
|
}
|
|
222
222
|
async registerBuilding(e) {
|
|
@@ -258,21 +258,21 @@ export class SqlImageIndex {
|
|
|
258
258
|
supersedes = EXCLUDED.supersedes,
|
|
259
259
|
signed = CASE WHEN sandbox_image_index.status = 'published'
|
|
260
260
|
THEN sandbox_image_index.signed ELSE EXCLUDED.signed END,
|
|
261
|
-
updated_at = EXCLUDED.updated_at`), this.db.dialect === "
|
|
261
|
+
updated_at = EXCLUDED.updated_at`), this.db.dialect === "mysql" ? tidbUpsertParams(e, id, now) : pgUpsertParams(e, id, now));
|
|
262
262
|
return id;
|
|
263
263
|
}
|
|
264
264
|
async getById(id) {
|
|
265
|
-
const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("
|
|
265
|
+
const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index WHERE id=? LIMIT 1`, `SELECT ${imageSelectCols("pg")} FROM sandbox_image_index WHERE id = $1 LIMIT 1`), [id]);
|
|
266
266
|
return rows[0] ? refusingImageRow(() => mapRow(rows[0])) : null;
|
|
267
267
|
}
|
|
268
268
|
async getByDigest(digest) {
|
|
269
|
-
const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("
|
|
269
|
+
const { rows } = await this.db.query(this.q(`SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index WHERE digest=? ORDER BY updated_at DESC LIMIT 1`, `SELECT ${imageSelectCols("pg")} FROM sandbox_image_index WHERE digest = $1 ORDER BY updated_at DESC LIMIT 1`), [digest]);
|
|
270
270
|
return rows[0] ? refusingImageRow(() => mapRow(rows[0])) : null;
|
|
271
271
|
}
|
|
272
272
|
async latestPublished(profile, viewer) {
|
|
273
|
-
if (this.db.dialect === "
|
|
273
|
+
if (this.db.dialect === "mysql") {
|
|
274
274
|
const vis = visibilityClauseTidb(viewer);
|
|
275
|
-
const { rows } = await this.db.query(`SELECT ${imageSelectCols("
|
|
275
|
+
const { rows } = await this.db.query(`SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index
|
|
276
276
|
WHERE profile=? AND status='published' AND ${vis.sql}
|
|
277
277
|
ORDER BY (build_date IS NULL), build_date DESC, created_at DESC LIMIT 1`, [profile, ...vis.params]);
|
|
278
278
|
return rows[0] ? refusingImageRow(() => mapRow(rows[0])) : null;
|
|
@@ -286,7 +286,7 @@ export class SqlImageIndex {
|
|
|
286
286
|
async list(filter) {
|
|
287
287
|
const limit = Math.min(Math.max(1, filter.limit ?? DEFAULT_LIMIT), MAX_LIMIT);
|
|
288
288
|
let rawRows;
|
|
289
|
-
if (this.db.dialect === "
|
|
289
|
+
if (this.db.dialect === "mysql") {
|
|
290
290
|
const vis = visibilityClauseTidb(filter);
|
|
291
291
|
const where = [vis.sql];
|
|
292
292
|
const params = [...vis.params];
|
|
@@ -309,7 +309,7 @@ export class SqlImageIndex {
|
|
|
309
309
|
params.push(new Date(c.createdAt), new Date(c.createdAt), c.id);
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
|
-
const sql = `SELECT ${imageSelectCols("
|
|
312
|
+
const sql = `SELECT ${imageSelectCols("mysql")} FROM sandbox_image_index WHERE ${where.join(" AND ")} ` +
|
|
313
313
|
`ORDER BY created_at DESC, id DESC LIMIT ?`;
|
|
314
314
|
const { rows } = await this.db.query(sql, [...params, limit + 1]);
|
|
315
315
|
rawRows = rows;
|
|
@@ -359,7 +359,7 @@ export class SqlImageIndex {
|
|
|
359
359
|
return { entries, nextCursor };
|
|
360
360
|
}
|
|
361
361
|
async setStatus(id, status, opts) {
|
|
362
|
-
if (this.db.dialect === "
|
|
362
|
+
if (this.db.dialect === "mysql") {
|
|
363
363
|
const sets = ["status=?", "updated_at=?"];
|
|
364
364
|
const params = [status, new Date()];
|
|
365
365
|
if (opts && "supersedes" in opts) {
|
|
@@ -95,8 +95,8 @@ export class SqlLeaderRunStore {
|
|
|
95
95
|
constructor(db) {
|
|
96
96
|
this.db = db;
|
|
97
97
|
}
|
|
98
|
-
q(
|
|
99
|
-
return this.db.dialect === "
|
|
98
|
+
q(mysql, pg) {
|
|
99
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
100
100
|
}
|
|
101
101
|
async createRun(input) {
|
|
102
102
|
const id = uuidv7();
|
|
@@ -120,12 +120,12 @@ export class SqlLeaderRunStore {
|
|
|
120
120
|
return { created: true, run };
|
|
121
121
|
}
|
|
122
122
|
async getRun(id) {
|
|
123
|
-
const res = await this.db.query(this.q(`SELECT ${selectCols("
|
|
123
|
+
const res = await this.db.query(this.q(`SELECT ${selectCols("mysql")} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = ?`, `SELECT ${selectCols("pg")} FROM ${LEADER_RUN_TABLE} WHERE leader_run_id = $1`), [id]);
|
|
124
124
|
const row = res.rows[0];
|
|
125
125
|
return row ? mapRow(row) : undefined;
|
|
126
126
|
}
|
|
127
127
|
async getByIdem(idemKey) {
|
|
128
|
-
const res = await this.db.query(this.q(`SELECT ${selectCols("
|
|
128
|
+
const res = await this.db.query(this.q(`SELECT ${selectCols("mysql")} FROM ${LEADER_RUN_TABLE} WHERE idem_key = ?`, `SELECT ${selectCols("pg")} FROM ${LEADER_RUN_TABLE} WHERE idem_key = $1`), [idemKey]);
|
|
129
129
|
const row = res.rows[0];
|
|
130
130
|
return row ? mapRow(row) : undefined;
|
|
131
131
|
}
|