@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
package/dist/http/server.d.ts
CHANGED
|
@@ -464,7 +464,8 @@ export interface ServiceSeamDeps {
|
|
|
464
464
|
}>;
|
|
465
465
|
/** 142-S5 §1.4: per-scope memory export reader (main.ts wires `exportMemoryScope` over the DB memory
|
|
466
466
|
* backend). Enables GET /v1/memory/export — present ONLY on a DB memory plane (MEMORY_ENGINE_BACKEND=
|
|
467
|
-
* pg|mysql); absent
|
|
467
|
+
* pg|mysql); absent ⇒ 501 (single-user file memory exports by copying the memory dir). The gate and the capability bit
|
|
468
|
+
* `memoryExport` read ONE fact (`capability-facts.ts` `memoryExport`) — whether this face is wired, never the backend name. */
|
|
468
469
|
memoryExport?: (scope: string) => Promise<MemoryEntry[]>;
|
|
469
470
|
/**
|
|
470
471
|
* [ref](core 5.57.0 `routePairingStatus`)—— **key↔URL 配对读面**:`GET /v1/models` 每行的
|
|
@@ -480,8 +481,10 @@ export interface ServiceSeamDeps {
|
|
|
480
481
|
/** 142-S2.5(设计 §2.5;裁定 plan 随数据走,core 零运行时半场):one full sync round for one
|
|
481
482
|
* (scope, peer) — main.ts wires `performMemorySync` over the DB memory backend + the sync-cursor
|
|
482
483
|
* store. Enables POST /v1/memory/sync/:scope — present ONLY on a DB memory plane
|
|
483
|
-
* (MEMORY_ENGINE_BACKEND=pg|mysql); absent
|
|
484
|
-
* IS the TOC side of a sync — it has no central-authority half to serve).
|
|
484
|
+
* (MEMORY_ENGINE_BACKEND=pg|mysql); absent ⇒ 501 honestly (a single-user file plane
|
|
485
|
+
* IS the TOC side of a sync — it has no central-authority half to serve). [ref]: the face also needs the center's
|
|
486
|
+
* declared sync identity (`config.memorySyncServeIdentity`) — the gate and the capability bit `memorySyncServe` read ONE
|
|
487
|
+
* fact (`capability-facts.ts` `memorySyncServe`). */
|
|
485
488
|
memorySync?: (scope: string, syncReq: MemorySyncRequest) => Promise<MemorySyncResponse>;
|
|
486
489
|
/** [ref] v2-c(core 5.40.0 `MemoryEngine.exportMemoryScopes`):治理完备包的**携出**面。main.ts 把它接到
|
|
487
490
|
* 装配好的 memory engine 上。启用 `POST /v1/memory/export`(operator-only)。
|
|
@@ -538,8 +541,10 @@ export interface ServiceSeamDeps {
|
|
|
538
541
|
* [ref] —— 本**部署**的记忆同步腿(客户端半场):`MEMORY_SYNC_URL` 设了且 file 记忆引擎亮着、执行器起了才在场(`boot/stores.ts`)。
|
|
539
542
|
* `GET /v1/sessions/:id/memory-status` 的 `memorySync` 格只读它的 `parked()` —— 停驻事实的唯一读口(写者 = 执行器的轮,运维行 `memory_sync_layer_parked` 读同一份);
|
|
540
543
|
* 缺席 ⇒ 那一格缺席(没有同步腿)。与 `memoryAutoConsolidationArmed` 同类:部署事实,不随会话、不随层座。
|
|
544
|
+
* [ref]:会话记忆同步触发口(`POST /v1/sessions/:id/memory/sync`)读它的 `peer`(目标比对)与 `syncOnce`(一轮,交回每层每次调用;从不 reject)——
|
|
545
|
+
* 能力事实 `sessionMemorySync` 的第一格就是它在不在。
|
|
541
546
|
*/
|
|
542
|
-
memorySyncLeg?: Pick<MemorySyncRunner, "parked">;
|
|
547
|
+
memorySyncLeg?: Pick<MemorySyncRunner, "parked" | "peer" | "syncOnce">;
|
|
543
548
|
/**
|
|
544
549
|
* [ref] / [ref] + 车PI 修复轮 ④ —— 记忆整理阀门的**生效判决**(boot 第 4 段 `createConsolidationValve`:显式词,或出厂缺省按
|
|
545
550
|
* 装配结果派生)。配置目录那一行(`GET /v1/config/catalog` 的 `MEMORY_CONSOLIDATION_DRIVER` `effectiveValue`)读它 —— 那一列的契约是
|
|
@@ -698,10 +703,11 @@ export interface ServiceDeploymentDeps {
|
|
|
698
703
|
/** [ref] ②:caps `centerWiring` 的读面(`capabilities/center-wiring.ts` `createCenterWiringReader`;入参 = 请求的已验 principal)。
|
|
699
704
|
* 缺席 = 没接中心(或测试夹具)⇒ 键整个缺席。 */
|
|
700
705
|
centerWiring?: (principal: string | undefined) => import("../capabilities/center-wiring.js").CenterWiring;
|
|
701
|
-
/** The member git-credential plane's
|
|
702
|
-
* seat answer
|
|
703
|
-
*
|
|
704
|
-
|
|
706
|
+
/** The member git-credential plane's read function (`sandbox-git-plane.ts` `createSandboxGitPlaneSeat`'s `now`: the judge over the boot readings — the
|
|
707
|
+
* secret seat answer stage 5 settles, the static patch judge — and the live config, whose `sandboxGit` block hot-swaps with each effective-config commit).
|
|
708
|
+
* Caps `credentialPlane.sandboxGit` calls it on every read and projects the answer as is. Absent = not built by the composition root (a fixture) ⇒
|
|
709
|
+
* the same judge on config alone, the seat counted as not asked. */
|
|
710
|
+
sandboxGitPlane?: () => import("../sandbox-git-plane.js").SandboxGitPlane;
|
|
705
711
|
/**
|
|
706
712
|
* 本进程起服装上的**运维声明** mod 包数(部署座装配口 `assembleDeploymentMods` 的 `loaded.length`;本服务自铸的注册不算)。一个事实两个出口,同读这一格:
|
|
707
713
|
* `/health` 的 `mods: { deploymentPackages }` 与 `/v1/capabilities` 的 `modsDeploymentPackages: true` —— 都只在 > 0 时在场;0 / 缺席(没声明,或不是由
|
package/dist/http/server.js
CHANGED
|
@@ -18,7 +18,7 @@ import { cascadeConfig } from "./run-meta.js";
|
|
|
18
18
|
export { cascadeConfig };
|
|
19
19
|
import { coarseStatusForState, errorCodeForExit } from "./routes/images.js";
|
|
20
20
|
export { coarseStatusForState, errorCodeForExit };
|
|
21
|
-
import { sendError, sendHttpError,
|
|
21
|
+
import { sendError, sendHttpError, sendOpenFaceFault, logRequestError } from "./send.js";
|
|
22
22
|
import { createRequestGuards } from "./request-guards.js";
|
|
23
23
|
import { sqlEngineSeatIssue } from "../sql-engine-posture.js";
|
|
24
24
|
import { parkDecisionStoreRefusal } from "../orchestration/workflow-park-decision.js";
|
|
@@ -169,13 +169,11 @@ export function createHttpServer(rawDeps) {
|
|
|
169
169
|
res.end();
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
172
|
-
if (sendSessionError(res, err, "open"))
|
|
173
|
-
return;
|
|
174
172
|
if (err instanceof SessionForkInProgressError) {
|
|
175
173
|
sendError(res, 409, err.code, err.message);
|
|
176
174
|
return;
|
|
177
175
|
}
|
|
178
|
-
|
|
176
|
+
sendOpenFaceFault(res, err);
|
|
179
177
|
});
|
|
180
178
|
});
|
|
181
179
|
const deviceLane = facts.deviceLane(deps);
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
*
|
|
19
19
|
* ## 回执两形(按提交腿)
|
|
20
20
|
* · `durable`(`POST /v1/runs`、A2A `message/send`)—— `202 { taskId, sessionId, status }`(这条腿首答的既有形);
|
|
21
|
-
* · `sync`(`POST /v1/tasks`;`/v1/tasks/stream` 把同一份体放进 `done.result`)—— `200 {
|
|
22
|
-
*
|
|
23
|
-
* `
|
|
21
|
+
* · `sync`(`POST /v1/tasks`;`/v1/tasks/stream` 把同一份体放进 `done.result`)—— `200 { …首答的信封, replay: true }`:
|
|
22
|
+
* 首答的信封由 `http/sync-submit-body.ts` 铸(`buildSyncSubmitBody`),重放经同文件的 `syncSubmitBodyOfRow` 读行 ——
|
|
23
|
+
* 终局 / 停驻行上有结果 ⇒ 与首答同形(结果的键在顶层,`result` 是串);行上没有结果可交(首投还在跑)⇒ 轮询回执 `{ taskId, sessionId, status }`。
|
|
24
|
+
* `replay: true` 是重放的判别位(本函数叠上,不在信封里)。
|
|
24
25
|
*/
|
|
25
26
|
import type { RunIdemBinding, RunRecord } from "../plugins/store-contracts.js";
|
|
26
27
|
import type { RunStore } from "../plugins/store-backend.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { runRowReadable } from "./owner-access.js";
|
|
2
|
-
import {
|
|
2
|
+
import { syncSubmitBodyOfRow } from "./sync-submit-body.js";
|
|
3
3
|
import { refusalBody } from "./refusal-body.js";
|
|
4
4
|
export function isRunExistsConflictBody(body) {
|
|
5
5
|
return typeof body === "object" && body !== null && body.errorCode === "conflict.run_exists";
|
|
@@ -19,7 +19,7 @@ export async function replayOfExistingRun(row, caller, face, deps, runStore) {
|
|
|
19
19
|
case "durable":
|
|
20
20
|
return { status: 202, body: { taskId: row.taskId, sessionId: row.sessionId, status: row.status } };
|
|
21
21
|
case "sync":
|
|
22
|
-
return { status: 200, body: { ...(
|
|
22
|
+
return { status: 200, body: { ...syncSubmitBodyOfRow(deps, row), replay: true } };
|
|
23
23
|
default: {
|
|
24
24
|
const unknown = face;
|
|
25
25
|
throw new Error(`replayOfExistingRun: unknown submit face ${String(unknown)}`);
|
|
@@ -25,14 +25,19 @@
|
|
|
25
25
|
* (`observability/run-terminal.ts`),调用方铸一次交进来;本函数只负责「体长什么样」。凭据(`paused.token` /
|
|
26
26
|
* `failed.nestedPause.token`)因此在进本函数之前就已摘掉,幂等缓存存的正是本函数的产物,重放体同体同脱。
|
|
27
27
|
*/
|
|
28
|
-
import type
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
import { type ParkedRunStatus, type PersistedTaskResult, type RunRecord } from "../plugins/store-contracts.js";
|
|
29
|
+
import { type RunFaceDeps } from "./run-read-face.js";
|
|
30
|
+
/** 同步 200 体上的 poll 回执三键:一条 run 的 poll 键、会话、对外词。park 体叠在结果之上带它({@link SyncParkReceipt});重放命中一条还没有结果可交的行时,它**就是**整个体({@link syncSubmitBodyOfRow})。 */
|
|
31
|
+
export interface SyncRunReceipt {
|
|
31
32
|
/** 同步腿提交时铸的 durable run id —— `GET /v1/runs/:taskId` 的 poll 键。 */
|
|
32
33
|
taskId: string;
|
|
33
34
|
/** 提交的会话。 */
|
|
34
35
|
sessionId: string;
|
|
35
|
-
/** 对外词 = `GET /v1/runs/:id` 行上的 `status
|
|
36
|
+
/** 对外词 = `GET /v1/runs/:id` 行上的 `status`。 */
|
|
37
|
+
status: RunRecord["status"];
|
|
38
|
+
}
|
|
39
|
+
/** park 中的 run 在同步 200 体上带的 poll 回执三键(修前 park 体的全部内容,值逐字不变);`status` 是 park 两词之一(闭集从 `RUN_STATUS_CLASS` 派生)。 */
|
|
40
|
+
export interface SyncParkReceipt extends SyncRunReceipt {
|
|
36
41
|
status: ParkedRunStatus;
|
|
37
42
|
}
|
|
38
43
|
/**
|
|
@@ -40,4 +45,16 @@ export interface SyncParkReceipt {
|
|
|
40
45
|
* 返回的是纯数据(无方法、无闭包)⇒ `build*`。
|
|
41
46
|
*/
|
|
42
47
|
export declare function buildSyncSubmitBody<R extends PersistedTaskResult>(exitResult: R, parkReceipt?: SyncParkReceipt): R | (R & SyncParkReceipt);
|
|
48
|
+
/**
|
|
49
|
+
* **同键重放读行的出口** —— 同一条 run 的同步 200 体,首答与重放读**同一只信封**:终局行 / 停驻行上有结果 ⇒ 经 {@link buildSyncSubmitBody} 铸
|
|
50
|
+
* (终局 = 结果本身;停驻 = 结果 + 回执三键),与首答的三个 200 出口逐字同形 —— 首答恒 = 「出口封口 + 脱敏后的引擎结果」,那份对象就是落在行上的那一份
|
|
51
|
+
* (调用方铸一次、既落行又进体),重放读到的是它被读入口(同一只 {@link runRowExitResult},`GET /v1/runs/:id` 的 `result` 读的也是它)再封再脱的样子。
|
|
52
|
+
* 行上没有结果可交(首投还在跑、在别的副本上没落定;或终局行的结果已被收割清空)⇒ 首答在这一刻**不存在**,体只剩 {@link SyncRunReceipt} 三键 —— 去 `GET /v1/runs/:taskId` 轮询,
|
|
53
|
+
* 不把「读面」塞进同步体里充首答(失联折叠与读面同一只判据 {@link runRowStale}:折成 `failed`)。
|
|
54
|
+
*
|
|
55
|
+
* 首答体上另有两类**本次请求**的附注(`notice` / `warnings`,见 `routes/tasks.ts` 的 `withSyncSubmitKeys`):重放在 `prepareSpec` 之前就答了,
|
|
56
|
+
* 结构上不知道本次请求带没带 verify / cascade、放行侧选择器里有没有空条目 ⇒ 重放体没有它们。判别位 `replay: true` 由重放出口(`submit-replay.ts`)叠上,不在信封里。
|
|
57
|
+
* 纯数据 ⇒ `build*` 的近亲;名字取 `…OfRow`(行 → 体的读法),与 `buildSyncSubmitBody` 同文件同铸点。
|
|
58
|
+
*/
|
|
59
|
+
export declare function syncSubmitBodyOfRow(deps: RunFaceDeps, row: RunRecord): ReturnType<typeof buildSyncSubmitBody> | SyncRunReceipt;
|
|
43
60
|
//# sourceMappingURL=sync-submit-body.d.ts.map
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
import { isParkedRunStatus, isTerminalRunStatus } from "../plugins/store-contracts.js";
|
|
2
|
+
import { runRowExitResult, runRowStale } from "./run-read-face.js";
|
|
1
3
|
export function buildSyncSubmitBody(exitResult, parkReceipt) {
|
|
2
4
|
return parkReceipt === undefined ? exitResult : { ...exitResult, ...parkReceipt };
|
|
3
5
|
}
|
|
6
|
+
export function syncSubmitBodyOfRow(deps, row) {
|
|
7
|
+
const stale = runRowStale(deps, row);
|
|
8
|
+
const exit = stale ? undefined : runRowExitResult(row);
|
|
9
|
+
if (exit !== undefined) {
|
|
10
|
+
if (isTerminalRunStatus(row.status))
|
|
11
|
+
return buildSyncSubmitBody(exit);
|
|
12
|
+
if (isParkedRunStatus(row.status))
|
|
13
|
+
return buildSyncSubmitBody(exit, { taskId: row.taskId, sessionId: row.sessionId, status: row.status });
|
|
14
|
+
}
|
|
15
|
+
return { taskId: row.taskId, sessionId: row.sessionId, status: stale ? "failed" : row.status };
|
|
16
|
+
}
|
|
4
17
|
//# sourceMappingURL=sync-submit-body.js.map
|
|
@@ -12,9 +12,13 @@
|
|
|
12
12
|
* 3. **缺席一种读法**:`null` = 这一行**没有「属主在受理时验过的凭据」的记录** —— 三列存在之前写下的行、没有任何验过凭据的受理(无鉴权写面
|
|
13
13
|
* `ALLOW_UNAUTHED_WRITES`)、以及别人替属主按下的那一跑(受理请求验过的主体不是这一行的属主:唤醒 / 决定投递铸的新一跑)。读者一律按「不合格」处置
|
|
14
14
|
* (fail-closed),不分辨三种成因;存量行不回填(没有事实可回填)。
|
|
15
|
+
* 4. **令牌声明装不进它的列就不记**(不截断:截断 = 两个不同的值折成一个):`sso` 臂从验过的令牌铸元组时,某一格装不进它的列宽({@link RUN_IDENTITY_CLAIM_COLUMNS})
|
|
16
|
+
* ⇒ 那一格「未记录」—— 可空的格(化身号)记 `null`;必填的格(scope)装不下就铸不出合形元组 ⇒ 无元组。两格结果不同来自元组的型,不是两条规则:
|
|
17
|
+
* 同一只判({@link runIdentityClaimOverflow})、同一行告警 `run_identity_claim_unrecorded { claim, chars, max }`(凭据门的铸点发;只名与长度,不带值)。
|
|
15
18
|
*
|
|
16
|
-
* 化身号 `principalInc`:令牌 `sub_inc`
|
|
17
|
-
*
|
|
19
|
+
* 化身号 `principalInc`:令牌 `sub_inc` 声明原样(契约包 `VerifiedIdentity.principalInc`)。来源链只有一条 —— 验证器读出 → 注入边界再判形(`auth-bridge.ts` 的
|
|
20
|
+
* `admitBridgeIdentity`)→ 入口预认证挂 request-local(`security.ts` 的 `setSsoPrincipalInc`)→ 凭据门 `admitRunIdentity` 的 `sso` 臂铸进元组(律 4)→ 建行原样写入。
|
|
21
|
+
* `null` 化身号只有一种读法:**未记录**(契约 §12.8「缺 ⇒ 化身号未记录」)—— 存量行、令牌没带、装不下,三种成因同一读法。
|
|
18
22
|
*/
|
|
19
23
|
/** 验过的身份来源的**闭集**(序 = 判定次序 = `/health.auth.kinds` 的出现序;加一个来源 = 加一个词,读它的各只穷举 `switch` 不接就编译红)。 */
|
|
20
24
|
export declare const IDENTITY_SOURCES: readonly ["service_token", "sso", "direct_door_jwt"];
|
|
@@ -36,6 +40,21 @@ export type RunIdentity = {
|
|
|
36
40
|
export declare const RUN_IDENTITY_SCOPE_MAX_CHARS = 190;
|
|
37
41
|
/** `identity_principal_inc` 的列宽(字符):化身号是签发方插入时铸的不透明随机串(与会话化身同宽)。 */
|
|
38
42
|
export declare const RUN_IDENTITY_PRINCIPAL_INC_MAX_CHARS = 64;
|
|
43
|
+
/** 令牌声明 → 它落的那一列的列宽(字符;律 4 的判读这一张)。 */
|
|
44
|
+
export declare const RUN_IDENTITY_CLAIM_COLUMNS: {
|
|
45
|
+
readonly scope: 190;
|
|
46
|
+
readonly principalInc: 64;
|
|
47
|
+
};
|
|
48
|
+
export type RunIdentityClaim = keyof typeof RUN_IDENTITY_CLAIM_COLUMNS;
|
|
49
|
+
/**
|
|
50
|
+
* 律 4 的判:这一格令牌声明装不装得进它的列(按码点数,与两方言 VARCHAR 同口径)。装得下 ⇒ `undefined`;装不下 ⇒ 告警行 `run_identity_claim_unrecorded` 的载荷
|
|
51
|
+
* (`claim` 名 + 码点数 + 列宽,不带值)。调用方按格的型处置:可空格记 `null`,必填格无元组。
|
|
52
|
+
*/
|
|
53
|
+
export declare function runIdentityClaimOverflow(claim: RunIdentityClaim, value: string): {
|
|
54
|
+
readonly claim: RunIdentityClaim;
|
|
55
|
+
readonly chars: number;
|
|
56
|
+
readonly max: number;
|
|
57
|
+
} | undefined;
|
|
39
58
|
/**
|
|
40
59
|
* `task_run` 三列的列定义 —— 两方言各一段,**唯一属主**:中央建表语句(`tidb-pool.ts` / `pg-pool.ts` 的 `CREATE TABLE task_run`)内插这同一段;
|
|
41
60
|
* 存量库缺列时起服列册对账(`schema-consistency.ts`)拒启,拒句里的 `ALTER TABLE task_run ADD COLUMN …` 由同一份建表语句解析生成 ⇒
|
package/dist/identity-source.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export const IDENTITY_SOURCES = ["service_token", "sso", "direct_door_jwt"];
|
|
2
2
|
export const RUN_IDENTITY_SCOPE_MAX_CHARS = 190;
|
|
3
3
|
export const RUN_IDENTITY_PRINCIPAL_INC_MAX_CHARS = 64;
|
|
4
|
+
export const RUN_IDENTITY_CLAIM_COLUMNS = { scope: RUN_IDENTITY_SCOPE_MAX_CHARS, principalInc: RUN_IDENTITY_PRINCIPAL_INC_MAX_CHARS };
|
|
5
|
+
export function runIdentityClaimOverflow(claim, value) {
|
|
6
|
+
const chars = [...value].length;
|
|
7
|
+
const max = RUN_IDENTITY_CLAIM_COLUMNS[claim];
|
|
8
|
+
return chars > max ? { claim, chars, max } : undefined;
|
|
9
|
+
}
|
|
4
10
|
export const RUN_IDENTITY_COLUMNS = {
|
|
5
11
|
mysql: [
|
|
6
12
|
"identity_source VARCHAR(32) NULL",
|
|
@@ -14,14 +14,17 @@
|
|
|
14
14
|
* consolidation),run-local 腿把整族印成 `engine_error` 并用 `String(err)` 抹掉 `err.code`。
|
|
15
15
|
*
|
|
16
16
|
* ## 形(一处定义,四处引用)
|
|
17
|
-
* `memory_engine_incident` + `{ sessionId?, code
|
|
17
|
+
* `memory_engine_incident` + `{ sessionId?, code?, detail }`,并计 `memory_engine_incident_total{code?}`。
|
|
18
18
|
* 🔴 `code` 是**结构化字段**而不是被折进散文:它是运维唯一能按名告警的那一位,而这一族里最要紧的几条
|
|
19
|
-
* (一次性布局搬家、无主根内容、控制面写失败)一辈子只发生一次 ——
|
|
20
|
-
*
|
|
19
|
+
* (一次性布局搬家、无主根内容、控制面写失败)一辈子只发生一次 —— 折平就等于没说。
|
|
20
|
+
* 码的读法与 `boot/runner-deps.ts` 的 `onError` 汇点其余七条行**同一只**(`terminal.ts` 的 `codeFieldOf`:不抛的读口 + 码判官 `isCodeToken`):是码才带;
|
|
21
|
+
* 无码(core 里有大量不带 `.code` 的记忆相 Error)、不是串 / 空串、非 `Error` 抛出物、读取时抛、或带的不是码(超长 / 含空白 / 凭据形 —— 引擎错上的 `code` 是别人写的串)⇒ 行里**没有** `code` 键、
|
|
22
|
+
* 计数**没有** `code` 标签:不编码,也不写 `null` / `"none"` 之类的占位。
|
|
21
23
|
*
|
|
22
|
-
* ⚠️
|
|
23
|
-
* `memory_consolidation_incident`)
|
|
24
|
-
*
|
|
24
|
+
* ⚠️ 与三只 **operator 面**引擎座(`memory_compliance_incident` / `memory_origin_incident` /
|
|
25
|
+
* `memory_consolidation_incident`)**事件名刻意不合并**:那三条的事件名回答「哪一张操作面」,是三个产品面的
|
|
26
|
+
* 分辨位;本工厂回答「引擎事故席」,是同一件事的四个落点。合并会丢掉前者的分辨位。三座有自己的工厂
|
|
27
|
+
* {@link createOperatorFaceIncidentSink}(事件名由装配处点名),`code` 位与本工厂读同一只 `codeFieldOf`。
|
|
25
28
|
*/
|
|
26
29
|
import type { Logger } from "./observability/logger.js";
|
|
27
30
|
import type { Metrics } from "./observability/metrics.js";
|
|
@@ -34,4 +37,14 @@ export interface MemoryEngineIncidentSinkDeps {
|
|
|
34
37
|
export declare function createMemoryEngineIncidentSink(deps: MemoryEngineIncidentSinkDeps): (err: unknown, ctx?: {
|
|
35
38
|
sessionId?: string;
|
|
36
39
|
}) => void;
|
|
40
|
+
/** 三只 operator 面引擎座的事件名(闭集):事件名回答「哪一张操作面」—— 出处 / 抹除合规 · 外源标记人面 · 整理三面,由装配处点名。 */
|
|
41
|
+
export type OperatorFaceIncidentEvent = "memory_compliance_incident" | "memory_origin_incident" | "memory_consolidation_incident";
|
|
42
|
+
/**
|
|
43
|
+
* operator 面引擎座(各面工厂的 `onIncident` 形参:`(err: Error & { code?: string }) => void`)的唯一落点形:`<事件名>` + `{ code?, detail }`。
|
|
44
|
+
* 这三座此前各在装配处写一份 `{ code: err.code ?? null, detail }` —— 引擎错上的 `code` 是别人写的串,什么都照写(凭据形也进行),无码写 `null` 占位;
|
|
45
|
+
* 现在 `code` 位读 `codeFieldOf`(与 `onError` 汇点其余各行、{@link createMemoryEngineIncidentSink} 同一只):是码才带,其余键缺席;`detail` 句只经运维文本座。
|
|
46
|
+
*/
|
|
47
|
+
export declare function createOperatorFaceIncidentSink(deps: Pick<MemoryEngineIncidentSinkDeps, "logger">, event: OperatorFaceIncidentEvent): (err: Error & {
|
|
48
|
+
code?: string;
|
|
49
|
+
}) => void;
|
|
37
50
|
//# sourceMappingURL=memory-incident-sink.d.ts.map
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { redactIncarnations } from "./session-incarnation.js";
|
|
2
|
+
import { codeFieldOf } from "./terminal.js";
|
|
3
|
+
import { seatText } from "./trace/exit-seat.js";
|
|
2
4
|
export function createMemoryEngineIncidentSink(deps) {
|
|
3
5
|
return (err, ctx) => {
|
|
4
|
-
const
|
|
5
|
-
const codeText = typeof code === "string" && code !== "" ? code : undefined;
|
|
6
|
+
const codeField = codeFieldOf(err);
|
|
6
7
|
deps.logger.warn("memory_engine_incident", {
|
|
7
8
|
...(ctx?.sessionId !== undefined ? { sessionId: ctx.sessionId } : {}),
|
|
8
|
-
|
|
9
|
+
...codeField,
|
|
9
10
|
detail: redactIncarnations(String(err)),
|
|
10
11
|
});
|
|
11
|
-
deps.metrics?.inc("memory_engine_incident_total", {
|
|
12
|
+
deps.metrics?.inc("memory_engine_incident_total", { ...codeField });
|
|
12
13
|
};
|
|
13
14
|
}
|
|
15
|
+
export function createOperatorFaceIncidentSink(deps, event) {
|
|
16
|
+
return (err) => deps.logger.warn(event, { ...codeFieldOf(err), detail: seatText("operator-log-text", err.message) });
|
|
17
|
+
}
|
|
14
18
|
//# sourceMappingURL=memory-incident-sink.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type memorySyncReportOf, type MemorySyncTransport } from "@sema-agent/core";
|
|
2
2
|
import type { ServiceConfig } from "./config-types.js";
|
|
3
3
|
import type { MemoryScopeStores } from "./memory-scope-stores.js";
|
|
4
4
|
import { type OneTimeMigrationWindow } from "./plugins/one-time-migrations.js";
|
|
@@ -8,15 +8,9 @@ export interface MemorySyncLog {
|
|
|
8
8
|
warn(msg: string, fields?: Record<string, unknown>): void;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* 不带 userinfo / query / fragment;拒句只点名规则、值只到 origin);
|
|
15
|
-
* ② 身份里有没有凭据 —— 判官是引擎包根的 `carriesCredential`(引擎同步客户端判 `transport.peer` 的同一只:任何 userinfo,或同主体出口脱敏会遮掉的任何一段,
|
|
16
|
-
* 例如路径段里的令牌形),判的就是规范化之后的身份 —— 引擎第一轮判的那个值。身份随每个请求体与落盘游标走,所以带凭据的地址拒而不清洗
|
|
17
|
-
* (清洗过的身份是另一个身份;令牌只走 `MEMORY_SYNC_TOKEN`);拒句不引地址的任何部分。
|
|
18
|
-
* 规范化 = WHATWG URL 的 `origin`(协议与主机小写、缺省端口省掉)+ 路径去尾斜杠;规范化之后装不进中心侧 `peer` 列({@link MEMORY_SYNC_PEER_COLUMN_CHARS},
|
|
19
|
-
* 每一轮都会被中心拒)⇒ 同样拒。
|
|
11
|
+
* 客户端腿的同步地址 `MEMORY_SYNC_URL` → 同步对端的身份(规范化基址)。判与铸只在 `memory-sync-identity.ts` 的唯一铸点({@link readMemorySyncIdentity});
|
|
12
|
+
* 本函数是那只铸点的「客户端腿」座位:值可以不是规范形(铸成规范形用),有缺陷 ⇒ 抛(拒句不引地址的路径与凭据)。`config.ts` 读 env 时调它拒启(引擎关着也拒),
|
|
13
|
+
* transport 建时再调它铸身份 —— 同一只纯函数,同一个答案。
|
|
20
14
|
*/
|
|
21
15
|
export declare function memorySyncPeerOf(url: string): string;
|
|
22
16
|
/**
|
|
@@ -75,20 +69,26 @@ export declare function relocateSyncCursorsM31(memoryEngineDir: string, log: Mem
|
|
|
75
69
|
readonly today: string;
|
|
76
70
|
readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
|
|
77
71
|
}): Promise<SyncCursorRelocationM31Report>;
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
/**
|
|
73
|
+
* 一层的一次调用如它落定的那样:`{ scope, result }`(`syncMemoryScope` 兑现)或 `{ scope, thrown }`(它或本服务在这一层上抛的)。
|
|
74
|
+
* 型**从引擎报告的入参派生**(`memorySyncReportOf` 的 `layers` 元素;引擎没有单独导出这只型)—— 本服务不手抄一份。
|
|
75
|
+
*/
|
|
76
|
+
export type MemorySyncLayerCall = Extract<Parameters<typeof memorySyncReportOf>[0], {
|
|
77
|
+
readonly layers: unknown;
|
|
78
|
+
}>["layers"][number];
|
|
79
|
+
/** 一轮:这一轮每一层的每一次调用,按跑的顺序(同步层在前、项目层按键;一层续轮 = 多条;一层失败 = 那一层最后一条是 `thrown`)。 */
|
|
84
80
|
export interface MemorySyncRound {
|
|
85
|
-
readonly
|
|
81
|
+
readonly calls: readonly MemorySyncLayerCall[];
|
|
86
82
|
}
|
|
87
83
|
export interface MemorySyncRunner {
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
/** 这一腿的对端身份(transport 的 `peer`;会话触发口拿请求点名的目标与它比)。 */
|
|
85
|
+
readonly peer: string;
|
|
86
|
+
/** 一轮同步(串行:排在上一轮之后),逐层一轮;**从不 reject**(⑧):一层失败那一层以 `{ scope, thrown }` 收尾、别的层照跑,逐层失败各一行运维 warn。
|
|
87
|
+
* `reading`:会话触发的读集 —— 里面被本腿覆盖的项目层并进这一轮(并登记进跟踪表);不被覆盖的层不进轮。 */
|
|
88
|
+
syncOnce(reason: string, opts?: {
|
|
89
|
+
readonly reading?: readonly string[];
|
|
90
|
+
}): Promise<MemorySyncRound>;
|
|
91
|
+
/** fire-and-forget 一轮:上一轮在飞(排队/执行中)则跳过(inflight 节流)。 */
|
|
92
92
|
trigger(reason: string): void;
|
|
93
93
|
/** 跟踪一只本层属主的项目层(本地还没有条目也每轮拉它);首次跟踪即排一轮。不是本同步腿覆盖的项目层 ⇒ 抛(调用方的错)。 */
|
|
94
94
|
track(scope: string): void;
|
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { mkdir, readFile, readdir, rename, rmdir, stat, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { syncMemoryScope, } from "@sema-agent/core";
|
|
5
5
|
import { replicationCovers } from "./memory-scope.js";
|
|
6
6
|
import { parseProjectLayerKey } from "./project-layer-key.js";
|
|
7
|
-
import { MEMORY_SYNC_PEER_COLUMN_CHARS } from "./plugins/memory-key-guards.js";
|
|
8
7
|
import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./plugins/one-time-migrations.js";
|
|
9
|
-
import {
|
|
8
|
+
import { buildMemorySyncIdentityBootRefusal, readMemorySyncIdentity } from "./memory-sync-identity.js";
|
|
10
9
|
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
10
|
+
import { errorText } from "./observability/err-text.js";
|
|
11
11
|
export function memorySyncPeerOf(url) {
|
|
12
|
-
const
|
|
13
|
-
if (
|
|
12
|
+
const r = readMemorySyncIdentity(url);
|
|
13
|
+
if ("flaw" in r) {
|
|
14
|
+
const refusal = buildMemorySyncIdentityBootRefusal("MEMORY_SYNC_URL", url, r.flaw, "put the token in MEMORY_SYNC_TOKEN and set MEMORY_SYNC_URL to the central base address itself");
|
|
14
15
|
throw new Error(refusal);
|
|
15
|
-
const u = new URL(url);
|
|
16
|
-
const identity = `${u.origin}${u.pathname.replace(/\/+$/, "")}`;
|
|
17
|
-
if (carriesCredential(identity)) {
|
|
18
|
-
throw new Error("memory sync: MEMORY_SYNC_URL holds a credential (a token-shaped part of its path) — the sync peer identity travels verbatim in every request body and " +
|
|
19
|
-
"cursor, so it is refused, never cleaned: put the token in MEMORY_SYNC_TOKEN and set MEMORY_SYNC_URL to the central base address itself");
|
|
20
16
|
}
|
|
21
|
-
|
|
22
|
-
throw new Error(`memory sync: MEMORY_SYNC_URL normalizes to ${identity.length} characters — the peer identity is the central request's peer, which holds at most ${MEMORY_SYNC_PEER_COLUMN_CHARS}: use a shorter base URL`);
|
|
23
|
-
}
|
|
24
|
-
return identity;
|
|
17
|
+
return r.identity;
|
|
25
18
|
}
|
|
26
19
|
export function buildMemorySyncEnabledFields(peer, scope, cursorPath) {
|
|
27
20
|
return { peer: buildDisplaySafeUrl(peer), scope, cursorPath };
|
|
@@ -123,7 +116,7 @@ async function loadLayerBaseline(path, scope) {
|
|
|
123
116
|
catch (err) {
|
|
124
117
|
if (err.code === "ENOENT")
|
|
125
118
|
return { kind: "none" };
|
|
126
|
-
throw new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — cannot read the cursor file ${path} (${
|
|
119
|
+
throw new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — cannot read the cursor file ${path} (${errorText(err)}); the next round reads it again once it is readable`);
|
|
127
120
|
}
|
|
128
121
|
let parsed;
|
|
129
122
|
try {
|
|
@@ -265,7 +258,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
265
258
|
const projectLayers = new Set([...(local.supported ? local.scopes : []), ...tracked].filter(isCoveredProjectLayer));
|
|
266
259
|
return [opts.scope, ...[...projectLayers].sort()];
|
|
267
260
|
};
|
|
268
|
-
const syncLayer = async (scope, reason) => {
|
|
261
|
+
const syncLayer = async (scope, reason, calls) => {
|
|
269
262
|
const cursorPath = memorySyncCursorPath(memoryEngineDir, scope);
|
|
270
263
|
const baseline = await loadLayerBaseline(cursorPath, scope);
|
|
271
264
|
let cursor = baseline.kind === "cursor" ? baseline.cursor : undefined;
|
|
@@ -290,6 +283,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
290
283
|
throw new MemorySyncLayerParked(scope, cursorPath, { kind: "cursor_refused", detail: refusal.detail });
|
|
291
284
|
throw err;
|
|
292
285
|
}
|
|
286
|
+
calls.push({ scope, result });
|
|
293
287
|
for (const w of result.warnings ?? [])
|
|
294
288
|
opts.log.warn("memory_sync_warning", { scope, subRound, warning: w });
|
|
295
289
|
if (!(result.pushTruncated === true || result.pullTruncated === true))
|
|
@@ -303,7 +297,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
303
297
|
await saveCursor(cursorPath, result.cursor);
|
|
304
298
|
}
|
|
305
299
|
catch (err) {
|
|
306
|
-
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err:
|
|
300
|
+
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err: errorText(err) });
|
|
307
301
|
}
|
|
308
302
|
}
|
|
309
303
|
cursor = result.cursor ?? cursor;
|
|
@@ -324,7 +318,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
324
318
|
await saveCursor(cursorPath, result.cursor);
|
|
325
319
|
}
|
|
326
320
|
catch (err) {
|
|
327
|
-
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err:
|
|
321
|
+
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err: errorText(err) });
|
|
328
322
|
}
|
|
329
323
|
}
|
|
330
324
|
opts.log.info("memory_sync_round", {
|
|
@@ -342,35 +336,46 @@ export function createMemorySyncRunner(opts) {
|
|
|
342
336
|
};
|
|
343
337
|
let relocated = false;
|
|
344
338
|
let parkedLayers = [];
|
|
339
|
+
const logLayerFailure = (reason, scope, err) => {
|
|
340
|
+
if (err instanceof MemorySyncLayerParked) {
|
|
341
|
+
const refused = err.parkCause.kind === "cursor_refused" ? err.parkCause.detail : undefined;
|
|
342
|
+
opts.log.warn("memory_sync_layer_parked", { reason, scope, cursorPath: err.cursorPath, ...(refused !== undefined ? { axis: refused.axis, found: refused.found } : {}), wayOut: err.wayOut, note: err.message });
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: errorText(err) });
|
|
346
|
+
}
|
|
347
|
+
};
|
|
345
348
|
const round = async (reason) => {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
+
const calls = [];
|
|
350
|
+
let layers;
|
|
351
|
+
try {
|
|
352
|
+
if (!relocated) {
|
|
353
|
+
await relocateSyncCursorsM31(memoryEngineDir, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
|
|
354
|
+
relocated = true;
|
|
355
|
+
}
|
|
356
|
+
layers = layersOfRound();
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
for (const scope of [opts.scope, ...[...tracked].filter(isCoveredProjectLayer).sort()]) {
|
|
360
|
+
calls.push({ scope, thrown: err });
|
|
361
|
+
logLayerFailure(reason, scope, err);
|
|
362
|
+
}
|
|
363
|
+
return { calls };
|
|
349
364
|
}
|
|
350
|
-
const layers = [];
|
|
351
|
-
const failures = [];
|
|
352
365
|
const parkedNow = [];
|
|
353
|
-
for (const scope of
|
|
366
|
+
for (const scope of layers) {
|
|
354
367
|
try {
|
|
355
|
-
|
|
368
|
+
await syncLayer(scope, reason, calls);
|
|
356
369
|
}
|
|
357
370
|
catch (err) {
|
|
358
|
-
|
|
359
|
-
if (err instanceof MemorySyncLayerParked)
|
|
371
|
+
calls.push({ scope, thrown: err });
|
|
372
|
+
if (err instanceof MemorySyncLayerParked)
|
|
360
373
|
parkedNow.push({ scope, wayOut: err.wayOut });
|
|
361
|
-
|
|
362
|
-
opts.log.warn("memory_sync_layer_parked", { reason, scope, cursorPath: err.cursorPath, ...(refused !== undefined ? { axis: refused.axis, found: refused.found } : {}), wayOut: err.wayOut, note: err.message });
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: String(err) });
|
|
366
|
-
}
|
|
374
|
+
logLayerFailure(reason, scope, err);
|
|
367
375
|
}
|
|
368
376
|
}
|
|
369
377
|
parkedLayers = Object.freeze(parkedNow);
|
|
370
|
-
|
|
371
|
-
throw new Error(`memory sync round failed for ${failures.length} layer(s): ${failures.map((f) => `${f.scope}: ${f.err instanceof Error ? f.err.message : String(f.err)}`).join("; ")}`);
|
|
372
|
-
}
|
|
373
|
-
return { layers };
|
|
378
|
+
return { calls };
|
|
374
379
|
};
|
|
375
380
|
let chain = Promise.resolve();
|
|
376
381
|
let pending = 0;
|
|
@@ -384,11 +389,14 @@ export function createMemorySyncRunner(opts) {
|
|
|
384
389
|
});
|
|
385
390
|
return run;
|
|
386
391
|
};
|
|
387
|
-
const syncOnce = (reason
|
|
392
|
+
const syncOnce = (reason, syncOpts) => {
|
|
393
|
+
for (const scope of syncOpts?.reading ?? [])
|
|
394
|
+
if (isCoveredProjectLayer(scope))
|
|
395
|
+
tracked.add(scope);
|
|
396
|
+
return exclusive(() => round(reason));
|
|
397
|
+
};
|
|
388
398
|
const queueRound = (reason) => {
|
|
389
|
-
void syncOnce(reason)
|
|
390
|
-
opts.log.warn("memory_sync_failed", { reason, scope: opts.scope, err: String(err) });
|
|
391
|
-
});
|
|
399
|
+
void syncOnce(reason);
|
|
392
400
|
};
|
|
393
401
|
const trigger = (reason) => {
|
|
394
402
|
if (pending > 0)
|
|
@@ -404,7 +412,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
404
412
|
tracked.add(scope);
|
|
405
413
|
queueRound("track");
|
|
406
414
|
};
|
|
407
|
-
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), parked: () => parkedLayers, pending: () => pending };
|
|
415
|
+
return { peer: opts.transport.peer, syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), parked: () => parkedLayers, pending: () => pending };
|
|
408
416
|
}
|
|
409
417
|
export function startMemorySyncRunner(deps) {
|
|
410
418
|
const { sync } = deps;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 同步身份的拒因词(闭集,判序):形的四词逐字是契约包 `HttpUrlFlaw`;`credential` = 规范化之后带凭据;`too-long` = 装不进中心 `peer` 列;
|
|
3
|
+
* `not-canonical` = 值本身没缺陷、但不是它自己的规范形(只在「声明的身份」座位出现)。wire 面:`request.field_invalid` 拒体的 `reason`。
|
|
4
|
+
*/
|
|
5
|
+
export declare const MEMORY_SYNC_IDENTITY_FLAWS: readonly ["not-http-url", "userinfo", "query", "fragment", "credential", "too-long", "not-canonical"];
|
|
6
|
+
export type MemorySyncIdentityFlaw = (typeof MEMORY_SYNC_IDENTITY_FLAWS)[number];
|
|
7
|
+
/** 铸点的答:身份,或首个缺陷。 */
|
|
8
|
+
export type MemorySyncIdentityReading = {
|
|
9
|
+
readonly identity: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly flaw: Exclude<MemorySyncIdentityFlaw, "not-canonical">;
|
|
12
|
+
};
|
|
13
|
+
/** 唯一铸点(纯函数)。值 → 规范化身份,或首个缺陷。 */
|
|
14
|
+
export declare function readMemorySyncIdentity(raw: string): MemorySyncIdentityReading;
|
|
15
|
+
/** 「声明的身份」座位(中心声明 / 会话触发的 `target` / 中心入站的 `peer`):值必须**就是**它自己的规范化身份。`undefined` = 合格。 */
|
|
16
|
+
export declare function declaredMemorySyncIdentityFlaw(raw: string): MemorySyncIdentityFlaw | undefined;
|
|
17
|
+
/** 一个拒因词的规则句(穷举;新词不补 = 编译红)。只说规则,不含值。 */
|
|
18
|
+
export declare function memorySyncIdentityRule(flaw: MemorySyncIdentityFlaw): string;
|
|
19
|
+
/**
|
|
20
|
+
* 起服座位(env 键)的拒句:`memory sync: <键> <规则句>[: <出路>]`。形的四词走 URL 形拒句的铸点(值只到 origin);其余三词值不回显 ——
|
|
21
|
+
* `not-canonical` 例外地给出**规范形**(它已过凭据判官,是运维要写下的那一串;仍经展示铸点)。
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildMemorySyncIdentityBootRefusal(envKey: string, raw: string, flaw: MemorySyncIdentityFlaw, exit: string): string;
|
|
24
|
+
//# sourceMappingURL=memory-sync-identity.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { carriesCredential } from "@sema-agent/core";
|
|
2
|
+
import { httpUrlRule, pathBaseUrlFlaws } from "@sema-agent/settings-schema";
|
|
3
|
+
import { MEMORY_SYNC_PEER_COLUMN_CHARS } from "./plugins/memory-key-guards.js";
|
|
4
|
+
import { buildHttpUrlRefusal } from "./url-refusal.js";
|
|
5
|
+
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
6
|
+
export const MEMORY_SYNC_IDENTITY_FLAWS = ["not-http-url", "userinfo", "query", "fragment", "credential", "too-long", "not-canonical"];
|
|
7
|
+
export function readMemorySyncIdentity(raw) {
|
|
8
|
+
const shape = pathBaseUrlFlaws(raw)[0];
|
|
9
|
+
if (shape !== undefined)
|
|
10
|
+
return { flaw: shape };
|
|
11
|
+
const u = new URL(raw);
|
|
12
|
+
const identity = `${u.origin}${u.pathname.replace(/\/+$/, "")}`;
|
|
13
|
+
if (carriesCredential(identity))
|
|
14
|
+
return { flaw: "credential" };
|
|
15
|
+
if (identity.length > MEMORY_SYNC_PEER_COLUMN_CHARS)
|
|
16
|
+
return { flaw: "too-long" };
|
|
17
|
+
return { identity };
|
|
18
|
+
}
|
|
19
|
+
export function declaredMemorySyncIdentityFlaw(raw) {
|
|
20
|
+
const r = readMemorySyncIdentity(raw);
|
|
21
|
+
if ("flaw" in r)
|
|
22
|
+
return r.flaw;
|
|
23
|
+
return r.identity === raw ? undefined : "not-canonical";
|
|
24
|
+
}
|
|
25
|
+
export function memorySyncIdentityRule(flaw) {
|
|
26
|
+
switch (flaw) {
|
|
27
|
+
case "not-http-url":
|
|
28
|
+
case "userinfo":
|
|
29
|
+
case "query":
|
|
30
|
+
case "fragment":
|
|
31
|
+
return httpUrlRule(flaw);
|
|
32
|
+
case "credential":
|
|
33
|
+
return "carries a credential (a token-shaped part of its path) — a sync identity travels verbatim in every request body and cursor and is published on the capability face, so it is refused, never cleaned";
|
|
34
|
+
case "too-long":
|
|
35
|
+
return `is longer than ${MEMORY_SYNC_PEER_COLUMN_CHARS} characters once normalized (the central peer column holds at most ${MEMORY_SYNC_PEER_COLUMN_CHARS})`;
|
|
36
|
+
case "not-canonical":
|
|
37
|
+
return "is not in canonical form (lower-case scheme and host, no default port, no trailing slash)";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function buildMemorySyncIdentityBootRefusal(envKey, raw, flaw, exit) {
|
|
41
|
+
switch (flaw) {
|
|
42
|
+
case "not-http-url":
|
|
43
|
+
case "userinfo":
|
|
44
|
+
case "query":
|
|
45
|
+
case "fragment":
|
|
46
|
+
return buildHttpUrlRefusal(`memory sync: ${envKey}`, raw, [flaw]);
|
|
47
|
+
case "credential":
|
|
48
|
+
return `memory sync: ${envKey} holds a credential — ${memorySyncIdentityRule(flaw)}: ${exit}`;
|
|
49
|
+
case "too-long":
|
|
50
|
+
return `memory sync: ${envKey} ${memorySyncIdentityRule(flaw)}: use a shorter base URL`;
|
|
51
|
+
case "not-canonical": {
|
|
52
|
+
const r = readMemorySyncIdentity(raw);
|
|
53
|
+
return `memory sync: ${envKey} ${memorySyncIdentityRule(flaw)} — what is declared is what is published and compared, so it is not normalized for you: set it to ${JSON.stringify("identity" in r ? buildDisplaySafeUrl(r.identity) : "")}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=memory-sync-identity.js.map
|