@sema-agent/server 7.5.0 → 7.7.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/LICENSE +1 -1
- package/README.md +18 -3
- package/README.zh-CN.md +14 -3
- package/USAGE.md +100 -2
- package/dist/approval-reconciler.d.ts +1 -1
- package/dist/approval-reconciler.js +6 -5
- package/dist/boot/budget-tracing.js +1 -1
- package/dist/boot/config-center.js +17 -3
- package/dist/boot/execution-env.js +1 -1
- package/dist/boot/parked-revive-gate.d.ts +78 -0
- package/dist/boot/parked-revive-gate.js +114 -0
- package/dist/boot/resolve-spec.d.ts +8 -20
- package/dist/boot/resolve-spec.js +40 -125
- package/dist/budget.d.ts +1 -1
- package/dist/budget.js +1 -1
- package/dist/capabilities/hands-lane.d.ts +91 -0
- package/dist/capabilities/hands-lane.js +104 -0
- package/dist/capabilities/repo-tools.d.ts +1 -1
- package/dist/capabilities/repo-tools.js +8 -2
- package/dist/capabilities/scenarios.d.ts +49 -2
- package/dist/capabilities/scenarios.js +84 -16
- package/dist/config-provider.d.ts +1 -0
- package/dist/config-provider.js +23 -3
- package/dist/config-types.d.ts +5 -3
- package/dist/config.js +5 -4
- package/dist/deployment-governance.d.ts +168 -0
- package/dist/deployment-governance.js +206 -0
- package/dist/fleet/fleet-bus.d.ts +11 -1
- package/dist/fleet/fleet-bus.js +43 -0
- package/dist/fleet/fleet-terminal-window.d.ts +98 -0
- package/dist/fleet/fleet-terminal-window.js +319 -0
- package/dist/hooks/hook-runner.js +11 -0
- package/dist/http/route-ctx.d.ts +7 -1
- package/dist/http/routes/approvals-assistant.js +6 -7
- package/dist/http/routes/fleet.js +164 -14
- package/dist/http/routes/runs.js +7 -2
- package/dist/http/routes/tasks.js +5 -4
- package/dist/http/routes/trace-usage.js +3 -4
- package/dist/http/send.d.ts +23 -0
- package/dist/http/send.js +23 -0
- package/dist/http/server.d.ts +10 -0
- package/dist/http/server.js +28 -12
- package/dist/http/sse-log.js +3 -4
- package/dist/leader/diffout.d.ts +10 -0
- package/dist/leader/diffout.js +14 -2
- package/dist/leader/diffup.js +3 -2
- package/dist/leader/planner.js +7 -0
- package/dist/main.js +79 -32
- package/dist/observability/fail-open.d.ts +4 -0
- package/dist/observability/fail-open.js +4 -0
- package/dist/observability/tool-trace.d.ts +13 -0
- package/dist/observability/tool-trace.js +14 -0
- package/dist/orchestration/workflow-notify-journal.d.ts +1 -1
- package/dist/orchestration/workflow-notify-journal.js +14 -34
- package/dist/plugins/approval-ask-store-sql.d.ts +33 -0
- package/dist/plugins/approval-ask-store-sql.js +66 -33
- package/dist/plugins/background-agent-store-sql.js +16 -16
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +5 -2
- package/dist/plugins/checkpoint-store-sql.js +5 -2
- package/dist/plugins/image-bake-store-sql.d.ts +1 -1
- package/dist/plugins/image-bake-store-sql.js +27 -27
- package/dist/plugins/image-index-sql.js +15 -15
- package/dist/plugins/mailbox-store-sql.js +3 -3
- package/dist/plugins/memory-engine-pg.js +9 -9
- package/dist/plugins/memory-engine-tidb.js +7 -7
- package/dist/plugins/memory-sync-store-pg.js +13 -13
- package/dist/plugins/memory-sync-store-tidb.js +5 -5
- package/dist/plugins/outcome-ledger-sql.js +7 -7
- package/dist/plugins/pg-cost-quota.js +3 -3
- package/dist/plugins/pg-pool.js +84 -75
- package/dist/plugins/pg-rate-limiter.js +3 -3
- package/dist/plugins/pg-session-storage.d.ts +1 -1
- package/dist/plugins/pg-session-storage.js +12 -13
- package/dist/plugins/remote-env-host.js +3 -1
- package/dist/plugins/remote-env-local-docker.js +6 -3
- package/dist/plugins/remote-env-ssh.d.ts +13 -1
- package/dist/plugins/roster-store-sql.js +8 -8
- package/dist/plugins/store-contracts.d.ts +19 -0
- package/dist/plugins/store-contracts.js +42 -0
- package/dist/plugins/task-attachment-store.js +5 -5
- package/dist/plugins/task-list-store-sql.js +1 -1
- package/dist/plugins/tidb-cost-quota.js +1 -1
- package/dist/plugins/tidb-pool.js +83 -60
- package/dist/plugins/tidb-rate-limiter.js +1 -1
- package/dist/plugins/tidb-session-store.js +2 -5
- package/dist/plugins/tool-result-store-sql.js +2 -2
- package/dist/plugins/usage-window-store-sql.js +13 -13
- package/dist/plugins/write-behind-counter.d.ts +10 -2
- package/dist/plugins/write-behind-counter.js +13 -3
- package/dist/resource-suspend.d.ts +3 -1
- package/dist/resource-suspend.js +3 -1
- package/dist/run-local.d.ts +73 -1
- package/dist/run-local.js +160 -9
- package/dist/runs.d.ts +3 -1
- package/dist/runs.js +3 -1
- package/dist/security.d.ts +12 -0
- package/dist/security.js +12 -0
- package/dist/session-sync-kernel.d.ts +13 -0
- package/dist/session-sync-kernel.js +13 -0
- package/dist/trace/core-keyset-guard.d.ts +1 -1
- package/dist/trace/project.d.ts +10 -1
- package/dist/trace/project.js +31 -0
- package/package.json +3 -3
package/dist/plugins/pg-pool.js
CHANGED
|
@@ -38,16 +38,25 @@ import { ensurePgImageBakeSchema as ensureImageBakeSchema } from "./image-bake-s
|
|
|
38
38
|
import { ensurePgApprovalAskSchema } from "./approval-ask-store-sql.js";
|
|
39
39
|
export const PG_SCHEMA_STATEMENTS = [
|
|
40
40
|
`CREATE TABLE IF NOT EXISTS task_run (
|
|
41
|
-
task_id VARCHAR(64) NOT NULL,
|
|
42
|
-
session_id VARCHAR(64) NOT NULL,
|
|
43
|
-
owner VARCHAR(190),
|
|
44
|
-
status VARCHAR(16) NOT NULL,
|
|
41
|
+
task_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
42
|
+
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
43
|
+
owner VARCHAR(190) COLLATE "C",
|
|
44
|
+
status VARCHAR(16) COLLATE "C" NOT NULL,
|
|
45
45
|
result JSONB,
|
|
46
|
-
error TEXT,
|
|
47
|
-
error_code VARCHAR(64),
|
|
48
|
-
instance_id VARCHAR(64),
|
|
49
|
-
job_id VARCHAR(64),
|
|
50
|
-
source VARCHAR(64),
|
|
46
|
+
error TEXT COLLATE "C",
|
|
47
|
+
error_code VARCHAR(64) COLLATE "C",
|
|
48
|
+
instance_id VARCHAR(64) COLLATE "C",
|
|
49
|
+
job_id VARCHAR(64) COLLATE "C",
|
|
50
|
+
source VARCHAR(64) COLLATE "C",
|
|
51
|
+
-- 🔴 **刻意不写 COLLATE C** —— 本表其余字符串列都钉了,只有这一根是例外,理由是实测出来的
|
|
52
|
+
-- (#192,codex 复审 round5;证据 scratchpad/evidence/u6-lower-collate-c.txt):
|
|
53
|
+
-- PG 的 lower() 按**表达式的排序规则**决定大小写映射,而 C 只带 ASCII 映射 ⇒
|
|
54
|
+
-- lower('ÄPFEL Deploy') 在 C 下得到 'Äpfel deploy'(非 ASCII 原样不折),于是
|
|
55
|
+
-- ?q=äpfel 的 LOWER(objective_preview) LIKE LOWER($1) 谓词**一条都搜不到**(实测 0 命中;
|
|
56
|
+
-- 库默认排序规则下是 1 命中)。MySQL 孪生没有这个面:utf8mb4_bin 下 LOWER() 照样折 Unicode
|
|
57
|
+
-- (实测 LOWER('ÄPFEL')='äpfel',搜得到)⇒ 把这一列也钉成 C 反而**制造**了一处方言分叉。
|
|
58
|
+
-- ⇒ 判据分轴:身份/隔离键要字节判等(钉 C);**人写的可搜文本**要 Unicode 大小写折叠(随库默认)。
|
|
59
|
+
-- 本列是后者唯一的成员;新增可搜列请照此处理,并在排序规则门的例外表里登记。
|
|
51
60
|
objective_preview VARCHAR(160),
|
|
52
61
|
cancel_requested SMALLINT NOT NULL DEFAULT 0,
|
|
53
62
|
preempt_requested SMALLINT NOT NULL DEFAULT 0,
|
|
@@ -60,29 +69,29 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
60
69
|
`CREATE INDEX IF NOT EXISTS idx_task_run_created ON task_run (created_at)`,
|
|
61
70
|
`CREATE INDEX IF NOT EXISTS idx_task_run_job ON task_run (job_id)`,
|
|
62
71
|
`CREATE TABLE IF NOT EXISTS task_event (
|
|
63
|
-
task_id VARCHAR(64) NOT NULL,
|
|
72
|
+
task_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
64
73
|
seq BIGINT NOT NULL,
|
|
65
|
-
type VARCHAR(24) NOT NULL,
|
|
74
|
+
type VARCHAR(24) COLLATE "C" NOT NULL,
|
|
66
75
|
data JSONB,
|
|
67
76
|
ts TIMESTAMPTZ(3) NOT NULL,
|
|
68
77
|
PRIMARY KEY (task_id, seq)
|
|
69
78
|
)`,
|
|
70
79
|
`CREATE TABLE IF NOT EXISTS task_active (
|
|
71
|
-
session_id VARCHAR(64) NOT NULL,
|
|
72
|
-
task_id VARCHAR(64) NOT NULL,
|
|
80
|
+
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
81
|
+
task_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
73
82
|
since TIMESTAMPTZ(3) NOT NULL,
|
|
74
83
|
PRIMARY KEY (session_id)
|
|
75
84
|
)`,
|
|
76
85
|
`CREATE INDEX IF NOT EXISTS idx_task_active_task ON task_active (task_id)`,
|
|
77
86
|
// Durable-checkpoint (core 1.67 / design/45) — the FULL table (matches tidb-pool.ts checkpoint + its migrations).
|
|
78
87
|
`CREATE TABLE IF NOT EXISTS checkpoint (
|
|
79
|
-
token VARCHAR(120) NOT NULL,
|
|
80
|
-
scope VARCHAR(190) NOT NULL,
|
|
81
|
-
session_id VARCHAR(190) NOT NULL,
|
|
88
|
+
token VARCHAR(120) COLLATE "C" NOT NULL,
|
|
89
|
+
scope VARCHAR(190) COLLATE "C" NOT NULL,
|
|
90
|
+
session_id VARCHAR(190) COLLATE "C" NOT NULL,
|
|
82
91
|
version INT NOT NULL DEFAULT 1,
|
|
83
|
-
status VARCHAR(16) NOT NULL DEFAULT 'pending',
|
|
84
|
-
tool_name VARCHAR(190),
|
|
85
|
-
tool_call_id VARCHAR(190),
|
|
92
|
+
status VARCHAR(16) COLLATE "C" NOT NULL DEFAULT 'pending',
|
|
93
|
+
tool_name VARCHAR(190) COLLATE "C",
|
|
94
|
+
tool_call_id VARCHAR(190) COLLATE "C",
|
|
86
95
|
tool_input JSONB,
|
|
87
96
|
checkpoint JSONB NOT NULL,
|
|
88
97
|
outcome JSONB,
|
|
@@ -90,34 +99,34 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
90
99
|
created_at BIGINT NOT NULL,
|
|
91
100
|
decided_at BIGINT,
|
|
92
101
|
rev BIGINT NOT NULL DEFAULT 0,
|
|
93
|
-
reopen_reason VARCHAR(32),
|
|
102
|
+
reopen_reason VARCHAR(32) COLLATE "C",
|
|
94
103
|
terminal_at BIGINT,
|
|
95
|
-
gate_kind VARCHAR(32),
|
|
96
|
-
bound_input_hash VARCHAR(190),
|
|
104
|
+
gate_kind VARCHAR(32) COLLATE "C",
|
|
105
|
+
bound_input_hash VARCHAR(190) COLLATE "C",
|
|
97
106
|
-- pending_steer / pending_steer_queue / pending_steer_rev:队列化后的 durable steering(core 5.14.0
|
|
98
107
|
-- #147)。三列的语义、「为什么队列另起一列而不是覆盖旧列」(滚动升级窗里旧副本的无条件覆盖写)、
|
|
99
108
|
-- 48000 字节预算、以及「为什么另起一个整数 rev 列而不拿 JSON 旧值当 CAS 守卫」的完整由来见 MySQL
|
|
100
109
|
-- 孪生(tidb-pool.ts 同三列的行内注)。PG text 无长度上限,预算由 core 侧统一执法。
|
|
101
|
-
pending_steer TEXT,
|
|
102
|
-
pending_steer_queue TEXT,
|
|
110
|
+
pending_steer TEXT COLLATE "C",
|
|
111
|
+
pending_steer_queue TEXT COLLATE "C",
|
|
103
112
|
pending_steer_rev BIGINT NOT NULL DEFAULT 0,
|
|
104
|
-
risk_descriptor TEXT,
|
|
113
|
+
risk_descriptor TEXT COLLATE "C",
|
|
105
114
|
PRIMARY KEY (token)
|
|
106
115
|
)`,
|
|
107
116
|
`CREATE INDEX IF NOT EXISTS idx_checkpoint_status_deadline ON checkpoint (status, deadline)`,
|
|
108
117
|
`CREATE INDEX IF NOT EXISTS idx_checkpoint_session_status ON checkpoint (session_id, status)`,
|
|
109
118
|
`CREATE TABLE IF NOT EXISTS checkpoint_ctx (
|
|
110
|
-
session_id VARCHAR(190) NOT NULL,
|
|
119
|
+
session_id VARCHAR(190) COLLATE "C" NOT NULL,
|
|
111
120
|
ctx JSONB NOT NULL,
|
|
112
121
|
updated_at BIGINT NOT NULL,
|
|
113
122
|
PRIMARY KEY (session_id)
|
|
114
123
|
)`,
|
|
115
124
|
// E18 resume-at anchor map (the PG twin of the TiDB resume_anchor table — eventId→entryId; see tidb-pool.ts).
|
|
116
125
|
`CREATE TABLE IF NOT EXISTS resume_anchor (
|
|
117
|
-
session_id VARCHAR(64) NOT NULL,
|
|
118
|
-
event_id VARCHAR(64) NOT NULL,
|
|
119
|
-
entry_id VARCHAR(64) NOT NULL,
|
|
120
|
-
owner VARCHAR(190),
|
|
126
|
+
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
127
|
+
event_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
128
|
+
entry_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
129
|
+
owner VARCHAR(190) COLLATE "C",
|
|
121
130
|
created_at TIMESTAMPTZ(3) NOT NULL,
|
|
122
131
|
PRIMARY KEY (session_id, event_id)
|
|
123
132
|
)`,
|
|
@@ -125,9 +134,9 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
125
134
|
// Per-session per-toolName approval exemption (the PG twin — see tidb-pool.ts /
|
|
126
135
|
// sema-internal server/docs/design/APPROVAL-SESSION-EXEMPTION.md).
|
|
127
136
|
`CREATE TABLE IF NOT EXISTS approval_exemption (
|
|
128
|
-
session_id VARCHAR(64) NOT NULL,
|
|
129
|
-
tool_name VARCHAR(190) NOT NULL,
|
|
130
|
-
granted_by VARCHAR(190),
|
|
137
|
+
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
138
|
+
tool_name VARCHAR(190) COLLATE "C" NOT NULL,
|
|
139
|
+
granted_by VARCHAR(190) COLLATE "C",
|
|
131
140
|
created_at TIMESTAMPTZ(3) NOT NULL,
|
|
132
141
|
PRIMARY KEY (session_id, tool_name)
|
|
133
142
|
)`,
|
|
@@ -135,16 +144,16 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
135
144
|
// key segment hides this from URLs. id = uuidv7 time-ordered PK = keyset cursor; see tidb-pool.ts /
|
|
136
145
|
// send-file-ledger.ts).
|
|
137
146
|
`CREATE TABLE IF NOT EXISTS sendfile_link (
|
|
138
|
-
id VARCHAR(64) NOT NULL,
|
|
139
|
-
scope VARCHAR(190) NOT NULL,
|
|
140
|
-
bucket VARCHAR(190) NOT NULL,
|
|
141
|
-
object_key VARCHAR(512) NOT NULL,
|
|
142
|
-
track VARCHAR(16) NOT NULL,
|
|
143
|
-
filename VARCHAR(190) NOT NULL,
|
|
147
|
+
id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
148
|
+
scope VARCHAR(190) COLLATE "C" NOT NULL,
|
|
149
|
+
bucket VARCHAR(190) COLLATE "C" NOT NULL,
|
|
150
|
+
object_key VARCHAR(512) COLLATE "C" NOT NULL,
|
|
151
|
+
track VARCHAR(16) COLLATE "C" NOT NULL,
|
|
152
|
+
filename VARCHAR(190) COLLATE "C" NOT NULL,
|
|
144
153
|
byte_len BIGINT NOT NULL,
|
|
145
154
|
ttl_sec BIGINT NOT NULL,
|
|
146
|
-
task_id VARCHAR(64),
|
|
147
|
-
session_id VARCHAR(64),
|
|
155
|
+
task_id VARCHAR(64) COLLATE "C",
|
|
156
|
+
session_id VARCHAR(64) COLLATE "C",
|
|
148
157
|
created_at TIMESTAMPTZ(3) NOT NULL,
|
|
149
158
|
PRIMARY KEY (id)
|
|
150
159
|
)`,
|
|
@@ -153,12 +162,12 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
153
162
|
// [sessionId, principal ?? null])) = a fixed 64-char hash PK; rules JSONB; CAS-versioned tighten-only — see
|
|
154
163
|
// tidb-pool.ts / session-policy-store-sql.ts).
|
|
155
164
|
`CREATE TABLE IF NOT EXISTS session_policy (
|
|
156
|
-
policy_key CHAR(64) NOT NULL,
|
|
157
|
-
session_id VARCHAR(64) NOT NULL,
|
|
165
|
+
policy_key CHAR(64) COLLATE "C" NOT NULL,
|
|
166
|
+
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
158
167
|
-- 2c DENORMALIZED principal, carried solely so listBySession can ENUMERATE: policy_key is a one-way
|
|
159
168
|
-- sha256, so the principal is not recoverable from it. Enumeration-only — it is NOT part of the key and
|
|
160
169
|
-- NOT part of the CAS / tighten-only path. NULL = a session-wide (principal-less) policy row.
|
|
161
|
-
principal VARCHAR(190),
|
|
170
|
+
principal VARCHAR(190) COLLATE "C",
|
|
162
171
|
rules JSONB NOT NULL,
|
|
163
172
|
rev BIGINT NOT NULL,
|
|
164
173
|
created_at TIMESTAMPTZ(3) NOT NULL,
|
|
@@ -169,15 +178,15 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
169
178
|
// E19 rewind-files (the PG twin of the TiDB snapshot_manifest/snapshot_blob; relPath→blob_hash manifest +
|
|
170
179
|
// content-addressed BYTEA blobs; core captureManifest/applyManifest do the walk/restore — see tidb-pool.ts).
|
|
171
180
|
`CREATE TABLE IF NOT EXISTS snapshot_manifest (
|
|
172
|
-
scope VARCHAR(64) NOT NULL,
|
|
173
|
-
snap_key VARCHAR(64) NOT NULL,
|
|
174
|
-
rel_path VARCHAR(1024) NOT NULL,
|
|
175
|
-
blob_hash CHAR(64) NOT NULL
|
|
181
|
+
scope VARCHAR(64) COLLATE "C" NOT NULL,
|
|
182
|
+
snap_key VARCHAR(64) COLLATE "C" NOT NULL,
|
|
183
|
+
rel_path VARCHAR(1024) COLLATE "C" NOT NULL,
|
|
184
|
+
blob_hash CHAR(64) COLLATE "C" NOT NULL
|
|
176
185
|
)`,
|
|
177
186
|
`CREATE INDEX IF NOT EXISTS idx_snapshot_manifest_sk ON snapshot_manifest (scope, snap_key)`,
|
|
178
187
|
`CREATE INDEX IF NOT EXISTS idx_snapshot_manifest_blob ON snapshot_manifest (blob_hash)`,
|
|
179
188
|
`CREATE TABLE IF NOT EXISTS snapshot_blob (
|
|
180
|
-
blob_hash CHAR(64) PRIMARY KEY,
|
|
189
|
+
blob_hash CHAR(64) COLLATE "C" PRIMARY KEY,
|
|
181
190
|
byte_len BIGINT NOT NULL,
|
|
182
191
|
-- \`bytes\` is NULLABLE and that nullability is LOAD-BEARING, not laxness: a SQL-backend row carries the
|
|
183
192
|
-- bytes inline; a MinIO-backend row keeps ONLY the INDEX (blob_hash, byte_len, created_at) here with
|
|
@@ -194,35 +203,35 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
194
203
|
// is TEXT, NOT JSONB — the byte-match contract is "string in, string out" (the store JSON.parses on load); JSONB would
|
|
195
204
|
// make `pg` auto-parse the row back to an object and break the twin's String()→JSON.parse path. See workflow-journal-store-sql.ts.
|
|
196
205
|
`CREATE TABLE IF NOT EXISTS workflow_journal (
|
|
197
|
-
run_id VARCHAR(191) NOT NULL,
|
|
206
|
+
run_id VARCHAR(191) COLLATE "C" NOT NULL,
|
|
198
207
|
ordinal INT NOT NULL,
|
|
199
208
|
-- SVC-2 / CORE-9 audit BLOCKER: cross-tenant resume isolation (the WorkflowJournalStore seam takes a scope
|
|
200
209
|
-- param). NOT NULL DEFAULT '' — a row written without a scope lands in the '' bucket, which is
|
|
201
210
|
-- CROSS-SCOPE-ORPHANED but harmless: a real resume always filters \`WHERE scope = <caller>\`, so '' rows are
|
|
202
211
|
-- never handed to a tenant. Keep the DEFAULT: it is what makes a scope-less write land in that dead bucket
|
|
203
212
|
-- instead of failing or leaking into a real tenant's scope.
|
|
204
|
-
scope VARCHAR(190) NOT NULL DEFAULT '',
|
|
205
|
-
call_key VARCHAR(255) NOT NULL,
|
|
206
|
-
result TEXT NOT NULL,
|
|
213
|
+
scope VARCHAR(190) COLLATE "C" NOT NULL DEFAULT '',
|
|
214
|
+
call_key VARCHAR(255) COLLATE "C" NOT NULL,
|
|
215
|
+
result TEXT COLLATE "C" NOT NULL,
|
|
207
216
|
created_at BIGINT NOT NULL,
|
|
208
217
|
PRIMARY KEY (run_id, ordinal)
|
|
209
218
|
)`,
|
|
210
219
|
// RB-242 / WF2 ([1981] core 拍板 a 形):cross-replica workflow resume admission lease — the PG twin of
|
|
211
220
|
// workflow_resume_claim in tidb-pool.ts (rationale there; TTL = crash backstop only).
|
|
212
221
|
`CREATE TABLE IF NOT EXISTS workflow_resume_claim (
|
|
213
|
-
source_run_id VARCHAR(191) NOT NULL,
|
|
214
|
-
scope VARCHAR(190) NOT NULL,
|
|
215
|
-
new_run_id VARCHAR(191) NOT NULL,
|
|
222
|
+
source_run_id VARCHAR(191) COLLATE "C" NOT NULL,
|
|
223
|
+
scope VARCHAR(190) COLLATE "C" NOT NULL,
|
|
224
|
+
new_run_id VARCHAR(191) COLLATE "C" NOT NULL,
|
|
216
225
|
claimed_at BIGINT NOT NULL,
|
|
217
226
|
PRIMARY KEY (source_run_id, scope)
|
|
218
227
|
)`,
|
|
219
228
|
// P1 (fleet failover): WorkflowRunStore + completion-inbox PG twins (design doc in workflow-run-store-sql.ts).
|
|
220
229
|
// `run` is TEXT not jsonb (node-pg auto-parse would break the shared String()→JSON.parse path).
|
|
221
230
|
`CREATE TABLE IF NOT EXISTS workflow_run (
|
|
222
|
-
id VARCHAR(191) NOT NULL,
|
|
223
|
-
scope VARCHAR(190) NOT NULL,
|
|
224
|
-
status VARCHAR(16) NOT NULL,
|
|
225
|
-
run TEXT NOT NULL,
|
|
231
|
+
id VARCHAR(191) COLLATE "C" NOT NULL,
|
|
232
|
+
scope VARCHAR(190) COLLATE "C" NOT NULL,
|
|
233
|
+
status VARCHAR(16) COLLATE "C" NOT NULL,
|
|
234
|
+
run TEXT COLLATE "C" NOT NULL,
|
|
226
235
|
rev INTEGER NOT NULL DEFAULT 0,
|
|
227
236
|
created_at BIGINT NOT NULL,
|
|
228
237
|
ended_at BIGINT,
|
|
@@ -232,17 +241,17 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
232
241
|
`CREATE INDEX IF NOT EXISTS idx_wfrun_scope_status ON workflow_run (scope, status)`,
|
|
233
242
|
`CREATE TABLE IF NOT EXISTS workflow_completion_inbox (
|
|
234
243
|
seq BIGSERIAL,
|
|
235
|
-
session_id VARCHAR(190) NOT NULL,
|
|
236
|
-
run_id VARCHAR(191) NOT NULL,
|
|
237
|
-
owner VARCHAR(190),
|
|
238
|
-
status VARCHAR(16) NOT NULL,
|
|
239
|
-
summary TEXT NOT NULL,
|
|
244
|
+
session_id VARCHAR(190) COLLATE "C" NOT NULL,
|
|
245
|
+
run_id VARCHAR(191) COLLATE "C" NOT NULL,
|
|
246
|
+
owner VARCHAR(190) COLLATE "C",
|
|
247
|
+
status VARCHAR(16) COLLATE "C" NOT NULL,
|
|
248
|
+
summary TEXT COLLATE "C" NOT NULL,
|
|
240
249
|
enqueued_at BIGINT NOT NULL,
|
|
241
250
|
-- 1.109 delivery envelope. kind NULL = a LEGACY \`workflow_complete\` row (the shape that predates the
|
|
242
251
|
-- envelope); readers must keep treating NULL as that kind, it is not "unknown". payload = the kind's
|
|
243
252
|
-- body, NULL for the legacy shape.
|
|
244
|
-
kind VARCHAR(24),
|
|
245
|
-
payload TEXT,
|
|
253
|
+
kind VARCHAR(24) COLLATE "C",
|
|
254
|
+
payload TEXT COLLATE "C",
|
|
246
255
|
PRIMARY KEY (session_id, run_id),
|
|
247
256
|
-- Twin-alignment with TiDB's \`UNIQUE KEY uq_wfinbox_seq (seq)\` (same constraint name on purpose, so the two
|
|
248
257
|
-- schemas can be diffed by name). This is NOT a correctness fix: seq is BIGSERIAL, so it is already unique in
|
|
@@ -254,10 +263,10 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
254
263
|
)`,
|
|
255
264
|
`CREATE INDEX IF NOT EXISTS idx_wfinbox_session_seq ON workflow_completion_inbox (session_id, seq)`,
|
|
256
265
|
`CREATE TABLE IF NOT EXISTS workflow_inbox_fence (
|
|
257
|
-
session_id VARCHAR(190) NOT NULL,
|
|
258
|
-
kind VARCHAR(8) NOT NULL,
|
|
259
|
-
run_id VARCHAR(191) NOT NULL DEFAULT '',
|
|
260
|
-
owner VARCHAR(190),
|
|
266
|
+
session_id VARCHAR(190) COLLATE "C" NOT NULL,
|
|
267
|
+
kind VARCHAR(8) COLLATE "C" NOT NULL,
|
|
268
|
+
run_id VARCHAR(191) COLLATE "C" NOT NULL DEFAULT '',
|
|
269
|
+
owner VARCHAR(190) COLLATE "C",
|
|
261
270
|
owner_known SMALLINT NOT NULL DEFAULT 0,
|
|
262
271
|
until_ms BIGINT NOT NULL,
|
|
263
272
|
PRIMARY KEY (session_id, kind, run_id)
|
|
@@ -265,11 +274,11 @@ export const PG_SCHEMA_STATEMENTS = [
|
|
|
265
274
|
`CREATE INDEX IF NOT EXISTS idx_wffence_until ON workflow_inbox_fence (until_ms)`,
|
|
266
275
|
// 1.108 review fix (lens③ HIGH): notify-journal PG twin (design note in tidb-pool.ts).
|
|
267
276
|
`CREATE TABLE IF NOT EXISTS workflow_notify_journal (
|
|
268
|
-
run_id VARCHAR(191) NOT NULL,
|
|
269
|
-
scope VARCHAR(190) NOT NULL,
|
|
277
|
+
run_id VARCHAR(191) COLLATE "C" NOT NULL,
|
|
278
|
+
scope VARCHAR(190) COLLATE "C" NOT NULL,
|
|
270
279
|
acked SMALLINT NOT NULL DEFAULT 0,
|
|
271
|
-
source_task_id VARCHAR(191),
|
|
272
|
-
principal VARCHAR(190),
|
|
280
|
+
source_task_id VARCHAR(191) COLLATE "C",
|
|
281
|
+
principal VARCHAR(190) COLLATE "C",
|
|
273
282
|
created_at BIGINT NOT NULL,
|
|
274
283
|
acked_at BIGINT,
|
|
275
284
|
PRIMARY KEY (run_id)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PgWriteBehindCounter } from "./write-behind-counter.js";
|
|
2
2
|
/** The counter's table binding for the per-key request rate limit (mirror of the TiDB twin's binding). */
|
|
3
|
-
const RATE_LIMIT_TABLE = { table: "rate_limit", keyCol: "limit_key", valCol: "
|
|
3
|
+
const RATE_LIMIT_TABLE = { table: "rate_limit", keyCol: "limit_key", valCol: "hits" };
|
|
4
4
|
export class PgRateLimiter {
|
|
5
5
|
limit;
|
|
6
6
|
counter;
|
|
@@ -43,9 +43,9 @@ export class PgRateLimiter {
|
|
|
43
43
|
*/
|
|
44
44
|
export const PG_RATE_LIMITER_SCHEMA = [
|
|
45
45
|
`CREATE TABLE IF NOT EXISTS rate_limit (
|
|
46
|
-
limit_key VARCHAR(190) NOT NULL,
|
|
46
|
+
limit_key VARCHAR(190) COLLATE "C" NOT NULL,
|
|
47
47
|
window_bucket BIGINT NOT NULL,
|
|
48
|
-
|
|
48
|
+
hits INTEGER NOT NULL DEFAULT 0,
|
|
49
49
|
updated_at TIMESTAMPTZ(3) NOT NULL,
|
|
50
50
|
PRIMARY KEY (limit_key, window_bucket)
|
|
51
51
|
)`,
|
|
@@ -4,7 +4,7 @@ import type { SessionSummary } from "./store-contracts.js";
|
|
|
4
4
|
import { type StagingHandle } from "../session-sync-kernel.js";
|
|
5
5
|
/**
|
|
6
6
|
* PG-dialect DDL for the L1 session log, translated from tidb-pool.ts SCHEMA_STATEMENTS:
|
|
7
|
-
* JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), inline `KEY`/`UNIQUE KEY`→separate CREATE INDEX (UNIQUE for
|
|
7
|
+
* JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), inline `KEY`/`UNIQUE KEY`→separate CREATE INDEX (UNIQUE for uq_session_event_entry).
|
|
8
8
|
*
|
|
9
9
|
* SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
|
|
10
10
|
* drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead.
|
|
@@ -24,9 +24,8 @@ import { escapeLike } from "./sql-escape.js";
|
|
|
24
24
|
import { pgSafeJsonStringify } from "./pg-safe-json.js";
|
|
25
25
|
import { toIso } from "./sql-row-helpers.js";
|
|
26
26
|
import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
|
|
27
|
-
import { classifySyncRelationshipByIds, SyncConflictError, stagingIdFor, STAGING_ID_MARKER,
|
|
28
|
-
|
|
29
|
-
const STAGING_GC_GRACE_MS = 3_600_000;
|
|
27
|
+
import { classifySyncRelationshipByIds, SyncConflictError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, // 两孪生店同源(理由见 kernel 处顶注)
|
|
28
|
+
} from "../session-sync-kernel.js";
|
|
30
29
|
function isDupKey(err) {
|
|
31
30
|
return Boolean(err) && err.code === "23505"; // PG unique_violation
|
|
32
31
|
}
|
|
@@ -36,36 +35,36 @@ function parseEntry(p) {
|
|
|
36
35
|
}
|
|
37
36
|
/**
|
|
38
37
|
* PG-dialect DDL for the L1 session log, translated from tidb-pool.ts SCHEMA_STATEMENTS:
|
|
39
|
-
* JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), inline `KEY`/`UNIQUE KEY`→separate CREATE INDEX (UNIQUE for
|
|
38
|
+
* JSON→JSONB, DATETIME(3)→TIMESTAMPTZ(3), inline `KEY`/`UNIQUE KEY`→separate CREATE INDEX (UNIQUE for uq_session_event_entry).
|
|
40
39
|
*
|
|
41
40
|
* SCHEMA POLICY: see the header of pg-pool.ts — the code is the single source of truth, schema changes are
|
|
42
41
|
* drop-and-recreate, and NO new `ALTER TABLE` seams are added here; fold into the CREATE instead.
|
|
43
42
|
*/
|
|
44
43
|
export const PG_SESSION_SCHEMA = [
|
|
45
44
|
`CREATE TABLE IF NOT EXISTS session_meta (
|
|
46
|
-
session_id VARCHAR(64) NOT NULL,
|
|
47
|
-
owner VARCHAR(190),
|
|
45
|
+
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
46
|
+
owner VARCHAR(190) COLLATE "C",
|
|
48
47
|
created_at TIMESTAMPTZ(3) NOT NULL,
|
|
49
48
|
updated_at TIMESTAMPTZ(3) NOT NULL,
|
|
50
|
-
leaf_id VARCHAR(64),
|
|
49
|
+
leaf_id VARCHAR(64) COLLATE "C",
|
|
51
50
|
leaf_seq BIGINT NOT NULL DEFAULT 0,
|
|
52
51
|
-- Write-once auto-title (setTitleIfNull / probeTitle): NULL = "untitled", i.e. still claimable; the
|
|
53
52
|
-- three-state probe distinguishes it from "no such session". PG twin of the TiDB COLUMN_MIGRATIONS leg.
|
|
54
|
-
title VARCHAR(120),
|
|
53
|
+
title VARCHAR(120) COLLATE "C",
|
|
55
54
|
PRIMARY KEY (session_id)
|
|
56
55
|
)`,
|
|
57
56
|
`CREATE INDEX IF NOT EXISTS idx_session_meta_owner ON session_meta (owner)`,
|
|
58
57
|
`CREATE TABLE IF NOT EXISTS session_event (
|
|
59
|
-
session_id VARCHAR(64) NOT NULL,
|
|
58
|
+
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
60
59
|
seq BIGINT NOT NULL,
|
|
61
|
-
entry_id VARCHAR(64) NOT NULL,
|
|
62
|
-
parent_id VARCHAR(64),
|
|
63
|
-
type VARCHAR(32) NOT NULL,
|
|
60
|
+
entry_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
61
|
+
parent_id VARCHAR(64) COLLATE "C",
|
|
62
|
+
type VARCHAR(32) COLLATE "C" NOT NULL,
|
|
64
63
|
payload JSONB NOT NULL,
|
|
65
64
|
ts TIMESTAMPTZ(3) NOT NULL,
|
|
66
65
|
PRIMARY KEY (session_id, seq)
|
|
67
66
|
)`,
|
|
68
|
-
`CREATE UNIQUE INDEX IF NOT EXISTS
|
|
67
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS uq_session_event_entry ON session_event (session_id, entry_id)`,
|
|
69
68
|
];
|
|
70
69
|
/** Idempotent schema apply for this store's tables (the test calls this; production aggregation is separate). */
|
|
71
70
|
export async function ensureSchema(pool) {
|
|
@@ -49,7 +49,9 @@ import { resolveHostShell, hostShell, spawnGroupOptions, killTreeHard, killTreeS
|
|
|
49
49
|
import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
|
|
50
50
|
import { FileError, ExecutionError, RemoteExecutionError, scrubSecretEnv, RollingTailBuffer, markTruncated, SchedulerError, BackgroundShellError, } from "@sema-agent/core";
|
|
51
51
|
const PROVIDER = "host";
|
|
52
|
-
/** Default per-command wall-clock when the caller passes none (NOT a hang detector — host commands rarely hang).
|
|
52
|
+
/** Default per-command wall-clock when the caller passes none (NOT a hang detector — host commands rarely hang).
|
|
53
|
+
* ⚠️ 同判据三腿之一(另两条:`remote-env-local-docker.ts` 同值 30min、`remote-env-ssh.ts` **5min**)——
|
|
54
|
+
* 分歧成文与候裁说明写在 ssh 那处的 `commandTimeoutMs` 顶注(#176 二轮扫描 finding)。 */
|
|
53
55
|
const DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60_000;
|
|
54
56
|
// ─── design/103 host-lane background shell: TOC's run_in_background / BashOutput / KillShell ───
|
|
55
57
|
// host driver = design/103 §5.2 path ①: a `detached` child (its OWN process group via `detached:true` → pgid ===
|
|
@@ -22,8 +22,9 @@
|
|
|
22
22
|
* snapshot to resume from, so a dead container cannot be re-attached.
|
|
23
23
|
*
|
|
24
24
|
* 🔴 **Domestic-images iron rule (clay):** the base `image` comes ENTIRELY from config (no hardcoded
|
|
25
|
-
* `docker.io/...` default). A deployment points it at the sandbox-image-pool (docker.io/claybobby public pool
|
|
26
|
-
*
|
|
25
|
+
* `docker.io/...` default). A deployment points it at the sandbox-image-pool (docker.io/claybobby public pool)
|
|
26
|
+
* or its own private registry — SWR was deprecated 2026-07-13 and the in-network Gitea registry container was
|
|
27
|
+
* retired 2026-08 (no CN fallback is baked in). This adapter never invents a registry.
|
|
27
28
|
*
|
|
28
29
|
* 🔴 **argv anti-injection (council, host/ssh §4):** every `docker` invocation is a `spawn(binary, argv[])`
|
|
29
30
|
* with NO host-side shell (`shell:false` default) — the container id, image, paths, and env values are
|
|
@@ -49,7 +50,9 @@ import { createPosixShellFs } from "./posix-shell-fs.js";
|
|
|
49
50
|
const PROVIDER = "local-docker";
|
|
50
51
|
/** Default workspace root INSIDE the container (the agent's tools resolve relative paths against it). */
|
|
51
52
|
const DEFAULT_MOUNT_PATH = "/workspace";
|
|
52
|
-
/** Per-command wall-clock when the caller passes none (a single exec bound, NOT the container lifetime).
|
|
53
|
+
/** Per-command wall-clock when the caller passes none (a single exec bound, NOT the container lifetime).
|
|
54
|
+
* ⚠️ 同判据三腿之一(另两条:`remote-env-host.ts` 同值 30min、`remote-env-ssh.ts` **5min**)——分歧成文
|
|
55
|
+
* 与候裁说明写在 ssh 那处的 `commandTimeoutMs` 顶注(#176 二轮扫描 finding)。 */
|
|
53
56
|
const DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60_000;
|
|
54
57
|
/** Wall-clock for a control-plane docker op (run/rm/inspect/cp) — should be fast; a hang surfaces typed. */
|
|
55
58
|
const DEFAULT_CONTROL_TIMEOUT_MS = 60_000;
|
|
@@ -11,7 +11,19 @@ export interface SshEnvConfig {
|
|
|
11
11
|
mountPath?: string;
|
|
12
12
|
/** Connect handshake bound (ms). */
|
|
13
13
|
readyTimeoutMs?: number;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Per-command wall-clock bound (ms) when the caller gives none — NOT the connection lifetime.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ **值与孪生腿不一致,原因未成文**(2026-08-08,#176 二轮扫描 finding,**候裁**):本腿默认
|
|
18
|
+
* `5 * 60_000`(见构造处),而同判据的另外两条腿 —— `remote-env-host.ts` / `remote-env-local-docker.ts`
|
|
19
|
+
* 的 `DEFAULT_COMMAND_TIMEOUT_MS` —— 都是 `30 * 60_000`,JSDoc 措辞还逐字同源。三处无共享常量、无对表
|
|
20
|
+
* 钉,ssh 这一处也没有任何注释说明 5min 是刻意收紧(git 史是 history-free snapshot,「30min 是后来调
|
|
21
|
+
* 参、ssh 漏调」的假说无从验证)。后果:一条跑满 6 分钟的构建命令在 host/docker 腿上正常收尾、在 ssh
|
|
22
|
+
* 腿上被判超时,而两腿都「按自己的默认值」正确工作。
|
|
23
|
+
* (`remote-env-k8s.ts` 的 30min **不属**同判据 —— 那是 pod 的 `activeDeadlineSeconds`,env 寿命,
|
|
24
|
+
* 与 e2b sandbox lifetime 同族,别把它数进来。)
|
|
25
|
+
* 取哪个值是行为面决定 ⇒ 留给属主裁,本批只把分歧成文,不动值。
|
|
26
|
+
*/
|
|
15
27
|
commandTimeoutMs?: number;
|
|
16
28
|
/** File-transfer (sftp) bound (ms). */
|
|
17
29
|
dataTimeoutMs?: number;
|
|
@@ -76,7 +76,7 @@ export async function ensureTiDBRosterSchema(pool) {
|
|
|
76
76
|
-- owner/scope:core 1.365 BREAKING 双轴必填(default-deny 谓词的两根轴;顶注 ① 记了曾 nullable 的由来)
|
|
77
77
|
owner VARCHAR(190) NOT NULL,
|
|
78
78
|
scope VARCHAR(190) NOT NULL,
|
|
79
|
-
session_scoped TINYINT NULL,
|
|
79
|
+
session_scoped TINYINT(1) NULL,
|
|
80
80
|
-- core 1.367 δ(additive):RosterEntry.rootSessionId verbatim 存(枚举/恢复读者;谓词暂无臂)
|
|
81
81
|
root_session_id VARCHAR(190) NULL,
|
|
82
82
|
-- core 1.373 additive:spawn 记录的解析后模型 id(NULL = 该 spawn 未记录模型)
|
|
@@ -96,20 +96,20 @@ export async function ensurePgRosterSchema(q) {
|
|
|
96
96
|
name_key VARCHAR(190) COLLATE "C" NOT NULL,
|
|
97
97
|
owner_key VARCHAR(190) COLLATE "C" NOT NULL,
|
|
98
98
|
scope_key VARCHAR(190) COLLATE "C" NOT NULL,
|
|
99
|
-
name VARCHAR(190) NOT NULL,
|
|
100
|
-
agent_id VARCHAR(190) NOT NULL,
|
|
101
|
-
session_id VARCHAR(190),
|
|
102
|
-
tool_use_id VARCHAR(190),
|
|
99
|
+
name VARCHAR(190) COLLATE "C" NOT NULL,
|
|
100
|
+
agent_id VARCHAR(190) COLLATE "C" NOT NULL,
|
|
101
|
+
session_id VARCHAR(190) COLLATE "C",
|
|
102
|
+
tool_use_id VARCHAR(190) COLLATE "C",
|
|
103
103
|
-- owner/scope:core 1.365 BREAKING 双轴必填(TiDB 同案;曾 nullable 的由来见两 ensure 上方顶注 ①)
|
|
104
104
|
owner VARCHAR(190) COLLATE "C" NOT NULL,
|
|
105
105
|
scope VARCHAR(190) COLLATE "C" NOT NULL,
|
|
106
106
|
session_scoped SMALLINT,
|
|
107
107
|
-- core 1.367 δ(additive,TiDB 同案):RosterEntry.rootSessionId verbatim 存
|
|
108
|
-
root_session_id VARCHAR(190),
|
|
108
|
+
root_session_id VARCHAR(190) COLLATE "C",
|
|
109
109
|
-- core 1.373 additive(TiDB 同案):解析后模型 id(NULL = 该 spawn 未记录模型)
|
|
110
|
-
model VARCHAR(190),
|
|
110
|
+
model VARCHAR(190) COLLATE "C",
|
|
111
111
|
-- core 5.10.0 additive(TiDB 同案):modelFallback 归因
|
|
112
|
-
model_fallback VARCHAR(64),
|
|
112
|
+
model_fallback VARCHAR(64) COLLATE "C",
|
|
113
113
|
created_at_ms BIGINT NOT NULL,
|
|
114
114
|
recorded_at_ms BIGINT NOT NULL,
|
|
115
115
|
PRIMARY KEY (name_key, owner_key, scope_key)
|
|
@@ -40,6 +40,25 @@ export interface RunRecord {
|
|
|
40
40
|
createdAt: string;
|
|
41
41
|
updatedAt: string;
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* 「这一行已经走到**终局**了吗」——`RunRecord["status"]`(= `"running" | TaskStatus`)的**穷举**判据,
|
|
45
|
+
* 与类型定义同住一个文件(词表的属主 = 判据的属主)。
|
|
46
|
+
*
|
|
47
|
+
* 🔴 为什么是 `switch` 而不是一张 `ReadonlySet<string>`(2026-08-08,#176 二轮扫描 finding):终局词表
|
|
48
|
+
* **真变过**(core 5.8.0 把 `"timeout"` 从 `TaskStatus` 里删了)。而集合形的门对象是 `string`,对词表
|
|
49
|
+
* 两个方向都无钉:
|
|
50
|
+
* · **增词**(core 新加一个终局态)⇒ 集合不含它 ⇒ 该行被静默判成「非终局」,收敛器会一直等一个
|
|
51
|
+
* 永远不会再动的行;
|
|
52
|
+
* · **删词** ⇒ 集合里留一个死字面量,没有任何东西会说话。
|
|
53
|
+
* 穷举 `switch` 把两个方向都变成**编译期**问题:core 加成员 ⇒ `default` 臂收不敛到 `never` ⇒ tsc 红,
|
|
54
|
+
* 加成员的人被迫当场回答「这个新态算不算终局」;删成员 ⇒ 对应 `case` 变成不可达字面量、同样红。
|
|
55
|
+
* 这条判据不许再被手抄成第二份 —— 两个消费点(`approval-reconciler.ts` 判据 2、`http/routes/runs.ts`
|
|
56
|
+
* 的取消后复读)都从这里取。
|
|
57
|
+
*
|
|
58
|
+
* 词表现状:终局 = completed / failed / blocked;非终局 = running / suspended / needs_review
|
|
59
|
+
* (suspended 与 needs_review 都还能再动,`running` 更不必说)。
|
|
60
|
+
*/
|
|
61
|
+
export declare function isTerminalRunStatus(status: RunRecord["status"]): boolean;
|
|
43
62
|
/** One row of the GET /v1/sessions list (CC /resume picker): a DISTINCT session (sessionId) aggregated from its
|
|
44
63
|
* task_run rows — newest-first by last activity. The preview/status are the LATEST run's ("continue where I left
|
|
45
64
|
* off"); the counts/timestamps span the whole session. Owner included so a fleet-wide (ops) listing is attributable. */
|
|
@@ -1,4 +1,46 @@
|
|
|
1
1
|
import { parseJsonOr, toIso as iso, normalizeIsoOrNull as isoOrNull } from "./sql-row-helpers.js";
|
|
2
|
+
/**
|
|
3
|
+
* 「这一行已经走到**终局**了吗」——`RunRecord["status"]`(= `"running" | TaskStatus`)的**穷举**判据,
|
|
4
|
+
* 与类型定义同住一个文件(词表的属主 = 判据的属主)。
|
|
5
|
+
*
|
|
6
|
+
* 🔴 为什么是 `switch` 而不是一张 `ReadonlySet<string>`(2026-08-08,#176 二轮扫描 finding):终局词表
|
|
7
|
+
* **真变过**(core 5.8.0 把 `"timeout"` 从 `TaskStatus` 里删了)。而集合形的门对象是 `string`,对词表
|
|
8
|
+
* 两个方向都无钉:
|
|
9
|
+
* · **增词**(core 新加一个终局态)⇒ 集合不含它 ⇒ 该行被静默判成「非终局」,收敛器会一直等一个
|
|
10
|
+
* 永远不会再动的行;
|
|
11
|
+
* · **删词** ⇒ 集合里留一个死字面量,没有任何东西会说话。
|
|
12
|
+
* 穷举 `switch` 把两个方向都变成**编译期**问题:core 加成员 ⇒ `default` 臂收不敛到 `never` ⇒ tsc 红,
|
|
13
|
+
* 加成员的人被迫当场回答「这个新态算不算终局」;删成员 ⇒ 对应 `case` 变成不可达字面量、同样红。
|
|
14
|
+
* 这条判据不许再被手抄成第二份 —— 两个消费点(`approval-reconciler.ts` 判据 2、`http/routes/runs.ts`
|
|
15
|
+
* 的取消后复读)都从这里取。
|
|
16
|
+
*
|
|
17
|
+
* 词表现状:终局 = completed / failed / blocked;非终局 = running / suspended / needs_review
|
|
18
|
+
* (suspended 与 needs_review 都还能再动,`running` 更不必说)。
|
|
19
|
+
*/
|
|
20
|
+
export function isTerminalRunStatus(status) {
|
|
21
|
+
switch (status) {
|
|
22
|
+
case "completed":
|
|
23
|
+
case "failed":
|
|
24
|
+
case "blocked":
|
|
25
|
+
return true;
|
|
26
|
+
case "running":
|
|
27
|
+
case "suspended":
|
|
28
|
+
case "needs_review":
|
|
29
|
+
return false;
|
|
30
|
+
default: {
|
|
31
|
+
// core 的 `TaskStatus` 出了新成员 ⇒ 这里收不敛到 never ⇒ tsc 红(整条判据的**编译期**执行点)。
|
|
32
|
+
const unhandled = status;
|
|
33
|
+
// 🔴 运行时**必须**照旧返回 false(#176 codex R1 真缺陷,红先复现):`never` 只活在编译期,
|
|
34
|
+
// `return unhandled` 在运行时返回的是那个字符串本身 = **truthy**。而 `plugins/run-store-sql.ts` 的
|
|
35
|
+
// rowToRun 把无约束的 SQL 文本列**裸 cast** 成本联合 ⇒ 滚动升级里一个更新的副本写入的新状态词
|
|
36
|
+
// 会被这里读到。判成「终局」的代价不可逆:approval-reconciler 判据 2 会把一张还活着的 ask 永久
|
|
37
|
+
// 打成 DENIED/routing_failure,取消路由也会把这一行当终局上报。未知 ⇒ 保守判**非终局**(落 hold,
|
|
38
|
+
// 下一轮重试),与本函数取代的那份 `ReadonlySet<string>.has()` 逐字节同行为。
|
|
39
|
+
void unhandled;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
2
44
|
/** SELECT row → {@link BakeRecord} — shared by BOTH SQL twins (design/158 S8 归位, A3 沉底). */
|
|
3
45
|
export function mapBakeRow(r) {
|
|
4
46
|
return {
|
|
@@ -87,14 +87,14 @@ export async function ensureTiDBTaskAttachmentSchema(pool) {
|
|
|
87
87
|
}
|
|
88
88
|
export async function ensurePgTaskAttachmentSchema(q) {
|
|
89
89
|
await q(`CREATE TABLE IF NOT EXISTS ${TASK_ATTACHMENT_TABLE} (
|
|
90
|
-
id VARCHAR(64) NOT NULL,
|
|
90
|
+
id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
91
91
|
-- owner:隔离键,'=' 必须字节等价(bga F2 同姿)
|
|
92
92
|
owner VARCHAR(190) COLLATE "C" NOT NULL,
|
|
93
|
-
name VARCHAR(255) NOT NULL,
|
|
94
|
-
mime VARCHAR(128) NOT NULL,
|
|
95
|
-
sha256 CHAR(64) NOT NULL,
|
|
93
|
+
name VARCHAR(255) COLLATE "C" NOT NULL,
|
|
94
|
+
mime VARCHAR(128) COLLATE "C" NOT NULL,
|
|
95
|
+
sha256 CHAR(64) COLLATE "C" NOT NULL,
|
|
96
96
|
size_bytes INT NOT NULL,
|
|
97
|
-
session_id VARCHAR(190),
|
|
97
|
+
session_id VARCHAR(190) COLLATE "C",
|
|
98
98
|
created_at_ms BIGINT NOT NULL,
|
|
99
99
|
bound_at_ms BIGINT,
|
|
100
100
|
PRIMARY KEY (id)
|
|
@@ -4,7 +4,7 @@ export class TiDBCostQuota {
|
|
|
4
4
|
counter;
|
|
5
5
|
constructor(pool, limitMicroUsd, windowMs, now, onDegraded, queryTimeoutMs) {
|
|
6
6
|
this.limitMicroUsd = limitMicroUsd;
|
|
7
|
-
this.counter = new WriteBehindCounter(pool, { table: "cost_quota", keyCol: "scope_key", valCol: "
|
|
7
|
+
this.counter = new WriteBehindCounter(pool, { table: "cost_quota", keyCol: "scope_key", valCol: "micro_usd" }, windowMs, now, onDegraded, queryTimeoutMs);
|
|
8
8
|
}
|
|
9
9
|
check(scopeKey) {
|
|
10
10
|
const used = this.counter.used(scopeKey);
|