@sema-agent/server 7.141.0 → 7.142.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 +215 -0
- package/USAGE.md +67 -12
- package/dist/approval-ask-audit-store.d.ts +54 -38
- package/dist/approval-ask-audit-store.js +58 -39
- package/dist/approval-card.d.ts +8 -8
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-reconciler.d.ts +33 -1
- package/dist/approval-reconciler.js +43 -40
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +16 -0
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +26 -23
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/memory-consolidation.js +2 -1
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.js +4 -3
- package/dist/boot/resolve-spec.js +11 -2
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.js +16 -13
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +3 -2
- package/dist/boot/stage-04-stores.js +3 -2
- package/dist/boot/stage-05-execution-env.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.js +8 -6
- package/dist/boot/stage-06-runners.d.ts +1 -1
- package/dist/boot/stage-06-runners.js +2 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -1
- package/dist/boot/stage-07-capability-layer.js +2 -1
- package/dist/boot/stage-08-reapers.d.ts +1 -1
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +5 -4
- package/dist/boot/stores.js +3 -2
- package/dist/boot/web-admin.js +17 -17
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capability-facts.d.ts +24 -0
- package/dist/capability-facts.js +23 -0
- package/dist/config-catalog.js +1 -0
- package/dist/config-types.d.ts +15 -4
- package/dist/config.d.ts +6 -0
- package/dist/config.js +17 -3
- package/dist/execution-lane-caps.d.ts +2 -1
- package/dist/fleet/fleet-bus.d.ts +6 -3
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +30 -1
- package/dist/http/dispatch.js +4 -2
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.js +5 -9
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +5 -2
- package/dist/http/routes/memory-policy.js +26 -6
- package/dist/http/routes/runs.js +3 -3
- package/dist/http/routes/session-memory-sync.d.ts +63 -0
- package/dist/http/routes/session-memory-sync.js +157 -0
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/tasks.js +7 -5
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +9 -3
- package/dist/http/send.d.ts +5 -0
- package/dist/http/send.js +9 -2
- package/dist/http/server.d.ts +14 -8
- package/dist/http/server.js +2 -4
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-sync-client.d.ts +21 -21
- package/dist/memory-sync-client.js +50 -42
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +7 -1
- package/dist/memory-sync.js +6 -3
- package/dist/observability/fail-open.d.ts +27 -10
- package/dist/observability/fail-open.js +20 -7
- package/dist/observability/run-terminal.d.ts +92 -34
- package/dist/observability/run-terminal.js +167 -162
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +48 -7
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +9 -8
- package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
- package/dist/plugins/approval-ask-store-sql.js +49 -19
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +11 -1
- package/dist/plugins/local-session-store.d.ts +36 -7
- package/dist/plugins/local-session-store.js +76 -32
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +6 -1
- package/dist/plugins/memory-run-store.js +11 -1
- package/dist/plugins/one-time-migrations.d.ts +27 -1
- package/dist/plugins/one-time-migrations.js +57 -3
- package/dist/plugins/remote-env-e2b.d.ts +23 -4
- package/dist/plugins/remote-env-e2b.js +111 -28
- package/dist/plugins/remote-env-k8s.d.ts +31 -5
- package/dist/plugins/remote-env-k8s.js +86 -19
- package/dist/plugins/remote-env-local-docker.d.ts +15 -3
- package/dist/plugins/remote-env-local-docker.js +40 -12
- package/dist/plugins/run-store-sql.d.ts +9 -1
- package/dist/plugins/run-store-sql.js +9 -1
- package/dist/plugins/store-backend.d.ts +10 -7
- package/dist/plugins/store-backend.js +1 -0
- package/dist/plugins/store-contracts.d.ts +26 -1
- package/dist/plugins/store-contracts.js +3 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +221 -28
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/run-local.js +2 -8
- package/dist/runs.js +1 -2
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +9 -0
- package/dist/security.js +8 -0
- package/dist/session-error-wire.d.ts +24 -0
- package/dist/session-error-wire.js +7 -0
- package/dist/task-settings.d.ts +10 -2
- package/dist/task-settings.js +15 -2
- package/dist/terminal.d.ts +39 -19
- package/dist/terminal.js +65 -22
- package/dist/tool-approval.d.ts +4 -3
- package/dist/tool-approval.js +12 -14
- package/dist/trace/core-keyset-guard.d.ts +123 -6
- package/dist/trace/engine-notice-wire.d.ts +8 -2
- package/dist/trace/engine-notice-wire.js +4 -2
- package/dist/trace/exit-seat.d.ts +2 -1
- package/dist/trace/ledger-sink.d.ts +8 -4
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/project.d.ts +18 -1
- package/dist/trace/project.js +16 -0
- package/dist/trace/self-minted-wire.d.ts +251 -2
- package/dist/trace/self-minted-wire.js +82 -0
- package/dist/trace/wire-projection-faces.d.ts +1 -1
- package/dist/trace/wire-projection-faces.js +1 -1
- package/dist/web-admin-client/apply-effective.js +21 -1
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/types.d.ts +4 -0
- package/package.json +1 -1
- package/wire-changes/server@7.142.0.json +639 -0
|
@@ -2,7 +2,7 @@ import type { TaskStatus } from "@sema-agent/core";
|
|
|
2
2
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
3
3
|
import type { Pool as PgPool } from "pg";
|
|
4
4
|
import { type UsageRow } from "../usage-analytics.js";
|
|
5
|
-
import { type ClaimFenced, type CreateRunOutcome, type FencedRun, type NewRunMeta, type RunIdemBinding, type RunListQuery, type RunRecord, type SessionSummary, type RunEvent, type PersistedTaskResult, type LatestRunRef } from "./store-contracts.js";
|
|
5
|
+
import { type ClaimFenced, type CreateRunOutcome, type FencedRun, type NewRunMeta, type RunIdemBinding, type RunListQuery, type RunRecord, type SessionSummary, type RunEvent, type PersistedTaskResult, type LatestRunRef, type SettledLegMemoryObservation } from "./store-contracts.js";
|
|
6
6
|
import { type SqlDriver, type SqlTxConn } from "./sql-driver.js";
|
|
7
7
|
import type { LedgerEventType } from "../trace/ledger-events.js";
|
|
8
8
|
import { type SpaceCommitClaim, type SpaceCommitReading, type SpaceCommitState } from "../workspace-space-commit.js";
|
|
@@ -228,6 +228,14 @@ export declare class SqlRunStore {
|
|
|
228
228
|
* 两方言只差占位符(动态子句为零,不像 listRuns/listSessions 那样需要各自的构造器臂)。
|
|
229
229
|
*/
|
|
230
230
|
latestRunForSession(sessionId: string): Promise<LatestRunRef | undefined>;
|
|
231
|
+
/**
|
|
232
|
+
* [ref] —— 这个会话**最近一条已结腿**与它的记忆观测(规则属主 = `store-contracts.ts` 的 {@link SettledLegMemoryObservation};
|
|
233
|
+
* 文件 / 内存两店是它的孪生)。排序口径与 {@link latestRunForSession} 逐字同一句;已结词集按本文件头注的惯例写字面(两方言并排可读),
|
|
234
|
+
* 与属主谓词 `isTerminalRunStatus` 的同源由方言对 parity 门钉(`test/source-hygiene-gate.test.ts` 的语句规则 `settled-leg-observation-read`)。
|
|
235
|
+
* 同一条索引路径(`idx_task_run_session_status` 的 `(session_id, status)` 前缀),**不新加索引**。
|
|
236
|
+
* 结果列走 {@link rowToRun} 同一只解码口与同一个失败方向(读不出 ⇒ 拒,不把一条结了的腿读成「没有观测」)。
|
|
237
|
+
*/
|
|
238
|
+
latestMemoryObservationForSession(sessionId: string): Promise<SettledLegMemoryObservation | undefined>;
|
|
231
239
|
/** GET /v1/sessions (CC /resume picker): DISTINCT sessions newest-first by last activity, keyset-paginated on
|
|
232
240
|
* (last_activity, session_id). Aggregates task_run by session_id — one window pass picks the latest run per
|
|
233
241
|
* session (rn=1) for the preview/status and rolls up first/last/count. Owner-scoped when set (the per-user view).
|
|
@@ -6,7 +6,7 @@ import { pgSanitizeText } from "./pg-safe-json.js";
|
|
|
6
6
|
import { toIso as iso } from "./sql-row-helpers.js";
|
|
7
7
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
8
8
|
import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, readJsonObjectColumn, readNonNullJsonValueColumn } from "./sql-json-column.js";
|
|
9
|
-
import { RUN_HOLDER_DEATH_CODES, RUN_STALLED_ERROR, readLegacyHolderDeathCode } from "./store-contracts.js";
|
|
9
|
+
import { RUN_HOLDER_DEATH_CODES, RUN_STALLED_ERROR, readLegacyHolderDeathCode, settledLegObservationOf } from "./store-contracts.js";
|
|
10
10
|
import { notifyRunTerminal, runTerminalLogLevel } from "../observability/run-terminal.js";
|
|
11
11
|
import { mysqlDriver, pgDriver, dialectJsonEncoder, SQL_DB_NOW_MS, sqlDbNowMs } from "./sql-driver.js";
|
|
12
12
|
import { isDupKeyError } from "./sql-errors.js";
|
|
@@ -413,6 +413,14 @@ export class SqlRunStore {
|
|
|
413
413
|
const r = rows[0];
|
|
414
414
|
return r === undefined ? undefined : { taskId: String(r.task_id), owner: r.owner ?? null, createdAt: iso(r.created_at) };
|
|
415
415
|
}
|
|
416
|
+
async latestMemoryObservationForSession(sessionId) {
|
|
417
|
+
const { rows } = await this.db.query(this.q("SELECT task_id, " + jsonColumnText("mysql", "result") + " FROM task_run WHERE session_id = ? AND status IN ('completed','failed','blocked') ORDER BY created_at DESC, task_id DESC LIMIT 1", "SELECT task_id, " + jsonColumnText("pg", "result") + " FROM task_run WHERE session_id = $1 AND status IN ('completed','failed','blocked') ORDER BY created_at DESC, task_id DESC LIMIT 1"), [sessionId]);
|
|
418
|
+
const r = rows[0];
|
|
419
|
+
if (r === undefined)
|
|
420
|
+
return undefined;
|
|
421
|
+
const taskId = String(r.task_id);
|
|
422
|
+
return settledLegObservationOf(taskId, refusing("read a settled leg as if it had observed no memory", () => decodeResultColumn(r.result, taskId)) ?? null);
|
|
423
|
+
}
|
|
416
424
|
async listSessions(opts) {
|
|
417
425
|
if (this.db.dialect === "mysql") {
|
|
418
426
|
const params = [];
|
|
@@ -326,16 +326,19 @@ export interface StoreBackend {
|
|
|
326
326
|
info?(msg: string, meta?: unknown): void;
|
|
327
327
|
}): CheckpointStoreFull;
|
|
328
328
|
/**
|
|
329
|
-
* 待决 checkpoint 的**只读**读口(**必选**成员)——
|
|
329
|
+
* 待决 checkpoint 的**只读**读口(**必选**成员)—— 「前世 / 别的副本留下的停驻」的读法,不走 {@link checkpoint}。两个读者:
|
|
330
|
+
* · 审批账本留存律(`approval-ask-retention.ts` 的挂靠集)读「还有哪些会话停驻着」(`listPendingSessionIds`);
|
|
331
|
+
* · local 车道起服收敛(`approval-crash-convergence.ts`)对审计判为孤儿的前世 `PARKING` 问跑对账器判据 ① 的候选读口(`findCheckpointCandidatesForAsk`:
|
|
332
|
+
* 停车行其实已提交 ⇒ `PARKED`,不收 `VOID`)。
|
|
330
333
|
* 区别只有一条:这一口**不牵动任何别的店**。local 车道的 `checkpoint()` 是 durable 停驻面的装配点 —— 它给 run 店接上 [ref] 的收割探针(停驻行的收割从 no-op 变生效),
|
|
331
|
-
* 那是开了 `DURABLE_APPROVAL`
|
|
332
|
-
*
|
|
334
|
+
* 那是开了 `DURABLE_APPROVAL` 的部署才该发生的事;两个读者在没开它的部署上同样要读(前世 / 别的副本留下的停驻不因这一世的开关消失:留存腿看不见就会把还能被决的停驻的问删掉,
|
|
335
|
+
* 起服收敛看不见就会把前世已提交的停驻的问收成 `VOID`),所以读和接线必须分口。SQL 孪生本来无状态(接线靠 JOIN),两口同一只。
|
|
333
336
|
*
|
|
334
|
-
* 🔴 为什么必选而不是跟着 {@link checkpoint}
|
|
335
|
-
*
|
|
336
|
-
* (`{ listPendingSessionIds: async () => [] }` —— 没有店 ⇒ 没有停驻,这是事实不是缺省)。
|
|
337
|
+
* 🔴 为什么必选而不是跟着 {@link checkpoint} 一起可选:读不到这一口的后果都在**不可逆方向**(挂靠集里少了待决停驻 ⇒ 还能被决的停驻的问按龄被删;判据 ① 恒不命中 ⇒
|
|
338
|
+
* 已提交的停驻的问被收成终态),「有 checkpoint 店却没交读口」若只是运行期的 `undefined`,就是一次静默少保护;必选 ⇒ 漏实现是**编译红**。没有 checkpoint 店的后端
|
|
339
|
+
* (`checkpoint` 缺席)交一只恒空的读口(`{ listPendingSessionIds: async () => [], findCheckpointCandidatesForAsk: async () => [] }` —— 没有店 ⇒ 没有停驻,这是事实不是缺省)。
|
|
337
340
|
*/
|
|
338
|
-
checkpointReadView(): Pick<CheckpointStoreFull, "listPendingSessionIds">;
|
|
341
|
+
checkpointReadView(): Pick<CheckpointStoreFull, "listPendingSessionIds" | "findCheckpointCandidatesForAsk">;
|
|
339
342
|
toolResult?(): ToolResultStoreFull;
|
|
340
343
|
imageIndex?(): ImageIndex;
|
|
341
344
|
imageBake?(): ImageBake;
|
|
@@ -304,6 +304,7 @@ class LocalBackend {
|
|
|
304
304
|
executionPlacementsBackfilled: placements.backfilled,
|
|
305
305
|
executionPlacementSessionsUnreadable: placements.unreadable,
|
|
306
306
|
askExecutionLanesBackfilled: backfillLocalAskExecutionLanes(this.approvalAskStore, { lanes: this.executionLanes }),
|
|
307
|
+
askJournalRecordsUpgraded: this.approvalAskStore.upgradeJournalEnvelope(),
|
|
307
308
|
localArchiveRowFactsBackfilled: archives.rowFactsBackfilled,
|
|
308
309
|
localArchivesAnonymized: archives.anonymized,
|
|
309
310
|
localArchivesKeptStamped: archives.keptStamped,
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
import type { PeerSessionTempo, TaskResult, TaskStatus } from "@sema-agent/core";
|
|
22
22
|
import type { RunIdentity } from "../identity-source.js";
|
|
23
|
+
import type { SessionUnreadable } from "../session-error-wire.js";
|
|
23
24
|
/**
|
|
24
25
|
* **File 店族的 boot 期拒启基类**([ref];把 `RunStoreStrictHydrateError` 的那条论证升成一条族规)。
|
|
25
26
|
*
|
|
@@ -443,7 +444,7 @@ export declare function isUnsuccessfulRunStatus(status: RunRecord["status"]): bo
|
|
|
443
444
|
* 未知词的方向与 {@link isTerminalRunStatus} 同向保守:判**不是** park。理由是消费点会据此做**不可逆**
|
|
444
445
|
* 动作(cancel 的恢复把手会 CAS-expire 一张 checkpoint);对一个本进程读不懂的状态,少动一次远好过错杀。
|
|
445
446
|
*/
|
|
446
|
-
export declare function isParkedRunStatus(status: RunRecord["status"]):
|
|
447
|
+
export declare function isParkedRunStatus(status: RunRecord["status"]): status is ParkedRunStatus;
|
|
447
448
|
/**
|
|
448
449
|
* 会话的**活动词**(core `PeerSessionTempo`)——「这条会话现在在忙还是闲」的**唯一**一张表([ref])。
|
|
449
450
|
*
|
|
@@ -531,6 +532,9 @@ export interface SessionSummary {
|
|
|
531
532
|
/** 行标签(`src/session-row.ts` 的 `sessionRowTagOf`):这一位住客「PATCH 能写的每一格」的单向摘要,present-iff 这一行可被条件写(店有行格写原语 ∧ 行上有化身);
|
|
532
533
|
* 只经店的列表行映射铸(`sessionRowTagKey`)。没有会话行的 lister(legacy task_run 聚合 / 内存孪生)缺席。wire 半场见 `security.ts SessionListItem.rowTag`(同名同义)。 */
|
|
533
534
|
rowTag?: string;
|
|
535
|
+
/** 这只会话的存档读不出:present-iff 头的叶子读不出(唯一铸点 `session-error-wire.ts` 的 `sessionUnreadableOf`;码与读口同表)。叶子是行上一列的店(SQL 孪生)
|
|
536
|
+
* 与没有会话行的 lister 恒缺席。wire 半场见 `security.ts SessionListItem.unreadable`(同名同义)。 */
|
|
537
|
+
unreadable?: SessionUnreadable;
|
|
534
538
|
}
|
|
535
539
|
/**
|
|
536
540
|
* [ref] —— 会话**枚举面**读 run 账里「每会话的 run 投影」的窄读面:`runCount` / `lastStatus` / `lastRunId` / `lastTaskRunId` 四位,
|
|
@@ -572,6 +576,27 @@ export interface LatestRunRef {
|
|
|
572
576
|
owner: string | null;
|
|
573
577
|
createdAt: string;
|
|
574
578
|
}
|
|
579
|
+
/** 引擎对一条腿实际挂上的记忆读集的观测(core `TaskResult.effectiveMemoryScopes` 的值;core 包根不单独导出这只型,从结果型取)。 */
|
|
580
|
+
export type EffectiveMemoryScopes = NonNullable<PersistedTaskResult["effectiveMemoryScopes"]>;
|
|
581
|
+
/**
|
|
582
|
+
* 「这个会话**最近一条已结腿**与它的记忆观测」的窄读面(run 店的 `latestMemoryObservationForSession` 的回值)—— 会话记忆同步触发口
|
|
583
|
+
* (`POST /v1/sessions/:id/memory/sync`)的**会话读集**只读这一只:读集 = 最近一条已结腿结果里引擎铸的观测(引擎一处铸、随 run 行持久;路由不重铸 spec)。
|
|
584
|
+
*
|
|
585
|
+
* 一条规则,三只实现同答:
|
|
586
|
+
* · 已结 = {@link isTerminalRunStatus}(成功 / 非成功终局;在飞与停在门上的腿还占着会话的认领,不算;读不懂的词同向保守判「未结」)。两孪生调这只谓词;
|
|
587
|
+
* SQL 两方言按本文件的惯例写字面词集 `status IN ('completed','failed','blocked')`,与谓词的同源由 `test/source-hygiene-gate.test.ts` 的方言对 parity 门钉;
|
|
588
|
+
* 取最近一条(排序口径与 {@link LatestRunRef} 逐字同一句:`created_at DESC, task_id DESC`);
|
|
589
|
+
* · `effectiveMemoryScopes` = 那一条腿结果里的观测**原样**(三态:`mounted` / `memoryless` / `none`,读法归调用方);
|
|
590
|
+
* · 那一条腿结了而**没有观测**(结果为空 / 结果里没有这一键 —— prepare 没走完)⇒ 交回那一条、观测缺席,**不往更早的腿找**
|
|
591
|
+
* (读集是「最近一条已结腿」的,不是「最近一条有观测的腿」的)。
|
|
592
|
+
* 结果列读不出 ⇒ 抛(与 `getRun` 同一个失败方向:不把一条结了的腿当成没有结果)。
|
|
593
|
+
*/
|
|
594
|
+
export interface SettledLegMemoryObservation {
|
|
595
|
+
readonly taskId: string;
|
|
596
|
+
readonly effectiveMemoryScopes: EffectiveMemoryScopes | undefined;
|
|
597
|
+
}
|
|
598
|
+
/** {@link SettledLegMemoryObservation} 的一只投影(三只实现共用):一条已结腿的结果 → 读口的回值。 */
|
|
599
|
+
export declare function settledLegObservationOf(taskId: string, result: PersistedTaskResult | null): SettledLegMemoryObservation;
|
|
575
600
|
export interface RunEvent {
|
|
576
601
|
seq: number;
|
|
577
602
|
type: string;
|
|
@@ -56,4 +56,7 @@ export function sessionTempoOfRunStatus(lastStatus) {
|
|
|
56
56
|
export function runProvenElsewhere(run, thisInstanceId) {
|
|
57
57
|
return typeof run.instanceId === "string" && run.instanceId.length > 0 && thisInstanceId !== undefined && run.instanceId !== thisInstanceId;
|
|
58
58
|
}
|
|
59
|
+
export function settledLegObservationOf(taskId, result) {
|
|
60
|
+
return { taskId, effectiveMemoryScopes: result?.effectiveMemoryScopes ?? undefined };
|
|
61
|
+
}
|
|
59
62
|
//# sourceMappingURL=store-contracts.js.map
|
|
@@ -21,12 +21,16 @@
|
|
|
21
21
|
* - 隔离键排序规则:TiDB 表级 utf8mb4_bin / PG owner 列 COLLATE "C"(bga F2 同姿,'=' 字节等价)。
|
|
22
22
|
* owner 值恒为 server 端从凭据推导(非用户输入),PAD SPACE 近撞面不可达,VARBINARY 键列不必要。
|
|
23
23
|
* - 布尔列:无(遵 1.287.0 归一约定,若未来加,PG 用 SMALLINT 0/1)。
|
|
24
|
-
* - 绑定:`bind` 把行钉到 sessionId(
|
|
25
|
-
*
|
|
24
|
+
* - 绑定:`bind` 把行钉到 sessionId,两条律都在店里、都不是先读后写(契约见 {@link TaskAttachmentStore.bind}):
|
|
25
|
+
* **一经绑定不再改绑**(UPDATE 谓词只放行未绑的行与已绑本会话的行,行级原子;同会话重绑 —— 首交重试、续跑腿重放存量体 —— 不改 `bound_at_ms`);
|
|
26
|
+
* **一只会话一只种子**(未绑的项目包只在这只会话还没有项目包时绑上:SQL 两方言是锚行加锁的一只事务,进程内的店是一段不被打断的判与改:内存店同一段同步,local 店的变更口同排一条串行段、bind 判在副本上、落成的行才合回)。
|
|
27
|
+
* 同 session 多 task 共享自然成立(listBySession 是物化输入)。
|
|
28
|
+
* 未绑定行 = 上传后从未被任何 task 引用 ⇒ reapUnbound(TTL)。
|
|
26
29
|
*/
|
|
27
30
|
import { z } from "zod";
|
|
28
31
|
import type { Pool } from "mysql2/promise";
|
|
29
32
|
import type { PgQueryFn } from "./pg-query.js";
|
|
33
|
+
import type { Pool as PgPool } from "pg";
|
|
30
34
|
import { type IndexSpec } from "./ensure-index.js";
|
|
31
35
|
/**
|
|
32
36
|
* 一条附件 meta 的**形属主**(整仓唯一那份键集)。
|
|
@@ -55,8 +59,14 @@ export interface TaskAttachmentStore {
|
|
|
55
59
|
/** meta(不带 bytes);owner 不匹配/不存在 → null(无 oracle)。 */
|
|
56
60
|
get(owner: string, id: string): Promise<TaskAttachmentRecord | null>;
|
|
57
61
|
getContent(owner: string, id: string): Promise<Uint8Array | null>;
|
|
58
|
-
/**
|
|
59
|
-
|
|
62
|
+
/**
|
|
63
|
+
* 把 ids(请求序)钉到 sessionId。绑定的两条律都在店里判,判与写对这只会话的其它绑定是原子的(不是先读后写):
|
|
64
|
+
* · **一经绑定不再改绑** —— 已绑别的会话的行 ⇒ `bound_elsewhere`(行不动);已绑本会话的行命中、不改 `boundAtMs`;
|
|
65
|
+
* · **一只会话一只种子** —— 一只**未绑的项目包**只在这只会话此刻没有项目包时绑上,否则 ⇒ `bundle_seed_fixed`(同一次调用里请求序靠前的那只先占);
|
|
66
|
+
* 两次并发绑定各带一只新项目包到同一会话 ⇒ 恰一只绑上。
|
|
67
|
+
* 属主门在店里(owner 不匹配 / 不存在的 id 两边都不出现,无 oracle)。`bound` 按请求序、不重复。
|
|
68
|
+
*/
|
|
69
|
+
bind(owner: string, ids: readonly string[], sessionId: string, boundAtMs: number): Promise<AttachmentStoreBindOutcome>;
|
|
60
70
|
/** 物化输入(env 建立时)——按 session 单键查(绑定时已 owner 验过;session uuid 不可枚举)。 */
|
|
61
71
|
listBySession(sessionId: string): Promise<TaskAttachmentRecord[]>;
|
|
62
72
|
delete(owner: string, id: string): Promise<boolean>;
|
|
@@ -89,22 +99,108 @@ export interface AttachmentBytesStore {
|
|
|
89
99
|
* (created_at_ms, id)分配 `attachments/<name>`;同名撞车时后者前缀 id 头 8 位。名字在上传时已
|
|
90
100
|
* 消毒为安全 basename([A-Za-z0-9._-]),拼接后仍是安全相对路径。 */
|
|
91
101
|
export declare function materializedRelPaths(atts: TaskAttachmentRecord[]): Map<string, string>;
|
|
92
|
-
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
102
|
+
/**
|
|
103
|
+
* 提交体 `attachmentIds` 绑定时的拒(闭集;码与状态的唯一映射 = {@link attachmentBindRefusalAnswer})。
|
|
104
|
+
* · `bound_elsewhere` —— 这只附件已绑在别的会话上(一经绑定不再改绑;每个会话各上传一次);
|
|
105
|
+
* · `bundle_lane_not_isolated` —— 项目包只交给 OS 隔离边界内的车道(`laneIsolated`),这条腿的车道不是;
|
|
106
|
+
* · `bundle_seed_fixed` —— 本会话的种子已定(最早绑定的那只项目包),第二只项目包不收。
|
|
107
|
+
*/
|
|
108
|
+
export type AttachmentBindRefusal = {
|
|
109
|
+
readonly kind: "bound_elsewhere";
|
|
110
|
+
readonly id: string;
|
|
111
|
+
} | {
|
|
112
|
+
readonly kind: "bundle_lane_not_isolated";
|
|
113
|
+
readonly id: string;
|
|
114
|
+
readonly lane: string;
|
|
115
|
+
} | {
|
|
116
|
+
readonly kind: "bundle_seed_fixed";
|
|
117
|
+
readonly id: string;
|
|
118
|
+
};
|
|
119
|
+
/** 店在一次绑定里答的拒(闭集的子集:车道那一只是提交侧对这条腿的判,店不判)。 */
|
|
120
|
+
export type AttachmentStoreBindRefusal = Extract<AttachmentBindRefusal, {
|
|
121
|
+
readonly kind: "bound_elsewhere" | "bundle_seed_fixed";
|
|
122
|
+
}>;
|
|
123
|
+
/** 一次 {@link TaskAttachmentStore.bind} 的答:此刻绑在这只会话上的 id(请求序)与店拒掉的 id;两边都不在的 id = 不在 / 他人的。 */
|
|
124
|
+
export interface AttachmentStoreBindOutcome {
|
|
125
|
+
readonly bound: readonly string[];
|
|
126
|
+
readonly refused: readonly AttachmentStoreBindRefusal[];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 进程内的店(local 文件店、测试内存店)的绑定律 —— {@link TaskAttachmentStore.bind} 那一条律在进程内的**唯一**写法:判与改在**同一段同步**里
|
|
130
|
+
* (本函数不 await),于是调用方只要不在本函数前后放开(内存店:整条 `bind` 就是这一段;local 店:`bind` 与店的其它变更口同排一条串行段),
|
|
131
|
+
* 同一进程里两次并发绑定不可能都读到「这只会话还没有种子」(local 契约本就是单进程,`local-task-attachment-store.ts` 头注)。
|
|
132
|
+
* 改的是传进来的 `rows` 里的行对象本身 —— 内存店传自己的索引;需要落盘的店传**副本**,只把落盘落成的那些 `claimed` 行合回自己的索引
|
|
133
|
+
* (`claimed` = 这一次新绑上的行,即副本里被改的那些行对象;索引只反映盘上已有的)。
|
|
134
|
+
*/
|
|
135
|
+
export declare function bindRowsInProcess(rows: ReadonlyMap<string, TaskAttachmentRecord>, owner: string, ids: readonly string[], sessionId: string, boundAtMs: number): {
|
|
136
|
+
readonly outcome: AttachmentStoreBindOutcome;
|
|
137
|
+
readonly claimed: readonly TaskAttachmentRecord[];
|
|
138
|
+
};
|
|
139
|
+
/** 绑定拒的码 → 状态:唯一属主表(一个码在本服务上只有一个状态;`<名>_HTTP_STATUS` 是本仓这类表的名形,码目录门按名形读它的键)。 */
|
|
140
|
+
export declare const ATTACHMENT_BIND_HTTP_STATUS: {
|
|
141
|
+
readonly "attachment.bound_elsewhere": 409;
|
|
142
|
+
readonly "attachment.bundle_lane_not_isolated": 409;
|
|
143
|
+
readonly "attachment.bundle_seed_fixed": 409;
|
|
144
|
+
};
|
|
145
|
+
export type AttachmentBindCode = keyof typeof ATTACHMENT_BIND_HTTP_STATUS;
|
|
146
|
+
/** 一只绑定拒在 wire 上的答(状态取自 {@link ATTACHMENT_BIND_HTTP_STATUS} + 码 + 句 + 体键);穷举 switch —— 闭集多一种而这里没跟 ⇒ 编译红。 */
|
|
147
|
+
export declare function attachmentBindRefusalAnswer(r: AttachmentBindRefusal): {
|
|
148
|
+
readonly status: number;
|
|
149
|
+
readonly errorCode: AttachmentBindCode;
|
|
150
|
+
readonly message: string;
|
|
151
|
+
readonly extra: Record<string, string>;
|
|
152
|
+
};
|
|
153
|
+
/** 会话里**附件**那一类的行(项目包行不在其中)—— 告知的路径分配与物化读的唯一过滤读口:过滤在路径分配之前(种子行不挤动附件的撞名前缀)。 */
|
|
154
|
+
export declare function listAttachmentsBySession(store: TaskAttachmentStore, sessionId: string): Promise<TaskAttachmentRecord[]>;
|
|
155
|
+
/**
|
|
156
|
+
* 会话种子的读口:最早绑定的项目包行 + 它的字节,字节**必核**行上的 `sha256` 与 `sizeBytes`(种子身份 = 上传那一刻的摘要;local 店读侧不核,这里核)。
|
|
157
|
+
* 三种答:没有种子 ⇒ `undefined`;种子在、字节在、摘要对 ⇒ `{ record, bytes }`;店里的定局坏了(行在、字节没了 / 摘要不符)⇒ `{ record, broken }`。
|
|
158
|
+
* 店读本身抛(连接断、超时)⇒ 原样抛:那是一时的失败,不是这只会话的定答 —— 调用方据此分「换一次恢复点重读」与「这只环境此后同一个拒」。
|
|
159
|
+
*/
|
|
160
|
+
export declare function seedOfSession(store: TaskAttachmentStore, sessionId: string): Promise<{
|
|
161
|
+
readonly record: TaskAttachmentRecord;
|
|
162
|
+
readonly bytes: Uint8Array;
|
|
163
|
+
} | {
|
|
164
|
+
readonly record: TaskAttachmentRecord;
|
|
165
|
+
readonly broken: string;
|
|
166
|
+
} | undefined>;
|
|
167
|
+
/**
|
|
168
|
+
* 半场②(提交时绑定 + objective 告知)的可测核。fresh 腿硬验证(未知/他人 id → `onMissing` throw;拒 → `onRefused` throw);
|
|
169
|
+
* resume 腿降级(warn+跳过 —— 4xx 会砖存量 task;记录不可变 ⇒ 在场行的 notice 字节恒稳)。判序:
|
|
170
|
+
* ① 行在不在(属主门);② 已绑别的会话 ⇒ `bound_elsewhere`;③ 项目包 × 车道不隔离 ⇒ `bundle_lane_not_isolated`;
|
|
171
|
+
* ④ 本次提交体里第二只新项目包 ⇒ `bundle_seed_fixed`(只看提交体本身,不读店);
|
|
172
|
+
* ⑤ 绑定 —— 「本会话已有种子」与「不改绑」两条律只在店里判({@link TaskAttachmentStore.bind},判与写原子:并发的两次提交在那里分胜负,这里不先读):
|
|
173
|
+
* 提交体那只新项目包先**单独**绑,店拒了它 ⇒ `onRefused` 在体里其余 id 绑上之前就到(新提交腿上,店拒了种子候选的提交,体里其余 id 一只都不绑;读与绑之间才被别的会话抢走的附件不在此列 —— 先绑上的不回滚,请求仍 409);其余 id 随后一次绑。
|
|
174
|
+
* notice 只罗列本次引用的**附件**(项目包是工作区本身,不进告知),路径从绑定后的全会话附件集推导(与物化共用 {@link materializedRelPaths})。
|
|
175
|
+
*/
|
|
95
176
|
export declare function bindAttachmentsForTask(opts: {
|
|
96
177
|
store: TaskAttachmentStore;
|
|
97
178
|
owner: string;
|
|
98
179
|
ids: string[];
|
|
99
180
|
sessionId: string;
|
|
181
|
+
/** 这条腿的执行车道(执行上下文那一读)与它是否在 OS 隔离边界内(`laneIsolated` 那一判,调用方判一次交进来)。 */
|
|
182
|
+
lane: {
|
|
183
|
+
readonly word: string;
|
|
184
|
+
readonly isolated: boolean;
|
|
185
|
+
};
|
|
100
186
|
leg: "fresh" | "resume";
|
|
101
187
|
nowMs: number;
|
|
102
188
|
onMissing: (id: string) => void | never;
|
|
189
|
+
onRefused: (refusal: AttachmentBindRefusal) => void | never;
|
|
103
190
|
}): Promise<{
|
|
104
191
|
notice: string | undefined;
|
|
105
192
|
}>;
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
193
|
+
/** 会话绑定的附件按工作区收它们的样子:行 + 物化相对路径(与 objective 告知同一份推导 {@link materializedRelPaths})。
|
|
194
|
+
* 两只物化座(没有恢复点的车道 = 执行环境建立时 {@link materializeAttachmentsInto};隔离车道 = 恢复点的填充 `workspace-fill.ts`)读的是这一份。 */
|
|
195
|
+
export interface AttachmentPlacement {
|
|
196
|
+
readonly record: TaskAttachmentRecord;
|
|
197
|
+
readonly relPath: string;
|
|
198
|
+
}
|
|
199
|
+
export declare function attachmentPlacementsOf(store: TaskAttachmentStore, sessionId: string): Promise<AttachmentPlacement[]>;
|
|
200
|
+
/** 一只要放进工作区的附件的字节;读不到 ⇒ 抛(fail-loud:objective 已宣告它在场)。 */
|
|
201
|
+
export declare function attachmentBytesOf(store: TaskAttachmentStore, rec: TaskAttachmentRecord): Promise<Uint8Array>;
|
|
202
|
+
/** 半场③(没有恢复点的车道:env 建立时物化)的可测核。**fail-loud**:objective 已宣告文件在场,静默缺文件=模型按幻影
|
|
203
|
+
* 文件行动,比 env 建立失败更糟 ⇒ 任一写失败即 throw。幂等:重建 env / resume 重写同路径同字节。 */
|
|
108
204
|
export declare function materializeAttachmentsInto(env: {
|
|
109
205
|
createDir(path: string, options?: {
|
|
110
206
|
recursive?: boolean;
|
|
@@ -129,10 +225,10 @@ export declare function ensureTiDBTaskAttachmentSchema(pool: Pool): Promise<void
|
|
|
129
225
|
/** [ref]:本 store 的索引**声明**(两方言共用一份)。PG 侧由下面的 `ensurePgTaskAttachmentSchema` 应用,MySQL 侧由 `tidb-pool.ts` 的中央 `ensureSchema` 应用(那里内联 `KEY` 已在 `CREATE TABLE` 里 ⇒ 新建库探到即零 DDL,存量库缺谁补谁)。加索引以外的 schema 变更仍归运维,见 `plugins/ensure-index.ts` 头注。 */
|
|
130
226
|
export declare const TASK_ATTACHMENT_INDEXES: readonly IndexSpec[];
|
|
131
227
|
export declare function ensurePgTaskAttachmentSchema(q: PgQueryFn): Promise<void>;
|
|
132
|
-
/** bytes → 4 MiB 块序列(共享切块器,两方言 + 完整性校验共用)。 */
|
|
133
228
|
export declare class TiDBTaskAttachmentStore implements TaskAttachmentStore {
|
|
134
229
|
private readonly pool;
|
|
135
230
|
private readonly bytes;
|
|
231
|
+
private readonly db;
|
|
136
232
|
constructor(pool: Pool, bytes: AttachmentBytesStore);
|
|
137
233
|
put(rec: TaskAttachmentRecord & {
|
|
138
234
|
content: Uint8Array;
|
|
@@ -143,7 +239,7 @@ export declare class TiDBTaskAttachmentStore implements TaskAttachmentStore {
|
|
|
143
239
|
* 失败只吞不抛——E21 purge 不被对象存储抖动卡死(blob-backend 异步删裁定同旨);无 meta 的对象
|
|
144
240
|
* 经 API 不可达,彻底清收敛记 follow-up。 */
|
|
145
241
|
private deleteBytesIfUnreferenced;
|
|
146
|
-
bind(owner: string, ids: string[], sessionId: string, boundAtMs: number): Promise<
|
|
242
|
+
bind(owner: string, ids: readonly string[], sessionId: string, boundAtMs: number): Promise<AttachmentStoreBindOutcome>;
|
|
147
243
|
listBySession(sessionId: string): Promise<TaskAttachmentRecord[]>;
|
|
148
244
|
delete(owner: string, id: string): Promise<boolean>;
|
|
149
245
|
deleteBySession(sessionId: string): Promise<number>;
|
|
@@ -160,16 +256,18 @@ export declare class TiDBTaskAttachmentStore implements TaskAttachmentStore {
|
|
|
160
256
|
private filterStillOrphan;
|
|
161
257
|
}
|
|
162
258
|
export declare class PgTaskAttachmentStore implements TaskAttachmentStore {
|
|
163
|
-
private readonly q;
|
|
164
259
|
private readonly bytes;
|
|
165
|
-
|
|
260
|
+
private readonly db;
|
|
261
|
+
private readonly q;
|
|
262
|
+
/** 收池(不是裸 `PgQueryFn`):绑定的种子腿要一只事务({@link bindOnSql}),事务要一条借出的连接。 */
|
|
263
|
+
constructor(pool: PgPool, bytes: AttachmentBytesStore);
|
|
166
264
|
put(rec: TaskAttachmentRecord & {
|
|
167
265
|
content: Uint8Array;
|
|
168
266
|
}): Promise<void>;
|
|
169
267
|
get(owner: string, id: string): Promise<TaskAttachmentRecord | null>;
|
|
170
268
|
getContent(owner: string, id: string): Promise<Uint8Array | null>;
|
|
171
269
|
private deleteBytesIfUnreferenced;
|
|
172
|
-
bind(owner: string, ids: string[], sessionId: string, boundAtMs: number): Promise<
|
|
270
|
+
bind(owner: string, ids: readonly string[], sessionId: string, boundAtMs: number): Promise<AttachmentStoreBindOutcome>;
|
|
173
271
|
listBySession(sessionId: string): Promise<TaskAttachmentRecord[]>;
|
|
174
272
|
delete(owner: string, id: string): Promise<boolean>;
|
|
175
273
|
deleteBySession(sessionId: string): Promise<number>;
|