@sema-agent/server 7.138.0 → 7.140.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +457 -7
- package/README.md +7 -4
- package/README.zh-CN.md +6 -4
- package/USAGE.md +91 -41
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +26 -26
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/live-deps.js +3 -2
- package/dist/bench/s1/row.d.ts +5 -2
- package/dist/bench/s1/row.js +2 -22
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/adoption.js +1 -1
- package/dist/boot/device-lane.d.ts +8 -3
- package/dist/boot/device-lane.js +7 -2
- package/dist/boot/execution-env.d.ts +4 -2
- package/dist/boot/execution-env.js +28 -34
- package/dist/boot/execution-lane.d.ts +9 -1
- package/dist/boot/execution-lane.js +6 -1
- package/dist/boot/governance-seams.d.ts +3 -2
- package/dist/boot/governance-seams.js +9 -11
- package/dist/boot/memory-boundary.d.ts +14 -51
- package/dist/boot/memory-boundary.js +10 -20
- package/dist/boot/memory-consolidation.d.ts +15 -11
- package/dist/boot/memory-consolidation.js +25 -14
- package/dist/boot/resolve-spec.d.ts +4 -6
- package/dist/boot/resolve-spec.js +10 -13
- package/dist/boot/retention-lane.d.ts +5 -5
- package/dist/boot/retention-lane.js +0 -6
- package/dist/boot/runner-deps.d.ts +6 -4
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.d.ts +0 -3
- package/dist/boot/session-faces.js +15 -8
- package/dist/boot/stage-02-web-admin-pull.js +2 -2
- package/dist/boot/stage-03-store-backend.d.ts +2 -6
- package/dist/boot/stage-03-store-backend.js +2 -3
- package/dist/boot/stage-04-stores.d.ts +2 -6
- package/dist/boot/stage-04-stores.js +5 -5
- package/dist/boot/stage-05-execution-env.d.ts +2 -6
- package/dist/boot/stage-05-execution-env.js +2 -2
- package/dist/boot/stage-06-runners.d.ts +2 -6
- package/dist/boot/stage-06-runners.js +2 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -6
- package/dist/boot/stage-08-reapers.d.ts +2 -6
- package/dist/boot/stage-09-leader.d.ts +2 -6
- package/dist/boot/stage-09-leader.js +1 -2
- package/dist/boot/stage-10-http-server.d.ts +2 -6
- package/dist/boot/stage-10-http-server.js +7 -6
- package/dist/boot/stores.d.ts +3 -6
- package/dist/boot/stores.js +27 -40
- package/dist/boot/task-list-lane.d.ts +1 -1
- package/dist/boot/task-list-lane.js +1 -1
- package/dist/boot/web-admin.js +37 -11
- package/dist/capabilities/execution-lane.d.ts +2 -2
- package/dist/capabilities/mcp-probe.js +1 -1
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +15 -7
- package/dist/capability-facts.js +8 -3
- package/dist/config-catalog.js +9 -8
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +33 -37
- package/dist/config-provider.js +51 -30
- package/dist/config-types.d.ts +21 -7
- package/dist/config.d.ts +33 -0
- package/dist/config.js +46 -16
- package/dist/delegated-session-owner.d.ts +4 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/deployment-shape.d.ts +10 -9
- package/dist/deployment-shape.js +4 -4
- package/dist/device-enrollment.d.ts +41 -2
- package/dist/device-enrollment.js +27 -12
- package/dist/device-store.d.ts +58 -85
- package/dist/device-store.js +19 -49
- package/dist/device-ws-hub.js +2 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -4
- package/dist/exec-cut.d.ts +60 -0
- package/dist/exec-cut.js +36 -0
- package/dist/execution-lane-caps.d.ts +115 -105
- package/dist/execution-lane-caps.js +76 -82
- package/dist/execution-placement.d.ts +259 -0
- package/dist/execution-placement.js +199 -0
- package/dist/field-conflict.d.ts +3 -2
- package/dist/field-conflict.js +1 -0
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +8 -19
- package/dist/http/checkpoint-ctx.d.ts +21 -12
- package/dist/http/checkpoint-ctx.js +4 -2
- package/dist/http/claimed-run.d.ts +1 -1
- package/dist/http/durable-run-leg.js +9 -5
- package/dist/http/execution-context.d.ts +218 -0
- package/dist/http/execution-context.js +330 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +3 -1
- package/dist/http/memory-compliance-failure-reply.js +5 -0
- package/dist/http/memory-layer-reply.d.ts +30 -0
- package/dist/http/memory-layer-reply.js +24 -0
- package/dist/http/resume-legs.js +63 -36
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/route-ctx.d.ts +14 -8
- package/dist/http/routes/adoption.js +1 -1
- package/dist/http/routes/background.d.ts +29 -1
- package/dist/http/routes/background.js +29 -2
- package/dist/http/routes/devices.d.ts +13 -19
- package/dist/http/routes/devices.js +21 -26
- package/dist/http/routes/memory-bundle.js +3 -0
- package/dist/http/routes/memory-compliance.js +8 -1
- package/dist/http/routes/memory-consolidation.js +12 -1
- package/dist/http/routes/memory-origin.js +14 -2
- package/dist/http/routes/memory-policy.js +5 -0
- package/dist/http/routes/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +143 -22
- package/dist/http/routes/side-query.d.ts +12 -4
- package/dist/http/routes/side-query.js +4 -5
- package/dist/http/routes/tasks.js +41 -33
- package/dist/http/server.d.ts +40 -44
- package/dist/http/server.js +13 -2
- package/dist/http/session-inherit-env.d.ts +3 -4
- package/dist/http/session-inherit-env.js +1 -1
- package/dist/http/wire-types.d.ts +8 -12
- package/dist/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/lane-home-probe.d.ts +68 -42
- package/dist/lane-home-probe.js +53 -30
- package/dist/leader/wire.js +5 -4
- package/dist/memory-bundle-engine.d.ts +16 -9
- package/dist/memory-bundle-engine.js +29 -6
- package/dist/memory-operator-faces.d.ts +85 -30
- package/dist/memory-operator-faces.js +136 -30
- package/dist/memory-project-layer.d.ts +3 -21
- package/dist/memory-project-layer.js +11 -8
- package/dist/memory-scope-stores.d.ts +73 -0
- package/dist/memory-scope-stores.js +86 -0
- package/dist/memory-scope.d.ts +29 -5
- package/dist/memory-scope.js +16 -4
- package/dist/memory-sync-client.d.ts +70 -18
- package/dist/memory-sync-client.js +215 -117
- package/dist/memory-sync.d.ts +1 -1
- package/dist/memory-sync.js +3 -2
- package/dist/observability/cost-taxonomy.d.ts +2 -2
- package/dist/observability/fail-open.d.ts +12 -0
- package/dist/observability/fail-open.js +12 -0
- package/dist/observability/run-terminal.js +0 -1
- package/dist/orchestration/workflow-notify-journal.js +4 -4
- package/dist/org-memory-admission.d.ts +4 -3
- package/dist/org-memory-admission.js +6 -6
- package/dist/parked-decide.d.ts +34 -9
- package/dist/parked-decide.js +12 -19
- package/dist/parked-revive-surface.d.ts +12 -15
- package/dist/parked-revive-surface.js +3 -6
- package/dist/peer-seat.d.ts +13 -7
- package/dist/peer-seat.js +37 -3
- package/dist/plugins/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-file.d.ts +7 -0
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
- package/dist/plugins/approval-ask-store-memory.js +12 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
- package/dist/plugins/approval-ask-store-sql.js +44 -26
- package/dist/plugins/approval-nonce-store.js +4 -4
- package/dist/plugins/blob-backend.d.ts +4 -4
- package/dist/plugins/blob-backend.js +7 -7
- package/dist/plugins/breaker-state-sql.js +2 -2
- package/dist/plugins/caching-session-store.d.ts +6 -2
- package/dist/plugins/caching-session-store.js +9 -3
- package/dist/plugins/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +6 -5
- package/dist/plugins/checkpoint-store-sql.js +21 -23
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +68 -122
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +151 -0
- package/dist/plugins/execution-placement-sql.js +223 -0
- package/dist/plugins/file-history-store-sql.d.ts +9 -8
- package/dist/plugins/file-history-store-sql.js +9 -9
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +4 -0
- package/dist/plugins/fork-routing-session-store.d.ts +6 -0
- package/dist/plugins/fork-routing-session-store.js +16 -0
- package/dist/plugins/image-bake-store-sql.js +9 -9
- package/dist/plugins/image-index-sql.js +11 -11
- package/dist/plugins/leader-run-store-sql.js +4 -4
- package/dist/plugins/local-session-store.d.ts +149 -50
- package/dist/plugins/local-session-store.js +382 -107
- package/dist/plugins/mailbox-store-sql.d.ts +2 -1
- package/dist/plugins/mailbox-store-sql.js +4 -4
- package/dist/plugins/memory-engine-tidb.js +5 -5
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +117 -8
- package/dist/plugins/one-time-migrations.js +408 -38
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -19
- package/dist/plugins/permission-rule-store-sql.js +29 -32
- package/dist/plugins/pg-pool.d.ts +7 -3
- package/dist/plugins/pg-session-storage.d.ts +31 -20
- package/dist/plugins/pg-session-storage.js +120 -52
- package/dist/plugins/remote-env-adb.d.ts +18 -19
- package/dist/plugins/remote-env-adb.js +91 -66
- package/dist/plugins/remote-env-device.d.ts +23 -31
- package/dist/plugins/remote-env-device.js +19 -17
- package/dist/plugins/remote-env-e2b.d.ts +27 -21
- package/dist/plugins/remote-env-e2b.js +91 -45
- package/dist/plugins/remote-env-file-error.d.ts +3 -1
- package/dist/plugins/remote-env-file-error.js +9 -3
- package/dist/plugins/remote-env-host.d.ts +31 -34
- package/dist/plugins/remote-env-host.js +91 -72
- package/dist/plugins/remote-env-k8s.d.ts +13 -29
- package/dist/plugins/remote-env-k8s.js +63 -55
- package/dist/plugins/remote-env-local-docker.d.ts +24 -12
- package/dist/plugins/remote-env-local-docker.js +49 -61
- package/dist/plugins/remote-env-ssh.d.ts +11 -19
- package/dist/plugins/remote-env-ssh.js +74 -57
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/resume-anchor-store-sql.js +2 -2
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/retention-lane-store-sql.js +2 -2
- package/dist/plugins/retention-store-sql.d.ts +10 -7
- package/dist/plugins/retention-store-sql.js +13 -7
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +20 -15
- package/dist/plugins/schema-consistency.d.ts +21 -11
- package/dist/plugins/schema-consistency.js +15 -9
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- package/dist/plugins/schema-ensure-legs.d.ts +2 -3
- package/dist/plugins/schema-ensure-legs.js +2 -2
- package/dist/plugins/schema-live-columns.d.ts +1 -1
- package/dist/plugins/session-capture-record-store-sql.js +1 -1
- package/dist/plugins/session-fork.d.ts +54 -0
- package/dist/plugins/session-fork.js +38 -1
- package/dist/plugins/session-row-sql.d.ts +22 -0
- package/dist/plugins/session-row-sql.js +81 -0
- package/dist/plugins/session-rules-column.d.ts +1 -1
- package/dist/plugins/session-rules-column.js +1 -1
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.js +1 -1
- package/dist/plugins/shared-memory-store-sql.js +2 -2
- package/dist/plugins/sql-driver.d.ts +10 -5
- package/dist/plugins/sql-driver.js +6 -6
- package/dist/plugins/sql-errors.js +1 -1
- package/dist/plugins/store-backend.d.ts +5 -7
- package/dist/plugins/store-backend.js +23 -11
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +13 -7
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.d.ts +57 -26
- package/dist/plugins/tidb-session-store.js +127 -59
- package/dist/plugins/tool-result-store-sql.d.ts +5 -4
- package/dist/plugins/tool-result-store-sql.js +21 -13
- package/dist/plugins/user-input-ledger-sql.js +2 -2
- package/dist/plugins/workflow-journal-store-sql.js +5 -5
- package/dist/plugins/workflow-run-store-sql.js +19 -19
- package/dist/plugins/write-behind-counter.d.ts +2 -3
- package/dist/plugins/write-behind-counter.js +5 -5
- package/dist/project-memory.d.ts +9 -15
- package/dist/project-memory.js +5 -4
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +6 -7
- package/dist/request-value-closure.js +1 -3
- package/dist/resource-suspend.d.ts +14 -12
- package/dist/resource-suspend.js +1 -1
- package/dist/resource-window.d.ts +4 -3
- package/dist/run-local.d.ts +6 -0
- package/dist/run-local.js +34 -35
- package/dist/runs.d.ts +10 -5
- package/dist/runs.js +52 -66
- package/dist/security.d.ts +38 -12
- package/dist/security.js +8 -2
- package/dist/server-secret-env.d.ts +9 -5
- package/dist/session-children-retire.d.ts +27 -9
- package/dist/session-children-retire.js +9 -5
- package/dist/session-incarnation.d.ts +20 -10
- package/dist/session-incarnation.js +5 -3
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-row.d.ts +114 -5
- package/dist/session-row.js +55 -5
- package/dist/session-titler.d.ts +2 -2
- package/dist/sql-engine-posture.d.ts +1 -1
- package/dist/sql-engine-posture.js +1 -1
- package/dist/{plugins → stores-sql}/background-agent-store-sql.d.ts +2 -2
- package/dist/{plugins → stores-sql}/background-agent-store-sql.js +8 -8
- package/dist/{plugins → stores-sql}/session-policy-store-sql.d.ts +7 -5
- package/dist/{plugins → stores-sql}/session-policy-store-sql.js +12 -12
- package/dist/{plugins → stores-sql}/usage-window-store-sql.d.ts +1 -1
- package/dist/{plugins → stores-sql}/usage-window-store-sql.js +3 -3
- package/dist/task-a2a.d.ts +1 -1
- package/dist/task-a2a.js +5 -6
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +22 -9
- package/dist/task-mcp.js +17 -9
- package/dist/terminal.d.ts +27 -0
- package/dist/terminal.js +6 -0
- package/dist/tool-approval.d.ts +6 -0
- package/dist/tool-approval.js +8 -1
- package/dist/trace/project.d.ts +1 -0
- package/dist/trace/project.js +1 -0
- package/dist/trace/redact.d.ts +3 -1
- package/dist/trace/redact.js +80 -28
- package/dist/trace/self-minted-wire.d.ts +169 -2
- package/dist/trace/self-minted-wire.js +54 -1
- package/dist/url-refusal.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +17 -3
- package/dist/web-admin-client/apply-effective.js +20 -10
- package/dist/web-admin-client/candidate-gate.d.ts +11 -7
- package/dist/web-admin-client/candidate-gate.js +1 -1
- package/dist/web-admin-client/http-client.d.ts +5 -4
- package/dist/web-admin-client/http-client.js +2 -2
- package/dist/web-admin-client/read-warnings.d.ts +7 -17
- package/dist/web-admin-client/read-warnings.js +5 -9
- package/dist/web-admin-client/skills-mcp.js +3 -2
- package/dist/web-admin-client/types.d.ts +6 -0
- package/package.json +3 -3
- package/wire-changes/server@7.139.0.json +348 -0
- package/wire-changes/server@7.140.0.json +184 -0
- package/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
|
@@ -1,29 +1,70 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type MemorySyncClientResult, type MemorySyncTransport } from "@sema-agent/core";
|
|
2
|
+
import type { ServiceConfig } from "./config-types.js";
|
|
3
|
+
import type { MemoryScopeStores } from "./memory-scope-stores.js";
|
|
4
|
+
import { type OneTimeMigrationWindow } from "./plugins/one-time-migrations.js";
|
|
2
5
|
/** 结构化日志面(src/observability/logger.ts Logger 的结构子集——测试可注入捕获器)。 */
|
|
3
6
|
export interface MemorySyncLog {
|
|
4
7
|
info(msg: string, fields?: Record<string, unknown>): void;
|
|
5
8
|
warn(msg: string, fields?: Record<string, unknown>): void;
|
|
6
9
|
}
|
|
7
|
-
/** 缺省对端名(core `syncMemoryScope` 同款缺省;cursor 按 (scope,peer) 分区)。 */
|
|
8
|
-
export declare const DEFAULT_MEMORY_SYNC_PEER = "central";
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* 中心基址 → 同步对端的身份(规范化基址)。本仓唯一的铸点:`config.ts` 读 env 时调它拒启(引擎关着也拒),transport 建时再调它铸身份 —— 同一只纯函数,同一个答案。
|
|
12
|
+
* 两道判,各归各的属主,本函数不另写规则:
|
|
13
|
+
* ① 地址的形 —— 这只地址是「后面要拼路径的基址」座位(post 把同步路径拼在它后面),判官与拒句就是那一座位的那一只({@link pathBaseUrlRefusal}:层级式 http(s),
|
|
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
|
+
* 每一轮都会被中心拒)⇒ 同样拒。
|
|
20
|
+
*/
|
|
21
|
+
export declare function memorySyncPeerOf(url: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer` 是运维改绑游标时要抄的那个值:
|
|
24
|
+
* 它已过 {@link memorySyncPeerOf} 的两道判(带凭据的身份起不来);仍过展示铸点({@link buildDisplaySafeUrl}),日志里的 URL 值一律经它 —— 两只判官
|
|
25
|
+
* (引擎的出口脱敏与本仓的展示形状律)不是同一份实现,本仓的那一只若遮了引擎放行的一段,这一行带遮记号而不是逐字(响亮,不漏)。
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildMemorySyncEnabledFields(peer: string, scope: string, cursorPath: string): {
|
|
28
|
+
readonly peer: string;
|
|
29
|
+
readonly scope: string;
|
|
30
|
+
readonly cursorPath: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* fetch transport(core `MemorySyncTransport` 的部署实现)。`peer` = {@link memorySyncPeerOf}(`url`)(建时铸一次;拒的地址在这里抛,一个请求都不发),
|
|
34
|
+
* `post` 打 `${peer}${path}`(core 传来的 path 以 `/` 开头);`token` 走 Authorization: Bearer(与中心侧 gatedPrincipal 的 verified-principal 门对齐)。
|
|
35
|
+
* 非 2xx throw(status + body 前 200 字节——中心 4xx/5xx 的 typed error body 就是最好的诊断线索);2xx 返回解码后的 JSON(形状校验在 core
|
|
36
|
+
* parseMemorySyncResponse,职责不重复)。`timeoutMs`(缺省 60s,AbortSignal.timeout)是活性保护:fire-and-forget 轮挂死会把 inflight 节流永久卡住
|
|
37
|
+
* (undici 默认 headers 超时 300s 太钝)。
|
|
16
38
|
*/
|
|
17
39
|
export declare function createMemorySyncTransport(opts: {
|
|
18
40
|
url: string;
|
|
19
41
|
token: string;
|
|
20
42
|
timeoutMs?: number;
|
|
21
|
-
|
|
43
|
+
/** The one call shape this transport makes (`fetch` itself fits it). */
|
|
44
|
+
fetchImpl?: (url: string, init: RequestInit) => Promise<Response>;
|
|
22
45
|
}): MemorySyncTransport;
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
25
|
-
|
|
26
|
-
|
|
46
|
+
/** 一层的游标文件路径:引擎根 `memoryEngineDir`(= `RunnerDeps.memoryEngineDir` 的那一个值)下 `sync-cursors/<scope 摘要>.json`。一层一只,不随对端变、不随店变。
|
|
47
|
+
* 落在子目录里:与更早的根下一层文件名 `sync-cursor-<peer>.json` 的空间不相交。 */
|
|
48
|
+
export declare function memorySyncCursorPath(memoryEngineDir: string, scope: string): string;
|
|
49
|
+
/** M31 一次的读数(测试按它断言)。`window: "closed"` ⇒ 一行都没读。 */
|
|
50
|
+
export interface SyncCursorRelocationM31Report {
|
|
51
|
+
readonly window: "open" | "closed";
|
|
52
|
+
readonly moved: number;
|
|
53
|
+
readonly left: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ⑤ 一次性迁移 M31:把升级前的游标搬到各自那一层的路径(`rename`,字节不变;只搬不改,判归引擎)。`today` = UTC 日历日;窗关 ⇒ 不搬。
|
|
57
|
+
* · 上一版的分区目录 `sync-cursors/central/<scope 摘要>.json` ⇒ `sync-cursors/<同名>`(两种布局的文件名是同一只 scope 摘要);搬空的目录删掉;
|
|
58
|
+
* 名字不是摘要形的(写游标的 staging 残片)原地不动。
|
|
59
|
+
* · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(没法说它是哪一层的基线)。
|
|
60
|
+
* · 目标路径上已有游标 ⇒ 不搬、不删,一行 warn(这一层用路径上的那一份;留下的那只没有读者)。
|
|
61
|
+
* 与同一数据根上并跑的另一个进程(server + run-local)同搬:源已被对方搬走(`ENOENT`)⇒ 跳过。残余(如实):先查目标再 `rename` 之间若有写者在目标路径上
|
|
62
|
+
* 落下游标,会被盖掉 —— 写者只有本模块的 runner,而 runner 写任何游标之前先跑完本步。别的读写错 ⇒ 抛(这一轮整轮失败、下一轮再搬)。
|
|
63
|
+
*/
|
|
64
|
+
export declare function relocateSyncCursorsM31(memoryEngineDir: string, log: MemorySyncLog, opts: {
|
|
65
|
+
readonly today: string;
|
|
66
|
+
readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
|
|
67
|
+
}): Promise<SyncCursorRelocationM31Report>;
|
|
27
68
|
/** 一轮里一层的结果。 */
|
|
28
69
|
export interface MemorySyncLayerResult {
|
|
29
70
|
readonly scope: string;
|
|
@@ -52,19 +93,30 @@ export interface MemorySyncRunner {
|
|
|
52
93
|
export declare const MAX_SYNC_ROUNDS = 32;
|
|
53
94
|
/**
|
|
54
95
|
* 组装同步执行器:同步层 `scope` + 同属主的项目层,逐层一轮(cursor 持久化 + core `syncMemoryScope` + conflicts 上报),串行化/节流。
|
|
55
|
-
*
|
|
96
|
+
* 每一层同步的店由记忆作用域店答(⑥);同步层建时就问(坏的层键 ⇒ 这里抛引擎的 `config.memory_scope_key`,原样)。
|
|
56
97
|
*/
|
|
57
98
|
export declare function createMemorySyncRunner(opts: {
|
|
58
|
-
backend
|
|
99
|
+
/** 记忆作用域店(本进程那一只):每一层的店问它的 `storeOf`;项目层的发现列它的 `backend`(部署的店本身);游标的根 = 它的 `root`。 */
|
|
100
|
+
stores: MemoryScopeStores;
|
|
59
101
|
/** 同步层(`config.memorySync.scope`);它同属主的项目层随它一起同步。 */
|
|
60
102
|
scope: string;
|
|
61
|
-
|
|
103
|
+
/** 对端 = 它的 `peer`(这一轮的对端只有这一个来源;游标绑的对端由引擎对它判)。 */
|
|
62
104
|
transport: MemorySyncTransport;
|
|
63
105
|
log: MemorySyncLog;
|
|
64
|
-
peer?: string;
|
|
65
106
|
now?: () => number;
|
|
66
107
|
/** S2.5 分批(core 1.284 两 seam):push 集/pull 集单轮上限。未设=不分批(wire 字节不变)。 */
|
|
67
108
|
maxPushEntries?: number;
|
|
68
109
|
maxPullEntries?: number;
|
|
69
110
|
}): MemorySyncRunner;
|
|
111
|
+
/**
|
|
112
|
+
* 两个入口(HTTP 服务 `boot/stores.ts` 与 `run-local.ts`)起同步执行器的**唯一一处**:建 transport(对端身份在这里铸一次)、建执行器、
|
|
113
|
+
* 起服日志一行 `memory_sync_enabled`、fire-and-forget 起服那一轮。两处此前各手抄一份同样的装配(上限两键漏过一次);
|
|
114
|
+
* 店只经入口装配出的那只记忆作用域店取 —— 它的两件事实就是交给 Runner 的那两只,会话把一层放在哪只店由同样的两件事实决定。
|
|
115
|
+
*/
|
|
116
|
+
export declare function startMemorySyncRunner(deps: {
|
|
117
|
+
sync: NonNullable<ServiceConfig["memorySync"]>;
|
|
118
|
+
/** 入口装配出的记忆作用域店(= 本入口的记忆引擎对象)。 */
|
|
119
|
+
stores: MemoryScopeStores;
|
|
120
|
+
log: MemorySyncLog;
|
|
121
|
+
}): MemorySyncRunner;
|
|
70
122
|
//# sourceMappingURL=memory-sync-client.d.ts.map
|
|
@@ -1,110 +1,202 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
-
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
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 { carriesCredential, syncMemoryScope, } from "@sema-agent/core";
|
|
5
5
|
import { replicationCovers } from "./memory-scope.js";
|
|
6
6
|
import { parseProjectLayerKey } from "./project-layer-key.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
import { MEMORY_SYNC_PEER_COLUMN_CHARS } from "./plugins/memory-key-guards.js";
|
|
8
|
+
import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./plugins/one-time-migrations.js";
|
|
9
|
+
import { pathBaseUrlRefusal } from "./web-admin-client/center-request.js";
|
|
10
|
+
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
11
|
+
export function memorySyncPeerOf(url) {
|
|
12
|
+
const refusal = pathBaseUrlRefusal("memory sync: MEMORY_SYNC_URL", url);
|
|
13
|
+
if (refusal !== undefined)
|
|
14
|
+
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
|
+
}
|
|
21
|
+
if (identity.length > MEMORY_SYNC_PEER_COLUMN_CHARS) {
|
|
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;
|
|
25
|
+
}
|
|
26
|
+
export function buildMemorySyncEnabledFields(peer, scope, cursorPath) {
|
|
27
|
+
return { peer: buildDisplaySafeUrl(peer), scope, cursorPath };
|
|
28
|
+
}
|
|
14
29
|
export function createMemorySyncTransport(opts) {
|
|
15
|
-
const
|
|
30
|
+
const peer = memorySyncPeerOf(opts.url);
|
|
16
31
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
17
32
|
const timeoutMs = opts.timeoutMs ?? 60_000;
|
|
18
|
-
return
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
peer,
|
|
35
|
+
async post(path, body) {
|
|
36
|
+
const res = await fetchImpl(`${peer}${path}`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: { authorization: `Bearer ${opts.token}`, "content-type": "application/json" },
|
|
39
|
+
body: JSON.stringify(body),
|
|
40
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
41
|
+
});
|
|
42
|
+
const text = await res.text();
|
|
43
|
+
if (!res.ok)
|
|
44
|
+
throw new Error(`memory sync POST ${path}: HTTP ${res.status} ${text.slice(0, 200)}`);
|
|
45
|
+
return JSON.parse(text);
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const SYNC_CURSOR_DIR = "sync-cursors";
|
|
50
|
+
function scopeDigest(scope) {
|
|
51
|
+
return createHash("sha256").update(scope, "utf8").digest("hex").slice(0, 32);
|
|
30
52
|
}
|
|
31
|
-
export function memorySyncCursorPath(
|
|
32
|
-
return join(
|
|
53
|
+
export function memorySyncCursorPath(memoryEngineDir, scope) {
|
|
54
|
+
return join(memoryEngineDir, SYNC_CURSOR_DIR, `${scopeDigest(scope)}.json`);
|
|
33
55
|
}
|
|
34
|
-
function
|
|
35
|
-
return
|
|
56
|
+
function unusableCursor(scope, path, why) {
|
|
57
|
+
return new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — the cursor file ${path} cannot be read as a sync cursor (${why}); the layer stays unsynced until the operator picks one of two ways out: ` +
|
|
58
|
+
"(1) restore the file from a backup; (2) delete it to accept re-convergence from an empty baseline — entries changed on both sides are reported as conflicts, " +
|
|
59
|
+
"and entries deleted on one side since the last round come back from the other side");
|
|
36
60
|
}
|
|
37
|
-
|
|
61
|
+
function cursorOf(parsed) {
|
|
62
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
63
|
+
return "not a JSON object";
|
|
64
|
+
const c = parsed;
|
|
65
|
+
const baseRevs = c["baseRevs"];
|
|
66
|
+
if (baseRevs === null || typeof baseRevs !== "object" || Array.isArray(baseRevs) || !Object.values(baseRevs).every((v) => typeof v === "string" && v.length > 0)) {
|
|
67
|
+
return "baseRevs is not a map of entry id to a non-empty rev";
|
|
68
|
+
}
|
|
69
|
+
const updatedAtMs = c["updatedAtMs"];
|
|
70
|
+
if (typeof updatedAtMs !== "number" || !Number.isFinite(updatedAtMs))
|
|
71
|
+
return "updatedAtMs is not a finite number";
|
|
72
|
+
return { scope: c["scope"], peer: c["peer"], store: c["store"], baseRevs: { ...baseRevs }, updatedAtMs };
|
|
73
|
+
}
|
|
74
|
+
async function loadLayerBaseline(path, scope) {
|
|
38
75
|
let raw;
|
|
39
76
|
try {
|
|
40
77
|
raw = await readFile(path, "utf8");
|
|
41
78
|
}
|
|
42
79
|
catch (err) {
|
|
43
|
-
|
|
80
|
+
if (err.code === "ENOENT")
|
|
81
|
+
return { kind: "none" };
|
|
82
|
+
throw new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — cannot read the cursor file ${path} (${String(err)}); the next round reads it again once it is readable`);
|
|
44
83
|
}
|
|
84
|
+
let parsed;
|
|
45
85
|
try {
|
|
46
|
-
|
|
47
|
-
return { kind: "parsed", value };
|
|
86
|
+
parsed = JSON.parse(raw);
|
|
48
87
|
}
|
|
49
|
-
catch
|
|
50
|
-
|
|
88
|
+
catch {
|
|
89
|
+
throw unusableCursor(scope, path, "not JSON");
|
|
51
90
|
}
|
|
91
|
+
const cursor = cursorOf(parsed);
|
|
92
|
+
if (typeof cursor === "string")
|
|
93
|
+
throw unusableCursor(scope, path, cursor);
|
|
94
|
+
return { kind: "cursor", cursor };
|
|
95
|
+
}
|
|
96
|
+
async function saveCursor(path, cursor) {
|
|
97
|
+
await mkdir(dirname(path), { recursive: true });
|
|
98
|
+
const tmp = `${path}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
99
|
+
await writeFile(tmp, JSON.stringify(cursor), "utf8");
|
|
100
|
+
await rename(tmp, path);
|
|
52
101
|
}
|
|
53
|
-
|
|
102
|
+
const LEGACY_PEER_SEGMENT = "central";
|
|
103
|
+
const LEGACY_ROOT_CURSOR_FILE = "sync-cursor-central.json";
|
|
104
|
+
function m31Window() {
|
|
105
|
+
const row = ONE_TIME_MIGRATIONS.find((w) => w.id === "M31");
|
|
106
|
+
if (row === undefined)
|
|
107
|
+
throw new Error("memory-sync-client: the one-time migration registry has no M31 row — this migration outlived its registry row; delete it with that row");
|
|
108
|
+
return row;
|
|
109
|
+
}
|
|
110
|
+
function scopeOfLegacyCursor(raw) {
|
|
111
|
+
let parsed;
|
|
112
|
+
try {
|
|
113
|
+
parsed = JSON.parse(raw);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
54
118
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
55
|
-
return
|
|
56
|
-
const
|
|
57
|
-
return
|
|
119
|
+
return undefined;
|
|
120
|
+
const scope = parsed["scope"];
|
|
121
|
+
return typeof scope === "string" && scope.length > 0 ? scope : undefined;
|
|
58
122
|
}
|
|
59
|
-
async function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
123
|
+
export async function relocateSyncCursorsM31(memoryEngineDir, log, opts) {
|
|
124
|
+
if (!migrationWindowOpen(opts.window ?? m31Window(), opts.today))
|
|
125
|
+
return { window: "closed", moved: 0, left: 0 };
|
|
126
|
+
let moved = 0;
|
|
127
|
+
let left = 0;
|
|
128
|
+
const codeOf = (err) => err?.code;
|
|
129
|
+
const exists = async (path) => {
|
|
130
|
+
try {
|
|
131
|
+
await stat(path);
|
|
132
|
+
return true;
|
|
67
133
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
134
|
+
catch (err) {
|
|
135
|
+
if (codeOf(err) === "ENOENT")
|
|
136
|
+
return false;
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const move = async (from, to) => {
|
|
141
|
+
if (await exists(to)) {
|
|
142
|
+
left += 1;
|
|
143
|
+
log.warn("memory_sync_cursor_not_relocated", { path: from, to, note: "this layer already has a cursor at its path, so the file written by an earlier version stays where it is and no round reads it; delete it" });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
await mkdir(dirname(to), { recursive: true });
|
|
147
|
+
try {
|
|
148
|
+
await rename(from, to);
|
|
82
149
|
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
if (codeOf(err) === "ENOENT")
|
|
152
|
+
return;
|
|
153
|
+
throw err;
|
|
154
|
+
}
|
|
155
|
+
moved += 1;
|
|
156
|
+
log.info("memory_sync_cursor_relocated", { from, to });
|
|
157
|
+
};
|
|
158
|
+
const legacyDir = join(memoryEngineDir, SYNC_CURSOR_DIR, LEGACY_PEER_SEGMENT);
|
|
159
|
+
let names = [];
|
|
160
|
+
try {
|
|
161
|
+
names = await readdir(legacyDir);
|
|
83
162
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
163
|
+
catch (err) {
|
|
164
|
+
if (codeOf(err) !== "ENOENT")
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
for (const name of [...names].sort()) {
|
|
168
|
+
if (/^[0-9a-f]{32}\.json$/.test(name))
|
|
169
|
+
await move(join(legacyDir, name), join(memoryEngineDir, SYNC_CURSOR_DIR, name));
|
|
170
|
+
}
|
|
171
|
+
if (names.length > 0) {
|
|
172
|
+
try {
|
|
173
|
+
await rmdir(legacyDir);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
if (codeOf(err) !== "ENOENT" && codeOf(err) !== "ENOTEMPTY" && codeOf(err) !== "EEXIST")
|
|
177
|
+
throw err;
|
|
99
178
|
}
|
|
100
179
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
180
|
+
const legacyFile = join(memoryEngineDir, LEGACY_ROOT_CURSOR_FILE);
|
|
181
|
+
let raw;
|
|
182
|
+
try {
|
|
183
|
+
raw = await readFile(legacyFile, "utf8");
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
if (codeOf(err) !== "ENOENT")
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
if (raw !== undefined) {
|
|
190
|
+
const scope = scopeOfLegacyCursor(raw);
|
|
191
|
+
if (scope === undefined) {
|
|
192
|
+
left += 1;
|
|
193
|
+
log.warn("memory_sync_cursor_not_relocated", { path: legacyFile, note: "a cursor file written by an earlier version that is not a JSON object naming its scope, so no layer can take it as its baseline; restore it from a backup and restart, or delete it" });
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
await move(legacyFile, memorySyncCursorPath(memoryEngineDir, scope));
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return { window: "open", moved, left };
|
|
108
200
|
}
|
|
109
201
|
export const MAX_SYNC_ROUNDS = 32;
|
|
110
202
|
function enumerateScopes(backend) {
|
|
@@ -112,58 +204,46 @@ function enumerateScopes(backend) {
|
|
|
112
204
|
return typeof face === "function" ? face.call(backend) : { supported: false, reason: "the memory backend has no listScopes face" };
|
|
113
205
|
}
|
|
114
206
|
export function createMemorySyncRunner(opts) {
|
|
115
|
-
const
|
|
207
|
+
const now = opts.now ?? Date.now;
|
|
116
208
|
const coverage = { kind: "layers", layers: [opts.scope] };
|
|
117
209
|
const tracked = new Set();
|
|
118
210
|
let enumerationWarned = false;
|
|
119
211
|
const isCoveredProjectLayer = (scope) => scope !== opts.scope && parseProjectLayerKey(scope) !== undefined && replicationCovers(coverage, scope);
|
|
212
|
+
const storeOf = (scope) => opts.stores.storeOf(scope);
|
|
213
|
+
const memoryEngineDir = opts.stores.root;
|
|
214
|
+
storeOf(opts.scope);
|
|
120
215
|
const layersOfRound = () => {
|
|
121
|
-
const local = enumerateScopes(opts.backend);
|
|
216
|
+
const local = enumerateScopes(opts.stores.backend);
|
|
122
217
|
if (!local.supported && !enumerationWarned) {
|
|
123
218
|
enumerationWarned = true;
|
|
124
|
-
opts.log.warn("memory_sync_layer_enumeration_unsupported", { scope: opts.scope,
|
|
219
|
+
opts.log.warn("memory_sync_layer_enumeration_unsupported", { scope: opts.scope, reason: local.reason, note: "only the sync layer and tracked project layers are synced" });
|
|
125
220
|
}
|
|
126
221
|
const projectLayers = new Set([...(local.supported ? local.scopes : []), ...tracked].filter(isCoveredProjectLayer));
|
|
127
222
|
return [opts.scope, ...[...projectLayers].sort()];
|
|
128
223
|
};
|
|
129
224
|
const syncLayer = async (scope, reason) => {
|
|
130
|
-
const cursorPath = memorySyncCursorPath(
|
|
131
|
-
const baseline = await loadLayerBaseline(cursorPath, scope
|
|
132
|
-
if (baseline.kind === "predates-store") {
|
|
133
|
-
opts.log.warn(CURSOR_PREDATES_STORE_NOTICE, { scope, peer, path: baseline.path, note: CURSOR_PREDATES_STORE_OUTS });
|
|
134
|
-
throw new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — its baseline ${baseline.path} predates the store binding (see warn ${CURSOR_PREDATES_STORE_NOTICE})`);
|
|
135
|
-
}
|
|
225
|
+
const cursorPath = memorySyncCursorPath(memoryEngineDir, scope);
|
|
226
|
+
const baseline = await loadLayerBaseline(cursorPath, scope);
|
|
136
227
|
let cursor = baseline.kind === "cursor" ? baseline.cursor : undefined;
|
|
137
228
|
let result;
|
|
138
229
|
let subRound = 0;
|
|
139
230
|
for (;;) {
|
|
140
231
|
subRound += 1;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
...(opts.maxPullEntries !== undefined ? { maxPullEntries: opts.maxPullEntries } : {}),
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
catch (err) {
|
|
154
|
-
if (cursor === undefined || err?.code !== CURSOR_MISMATCH_CODE)
|
|
155
|
-
throw err;
|
|
156
|
-
opts.log.warn("memory_sync_cursor_rebound", { scope, peer, subRound, note: "the persisted cursor is bound to another store — running this round from no cursor and replacing it" });
|
|
157
|
-
cursor = undefined;
|
|
158
|
-
subRound -= 1;
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
232
|
+
result = await syncMemoryScope({
|
|
233
|
+
backend: storeOf(scope),
|
|
234
|
+
scope,
|
|
235
|
+
transport: opts.transport,
|
|
236
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
237
|
+
...(opts.now !== undefined ? { now: opts.now } : {}),
|
|
238
|
+
...(opts.maxPushEntries !== undefined ? { maxPushEntries: opts.maxPushEntries } : {}),
|
|
239
|
+
...(opts.maxPullEntries !== undefined ? { maxPullEntries: opts.maxPullEntries } : {}),
|
|
240
|
+
});
|
|
161
241
|
for (const w of result.warnings ?? [])
|
|
162
|
-
opts.log.warn("memory_sync_warning", { scope,
|
|
242
|
+
opts.log.warn("memory_sync_warning", { scope, subRound, warning: w });
|
|
163
243
|
if (!(result.pushTruncated === true || result.pullTruncated === true))
|
|
164
244
|
break;
|
|
165
245
|
if (subRound >= MAX_SYNC_ROUNDS) {
|
|
166
|
-
opts.log.warn("memory_sync_rounds_exhausted", { scope,
|
|
246
|
+
opts.log.warn("memory_sync_rounds_exhausted", { scope, rounds: subRound, note: "truncation flag still set at the round cap — remaining items ride the next trigger" });
|
|
167
247
|
break;
|
|
168
248
|
}
|
|
169
249
|
if (result.cursorAdvanced && result.cursor !== undefined) {
|
|
@@ -175,12 +255,11 @@ export function createMemorySyncRunner(opts) {
|
|
|
175
255
|
}
|
|
176
256
|
}
|
|
177
257
|
cursor = result.cursor ?? cursor;
|
|
178
|
-
opts.log.info("memory_sync_continuation", { reason, scope,
|
|
258
|
+
opts.log.info("memory_sync_continuation", { reason, scope, subRound, pushTruncated: result.pushTruncated === true, pullTruncated: result.pullTruncated === true });
|
|
179
259
|
}
|
|
180
260
|
for (const c of result.conflicts) {
|
|
181
261
|
opts.log.warn("memory_sync_conflict", {
|
|
182
262
|
scope,
|
|
183
|
-
peer,
|
|
184
263
|
side: c.side,
|
|
185
264
|
id: c.id,
|
|
186
265
|
reason: c.reason,
|
|
@@ -199,7 +278,6 @@ export function createMemorySyncRunner(opts) {
|
|
|
199
278
|
opts.log.info("memory_sync_round", {
|
|
200
279
|
reason,
|
|
201
280
|
scope,
|
|
202
|
-
peer,
|
|
203
281
|
ok: result.ok,
|
|
204
282
|
cursorAdvanced: result.cursorAdvanced,
|
|
205
283
|
pushedEntries: result.pushed.entries.length,
|
|
@@ -210,7 +288,12 @@ export function createMemorySyncRunner(opts) {
|
|
|
210
288
|
});
|
|
211
289
|
return result;
|
|
212
290
|
};
|
|
291
|
+
let relocated = false;
|
|
213
292
|
const round = async (reason) => {
|
|
293
|
+
if (!relocated) {
|
|
294
|
+
await relocateSyncCursorsM31(memoryEngineDir, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
|
|
295
|
+
relocated = true;
|
|
296
|
+
}
|
|
214
297
|
const layers = [];
|
|
215
298
|
const failures = [];
|
|
216
299
|
for (const scope of layersOfRound()) {
|
|
@@ -219,7 +302,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
219
302
|
}
|
|
220
303
|
catch (err) {
|
|
221
304
|
failures.push({ scope, err });
|
|
222
|
-
opts.log.warn("memory_sync_layer_failed", { reason, scope,
|
|
305
|
+
opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: String(err) });
|
|
223
306
|
}
|
|
224
307
|
}
|
|
225
308
|
if (failures.length > 0) {
|
|
@@ -242,7 +325,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
242
325
|
const syncOnce = (reason) => exclusive(() => round(reason));
|
|
243
326
|
const queueRound = (reason) => {
|
|
244
327
|
void syncOnce(reason).catch((err) => {
|
|
245
|
-
opts.log.warn("memory_sync_failed", { reason, scope: opts.scope,
|
|
328
|
+
opts.log.warn("memory_sync_failed", { reason, scope: opts.scope, err: String(err) });
|
|
246
329
|
});
|
|
247
330
|
};
|
|
248
331
|
const trigger = (reason) => {
|
|
@@ -259,6 +342,21 @@ export function createMemorySyncRunner(opts) {
|
|
|
259
342
|
tracked.add(scope);
|
|
260
343
|
queueRound("track");
|
|
261
344
|
};
|
|
262
|
-
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(
|
|
345
|
+
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), pending: () => pending };
|
|
346
|
+
}
|
|
347
|
+
export function startMemorySyncRunner(deps) {
|
|
348
|
+
const { sync } = deps;
|
|
349
|
+
const transport = createMemorySyncTransport({ url: sync.url, token: sync.token });
|
|
350
|
+
const runner = createMemorySyncRunner({
|
|
351
|
+
stores: deps.stores,
|
|
352
|
+
scope: sync.scope,
|
|
353
|
+
transport,
|
|
354
|
+
log: deps.log,
|
|
355
|
+
...(sync.maxPushEntries !== undefined ? { maxPushEntries: sync.maxPushEntries } : {}),
|
|
356
|
+
...(sync.maxPullEntries !== undefined ? { maxPullEntries: sync.maxPullEntries } : {}),
|
|
357
|
+
});
|
|
358
|
+
deps.log.info("memory_sync_enabled", buildMemorySyncEnabledFields(transport.peer, sync.scope, runner.cursorPathFor(sync.scope)));
|
|
359
|
+
runner.trigger("boot");
|
|
360
|
+
return runner;
|
|
263
361
|
}
|
|
264
362
|
//# sourceMappingURL=memory-sync-client.js.map
|
package/dist/memory-sync.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type MemoryBackend, type MemoryEntry, type PatchReport } from "@sema-ag
|
|
|
2
2
|
import type { MemorySyncStore } from "./plugins/memory-sync-store-pg.js";
|
|
3
3
|
/** 一轮同步请求(验型后的干净形状——`parseMemorySyncRequest` 是唯一铸造点)。 */
|
|
4
4
|
export interface MemorySyncRequest {
|
|
5
|
-
/** 对端标识(`CentralSyncCursor.peer`,per-(scope,peer) 游标分区键)。 */
|
|
5
|
+
/** 对端标识(`CentralSyncCursor.peer`,per-(scope,peer) 游标分区键)= 客户端 transport 的身份(本仓的客户端腿:规范化的中心基址,`memory-sync-client.ts` `memorySyncPeerOf`)。 */
|
|
6
6
|
peer: string;
|
|
7
7
|
/** 客户端游标的共同基线:id → rev at last completed sync round(空 map = 首轮)。 */
|
|
8
8
|
baseRevs: Record<string, string>;
|
package/dist/memory-sync.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { computeEntryRev, MEMORY_ORIGIN_CAUSES, nextSyncBaseline, reconcileMemoryEntries, screenInboundEntries, } from "@sema-agent/core";
|
|
2
2
|
import { exportMemoryScope } from "./memory-export.js";
|
|
3
|
+
import { MEMORY_SYNC_PEER_COLUMN_CHARS } from "./plugins/memory-key-guards.js";
|
|
3
4
|
export const MEMORY_SYNC_MAX_ENTRIES = 500;
|
|
4
5
|
export const MEMORY_SYNC_MAX_DELETES = 2000;
|
|
5
6
|
export function parseMemorySyncRequest(body, scope) {
|
|
@@ -7,8 +8,8 @@ export function parseMemorySyncRequest(body, scope) {
|
|
|
7
8
|
return { ok: false, error: "body must be a JSON object { peer, baseRevs, entries, deletes }" };
|
|
8
9
|
const b = body;
|
|
9
10
|
const peer = b["peer"];
|
|
10
|
-
if (typeof peer !== "string" || peer.length === 0 || peer.length >
|
|
11
|
-
return { ok: false, error:
|
|
11
|
+
if (typeof peer !== "string" || peer.length === 0 || peer.length > MEMORY_SYNC_PEER_COLUMN_CHARS)
|
|
12
|
+
return { ok: false, error: `peer must be a non-empty string of at most ${MEMORY_SYNC_PEER_COLUMN_CHARS} characters` };
|
|
12
13
|
const rawBase = b["baseRevs"];
|
|
13
14
|
if (rawBase === null || typeof rawBase !== "object" || Array.isArray(rawBase))
|
|
14
15
|
return { ok: false, error: "baseRevs must be an object mapping entry id → rev (may be empty on the first round)" };
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* batch; stacking a second wide-fanout move on it in one session was the higher-risk option for no added
|
|
11
11
|
* clarity.)
|
|
12
12
|
*
|
|
13
|
-
* The engine prices ONLY LLM tokens (core's `Stats.costBreakdown` = llmRoot / nestedSubagent /
|
|
14
|
-
*
|
|
13
|
+
* The engine prices ONLY LLM tokens (core's `Stats.costBreakdown` = llmRoot / nestedSubagent / compaction, plus the
|
|
14
|
+
* suggestions and side-call lines). It has NO $ for tool calls, sandbox wall-clock, or network egress — those are INFRA/service costs
|
|
15
15
|
* the service owns the data + rates for. This module is the thin report layer: it turns a
|
|
16
16
|
* per-run infra USAGE + the deployment's RATES into an infra breakdown, then composes it with the core LLM
|
|
17
17
|
* breakdown into one supervisor-facing total. Pure + rate-driven (rates default 0 ⇒ infra reads $0, exactly like
|