@sema-agent/server 7.139.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 +181 -0
- package/USAGE.md +19 -13
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/approval-card.d.ts +6 -6
- 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/boot/adoption.js +1 -1
- package/dist/boot/execution-env.js +12 -10
- package/dist/boot/execution-lane.d.ts +8 -0
- package/dist/boot/execution-lane.js +6 -1
- 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 +23 -12
- package/dist/boot/resolve-spec.d.ts +3 -5
- package/dist/boot/resolve-spec.js +2 -4
- package/dist/boot/runner-deps.d.ts +3 -3
- package/dist/boot/runner-deps.js +1 -0
- 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-06-runners.d.ts +2 -6
- package/dist/boot/stage-06-runners.js +2 -2
- 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-10-http-server.d.ts +2 -6
- package/dist/boot/stage-10-http-server.js +5 -4
- 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 +35 -9
- package/dist/capabilities/execution-lane.d.ts +2 -2
- package/dist/config-catalog.js +2 -1
- package/dist/config-provider.d.ts +14 -13
- package/dist/config-provider.js +24 -18
- package/dist/config-types.d.ts +11 -2
- package/dist/config.js +2 -1
- package/dist/delegated-session-owner.d.ts +2 -1
- package/dist/delegated-session-owner.js +1 -1
- package/dist/deployment-shape.d.ts +9 -8
- package/dist/deployment-shape.js +4 -4
- package/dist/execution-lane-caps.d.ts +97 -106
- package/dist/execution-lane-caps.js +67 -83
- 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 +13 -16
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/routes/adoption.js +1 -1
- 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/sessions.js +28 -11
- package/dist/http/routes/tasks.js +4 -2
- package/dist/http/server.d.ts +5 -25
- package/dist/http/server.js +2 -2
- package/dist/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- 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 +20 -7
- package/dist/memory-sync-client.js +31 -13
- package/dist/plugins/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-sql.js +14 -14
- 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/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.js +16 -16
- package/dist/plugins/device-store-sql.js +19 -19
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +8 -8
- package/dist/plugins/execution-placement-sql.js +8 -8
- package/dist/plugins/file-history-store-sql.d.ts +2 -2
- package/dist/plugins/file-history-store-sql.js +4 -4
- 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/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-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +10 -9
- package/dist/plugins/one-time-migrations.js +36 -36
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
- package/dist/plugins/permission-rule-store-sql.js +16 -16
- package/dist/plugins/remote-env-adb.d.ts +7 -24
- package/dist/plugins/remote-env-adb.js +22 -5
- package/dist/plugins/remote-env-device.d.ts +2 -28
- package/dist/plugins/remote-env-device.js +2 -1
- package/dist/plugins/remote-env-e2b.d.ts +18 -26
- package/dist/plugins/remote-env-e2b.js +51 -4
- package/dist/plugins/remote-env-file-error.d.ts +1 -1
- package/dist/plugins/remote-env-file-error.js +8 -2
- package/dist/plugins/remote-env-host.d.ts +19 -35
- package/dist/plugins/remote-env-host.js +26 -3
- package/dist/plugins/remote-env-k8s.d.ts +3 -14
- package/dist/plugins/remote-env-k8s.js +3 -2
- package/dist/plugins/remote-env-local-docker.d.ts +4 -15
- package/dist/plugins/remote-env-local-docker.js +5 -4
- package/dist/plugins/remote-env-ssh.d.ts +4 -24
- package/dist/plugins/remote-env-ssh.js +4 -3
- 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 +1 -1
- package/dist/plugins/retention-store-sql.js +6 -6
- package/dist/plugins/run-store-sql.js +15 -15
- package/dist/plugins/schema-consistency.d.ts +6 -6
- package/dist/plugins/schema-consistency.js +1 -1
- 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-rules-column.d.ts +1 -1
- package/dist/plugins/session-rules-column.js +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 +4 -6
- package/dist/plugins/store-backend.js +6 -6
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.js +5 -5
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.js +13 -13
- package/dist/plugins/tool-result-store-sql.d.ts +2 -2
- package/dist/plugins/tool-result-store-sql.js +3 -3
- 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/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.js +14 -19
- package/dist/runs.d.ts +8 -4
- package/dist/runs.js +49 -65
- package/dist/session-incarnation.d.ts +1 -1
- package/dist/session-incarnation.js +1 -1
- package/dist/session-policy-wire.d.ts +1 -1
- 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.js +5 -6
- package/dist/task-mcp.d.ts +20 -7
- package/dist/task-mcp.js +17 -9
- package/dist/terminal.d.ts +27 -0
- package/dist/terminal.js +6 -0
- package/dist/trace/redact.d.ts +3 -1
- package/dist/trace/redact.js +80 -28
- package/dist/trace/self-minted-wire.d.ts +58 -0
- package/dist/trace/self-minted-wire.js +16 -0
- 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/package.json +2 -2
- package/wire-changes/server@7.140.0.json +184 -0
|
@@ -24,8 +24,8 @@ export class SqlBreakerState {
|
|
|
24
24
|
this.db = db;
|
|
25
25
|
this.onWriteFail = onWriteFail;
|
|
26
26
|
}
|
|
27
|
-
q(
|
|
28
|
-
return this.db.dialect === "
|
|
27
|
+
q(mysql, pg) {
|
|
28
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
29
29
|
}
|
|
30
30
|
get(key) {
|
|
31
31
|
return this.cache.get(key);
|
|
@@ -33,7 +33,7 @@ export function buildCheckpointIndexCells(cp) {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
export const APPROVAL_CARD_COLUMN_MAX_BYTES = {
|
|
36
|
-
|
|
36
|
+
mysql: 65_535,
|
|
37
37
|
pg: Number.POSITIVE_INFINITY,
|
|
38
38
|
};
|
|
39
39
|
export function judgeCheckpointIndexCells(cp, dialect) {
|
|
@@ -83,7 +83,7 @@ function renderFacetCase(compared) {
|
|
|
83
83
|
return `CASE ${CHECKPOINT_FACET_CASES.map((c) => `WHEN ${compared(c.column)} IN (${c.words.map(literal).join(", ")}) THEN ${literal(c.facet)}`).join(" ")} END`;
|
|
84
84
|
}
|
|
85
85
|
export const CHECKPOINT_FACET_SQL = Object.freeze({
|
|
86
|
-
|
|
86
|
+
mysql: renderFacetCase((column) => `CAST(${column} AS BINARY)`),
|
|
87
87
|
pg: renderFacetCase((column) => column),
|
|
88
88
|
});
|
|
89
89
|
export const CHECKPOINT_SWEEP_ORDER_SQL = "CASE WHEN approval_card IS NULL THEN 1 ELSE 0 END ASC, deadline ASC, token ASC";
|
|
@@ -343,8 +343,8 @@ export class SqlCheckpointStore {
|
|
|
343
343
|
this.logger = logger;
|
|
344
344
|
this.retention = opts.retention ?? UNMANAGED_RETENTION;
|
|
345
345
|
}
|
|
346
|
-
q(
|
|
347
|
-
return this.db.dialect === "
|
|
346
|
+
q(mysql, pg) {
|
|
347
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
348
348
|
}
|
|
349
349
|
json(value, label) {
|
|
350
350
|
return dialectProtocolJsonEncoder(this.db.dialect)(value, label);
|
|
@@ -353,7 +353,7 @@ export class SqlCheckpointStore {
|
|
|
353
353
|
return isDupKeyError(this.db.dialect, e);
|
|
354
354
|
}
|
|
355
355
|
ph(n) {
|
|
356
|
-
return this.db.dialect === "
|
|
356
|
+
return this.db.dialect === "mysql" ? "?" : `$${n}`;
|
|
357
357
|
}
|
|
358
358
|
pendingRowQuery(select, sessionId, scope, binding) {
|
|
359
359
|
const params = [sessionId];
|
|
@@ -424,8 +424,8 @@ export class SqlCheckpointStore {
|
|
|
424
424
|
}
|
|
425
425
|
}
|
|
426
426
|
async get(token) {
|
|
427
|
-
const { rows } = await this.db.query(this.q(`SELECT version, status, ${jsonColumnText("
|
|
428
|
-
`${jsonColumnText("
|
|
427
|
+
const { rows } = await this.db.query(this.q(`SELECT version, status, ${jsonColumnText("mysql", "checkpoint")}, ${jsonColumnText("mysql", "outcome")}, reopen_reason, rev, ` +
|
|
428
|
+
`${jsonColumnText("mysql", "pending_steer_queue")}, ${jsonColumnText("mysql", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = ?`, `SELECT version, status, ${jsonColumnText("pg", "checkpoint")}, ${jsonColumnText("pg", "outcome")}, reopen_reason, rev, ` +
|
|
429
429
|
`${jsonColumnText("pg", "pending_steer_queue")}, ${jsonColumnText("pg", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = $1`), [token]);
|
|
430
430
|
const r = rows[0];
|
|
431
431
|
if (!r)
|
|
@@ -480,7 +480,7 @@ export class SqlCheckpointStore {
|
|
|
480
480
|
}
|
|
481
481
|
async recordExecutionOutcome(token, scope, gate) {
|
|
482
482
|
for (let attempt = 0; attempt < EXECUTION_RECORD_CAS_ATTEMPTS; attempt++) {
|
|
483
|
-
const { rows } = await this.db.query(this.q(`SELECT scope, status, ${jsonColumnText("
|
|
483
|
+
const { rows } = await this.db.query(this.q(`SELECT scope, status, ${jsonColumnText("mysql", "execution_outcome")} FROM checkpoint WHERE token = ?`, `SELECT scope, status, ${jsonColumnText("pg", "execution_outcome")} FROM checkpoint WHERE token = $1`), [token]);
|
|
484
484
|
const row = rows[0];
|
|
485
485
|
const facts = row === undefined
|
|
486
486
|
? undefined
|
|
@@ -504,7 +504,7 @@ export class SqlCheckpointStore {
|
|
|
504
504
|
async setPendingSteer(token, scope, steer) {
|
|
505
505
|
const clean = validatePendingSteer(steer);
|
|
506
506
|
for (let attempt = 0; attempt < STEER_CAS_ATTEMPTS; attempt++) {
|
|
507
|
-
const { rows } = await this.db.query(this.q(`SELECT status, ${jsonColumnText("
|
|
507
|
+
const { rows } = await this.db.query(this.q(`SELECT status, ${jsonColumnText("mysql", "pending_steer_queue")}, pending_steer_rev FROM checkpoint WHERE token = ? AND scope = ?`, `SELECT status, ${jsonColumnText("pg", "pending_steer_queue")}, pending_steer_rev FROM checkpoint WHERE token = $1 AND scope = $2`), [token, scope]);
|
|
508
508
|
const row = rows[0];
|
|
509
509
|
if (!row || row.status !== "pending")
|
|
510
510
|
return false;
|
|
@@ -537,12 +537,12 @@ export class SqlCheckpointStore {
|
|
|
537
537
|
}
|
|
538
538
|
async reapExpired(cutoff) {
|
|
539
539
|
const res = await this.db.query(this.q("UPDATE checkpoint SET status = 'expired', decided_at_ms = ? " +
|
|
540
|
-
`WHERE status = 'pending' AND ((deadline IS NOT NULL AND deadline <= ? AND COALESCE(${CHECKPOINT_FACET_SQL.
|
|
540
|
+
`WHERE status = 'pending' AND ((deadline IS NOT NULL AND deadline <= ? AND COALESCE(${CHECKPOINT_FACET_SQL.mysql}, '') <> 'A') OR (terminal_at_ms IS NOT NULL AND terminal_at_ms <= ?))`, "UPDATE checkpoint SET status = 'expired', decided_at_ms = $1 " +
|
|
541
541
|
`WHERE status = 'pending' AND ((deadline IS NOT NULL AND deadline <= $2 AND COALESCE(${CHECKPOINT_FACET_SQL.pg}, '') <> 'A') OR (terminal_at_ms IS NOT NULL AND terminal_at_ms <= $3))`), [Date.now(), cutoff, cutoff]);
|
|
542
542
|
return res.affected;
|
|
543
543
|
}
|
|
544
544
|
async listExpiredApprovalGates(cutoff, limit = 100) {
|
|
545
|
-
const { rows } = await this.db.query(this.q(`SELECT session_id, scope FROM checkpoint WHERE status = 'pending' AND deadline IS NOT NULL AND deadline <= ? AND ${CHECKPOINT_FACET_SQL.
|
|
545
|
+
const { rows } = await this.db.query(this.q(`SELECT session_id, scope FROM checkpoint WHERE status = 'pending' AND deadline IS NOT NULL AND deadline <= ? AND ${CHECKPOINT_FACET_SQL.mysql} = 'A' ORDER BY ${CHECKPOINT_SWEEP_ORDER_SQL} LIMIT ?`, `SELECT session_id, scope FROM checkpoint WHERE status = 'pending' AND deadline IS NOT NULL AND deadline <= $1 AND ${CHECKPOINT_FACET_SQL.pg} = 'A' ORDER BY ${CHECKPOINT_SWEEP_ORDER_SQL} LIMIT $2`), [cutoff, limit]);
|
|
546
546
|
return rows.map((r) => ({ sessionId: String(r.session_id), scope: String(r.scope) }));
|
|
547
547
|
}
|
|
548
548
|
async listPending(scope) {
|
|
@@ -591,7 +591,7 @@ export class SqlCheckpointStore {
|
|
|
591
591
|
return out.sort((a, b) => (b.riskDescriptor?.severity ?? 0) - (a.riskDescriptor?.severity ?? 0) || a.createdAt - b.createdAt);
|
|
592
592
|
}
|
|
593
593
|
async readParkedSkillCardFacts(token) {
|
|
594
|
-
const { rows } = await this.db.query(this.q(`SELECT ${jsonColumnText("
|
|
594
|
+
const { rows } = await this.db.query(this.q(`SELECT ${jsonColumnText("mysql", "checkpoint")} FROM checkpoint WHERE token = ? AND status = 'pending'`, `SELECT ${jsonColumnText("pg", "checkpoint")} FROM checkpoint WHERE token = $1 AND status = 'pending'`), [token]);
|
|
595
595
|
const r = rows[0];
|
|
596
596
|
if (r === undefined)
|
|
597
597
|
return {};
|
|
@@ -614,7 +614,7 @@ export class SqlCheckpointStore {
|
|
|
614
614
|
}
|
|
615
615
|
}
|
|
616
616
|
async listByScope(scope) {
|
|
617
|
-
const { rows } = await this.db.query(this.q(`SELECT ${jsonColumnText("
|
|
617
|
+
const { rows } = await this.db.query(this.q(`SELECT ${jsonColumnText("mysql", "checkpoint")}, status FROM checkpoint WHERE status = 'pending' AND scope = ? ORDER BY created_at_ms ASC LIMIT 500`, `SELECT ${jsonColumnText("pg", "checkpoint")}, status FROM checkpoint WHERE status = 'pending' AND scope = $1 ORDER BY created_at_ms ASC LIMIT 500`), [scope]);
|
|
618
618
|
const out = [];
|
|
619
619
|
for (const r of rows) {
|
|
620
620
|
const cp = refusing("a checkpoint nobody can resume", () => decodeCheckpointColumn(r.checkpoint, `in the ${scope} scheduler view`));
|
|
@@ -633,7 +633,7 @@ export class SqlCheckpointStore {
|
|
|
633
633
|
return rows[0] ? String(rows[0].token) : null;
|
|
634
634
|
}
|
|
635
635
|
async findCheckpointCandidatesForAsk(scope, sessionId, toolCallId, sinceMs) {
|
|
636
|
-
const { rows } = await this.db.query(this.q(`SELECT token, status, created_at_ms, tool_call_id, bound_input_hash, version, ${jsonColumnText("
|
|
636
|
+
const { rows } = await this.db.query(this.q(`SELECT token, status, created_at_ms, tool_call_id, bound_input_hash, version, ${jsonColumnText("mysql", "checkpoint")} FROM checkpoint ` +
|
|
637
637
|
"WHERE scope=? AND session_id=? AND created_at_ms>=? AND (tool_call_id=? OR tool_call_id IS NULL) ORDER BY created_at_ms ASC", `SELECT token, status, created_at_ms, tool_call_id, bound_input_hash, version, ${jsonColumnText("pg", "checkpoint")} FROM checkpoint ` +
|
|
638
638
|
"WHERE scope=$1 AND session_id=$2 AND created_at_ms>=$3 AND (tool_call_id=$4 OR tool_call_id IS NULL) ORDER BY created_at_ms ASC"), [scope, sessionId, sinceMs, toolCallId]);
|
|
639
639
|
const out = [];
|
|
@@ -671,7 +671,7 @@ export class SqlCheckpointStore {
|
|
|
671
671
|
return out;
|
|
672
672
|
}
|
|
673
673
|
async findDecidedApprovalsForBinding(sessionId, boundCallId) {
|
|
674
|
-
const { rows } = await this.db.query(this.q(`SELECT scope, bound_input_hash, ${jsonColumnText("
|
|
674
|
+
const { rows } = await this.db.query(this.q(`SELECT scope, bound_input_hash, ${jsonColumnText("mysql", "outcome")}, ${jsonColumnText("mysql", "execution_outcome")} FROM checkpoint ` +
|
|
675
675
|
`WHERE session_id=? AND tool_call_id=? AND status='resolved' AND decided_at_ms IS NOT NULL ORDER BY decided_at_ms DESC LIMIT ${DECIDED_REPLAY_SCAN_CAP}`, `SELECT scope, bound_input_hash, ${jsonColumnText("pg", "outcome")}, ${jsonColumnText("pg", "execution_outcome")} FROM checkpoint ` +
|
|
676
676
|
`WHERE session_id=$1 AND tool_call_id=$2 AND status='resolved' AND decided_at_ms IS NOT NULL ORDER BY decided_at_ms DESC LIMIT ${DECIDED_REPLAY_SCAN_CAP}`), [sessionId, boundCallId]);
|
|
677
677
|
return rows.map((r) => {
|
|
@@ -733,14 +733,14 @@ export class SqlCheckpointStore {
|
|
|
733
733
|
}
|
|
734
734
|
}
|
|
735
735
|
async getCtx(sessionId) {
|
|
736
|
-
const { rows } = await this.db.query(this.q(`SELECT ${jsonColumnText("
|
|
736
|
+
const { rows } = await this.db.query(this.q(`SELECT ${jsonColumnText("mysql", "ctx")} FROM checkpoint_ctx WHERE session_id=?`, `SELECT ${jsonColumnText("pg", "ctx")} FROM checkpoint_ctx WHERE session_id=$1`), [sessionId]);
|
|
737
737
|
return rows[0] ? refusing("a resume context nobody can read", () => decodeCtxColumn(rows[0].ctx, `for session ${JSON.stringify(sessionId)}`)) : null;
|
|
738
738
|
}
|
|
739
739
|
async rewriteCtxRows(rewrite) {
|
|
740
740
|
const out = { scanned: 0, rewritten: 0, valuesRewritten: 0, unreadable: 0, tooLarge: 0 };
|
|
741
741
|
let after = "";
|
|
742
742
|
for (;;) {
|
|
743
|
-
const { rows } = await this.db.query(this.q(`SELECT session_id, ${jsonColumnText("
|
|
743
|
+
const { rows } = await this.db.query(this.q(`SELECT session_id, ${jsonColumnText("mysql", "ctx")} FROM checkpoint_ctx WHERE session_id > ? ORDER BY session_id LIMIT ${CTX_REWRITE_PAGE}`, `SELECT session_id, ${jsonColumnText("pg", "ctx")} FROM checkpoint_ctx WHERE session_id > $1 ORDER BY session_id LIMIT ${CTX_REWRITE_PAGE}`), [after]);
|
|
744
744
|
for (const row of rows) {
|
|
745
745
|
const sessionId = String(row.session_id);
|
|
746
746
|
after = sessionId;
|
|
@@ -789,7 +789,7 @@ export class SqlCheckpointStore {
|
|
|
789
789
|
}
|
|
790
790
|
async deleteBySession(sessionId, owner) {
|
|
791
791
|
const ownerGuard = this.q("EXISTS (SELECT 1 FROM session_meta sm WHERE sm.session_id = ? AND sm.owner <=> ?)", "EXISTS (SELECT 1 FROM session_meta sm WHERE sm.session_id = $1 AND sm.owner IS NOT DISTINCT FROM $2)");
|
|
792
|
-
const params = this.db.dialect === "
|
|
792
|
+
const params = this.db.dialect === "mysql" ? [sessionId, sessionId, owner] : [sessionId, owner];
|
|
793
793
|
const res = await this.db.query(this.q(`DELETE FROM checkpoint WHERE session_id = ? AND ${ownerGuard}`, `DELETE FROM checkpoint WHERE session_id = $1 AND ${ownerGuard}`), params);
|
|
794
794
|
await this.db.query(this.q(`DELETE FROM checkpoint_ctx WHERE session_id = ? AND ${ownerGuard}`, `DELETE FROM checkpoint_ctx WHERE session_id = $1 AND ${ownerGuard}`), params);
|
|
795
795
|
return res.affected;
|
|
@@ -140,7 +140,7 @@ export async function assertDeviceAuditRebindEventSchema(db) {
|
|
|
140
140
|
try {
|
|
141
141
|
await conn.begin();
|
|
142
142
|
try {
|
|
143
|
-
await conn.query(db.dialect === "
|
|
143
|
+
await conn.query(db.dialect === "mysql"
|
|
144
144
|
? `INSERT INTO ${DEVICE_AUDIT_TABLE} (at, event, device_id, owner_tenant, owner_subject, detail) VALUES (NOW(3), ?, NULL, NULL, NULL, NULL)`
|
|
145
145
|
: `INSERT INTO ${DEVICE_AUDIT_TABLE} (at, event, device_id, owner_tenant, owner_subject, detail) VALUES (now(), $1, NULL, NULL, NULL, NULL)`, ["session_rebound"]);
|
|
146
146
|
}
|
|
@@ -168,9 +168,9 @@ export async function assertDeviceAuditRebindEventSchema(db) {
|
|
|
168
168
|
conn.release();
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
const ms = (dialect, col, alias) => dialect === "
|
|
172
|
-
const NOW = (dialect) => (dialect === "
|
|
173
|
-
const nowPlusSeconds = (dialect, placeholder) => dialect === "
|
|
171
|
+
const ms = (dialect, col, alias) => dialect === "mysql" ? `ROUND(UNIX_TIMESTAMP(${col}) * 1000) AS ${alias}` : `(EXTRACT(EPOCH FROM ${col}) * 1000)::bigint AS ${alias}`;
|
|
172
|
+
const NOW = (dialect) => (dialect === "mysql" ? "NOW(3)" : "now()");
|
|
173
|
+
const nowPlusSeconds = (dialect, placeholder) => dialect === "mysql" ? `NOW(3) + INTERVAL ${placeholder} SECOND` : `now() + (${placeholder}::double precision * INTERVAL '1 second')`;
|
|
174
174
|
function deviceCols(dialect) {
|
|
175
175
|
return [
|
|
176
176
|
"device_id",
|
|
@@ -286,10 +286,10 @@ function readAuditDetail(raw, id) {
|
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
function ph(dialect, n) {
|
|
289
|
-
return dialect === "
|
|
289
|
+
return dialect === "mysql" ? "?" : `$${n}`;
|
|
290
290
|
}
|
|
291
291
|
function dupKeyName(dialect, err) {
|
|
292
|
-
if (dialect === "
|
|
292
|
+
if (dialect === "mysql") {
|
|
293
293
|
const msg = String(err?.sqlMessage ?? err?.message ?? "");
|
|
294
294
|
if (new RegExp(UQ_DEVICES_PUBKEY, "i").test(msg))
|
|
295
295
|
return "pubkey";
|
|
@@ -309,8 +309,8 @@ export class SqlDeviceStore {
|
|
|
309
309
|
constructor(db) {
|
|
310
310
|
this.db = db;
|
|
311
311
|
}
|
|
312
|
-
q(
|
|
313
|
-
return this.db.dialect === "
|
|
312
|
+
q(mysql, pg) {
|
|
313
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
314
314
|
}
|
|
315
315
|
json(value) {
|
|
316
316
|
return dialectJsonEncoder(this.db.dialect)(value);
|
|
@@ -319,7 +319,7 @@ export class SqlDeviceStore {
|
|
|
319
319
|
assertIssueTokenShape(input);
|
|
320
320
|
try {
|
|
321
321
|
await this.db.query(this.q(`INSERT INTO ${DEVICE_ENROLL_TOKENS_TABLE} (token_hash, target_tenant, target_subject, created_by, created_at, expires_at, consumed_at, consumed_device_id, consumed_pubkey, rev)
|
|
322
|
-
VALUES (?, ?, ?, ?, NOW(3), ${nowPlusSeconds("
|
|
322
|
+
VALUES (?, ?, ?, ?, NOW(3), ${nowPlusSeconds("mysql", "?")}, NULL, NULL, NULL, 0)`, `INSERT INTO ${DEVICE_ENROLL_TOKENS_TABLE} (token_hash, target_tenant, target_subject, created_by, created_at, expires_at, consumed_at, consumed_device_id, consumed_pubkey, rev)
|
|
323
323
|
VALUES ($1, $2, $3, $4, now(), ${nowPlusSeconds("pg", "$5")}, NULL, NULL, NULL, 0)`), [input.tokenHash, input.target.tenant, input.target.subject, input.createdBy, input.ttlSeconds]);
|
|
324
324
|
}
|
|
325
325
|
catch (err) {
|
|
@@ -334,7 +334,7 @@ export class SqlDeviceStore {
|
|
|
334
334
|
}
|
|
335
335
|
async getEnrollToken(tokenHash) {
|
|
336
336
|
assertEnrollTokenHashShape(tokenHash);
|
|
337
|
-
const { rows } = await this.db.query(this.q(`SELECT ${tokenCols("
|
|
337
|
+
const { rows } = await this.db.query(this.q(`SELECT ${tokenCols("mysql")} FROM ${DEVICE_ENROLL_TOKENS_TABLE} WHERE token_hash = ?`, `SELECT ${tokenCols("pg")} FROM ${DEVICE_ENROLL_TOKENS_TABLE} WHERE token_hash = $1`), [tokenHash]);
|
|
338
338
|
return rows[0] ? mapTokenRow(rows[0]) : null;
|
|
339
339
|
}
|
|
340
340
|
async consumeEnrollToken(input) {
|
|
@@ -382,7 +382,7 @@ export class SqlDeviceStore {
|
|
|
382
382
|
return { outcome: "enrolled", device };
|
|
383
383
|
}
|
|
384
384
|
await conn.rollback();
|
|
385
|
-
const { rows } = await conn.query(this.q(`SELECT ${tokenCols("
|
|
385
|
+
const { rows } = await conn.query(this.q(`SELECT ${tokenCols("mysql")} FROM ${DEVICE_ENROLL_TOKENS_TABLE} WHERE token_hash = ?`, `SELECT ${tokenCols("pg")} FROM ${DEVICE_ENROLL_TOKENS_TABLE} WHERE token_hash = $1`), [input.tokenHash]);
|
|
386
386
|
const token = rows[0] ? mapTokenRow(rows[0]) : null;
|
|
387
387
|
if (!token)
|
|
388
388
|
return { outcome: "unknown_token" };
|
|
@@ -405,28 +405,28 @@ export class SqlDeviceStore {
|
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
async readDeviceOn(conn, deviceId) {
|
|
408
|
-
const { rows } = await conn.query(this.q(`SELECT ${deviceCols("
|
|
408
|
+
const { rows } = await conn.query(this.q(`SELECT ${deviceCols("mysql")} FROM ${DEVICES_TABLE} WHERE device_id = ?`, `SELECT ${deviceCols("pg")} FROM ${DEVICES_TABLE} WHERE device_id = $1`), [deviceId]);
|
|
409
409
|
return rows[0] ? mapDeviceRow(rows[0]) : null;
|
|
410
410
|
}
|
|
411
411
|
async readDeviceForAdmission(deviceId, owner) {
|
|
412
412
|
assertDeviceIdShape(deviceId);
|
|
413
413
|
assertDeviceOwnerShape(owner);
|
|
414
|
-
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("
|
|
414
|
+
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("mysql")}, ${onlineCol("mysql")} FROM ${DEVICES_TABLE} WHERE device_id = ? AND owner_tenant = ? AND owner_subject = ?`, `SELECT ${deviceCols("pg")}, ${onlineCol("pg")} FROM ${DEVICES_TABLE} WHERE device_id = $1 AND owner_tenant = $2 AND owner_subject = $3`), [deviceId, owner.tenant, owner.subject]);
|
|
415
415
|
const r = rows[0];
|
|
416
416
|
return r ? { row: mapDeviceRow(r), online: Number(r.online) === 1 } : null;
|
|
417
417
|
}
|
|
418
418
|
async getDeviceUnscoped(deviceId) {
|
|
419
419
|
assertDeviceIdShape(deviceId);
|
|
420
|
-
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("
|
|
420
|
+
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("mysql")} FROM ${DEVICES_TABLE} WHERE device_id = ?`, `SELECT ${deviceCols("pg")} FROM ${DEVICES_TABLE} WHERE device_id = $1`), [deviceId]);
|
|
421
421
|
return rows[0] ? mapDeviceRow(rows[0]) : null;
|
|
422
422
|
}
|
|
423
423
|
async listDevicesByOwner(owner) {
|
|
424
424
|
assertDeviceOwnerShape(owner);
|
|
425
|
-
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("
|
|
425
|
+
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("mysql")} FROM ${DEVICES_TABLE} WHERE owner_tenant = ? AND owner_subject = ? ORDER BY device_id ASC`, `SELECT ${deviceCols("pg")} FROM ${DEVICES_TABLE} WHERE owner_tenant = $1 AND owner_subject = $2 ORDER BY device_id ASC`), [owner.tenant, owner.subject]);
|
|
426
426
|
return rows.map(mapDeviceRow);
|
|
427
427
|
}
|
|
428
428
|
async listDevicesUnscoped() {
|
|
429
|
-
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("
|
|
429
|
+
const { rows } = await this.db.query(this.q(`SELECT ${deviceCols("mysql")} FROM ${DEVICES_TABLE} ORDER BY device_id ASC`, `SELECT ${deviceCols("pg")} FROM ${DEVICES_TABLE} ORDER BY device_id ASC`));
|
|
430
430
|
return rows.map(mapDeviceRow);
|
|
431
431
|
}
|
|
432
432
|
async revokeDevice(input) {
|
|
@@ -446,7 +446,7 @@ export class SqlDeviceStore {
|
|
|
446
446
|
return { outcome: "revoked", row };
|
|
447
447
|
}
|
|
448
448
|
await conn.rollback();
|
|
449
|
-
const { rows } = await conn.query(this.q(`SELECT ${deviceCols("
|
|
449
|
+
const { rows } = await conn.query(this.q(`SELECT ${deviceCols("mysql")} FROM ${DEVICES_TABLE} WHERE device_id = ? AND owner_tenant = ? AND owner_subject = ?`, `SELECT ${deviceCols("pg")} FROM ${DEVICES_TABLE} WHERE device_id = $1 AND owner_tenant = $2 AND owner_subject = $3`), [input.deviceId, input.owner.tenant, input.owner.subject]);
|
|
450
450
|
if (!rows[0])
|
|
451
451
|
return { outcome: "not_found" };
|
|
452
452
|
return { outcome: "already_revoked", row: mapDeviceRow(rows[0]) };
|
|
@@ -464,7 +464,7 @@ export class SqlDeviceStore {
|
|
|
464
464
|
const conn = await this.db.connect();
|
|
465
465
|
try {
|
|
466
466
|
await conn.begin();
|
|
467
|
-
const cas = await conn.query(this.q(`UPDATE ${DEVICES_TABLE} SET conn_generation = conn_generation + 1, conn_epoch = ?, replica_id = ?, lease_expires_at = ${nowPlusSeconds("
|
|
467
|
+
const cas = await conn.query(this.q(`UPDATE ${DEVICES_TABLE} SET conn_generation = conn_generation + 1, conn_epoch = ?, replica_id = ?, lease_expires_at = ${nowPlusSeconds("mysql", "?")}, connected_at = NOW(3), last_seen_at = NOW(3), rev = rev + 1
|
|
468
468
|
WHERE device_id = ? AND owner_tenant = ? AND owner_subject = ? AND status = 'active' AND (lease_expires_at IS NULL OR lease_expires_at <= NOW(3))`, `UPDATE ${DEVICES_TABLE} SET conn_generation = conn_generation + 1, conn_epoch = $1, replica_id = $2, lease_expires_at = ${nowPlusSeconds("pg", "$3")}, connected_at = now(), last_seen_at = now(), rev = rev + 1
|
|
469
469
|
WHERE device_id = $4 AND owner_tenant = $5 AND owner_subject = $6 AND status = 'active' AND (lease_expires_at IS NULL OR lease_expires_at <= now())`), [input.connEpoch, input.replicaId, input.leaseSeconds, input.deviceId, input.owner.tenant, input.owner.subject]);
|
|
470
470
|
const { rows } = await conn.query(this.q(`SELECT conn_generation, status FROM ${DEVICES_TABLE} WHERE device_id = ? AND owner_tenant = ? AND owner_subject = ? FOR UPDATE`, `SELECT conn_generation, status FROM ${DEVICES_TABLE} WHERE device_id = $1 AND owner_tenant = $2 AND owner_subject = $3 FOR UPDATE`), [input.deviceId, input.owner.tenant, input.owner.subject]);
|
|
@@ -488,7 +488,7 @@ export class SqlDeviceStore {
|
|
|
488
488
|
async heartbeatConnection(input) {
|
|
489
489
|
assertDeviceIdShape(input.deviceId);
|
|
490
490
|
assertWholeSeconds("leaseSeconds", input.leaseSeconds);
|
|
491
|
-
const res = await this.db.query(this.q(`UPDATE ${DEVICES_TABLE} SET last_seen_at = NOW(3), lease_expires_at = ${nowPlusSeconds("
|
|
491
|
+
const res = await this.db.query(this.q(`UPDATE ${DEVICES_TABLE} SET last_seen_at = NOW(3), lease_expires_at = ${nowPlusSeconds("mysql", "?")}, rev = rev + 1 WHERE device_id = ? AND conn_generation = ? AND status = 'active'`, `UPDATE ${DEVICES_TABLE} SET last_seen_at = now(), lease_expires_at = ${nowPlusSeconds("pg", "$1")}, rev = rev + 1 WHERE device_id = $2 AND conn_generation = $3 AND status = 'active'`), [input.leaseSeconds, input.deviceId, input.generation]);
|
|
492
492
|
return res.affected === 1;
|
|
493
493
|
}
|
|
494
494
|
async releaseConnection(deviceId, generation) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export type SqlDialect = "mysql" | "pg";
|
|
1
|
+
import type { SqlDialect } from "./sql-driver.js";
|
|
3
2
|
/**
|
|
4
3
|
* 一条索引的**声明**(纯数据)。两方言共用同一份声明 —— 这就是"一处属主"的兑现物:
|
|
5
4
|
* per-store 文件各导出自己那份 `readonly IndexSpec[]`,PG 侧由该 store 的 `ensurePg*Schema` 应用,
|
|
@@ -35,35 +35,35 @@ export interface PlacementStatement {
|
|
|
35
35
|
*/
|
|
36
36
|
export declare const SESSION_META_PLACEMENT_COLUMNS: readonly [{
|
|
37
37
|
readonly name: "execution_role";
|
|
38
|
-
readonly
|
|
38
|
+
readonly mysql: "execution_role VARCHAR(16) NULL";
|
|
39
39
|
readonly pg: "execution_role VARCHAR(16) COLLATE \"C\"";
|
|
40
40
|
}, {
|
|
41
41
|
readonly name: "execution_lane";
|
|
42
|
-
readonly
|
|
42
|
+
readonly mysql: "execution_lane VARCHAR(32) NULL";
|
|
43
43
|
readonly pg: "execution_lane VARCHAR(32) COLLATE \"C\"";
|
|
44
44
|
}, {
|
|
45
45
|
readonly name: "execution_device_id";
|
|
46
|
-
readonly
|
|
46
|
+
readonly mysql: "execution_device_id VARCHAR(64) NULL";
|
|
47
47
|
readonly pg: "execution_device_id VARCHAR(64) COLLATE \"C\"";
|
|
48
48
|
}, {
|
|
49
49
|
readonly name: "execution_device_owner_tenant";
|
|
50
|
-
readonly
|
|
50
|
+
readonly mysql: "execution_device_owner_tenant VARCHAR(190) NULL";
|
|
51
51
|
readonly pg: "execution_device_owner_tenant VARCHAR(190) COLLATE \"C\"";
|
|
52
52
|
}, {
|
|
53
53
|
readonly name: "execution_device_owner_subject";
|
|
54
|
-
readonly
|
|
54
|
+
readonly mysql: "execution_device_owner_subject VARCHAR(190) NULL";
|
|
55
55
|
readonly pg: "execution_device_owner_subject VARCHAR(190) COLLATE \"C\"";
|
|
56
56
|
}, {
|
|
57
57
|
readonly name: "execution_placed_at";
|
|
58
|
-
readonly
|
|
58
|
+
readonly mysql: "execution_placed_at DATETIME(3) NULL";
|
|
59
59
|
readonly pg: "execution_placed_at TIMESTAMPTZ(3)";
|
|
60
60
|
}, {
|
|
61
61
|
readonly name: "execution_rev";
|
|
62
|
-
readonly
|
|
62
|
+
readonly mysql: "execution_rev BIGINT NULL";
|
|
63
63
|
readonly pg: "execution_rev BIGINT";
|
|
64
64
|
}, {
|
|
65
65
|
readonly name: "fork_key";
|
|
66
|
-
readonly
|
|
66
|
+
readonly mysql: "fork_key VARCHAR(255) NULL";
|
|
67
67
|
readonly pg: "fork_key VARCHAR(255) COLLATE \"C\"";
|
|
68
68
|
}];
|
|
69
69
|
/** 中央 DDL 内插的那一段(列与列之间逗号分隔,不带首尾逗号)。 */
|
|
@@ -3,14 +3,14 @@ import { sessionFaceFromRow } from "../session-incarnation.js";
|
|
|
3
3
|
import { toIso } from "./sql-row-helpers.js";
|
|
4
4
|
import { WRITABLE, assertPlaceableLane, settledMirrorOutcome, settledPlaceOutcome, } from "../execution-placement.js";
|
|
5
5
|
export const SESSION_META_PLACEMENT_COLUMNS = [
|
|
6
|
-
{ name: "execution_role",
|
|
7
|
-
{ name: "execution_lane",
|
|
8
|
-
{ name: "execution_device_id",
|
|
9
|
-
{ name: "execution_device_owner_tenant",
|
|
10
|
-
{ name: "execution_device_owner_subject",
|
|
11
|
-
{ name: "execution_placed_at",
|
|
12
|
-
{ name: "execution_rev",
|
|
13
|
-
{ name: "fork_key",
|
|
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
14
|
];
|
|
15
15
|
export function sessionMetaPlacementColumnsDdl(dialect) {
|
|
16
16
|
return SESSION_META_PLACEMENT_COLUMNS.map((c) => c[dialect]).join(",\n ");
|
|
@@ -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
|
|
@@ -196,5 +196,5 @@ export declare class PgFileHistoryStore extends SqlFileHistoryStore {
|
|
|
196
196
|
*/
|
|
197
197
|
export declare function assertFileHistorySchema(query: (sql: string) => Promise<{
|
|
198
198
|
rows: Record<string, unknown>[];
|
|
199
|
-
}>, dialect:
|
|
199
|
+
}>, dialect: SqlDialect): Promise<void>;
|
|
200
200
|
//# sourceMappingURL=file-history-store-sql.d.ts.map
|
|
@@ -33,13 +33,13 @@ export class SqlFileHistoryStore {
|
|
|
33
33
|
this.db = db;
|
|
34
34
|
this.pool = pool;
|
|
35
35
|
this.blobs = blobBackend ?? new SqlBlobBackend(this.db.dialect, this.pool, undefined, FILE_HISTORY_BLOB_TABLE);
|
|
36
|
-
this.boundaryKeep = resolveFileHistoryRetention(this.db.dialect === "
|
|
36
|
+
this.boundaryKeep = resolveFileHistoryRetention(this.db.dialect === "mysql" ? "TiDBFileHistoryStore" : "PgFileHistoryStore", opts?.retention);
|
|
37
37
|
}
|
|
38
|
-
q(
|
|
39
|
-
return this.db.dialect === "
|
|
38
|
+
q(mysql, pg) {
|
|
39
|
+
return this.db.dialect === "mysql" ? mysql : pg;
|
|
40
40
|
}
|
|
41
41
|
bool(v) {
|
|
42
|
-
return this.db.dialect === "
|
|
42
|
+
return this.db.dialect === "mysql" ? (v ? 1 : 0) : v;
|
|
43
43
|
}
|
|
44
44
|
leaseKey(scope, entryId) {
|
|
45
45
|
return `${scope}\x00${entryId}`;
|
|
@@ -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
|
}
|
|
@@ -3,6 +3,7 @@ import type { Pool as PgPool } from "pg";
|
|
|
3
3
|
import type { MailboxAckResult, MailboxAppendMessage, MailboxDecision, MailboxDecisionOutcome, MailboxLease, MailboxStore } from "@sema-agent/core";
|
|
4
4
|
import { type IndexSpec } from "./ensure-index.js";
|
|
5
5
|
import type { MailboxRecipientLifecycle, MailboxRecipientTombstone } from "./mailbox-recipient-lifecycle.js";
|
|
6
|
+
import type { SqlDialect } from "./sql-driver.js";
|
|
6
7
|
export declare const MAILBOX_TABLE = "mailbox";
|
|
7
8
|
export declare const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
8
9
|
/** [ref] 收件人墓碑表(预删除态的**唯一**真源;形与理由见 `mailbox-recipient-lifecycle.ts` 模块头)。
|
|
@@ -40,7 +41,7 @@ export declare function storedMailboxKeyless(scopeKey: unknown, handle: unknown)
|
|
|
40
41
|
export declare const MAILBOX_KEYED_TABLES: readonly ["mailbox", "mailbox_message", "mailbox_tombstone"];
|
|
41
42
|
export type MailboxKeyedTable = (typeof MAILBOX_KEYED_TABLES)[number];
|
|
42
43
|
/** 按键删收件箱表族的一张表(两方言;参数 = [scope_key, handle])。 */
|
|
43
|
-
export declare function mailboxKeyDeleteSql(dialect:
|
|
44
|
+
export declare function mailboxKeyDeleteSql(dialect: SqlDialect, table: MailboxKeyedTable): string;
|
|
44
45
|
/** 店侧日志座(可选;boot 注入 `Logger`)。7.58.0 合并重扫 [medium]:坏行的 fail-closed 抛必须**响亮**——core 唯一的
|
|
45
46
|
* claimLease 调用方(peer-session-drain)吞掉该错,店不记这一笔就成了「静默永久卡箱」。抛之前记
|
|
46
47
|
* `mailbox_message_column_corrupt`(scope/handle/seq/column/code),运维按 seq 修行。
|