@sema-agent/server 7.144.0 → 7.146.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 +621 -0
- package/README.md +3 -0
- package/README.zh-CN.md +1 -0
- package/USAGE.md +233 -54
- package/deploy/facts/CHANGES.json +25 -0
- package/deploy/facts/README.md +57 -0
- package/deploy/facts/lanes.json +54 -0
- package/deploy/facts/lanes.schema.json +57 -0
- package/deploy/facts/sandbox-rbac.json +113 -0
- package/deploy/facts/sandbox-rbac.schema.json +74 -0
- package/deploy/facts/sealed-key.json +9 -0
- package/deploy/facts/sealed-key.schema.json +52 -0
- package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
- package/deploy/sema-up/chart/templates/server.yaml +4 -0
- package/deploy/sema-up/chart/values.yaml +7 -0
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
- package/dist/adoption/import-limits.d.ts +6 -0
- package/dist/adoption/import-limits.js +7 -0
- package/dist/adoption/import.d.ts +62 -0
- package/dist/adoption/import.js +107 -0
- package/dist/adoption/wire.d.ts +52 -0
- package/dist/adoption/wire.js +9 -0
- package/dist/approval-ask-machine.d.ts +2 -2
- package/dist/approval-card.d.ts +3 -12
- package/dist/approval-card.js +0 -4
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-gate-currency.js +1 -1
- package/dist/approval-reconciler.d.ts +1 -1
- package/dist/audit.d.ts +2 -21
- package/dist/audit.js +0 -9
- package/dist/boot/deployment-mods.d.ts +11 -1
- package/dist/boot/deployment-mods.js +15 -2
- package/dist/boot/device-lane.js +2 -0
- package/dist/boot/engine-lease.d.ts +193 -21
- package/dist/boot/engine-lease.js +146 -20
- package/dist/boot/execution-env.d.ts +4 -0
- package/dist/boot/execution-env.js +12 -0
- package/dist/boot/exit-boundary.d.ts +50 -0
- package/dist/boot/exit-boundary.js +32 -0
- package/dist/boot/exit-last.d.ts +20 -4
- package/dist/boot/exit-last.js +21 -4
- package/dist/boot/governance-seams.js +1 -0
- package/dist/boot/memory-consolidation.d.ts +29 -17
- package/dist/boot/memory-consolidation.js +26 -15
- package/dist/boot/reapers.d.ts +12 -4
- package/dist/boot/reapers.js +39 -15
- package/dist/boot/resolve-spec.d.ts +2 -0
- package/dist/boot/resolve-spec.js +38 -15
- package/dist/boot/runner-deps.js +1 -1
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +6 -4
- package/dist/boot/shutdown.js +27 -9
- package/dist/boot/stage-01-config.js +1 -1
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +3 -0
- package/dist/boot/stage-05-execution-env.js +3 -3
- package/dist/boot/stage-06-runners.d.ts +3 -0
- package/dist/boot/stage-06-runners.js +11 -10
- package/dist/boot/stage-07-capability-layer.d.ts +3 -0
- package/dist/boot/stage-07-capability-layer.js +7 -2
- package/dist/boot/stage-08-reapers.d.ts +3 -0
- package/dist/boot/stage-08-reapers.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +4 -1
- package/dist/boot/stage-10-http-server.d.ts +4 -1
- package/dist/boot/stage-10-http-server.js +4 -3
- package/dist/boot/stores.d.ts +1 -0
- package/dist/boot/stores.js +35 -19
- package/dist/boot/workflow-orchestration.d.ts +1 -1
- package/dist/boot/workflow-orchestration.js +2 -1
- package/dist/capabilities/composite-members.d.ts +3 -1
- package/dist/capabilities/composite-members.js +3 -1
- package/dist/capabilities/scenarios.d.ts +2 -2
- package/dist/capability-facts.d.ts +18 -1
- package/dist/capability-facts.js +25 -7
- package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
- package/dist/config-catalog.js +13 -4
- package/dist/config-d-migration.d.ts +3 -2
- package/dist/config-provider.d.ts +2 -2
- package/dist/config-types.d.ts +43 -4
- package/dist/config.d.ts +6 -1
- package/dist/config.js +67 -11
- package/dist/deleted-session-ids.d.ts +19 -0
- package/dist/deleted-session-ids.js +26 -0
- package/dist/deploy-facts.d.ts +155 -0
- package/dist/deploy-facts.js +94 -0
- package/dist/device-ws-hub.d.ts +48 -3
- package/dist/device-ws-hub.js +48 -2
- package/dist/error-code-family.d.ts +35 -0
- package/dist/error-code-family.js +26 -0
- package/dist/fleet/drain-entry.d.ts +39 -3
- package/dist/fleet/drain-entry.js +6 -0
- package/dist/fleet/fleet-bus.d.ts +15 -4
- package/dist/fleet/fleet-bus.js +17 -3
- package/dist/hooks/branch-transcript.d.ts +1 -1
- package/dist/hooks/branch-transcript.js +1 -0
- package/dist/hooks/classic-stations.d.ts +25 -8
- package/dist/hooks/classic-stations.js +29 -3
- package/dist/hooks/hook-runner.d.ts +33 -16
- package/dist/hooks/hook-runner.js +166 -73
- package/dist/hooks/hook-source.d.ts +14 -2
- package/dist/hooks/hook-source.js +8 -0
- package/dist/http/active-run-conflict.d.ts +1 -1
- package/dist/http/active-run-conflict.js +1 -1
- package/dist/http/admission.js +13 -2
- package/dist/http/checkpoint-ctx.d.ts +60 -71
- package/dist/http/checkpoint-ctx.js +35 -40
- package/dist/http/dispatch.js +18 -2
- package/dist/http/durable-run-leg.d.ts +5 -0
- package/dist/http/durable-run-leg.js +8 -6
- package/dist/http/execution-context.d.ts +4 -0
- package/dist/http/execution-context.js +9 -0
- package/dist/http/health-self-report.d.ts +6 -2
- package/dist/http/health-self-report.js +3 -2
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.js +2 -15
- package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
- package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
- package/dist/http/principal-gate.d.ts +35 -12
- package/dist/http/principal-gate.js +16 -12
- package/dist/http/request-guards.d.ts +1 -1
- package/dist/http/request-guards.js +5 -5
- package/dist/http/resume-legs.d.ts +22 -0
- package/dist/http/resume-legs.js +31 -20
- package/dist/http/route-ctx.d.ts +26 -10
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/adoption.d.ts +11 -0
- package/dist/http/routes/adoption.js +80 -6
- package/dist/http/routes/agents-roster.js +1 -0
- package/dist/http/routes/approvals-assistant.d.ts +20 -1
- package/dist/http/routes/approvals-assistant.js +29 -7
- package/dist/http/routes/background.js +1 -0
- package/dist/http/routes/capabilities.js +4 -1
- package/dist/http/routes/diagnostics.d.ts +9 -14
- package/dist/http/routes/diagnostics.js +10 -20
- package/dist/http/routes/engine.d.ts +31 -0
- package/dist/http/routes/engine.js +81 -0
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-consolidation.js +3 -5
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/memory-origin.js +2 -4
- package/dist/http/routes/memory-policy.js +5 -1
- package/dist/http/routes/observability.js +3 -6
- package/dist/http/routes/rules.d.ts +1 -1
- package/dist/http/routes/rules.js +5 -2
- package/dist/http/routes/runs.js +7 -3
- package/dist/http/routes/session-sync.js +24 -25
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.js +4 -0
- package/dist/http/routes/tasks.js +35 -14
- package/dist/http/routes/trace-usage.js +1 -1
- package/dist/http/run-concurrency-cap.d.ts +86 -0
- package/dist/http/run-concurrency-cap.js +54 -0
- package/dist/http/run-meta.d.ts +14 -7
- package/dist/http/run-meta.js +5 -2
- package/dist/http/server.d.ts +27 -16
- package/dist/http/server.js +21 -3
- package/dist/http/stored-body-replay.d.ts +9 -6
- package/dist/http/stored-body-replay.js +19 -5
- package/dist/http/submit-ctx.d.ts +12 -0
- package/dist/http/submit-ctx.js +23 -0
- package/dist/http/wire-types.d.ts +8 -3
- package/dist/json-body-limit.d.ts +8 -0
- package/dist/json-body-limit.js +2 -0
- package/dist/leader/diffout.d.ts +1 -1
- package/dist/local-data-root.d.ts +2 -2
- package/dist/memory-operator-faces.d.ts +9 -4
- package/dist/memory-scope-stores.d.ts +9 -7
- package/dist/memory-scope-stores.js +3 -14
- package/dist/memory-scope.d.ts +36 -0
- package/dist/memory-scope.js +32 -1
- package/dist/net-address.d.ts +53 -0
- package/dist/net-address.js +111 -0
- package/dist/observability/fail-open.d.ts +28 -12
- package/dist/observability/fail-open.js +28 -12
- package/dist/orchestration/workflow-notify-journal.js +2 -3
- package/dist/org-delivery-gate.js +1 -1
- package/dist/peer-directory.d.ts +6 -12
- package/dist/peer-directory.js +5 -1
- package/dist/plugins/approval-ask-store-file.d.ts +16 -2
- package/dist/plugins/approval-ask-store-file.js +21 -1
- package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
- package/dist/plugins/approval-ask-store-memory.js +11 -8
- package/dist/plugins/caching-session-store.d.ts +6 -1
- package/dist/plugins/caching-session-store.js +6 -0
- package/dist/plugins/checkpoint-store-face.d.ts +426 -0
- package/dist/plugins/checkpoint-store-face.js +152 -0
- package/dist/plugins/file-run-store.d.ts +13 -7
- package/dist/plugins/file-run-store.js +12 -13
- package/dist/plugins/fork-routing-session-store.d.ts +6 -2
- package/dist/plugins/fork-routing-session-store.js +2 -2
- package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
- package/dist/plugins/local-checkpoint-store.d.ts +16 -5
- package/dist/plugins/local-checkpoint-store.js +25 -6
- package/dist/plugins/local-session-store.d.ts +80 -21
- package/dist/plugins/local-session-store.js +79 -22
- package/dist/plugins/memory-engine-pg.d.ts +29 -6
- package/dist/plugins/memory-engine-pg.js +96 -214
- package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
- package/dist/plugins/memory-engine-sql-apply.js +358 -0
- package/dist/plugins/memory-engine-tidb.d.ts +35 -9
- package/dist/plugins/memory-engine-tidb.js +97 -227
- package/dist/plugins/memory-key-guards.d.ts +1 -1
- package/dist/plugins/memory-origin-law.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +2 -0
- package/dist/plugins/memory-run-store.js +6 -0
- package/dist/plugins/one-time-migrations.d.ts +32 -56
- package/dist/plugins/one-time-migrations.js +27 -119
- package/dist/plugins/permission-rule-store-file.d.ts +17 -3
- package/dist/plugins/permission-rule-store-file.js +8 -1
- package/dist/plugins/pg-pool.js +1 -1
- package/dist/plugins/pg-session-storage.d.ts +17 -4
- package/dist/plugins/pg-session-storage.js +8 -4
- package/dist/plugins/run-store-sql.d.ts +4 -0
- package/dist/plugins/run-store-sql.js +5 -2
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/session-fork.d.ts +31 -9
- package/dist/plugins/session-fork.js +3 -2
- package/dist/plugins/session-placement.d.ts +4 -2
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/sql-json-column.d.ts +5 -1
- package/dist/plugins/store-backend.d.ts +11 -8
- package/dist/plugins/store-backend.js +8 -7
- package/dist/plugins/store-contracts.d.ts +1 -11
- package/dist/plugins/store-contracts.js +0 -9
- package/dist/plugins/store-sweep.d.ts +63 -0
- package/dist/plugins/store-sweep.js +48 -0
- package/dist/plugins/tidb-pool.js +1 -1
- package/dist/plugins/tidb-session-store.d.ts +16 -3
- package/dist/plugins/tidb-session-store.js +8 -4
- package/dist/prompt-screen.d.ts +18 -0
- package/dist/prompt-screen.js +5 -0
- package/dist/rules-consent.d.ts +56 -51
- package/dist/rules-consent.js +86 -14
- package/dist/run-acceptance.d.ts +40 -10
- package/dist/run-acceptance.js +38 -5
- package/dist/run-cancel-context.d.ts +4 -1
- package/dist/run-cancel-context.js +2 -1
- package/dist/runs.js +2 -2
- package/dist/sandbox-egress-policy.js +6 -11
- package/dist/sandbox-rbac-rules.d.ts +88 -0
- package/dist/sandbox-rbac-rules.js +80 -0
- package/dist/sealed-key-layout.d.ts +17 -0
- package/dist/sealed-key-layout.js +4 -0
- package/dist/sealed-key.js +5 -5
- package/dist/security.d.ts +48 -9
- package/dist/security.js +9 -2
- package/dist/session-agent.d.ts +39 -0
- package/dist/session-agent.js +16 -0
- package/dist/session-error-wire.d.ts +4 -4
- package/dist/session-notify-door.d.ts +5 -3
- package/dist/session-resident-background.d.ts +57 -0
- package/dist/session-resident-background.js +20 -0
- package/dist/session-sync-kernel.d.ts +53 -14
- package/dist/session-sync-kernel.js +70 -1
- package/dist/session-sync.d.ts +5 -3
- package/dist/session-workspace.d.ts +1 -0
- package/dist/session-workspace.js +1 -0
- package/dist/sighup-idle.d.ts +9 -7
- package/dist/sighup-idle.js +4 -5
- package/dist/spec-fields.d.ts +16 -0
- package/dist/spec-fields.js +7 -0
- package/dist/stored-request-env-credentials-m38.d.ts +3 -0
- package/dist/stored-request-env-credentials-m38.js +8 -0
- package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +33 -429
- package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +70 -209
- package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
- package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
- package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
- package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
- package/dist/task-settings.d.ts +59 -4
- package/dist/task-settings.js +50 -4
- package/dist/tool-approval.d.ts +2 -2
- package/dist/trace/core-keyset-guard.d.ts +8 -7
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +6 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/model-segment.d.ts +3 -2
- package/dist/trace/model-segment.js +16 -8
- package/dist/trace/project.js +9 -0
- package/dist/trace/redact.d.ts +79 -4
- package/dist/trace/redact.js +523 -123
- package/dist/trace/self-minted-wire.d.ts +391 -1
- package/dist/trace/self-minted-wire.js +145 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/usage-analytics.d.ts +4 -0
- package/package.json +12 -4
- package/scripts/drain-check-local.mjs +8 -29
- package/scripts/drain-check.mysql.sql +3 -24
- package/scripts/drain-check.pg.sql +3 -24
- package/skills/update-config.md +1 -1
- package/wire-changes/server@7.145.0.json +146 -0
- package/wire-changes/server@7.146.0.json +3221 -0
- package/dist/approval-batch-census.d.ts +0 -27
- package/dist/approval-batch-census.js +0 -27
|
@@ -1,23 +1,18 @@
|
|
|
1
1
|
import { parkedApprovalCard } from "../approval-content-kind.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { CHECKPOINT_FACET_SQL, CHECKPOINT_SWEEP_ORDER_SQL, CheckpointIndexRefusedError, judgeCheckpointIndexCells } from "
|
|
4
|
-
import { CheckpointError,
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextExpr, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readJsonObjectColumn, readNonNullJsonValueColumn, } from "./sql-json-column.js";
|
|
3
|
+
import { CHECKPOINT_FACET_SQL, CHECKPOINT_SWEEP_ORDER_SQL, CheckpointIndexRefusedError, judgeCheckpointIndexCells } from "../plugins/checkpoint-index-columns.js";
|
|
4
|
+
import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, } from "@sema-agent/core";
|
|
5
|
+
import { parkRowFacts, skillCardFacts } from "../approval-card.js";
|
|
6
|
+
import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
|
|
8
7
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
9
8
|
import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
|
|
10
|
-
import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "
|
|
11
|
-
import { isDupKeyError, isMissingColumnError, rowTooLargeOrSelf, StoreRowTooLargeError } from "
|
|
12
|
-
import { describeProbedTableDrift } from "
|
|
13
|
-
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "
|
|
14
|
-
import { approvalPayloadFingerprint } from "
|
|
9
|
+
import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "../plugins/sql-driver.js";
|
|
10
|
+
import { isDupKeyError, isMissingColumnError, rowTooLargeOrSelf, StoreRowTooLargeError } from "../plugins/sql-errors.js";
|
|
11
|
+
import { describeProbedTableDrift } from "../plugins/schema-drift-sentence.js";
|
|
12
|
+
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-store-sql.js";
|
|
13
|
+
import { approvalPayloadFingerprint } from "../plugins/approval-payload-fingerprint.js";
|
|
14
|
+
import { approvalDecisionOfWinner, boundedRuleOffers, boundedToolInput, candidateIdentityOf, DECIDED_REPLAY_SCAN_CAP, queueRowGateKind, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS, UnreadableCheckpointRowError, } from "../plugins/checkpoint-store-face.js";
|
|
15
15
|
const CTX_REWRITE_PAGE = 500;
|
|
16
|
-
const KINDLESS_SCAN_PAGE = 200;
|
|
17
|
-
export { approvalPayloadFingerprint } from "./approval-payload-fingerprint.js";
|
|
18
|
-
const MAX_TOOL_INPUT_CHARS = 8192;
|
|
19
|
-
export const TERMINAL_BACKSTOP_MS = Math.max(60_000, (Number.isFinite(Number(process.env.APPROVAL_TERMINAL_BACKSTOP_MS)) ? Number(process.env.APPROVAL_TERMINAL_BACKSTOP_MS) : 0) || 30 * 86_400_000);
|
|
20
|
-
export const TERMINAL_GRACE_MS = 3_600_000;
|
|
21
16
|
export function tokenFingerprint(token) {
|
|
22
17
|
return "sha256:" + createHash("sha256").update(token).digest("hex").slice(0, 12);
|
|
23
18
|
}
|
|
@@ -50,21 +45,6 @@ class StoredRowProjectionMemo {
|
|
|
50
45
|
}
|
|
51
46
|
const STORED_ROW_READ_BATCH = 100;
|
|
52
47
|
const CHECKPOINT_SHA256_AS = "checkpoint_sha256";
|
|
53
|
-
const GATE_KIND_IS_LIVE_RUN_PARK = {
|
|
54
|
-
human: true,
|
|
55
|
-
irreversible_ask: true,
|
|
56
|
-
resource_limit: true,
|
|
57
|
-
needs_review: true,
|
|
58
|
-
plan_review: true,
|
|
59
|
-
task_done: false,
|
|
60
|
-
};
|
|
61
|
-
export const LIVE_RUN_GATE_KINDS = Object.keys(GATE_KIND_IS_LIVE_RUN_PARK).filter((k) => GATE_KIND_IS_LIVE_RUN_PARK[k]);
|
|
62
|
-
export const TERMINAL_PARK_GATE_KINDS = Object.keys(GATE_KIND_IS_LIVE_RUN_PARK).filter((k) => !GATE_KIND_IS_LIVE_RUN_PARK[k]);
|
|
63
|
-
export function pendingRowPrecedes(a, b, pick) {
|
|
64
|
-
if (pick === "oldest")
|
|
65
|
-
return a.createdAt < b.createdAt || (a.createdAt === b.createdAt && a.token < b.token);
|
|
66
|
-
return a.createdAt > b.createdAt || (a.createdAt === b.createdAt && a.token > b.token);
|
|
67
|
-
}
|
|
68
48
|
export const CHECKPOINT_ADDED_COLUMNS = [
|
|
69
49
|
{
|
|
70
50
|
column: "has_bidi_controls",
|
|
@@ -99,53 +79,6 @@ export async function assertCheckpointColumnSchema(query, dialect) {
|
|
|
99
79
|
}
|
|
100
80
|
}
|
|
101
81
|
}
|
|
102
|
-
const MAX_RULE_OFFER_SCAN = 32;
|
|
103
|
-
function preboundBatchMembers(item) {
|
|
104
|
-
if (typeof item !== "object" || item === null)
|
|
105
|
-
return item;
|
|
106
|
-
const candidate = item;
|
|
107
|
-
if (candidate.kind !== "batch" || !Array.isArray(candidate.rules) || candidate.rules.length <= MAX_RULE_OFFER_BATCH_MEMBERS)
|
|
108
|
-
return item;
|
|
109
|
-
return { ...candidate, rules: candidate.rules.slice(0, MAX_RULE_OFFER_BATCH_MEMBERS) };
|
|
110
|
-
}
|
|
111
|
-
export function boundedReaderInput(raw) {
|
|
112
|
-
return Array.isArray(raw) ? raw.slice(0, MAX_RULE_OFFER_SCAN).map(preboundBatchMembers) : raw;
|
|
113
|
-
}
|
|
114
|
-
export function boundedRuleOffers(raw, opts = {}) {
|
|
115
|
-
const observe = opts.countRedactions ?? true;
|
|
116
|
-
const scrub = (text) => redactSecrets(text, { observe }).slice(0, MAX_RULE_TEXT_CHARS);
|
|
117
|
-
const read = readRuleOfferList(boundedReaderInput(raw));
|
|
118
|
-
const out = [];
|
|
119
|
-
for (const { offer } of read) {
|
|
120
|
-
if (out.length >= MAX_RULE_OFFERS)
|
|
121
|
-
break;
|
|
122
|
-
switch (offer.kind) {
|
|
123
|
-
case "single":
|
|
124
|
-
out.push({ kind: "single", rule: scrub(offer.rule), match: offer.match, command: scrub(offer.command) });
|
|
125
|
-
break;
|
|
126
|
-
case "batch":
|
|
127
|
-
out.push({
|
|
128
|
-
kind: "batch",
|
|
129
|
-
rules: offer.rules.slice(0, MAX_RULE_OFFER_BATCH_MEMBERS).map((r) => {
|
|
130
|
-
switch (r.kind) {
|
|
131
|
-
case "command":
|
|
132
|
-
return { kind: "command", rule: scrub(r.rule), match: r.match, command: scrub(r.command), segment: scrub(r.segment) };
|
|
133
|
-
case "directoryRead":
|
|
134
|
-
return { kind: "directoryRead", rule: scrub(r.rule), directory: scrub(r.directory), segment: scrub(r.segment) };
|
|
135
|
-
default:
|
|
136
|
-
throw new Error(`unknown rule-offer batch member kind: ${JSON.stringify(r)}`);
|
|
137
|
-
}
|
|
138
|
-
}),
|
|
139
|
-
uncoveredSegments: offer.uncoveredSegments,
|
|
140
|
-
...(offer.uncoveredDetail !== undefined ? { uncoveredDetail: offer.uncoveredDetail.map((d) => ({ segment: scrub(d.segment), reason: d.reason })) } : {}),
|
|
141
|
-
});
|
|
142
|
-
break;
|
|
143
|
-
default:
|
|
144
|
-
throw new Error(`unknown rule-offer kind: ${JSON.stringify(offer)}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return out.length > 0 ? out : undefined;
|
|
148
|
-
}
|
|
149
82
|
const readCheckpointBlobBody = createObjectColumnReader();
|
|
150
83
|
const readResumeOutcomeBody = createObjectColumnReader();
|
|
151
84
|
const readGateOutcomeBody = createObjectColumnReader();
|
|
@@ -153,6 +86,22 @@ const readRiskDescriptorBody = createObjectColumnReader();
|
|
|
153
86
|
function decodeCheckpointColumn(cell, where) {
|
|
154
87
|
return decodeJsonColumn(cell, readCheckpointBlobBody, `checkpoint.checkpoint ${where}`);
|
|
155
88
|
}
|
|
89
|
+
function verdictOfUnreadableBody(cell) {
|
|
90
|
+
return judgeCheckpointRow(cell);
|
|
91
|
+
}
|
|
92
|
+
const UNREADABLE_SUMMARY_MEMBERS = new Set(["token", "rowVerdict", "sessionId", "scope", "gateKind", "createdAt", "deadline"]);
|
|
93
|
+
function isUnreadableSummaryForm(summary) {
|
|
94
|
+
const names = UNREADABLE_SUMMARY_MEMBERS;
|
|
95
|
+
return summary.rowVerdict.kind !== "ok" && Object.keys(summary).every((member) => names.has(member));
|
|
96
|
+
}
|
|
97
|
+
function readCheckpointBodyOrRefuse(cell, where) {
|
|
98
|
+
try {
|
|
99
|
+
return refusing("a checkpoint nobody can resume", () => decodeCheckpointColumn(cell, where));
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
throw e instanceof StoreCorruptError ? new UnreadableCheckpointRowError(e, verdictOfUnreadableBody(cell)) : e;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
156
105
|
function decodeOutcomeColumn(cell, where) {
|
|
157
106
|
return decodeNullableJsonColumn(cell, readResumeOutcomeBody, `checkpoint.outcome ${where}`);
|
|
158
107
|
}
|
|
@@ -225,80 +174,7 @@ function readCheckpointIdCell(cell) {
|
|
|
225
174
|
return null;
|
|
226
175
|
}
|
|
227
176
|
}
|
|
228
|
-
export function queueRowGateKind(kind) {
|
|
229
|
-
if (isGateKind(kind))
|
|
230
|
-
return kind;
|
|
231
|
-
recordFailOpen("server.approvals.queue-row-gate-kind-missing", `gate_kind=${kind === null || kind === undefined ? "NULL" : redactSecrets(String(kind)).slice(0, 40)}`);
|
|
232
|
-
return undefined;
|
|
233
|
-
}
|
|
234
177
|
const readPendingActionMemberBody = createObjectColumnReader();
|
|
235
|
-
export function boundedToolInput(args) {
|
|
236
|
-
if (args === undefined)
|
|
237
|
-
return null;
|
|
238
|
-
const redacted = redactDeep(args);
|
|
239
|
-
const json = JSON.stringify(redacted);
|
|
240
|
-
if (json === undefined)
|
|
241
|
-
return null;
|
|
242
|
-
if (json.length > MAX_TOOL_INPUT_CHARS)
|
|
243
|
-
return { truncated: true, bytes: json.length };
|
|
244
|
-
return redacted;
|
|
245
|
-
}
|
|
246
|
-
export const DECIDED_REPLAY_SCAN_CAP = 8;
|
|
247
|
-
export function approvalDecisionOfWinner(winner, boundCallId) {
|
|
248
|
-
if (winner === undefined)
|
|
249
|
-
return null;
|
|
250
|
-
if (winner.boundCallId !== boundCallId)
|
|
251
|
-
return null;
|
|
252
|
-
switch (winner.decision) {
|
|
253
|
-
case "allow":
|
|
254
|
-
return "approve";
|
|
255
|
-
case "deny":
|
|
256
|
-
return "deny";
|
|
257
|
-
case "approve":
|
|
258
|
-
case "reject":
|
|
259
|
-
case "edit":
|
|
260
|
-
return null;
|
|
261
|
-
default: {
|
|
262
|
-
const unhandled = winner.decision;
|
|
263
|
-
void unhandled;
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
function pendingActionToolCallId(body) {
|
|
269
|
-
if (typeof body !== "object" || body === null)
|
|
270
|
-
return { readable: false };
|
|
271
|
-
if ("sourceTaskId" in body)
|
|
272
|
-
return { readable: false };
|
|
273
|
-
const rawSource = "sourceSessionId" in body ? body.sourceSessionId : null;
|
|
274
|
-
if (rawSource !== null && rawSource !== undefined && typeof rawSource !== "string")
|
|
275
|
-
return { readable: false };
|
|
276
|
-
const sourceSessionId = typeof rawSource === "string" ? rawSource : null;
|
|
277
|
-
if (!("pendingAction" in body))
|
|
278
|
-
return { readable: true, toolCallId: null, boundInputHash: null, kind: null, sourceSessionId };
|
|
279
|
-
const pa = body.pendingAction;
|
|
280
|
-
if (typeof pa !== "object" || pa === null)
|
|
281
|
-
return { readable: false };
|
|
282
|
-
const kind = "kind" in pa ? pa.kind : null;
|
|
283
|
-
if (kind !== null && typeof kind !== "string")
|
|
284
|
-
return { readable: false };
|
|
285
|
-
if (!("toolCallId" in pa))
|
|
286
|
-
return { readable: true, toolCallId: null, boundInputHash: null, kind, sourceSessionId };
|
|
287
|
-
const id = pa.toolCallId;
|
|
288
|
-
if (typeof id !== "string")
|
|
289
|
-
return { readable: false };
|
|
290
|
-
if (kind !== "tool_approval")
|
|
291
|
-
return { readable: false };
|
|
292
|
-
const bih = "boundInputHash" in pa ? pa.boundInputHash : null;
|
|
293
|
-
if (bih !== null && typeof bih !== "string")
|
|
294
|
-
return { readable: false };
|
|
295
|
-
return { readable: true, toolCallId: id, boundInputHash: bih, kind, sourceSessionId };
|
|
296
|
-
}
|
|
297
|
-
export function candidateIdentityOf(cp) {
|
|
298
|
-
if (judgeCheckpointRow(cp).kind === "lacking")
|
|
299
|
-
return { readable: false };
|
|
300
|
-
return pendingActionToolCallId(cp);
|
|
301
|
-
}
|
|
302
178
|
function candidateIdentityOfColumn(cell, where) {
|
|
303
179
|
let cp;
|
|
304
180
|
try {
|
|
@@ -371,7 +247,6 @@ export function readPendingSteerQueueColumn(queueRaw) {
|
|
|
371
247
|
}
|
|
372
248
|
const STEER_CAS_ATTEMPTS = 8;
|
|
373
249
|
const EXECUTION_RECORD_CAS_ATTEMPTS = 8;
|
|
374
|
-
export const CTX_CLAIM_REWRITES = ["written", "claim_released", "context_absent"];
|
|
375
250
|
export class SqlCheckpointStore {
|
|
376
251
|
db;
|
|
377
252
|
logger;
|
|
@@ -474,7 +349,7 @@ export class SqlCheckpointStore {
|
|
|
474
349
|
const r = rows[0];
|
|
475
350
|
if (!r)
|
|
476
351
|
return null;
|
|
477
|
-
const cp =
|
|
352
|
+
const cp = readCheckpointBodyOrRefuse(r.checkpoint, `for token ${tokenFingerprint(token)}`);
|
|
478
353
|
cp.status = r.status;
|
|
479
354
|
const outcomeRaw = refusing("a decision record nobody can read", () => decodeOutcomeColumn(r.outcome, `for token ${tokenFingerprint(token)}`));
|
|
480
355
|
if (outcomeRaw)
|
|
@@ -653,47 +528,74 @@ export class SqlCheckpointStore {
|
|
|
653
528
|
const token = String(r.token);
|
|
654
529
|
const sha256 = readJsonColumnTextSha256(r[CHECKPOINT_SHA256_AS], "checkpoint.checkpoint_sha256 (whole-row read)");
|
|
655
530
|
const projection = this.projectStoredRow(r, sha256);
|
|
656
|
-
if (
|
|
531
|
+
if (sha256 === expected.get(token))
|
|
657
532
|
out.set(token, projection);
|
|
658
533
|
}
|
|
659
534
|
}
|
|
660
535
|
return out;
|
|
661
536
|
}
|
|
662
537
|
projectStoredRow(r, sha256) {
|
|
538
|
+
const read = this.readStoredRow(r, "(operator queue row, whole-row read)", sha256);
|
|
539
|
+
return read.summary === undefined ? { rowVerdict: read.rowVerdict, skillCardFacts: {} } : { rowVerdict: read.rowVerdict, skillCardFacts: skillCardFacts(read.summary) };
|
|
540
|
+
}
|
|
541
|
+
readStoredRow(r, where, sha256, discloseFor) {
|
|
542
|
+
const memo = this.storedRowMemo.get(sha256);
|
|
663
543
|
let cp;
|
|
664
544
|
try {
|
|
665
|
-
cp = decodeCheckpointColumn(r.checkpoint,
|
|
545
|
+
cp = decodeCheckpointColumn(r.checkpoint, where);
|
|
666
546
|
}
|
|
667
547
|
catch (e) {
|
|
668
548
|
if (!isJsonColumnBodyProblem(e))
|
|
669
549
|
throw e;
|
|
670
550
|
recordFailOpen("server.approvals.park-row-blob-unreadable");
|
|
671
|
-
return
|
|
551
|
+
return { rowVerdict: verdictOfUnreadableBody(r.checkpoint) };
|
|
672
552
|
}
|
|
673
553
|
let summary;
|
|
554
|
+
const disclosed = [];
|
|
674
555
|
try {
|
|
675
|
-
summary = summarizeCheckpoint(cp);
|
|
556
|
+
summary = summarizeCheckpoint(cp, (sentence) => disclosed.push(sentence), memo?.rowVerdict);
|
|
676
557
|
}
|
|
677
558
|
catch {
|
|
678
559
|
recordFailOpen("server.approvals.park-row-blob-unreadable");
|
|
679
|
-
return { rowVerdict: judgeCheckpointRow(cp)
|
|
560
|
+
return { rowVerdict: judgeCheckpointRow(cp) };
|
|
680
561
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
562
|
+
if (isUnreadableSummaryForm(summary)) {
|
|
563
|
+
recordFailOpen("server.approvals.park-row-blob-unreadable");
|
|
564
|
+
return { rowVerdict: summary.rowVerdict };
|
|
565
|
+
}
|
|
566
|
+
const seat = discloseFor?.(cp);
|
|
567
|
+
for (const sentence of disclosed)
|
|
568
|
+
seat?.(sentence);
|
|
569
|
+
if (memo === undefined)
|
|
570
|
+
this.storedRowMemo.set(sha256, { rowVerdict: summary.rowVerdict, skillCardFacts: skillCardFacts(summary) });
|
|
571
|
+
return { rowVerdict: summary.rowVerdict, summary };
|
|
684
572
|
}
|
|
685
573
|
async listByScope(scope) {
|
|
686
|
-
const
|
|
574
|
+
const d = this.db.dialect;
|
|
575
|
+
const { rows } = await this.db.query(`SELECT ${jsonColumnText(d, "checkpoint")}, ${jsonColumnTextSha256(d, "checkpoint", CHECKPOINT_SHA256_AS)}, ${jsonColumnText(d, "checkpoint", PARK_ROW_CHECKPOINT_ID)}, ` +
|
|
576
|
+
`token, session_id, scope, created_at_ms, deadline, gate_kind FROM checkpoint WHERE status = 'pending' AND scope = ${this.q("?", "$1")} ORDER BY created_at_ms ASC LIMIT 500`, [scope]);
|
|
687
577
|
const out = [];
|
|
688
578
|
for (const r of rows) {
|
|
689
579
|
const sha256 = readJsonColumnTextSha256(r[CHECKPOINT_SHA256_AS], `checkpoint.checkpoint_sha256 in the ${scope} scheduler view`);
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
580
|
+
const read = this.readStoredRow(r, `in the ${scope} scheduler view`, sha256, (cp) => (sentence) => recordFailOpen("server.checkpoint.inbox-risk-axes-damaged", withoutCheckpointToken(sentence, cp.token)));
|
|
581
|
+
if (read.summary !== undefined) {
|
|
582
|
+
out.push(read.summary);
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
const gateKind = queueRowGateKind(r.gate_kind);
|
|
586
|
+
if (gateKind === undefined)
|
|
587
|
+
continue;
|
|
588
|
+
const checkpointId = readCheckpointIdCell(r[PARK_ROW_CHECKPOINT_ID.as]);
|
|
589
|
+
out.push({
|
|
590
|
+
token: String(r.token),
|
|
591
|
+
rowVerdict: read.rowVerdict,
|
|
592
|
+
...(typeof checkpointId === "string" ? { checkpointId } : {}),
|
|
593
|
+
sessionId: String(r.session_id),
|
|
594
|
+
scope: String(r.scope),
|
|
595
|
+
gateKind,
|
|
596
|
+
createdAt: Number(r.created_at_ms),
|
|
597
|
+
...(r.deadline == null ? {} : { deadline: Number(r.deadline) }),
|
|
598
|
+
});
|
|
697
599
|
}
|
|
698
600
|
return out;
|
|
699
601
|
}
|
|
@@ -872,47 +774,6 @@ export class SqlCheckpointStore {
|
|
|
872
774
|
}
|
|
873
775
|
return out;
|
|
874
776
|
}
|
|
875
|
-
async scanKindlessBatchRows() {
|
|
876
|
-
const out = [];
|
|
877
|
-
let unreadableCells = 0;
|
|
878
|
-
const tolerant = (read) => {
|
|
879
|
-
try {
|
|
880
|
-
return read();
|
|
881
|
-
}
|
|
882
|
-
catch (e) {
|
|
883
|
-
if (!isJsonColumnBodyProblem(e))
|
|
884
|
-
throw e;
|
|
885
|
-
unreadableCells += 1;
|
|
886
|
-
return undefined;
|
|
887
|
-
}
|
|
888
|
-
};
|
|
889
|
-
const prefilter = (d) => `(rule_suggestions IS NOT NULL OR ${jsonColumnTextExpr(d, "checkpoint", PARK_ROW_PENDING_ACTION.key)} LIKE '%"batch"%')`;
|
|
890
|
-
let after = "";
|
|
891
|
-
for (;;) {
|
|
892
|
-
const { rows } = await this.db.query(this.q(`SELECT token, ${jsonColumnText("mysql", "rule_suggestions")}, ${jsonColumnText("mysql", "checkpoint", PARK_ROW_PENDING_ACTION)} FROM checkpoint WHERE token > ? AND ${prefilter("mysql")} ORDER BY token LIMIT ${KINDLESS_SCAN_PAGE}`, `SELECT token, ${jsonColumnText("pg", "rule_suggestions")}, ${jsonColumnText("pg", "checkpoint", PARK_ROW_PENDING_ACTION)} FROM checkpoint WHERE token > $1 AND ${prefilter("pg")} ORDER BY token LIMIT ${KINDLESS_SCAN_PAGE}`), [after]);
|
|
893
|
-
for (const row of rows) {
|
|
894
|
-
const token = String(row.token);
|
|
895
|
-
after = token;
|
|
896
|
-
const carriers = [];
|
|
897
|
-
const column = tolerant(() => decodeNullableJsonColumn(row.rule_suggestions, readJsonArrayColumn, "checkpoint.rule_suggestions (kind-less batch census)"));
|
|
898
|
-
if (countKindlessBatchOffers(boundedReaderInput(column)) > 0)
|
|
899
|
-
carriers.push("rule_suggestions");
|
|
900
|
-
const pendingAction = tolerant(() => decodeNullableJsonColumn(row[PARK_ROW_PENDING_ACTION.as], readJsonObjectColumn, "checkpoint.checkpoint pendingAction member (kind-less batch census)"));
|
|
901
|
-
if (typeof pendingAction === "object" && pendingAction !== null) {
|
|
902
|
-
const blobOffers = Reflect.get(pendingAction, "ruleOffers");
|
|
903
|
-
if (blobOffers !== undefined && !Array.isArray(blobOffers))
|
|
904
|
-
unreadableCells += 1;
|
|
905
|
-
else if (countKindlessBatchOffers(blobOffers) > 0)
|
|
906
|
-
carriers.push("pendingAction.ruleOffers");
|
|
907
|
-
}
|
|
908
|
-
if (carriers.length > 0)
|
|
909
|
-
out.push({ ref: tokenFingerprint(token), carriers });
|
|
910
|
-
}
|
|
911
|
-
if (rows.length < KINDLESS_SCAN_PAGE)
|
|
912
|
-
break;
|
|
913
|
-
}
|
|
914
|
-
return { rows: out, unreadableCells };
|
|
915
|
-
}
|
|
916
777
|
async reapCtx(cutoff) {
|
|
917
778
|
const res = await this.db.query(this.q("DELETE FROM checkpoint_ctx WHERE updated_at_ms < ? " +
|
|
918
779
|
"AND session_id NOT IN (SELECT session_id FROM checkpoint WHERE status='pending') " +
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
2
2
|
import type { Pool as PgPool } from "pg";
|
|
3
3
|
import type { MailboxAckResult, MailboxAppendMessage, MailboxDecision, MailboxDecisionOutcome, MailboxLease, MailboxRecipientTombstone, MailboxStore } from "@sema-agent/core";
|
|
4
|
-
import { type IndexSpec } from "
|
|
5
|
-
import type { SqlDialect } from "
|
|
4
|
+
import { type IndexSpec } from "../plugins/ensure-index.js";
|
|
5
|
+
import type { SqlDialect } from "../plugins/sql-driver.js";
|
|
6
6
|
export declare const MAILBOX_TABLE = "mailbox";
|
|
7
7
|
export declare const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
8
8
|
/** [ref] 收件人墓碑表(预删除态的**唯一**真源;形与理由见 `mailbox-recipient-lifecycle.ts` 模块头)。
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { isUtf8 } from "node:buffer";
|
|
2
2
|
import { MAILBOX_CONSUMED_ID_WINDOW, MAILBOX_INVALID_PEER_META_CODE, MAILBOX_RECORD_ID_MAX_CHARS, MAILBOX_REPLAY_FACETS, MAILBOX_TOMBSTONED_RECIPIENT_CODE, MailboxStoreError, assertMailboxDecisionMove, judgeMailboxAppendById, mailboxReplayDigests, readMailboxAppend, readMailboxPeerMeta, readStoredMailboxDecision } from "@sema-agent/core";
|
|
3
|
-
import { pgHasUnstorable, pgSanitizeText, PgUnstorableError } from "
|
|
3
|
+
import { pgHasUnstorable, pgSanitizeText, PgUnstorableError } from "../plugins/pg-safe-json.js";
|
|
4
4
|
import { isWellFormedUnicode } from "../principal-jwt.js";
|
|
5
|
-
import { ensureIndexes } from "
|
|
6
|
-
import { escapeLike } from "
|
|
7
|
-
import { SqlJsonColumnError, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, readStringArrayColumn } from "
|
|
5
|
+
import { ensureIndexes } from "../plugins/ensure-index.js";
|
|
6
|
+
import { escapeLike } from "../plugins/sql-escape.js";
|
|
7
|
+
import { SqlJsonColumnError, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, readStringArrayColumn } from "../plugins/sql-json-column.js";
|
|
8
8
|
export const MAILBOX_TABLE = "mailbox";
|
|
9
9
|
export const MAILBOX_MSG_TABLE = "mailbox_message";
|
|
10
10
|
export const MAILBOX_TOMBSTONE_TABLE = "mailbox_tombstone";
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import type { Pool } from "mysql2/promise";
|
|
24
24
|
import { type RosterStore, type RosterEntry, type RosterAccess } from "@sema-agent/core";
|
|
25
|
-
import type { PgQueryFn } from "
|
|
26
|
-
import { type IndexSpec } from "
|
|
25
|
+
import type { PgQueryFn } from "../plugins/pg-query.js";
|
|
26
|
+
import { type IndexSpec } from "../plugins/ensure-index.js";
|
|
27
27
|
export declare const ROSTER_TABLE = "agent_roster";
|
|
28
28
|
export declare function ensureTiDBRosterSchema(pool: Pool): Promise<void>;
|
|
29
29
|
/** [ref]:本 store 的索引**声明**(两方言共用一份)。PG 侧由下面的 `ensurePgRosterSchema` 应用,MySQL 侧由 `tidb-pool.ts` 的中央 `ensureSchema` 应用(那里内联 `KEY` 已在 `CREATE TABLE` 里 ⇒ 新建库探到即零 DDL,存量库缺谁补谁)。加索引以外的 schema 变更仍归运维,见 `plugins/ensure-index.ts` 头注。 */
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { ToolResultProvenance, ToolResultStore, ToolResultSlice, ToolResultDeletionReport } from "@sema-agent/core";
|
|
2
2
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
3
3
|
import type { Pool as PgPool, PoolClient as PgPoolClient } from "pg";
|
|
4
|
-
import { type SqlDriver, type SqlDialect } from "
|
|
4
|
+
import { type SqlDriver, type SqlDialect } from "../plugins/sql-driver.js";
|
|
5
5
|
import type { RetentionDeclaration } from "@sema-agent/core";
|
|
6
|
-
import { type IndexSpec } from "
|
|
6
|
+
import { type IndexSpec } from "../plugins/ensure-index.js";
|
|
7
7
|
/** PG schema for the tool_result table — the PG translation of tidb-pool.ts's tool_result DDL
|
|
8
8
|
* (LONGTEXT→TEXT, DATETIME(3)→TIMESTAMPTZ(3), inline KEY→separate CREATE INDEX). Disjoint from other stores'
|
|
9
9
|
* tables, so a self-contained ensureSchema is safe (central pg-pool aggregation is done separately). */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { MAX_MINTED_TOOL_RESULT_REF_CHARS, assertSafeToolResultRef, assertToolResultProvenanceMatch, normalizeToolResultProvenance } from "@sema-agent/core";
|
|
2
|
-
import { pgSanitizeText } from "
|
|
3
|
-
import { mysqlDriver, pgDriver } from "
|
|
4
|
-
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "
|
|
5
|
-
import { ensureIndexes } from "
|
|
6
|
-
import { describeProbedTableDrift, schemaWaysOut } from "
|
|
7
|
-
import { isMissingColumnError } from "
|
|
2
|
+
import { pgSanitizeText } from "../plugins/pg-safe-json.js";
|
|
3
|
+
import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
|
|
4
|
+
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-store-sql.js";
|
|
5
|
+
import { ensureIndexes } from "../plugins/ensure-index.js";
|
|
6
|
+
import { describeProbedTableDrift, schemaWaysOut } from "../plugins/schema-drift-sentence.js";
|
|
7
|
+
import { isMissingColumnError } from "../plugins/sql-errors.js";
|
|
8
8
|
export const PG_TOOL_RESULT_SCHEMA = [
|
|
9
9
|
`CREATE TABLE IF NOT EXISTS tool_result (
|
|
10
10
|
ref VARCHAR(518) COLLATE "C" NOT NULL,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* {@link import("@sema-agent/core").InMemoryWorkflowJournalStore} (single-replica, lost on crash); this is the
|
|
9
9
|
* byte-match durable twin so a workflow survives a replica crash and RESUMES (replay the longest unchanged
|
|
10
10
|
* PREFIX of recorded `ctx.agent` results, run only the first changed/new call + everything after it live). The
|
|
11
|
-
* journal is LOAD-BEARING (unlike the best-effort {@link import("
|
|
11
|
+
* journal is LOAD-BEARING (unlike the best-effort {@link import("../plugins/run-store-sql.js")} observation layer):
|
|
12
12
|
* append/load failures MUST throw — a silently-dropped entry would silently re-run already-done work.
|
|
13
13
|
*
|
|
14
14
|
* Schema = two tables (DDL in tidb-pool.ts / pg-pool.ts ensureSchema), the SQL normal form of core's per-run
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* `run_id = ? AND scope = ?`.
|
|
37
37
|
* Why the owner record's incarnation is in the row's PRIMARY KEY (one rule: **a row belongs to the owner record that
|
|
38
38
|
* wrote it — readable only through it, overwritable only by a write made through it**): a write can outlive the owner
|
|
39
|
-
* record it was judged against —
|
|
39
|
+
* record it was judged against — the GC sweep racing an append, an append stalled between its two
|
|
40
40
|
* statements. Keyed and joined on run_id alone, (a) a leftover row would be adopted by the next claim of that run_id by
|
|
41
41
|
* ANY scope, and (b) on PostgreSQL READ COMMITTED a stale guarded write whose SELECT still saw the old record would, via
|
|
42
42
|
* the ON CONFLICT arm (which updates a row its snapshot never saw, without re-checking the source), overwrite the NEW
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
78
78
|
import type { Pool as PgPool } from "pg";
|
|
79
79
|
import { type WorkflowJournalEntry, type WorkflowJournalStore } from "@sema-agent/core";
|
|
80
|
-
import { type SqlDriver } from "
|
|
80
|
+
import { type SqlDriver } from "../plugins/sql-driver.js";
|
|
81
81
|
/** Dual-dialect durable WorkflowJournalStore. See the file header for the dialect-delta ledger. */
|
|
82
82
|
/** [ref] 租约旋钮。TTL 只是**崩溃兜底**(engine 终态 finally 显式释放;[ref]/[ref] 两层分工:
|
|
83
83
|
* engine 崩了没释放 ⇒ 陈旧 claim 可被接管)。
|
|
@@ -137,11 +137,6 @@ export declare class SqlWorkflowJournalStore implements WorkflowJournalStore {
|
|
|
137
137
|
private oversizeCellOf;
|
|
138
138
|
/** 读面的**唯一**行读法(`loadPage` 的块读与超门行的单条读共用):序数 ≥ `from` 的至多 `limit` 行,字节超 `inlineBytes` 的行 `resultJson` 为 null(只回 `resultBytes`)。 */
|
|
139
139
|
private readRows;
|
|
140
|
-
/** GC — purge ALL journal entries for a run (every incarnation), then its owner row ([ref]). Scoped by run_id alone;
|
|
141
|
-
* idempotent (returns the journal rows removed). Mirrors the resume-anchor store's deleteBySession. An append racing the
|
|
142
|
-
* two DELETEs can leave a row behind without its owner record; it is keyed by the dead incarnation — readable and
|
|
143
|
-
* overwritable by no one (file header) — and the GC sweep removes it by age. */
|
|
144
|
-
deleteByRun(runId: string): Promise<number>;
|
|
145
140
|
/** [ref] / WF2([ref] core 拍板 a 形,签名逐字)—— cross-replica resume admission lease。
|
|
146
141
|
*
|
|
147
142
|
* 语义(bake-store `idem_key UNIQUE` 先例):PK (source_run_id, scope) 上的原子赢或观察。四步,每步
|
|
@@ -165,10 +160,10 @@ export declare class SqlWorkflowJournalStore implements WorkflowJournalStore {
|
|
|
165
160
|
newRunId: string;
|
|
166
161
|
scope: string;
|
|
167
162
|
}): Promise<void>;
|
|
168
|
-
/** GC — time-based sweep: purge journal entries older than `maxAgeMs` (created_at_ms < now - maxAgeMs).
|
|
169
|
-
*
|
|
170
|
-
* — wired into the service reaper
|
|
171
|
-
* bound. A resume of a journal older than the retention window simply re-runs live (resume is an optimization).
|
|
163
|
+
/** GC — time-based sweep: purge journal entries older than `maxAgeMs` (created_at_ms < now - maxAgeMs). This is the
|
|
164
|
+
* journal's ONE delete path (the engine's contract has no per-run delete; its File store's was retired in core 7.61.0,
|
|
165
|
+
* and this store's went with it) — wired into the service reaper, it is what keeps the heaviest table
|
|
166
|
+
* (TaskResult-bearing) from growing without bound. A resume of a journal older than the retention window simply re-runs live (resume is an optimization).
|
|
172
167
|
* Idempotent; returns the journal rows purged.
|
|
173
168
|
*
|
|
174
169
|
* [ref]: the same cutoff then purges owner rows whose OWN clock (`appended_at_ms`, pushed by every append of the
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { callKeyOrdinal } from "@sema-agent/core";
|
|
2
2
|
import { assertJournalEntryFits } from "@sema-agent/core";
|
|
3
|
-
import { journalEntryOfCell, journalRowPayloadOf } from "
|
|
3
|
+
import { journalEntryOfCell, journalRowPayloadOf } from "../plugins/workflow-journal-payload.js";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { mysqlDriver, pgDriver } from "
|
|
6
|
-
import { jsonColumnText, jsonColumnTextExpr } from "
|
|
5
|
+
import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
|
|
6
|
+
import { jsonColumnText, jsonColumnTextExpr } from "../plugins/sql-json-column.js";
|
|
7
7
|
const DEFAULT_RESUME_CLAIM_TTL_MS = 60 * 60 * 1000;
|
|
8
8
|
const INT_MIN = -2147483648;
|
|
9
9
|
const INT_MAX = 2147483647;
|
|
@@ -75,11 +75,6 @@ export class SqlWorkflowJournalStore {
|
|
|
75
75
|
const { rows } = await this.db.query(this.q(`SELECT ordinal, call_key, CASE WHEN LENGTH(result) > ? THEN NULL ELSE ${jsonColumnTextExpr("mysql", "result")} END AS result_json, LENGTH(result) AS result_bytes FROM workflow_journal JOIN workflow_journal_owner USING (run_id, incarnation) WHERE run_id = ? AND scope = ? AND ordinal >= ? ORDER BY ordinal ASC LIMIT ?`, `SELECT ordinal, call_key, CASE WHEN OCTET_LENGTH(result) > $3 THEN NULL ELSE ${jsonColumnTextExpr("pg", "result")} END AS result_json, OCTET_LENGTH(result) AS result_bytes FROM workflow_journal JOIN workflow_journal_owner USING (run_id, incarnation) WHERE run_id = $1 AND scope = $2 AND ordinal >= $4 ORDER BY ordinal ASC LIMIT $5`), params);
|
|
76
76
|
return rows.map((r) => ({ ordinal: Number(r.ordinal), callKey: String(r.call_key), resultJson: r.result_json === null ? null : String(r.result_json), resultBytes: Number(r.result_bytes) }));
|
|
77
77
|
}
|
|
78
|
-
async deleteByRun(runId) {
|
|
79
|
-
const res = await this.db.query(this.q("DELETE FROM workflow_journal WHERE run_id = ?", "DELETE FROM workflow_journal WHERE run_id = $1"), [runId]);
|
|
80
|
-
await this.db.query(this.q("DELETE FROM workflow_journal_owner WHERE run_id = ?", "DELETE FROM workflow_journal_owner WHERE run_id = $1"), [runId]);
|
|
81
|
-
return res.affected;
|
|
82
|
-
}
|
|
83
78
|
async resumeClaim(input) {
|
|
84
79
|
const now = Date.now();
|
|
85
80
|
const ins = await this.db.query(this.q("INSERT IGNORE INTO workflow_resume_claim (source_run_id, scope, new_run_id, claimed_at_ms) VALUES (?, ?, ?, ?)", "INSERT INTO workflow_resume_claim (source_run_id, scope, new_run_id, claimed_at_ms) VALUES ($1, $2, $3, $4) ON CONFLICT (source_run_id, scope) DO NOTHING"), [input.sourceRunId, input.scope, input.newRunId, now]);
|