@sema-agent/server 7.139.0 → 7.141.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 +471 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +94 -26
- package/dist/adoption/runner.js +1 -1
- package/dist/adoption/sql.js +1 -1
- package/dist/approval-batch-census.d.ts +27 -0
- package/dist/approval-batch-census.js +27 -0
- package/dist/approval-card.d.ts +37 -22
- package/dist/approval-card.js +6 -3
- package/dist/approval.d.ts +15 -0
- package/dist/approval.js +10 -0
- package/dist/audit.d.ts +4 -2
- package/dist/audit.js +2 -2
- 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/boot/adoption.js +1 -1
- package/dist/boot/device-lane.d.ts +11 -0
- package/dist/boot/device-lane.js +16 -0
- package/dist/boot/engine-lease.d.ts +24 -3
- package/dist/boot/engine-lease.js +9 -2
- package/dist/boot/execution-env.js +18 -12
- package/dist/boot/execution-lane.d.ts +8 -0
- package/dist/boot/execution-lane.js +6 -1
- package/dist/boot/memory-boundary.d.ts +16 -53
- package/dist/boot/memory-boundary.js +10 -20
- package/dist/boot/memory-consolidation.d.ts +16 -12
- package/dist/boot/memory-consolidation.js +24 -13
- package/dist/boot/reapers.d.ts +2 -2
- package/dist/boot/resolve-spec.d.ts +3 -5
- package/dist/boot/resolve-spec.js +9 -8
- package/dist/boot/runner-deps.d.ts +3 -3
- package/dist/boot/runner-deps.js +1 -0
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.js +5 -0
- 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 +3 -1
- package/dist/boot/stage-06-runners.d.ts +2 -8
- package/dist/boot/stage-06-runners.js +3 -3
- package/dist/boot/stage-07-capability-layer.d.ts +2 -7
- package/dist/boot/stage-07-capability-layer.js +8 -10
- package/dist/boot/stage-08-reapers.d.ts +2 -7
- package/dist/boot/stage-09-leader.d.ts +2 -7
- package/dist/boot/stage-10-http-server.d.ts +2 -7
- package/dist/boot/stage-10-http-server.js +6 -4
- package/dist/boot/stores.d.ts +3 -6
- package/dist/boot/stores.js +37 -44
- 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 +36 -9
- package/dist/capabilities/execution-lane.d.ts +2 -2
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +9 -0
- package/dist/capability-facts.js +9 -0
- package/dist/card-void.d.ts +38 -0
- package/dist/card-void.js +23 -0
- package/dist/config-catalog.js +12 -8
- package/dist/config-provider.d.ts +18 -17
- package/dist/config-provider.js +28 -36
- package/dist/config-types.d.ts +36 -9
- package/dist/config.js +56 -46
- package/dist/delegated-session-owner.d.ts +23 -20
- package/dist/delegated-session-owner.js +2 -2
- package/dist/deployment-shape.d.ts +9 -8
- package/dist/deployment-shape.js +4 -4
- package/dist/device-store.d.ts +7 -0
- package/dist/device-store.js +3 -0
- package/dist/execution-config-root.d.ts +22 -0
- package/dist/execution-config-root.js +35 -0
- package/dist/execution-lane-caps.d.ts +145 -107
- package/dist/execution-lane-caps.js +144 -83
- package/dist/execution-placement.d.ts +3 -0
- package/dist/execution-placement.js +7 -0
- package/dist/fleet/drain-entry.d.ts +3 -0
- package/dist/hosted-posture.d.ts +8 -0
- package/dist/hosted-posture.js +4 -0
- package/dist/http/admission.js +3 -3
- package/dist/http/caller-trust.d.ts +3 -2
- package/dist/http/caller-trust.js +2 -1
- package/dist/http/dispatch.js +4 -0
- package/dist/http/live-frame.d.ts +24 -0
- package/dist/http/live-frame.js +16 -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 +49 -35
- package/dist/http/retired-service-deps.d.ts +1 -1
- package/dist/http/retired-service-deps.js +1 -1
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/adoption.js +1 -1
- package/dist/http/routes/approvals-assistant.js +1 -0
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/diagnostics.d.ts +0 -19
- package/dist/http/routes/diagnostics.js +16 -2
- package/dist/http/routes/memory-bundle.d.ts +2 -2
- package/dist/http/routes/memory-bundle.js +4 -1
- package/dist/http/routes/memory-compliance.js +9 -2
- 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 +7 -2
- package/dist/http/routes/run-permission-mode.d.ts +75 -0
- package/dist/http/routes/run-permission-mode.js +170 -0
- package/dist/http/routes/session-sync.d.ts +3 -6
- package/dist/http/routes/session-sync.js +7 -2
- package/dist/http/routes/sessions.js +33 -15
- package/dist/http/routes/tasks.js +67 -68
- package/dist/http/run-read-face.js +3 -3
- package/dist/http/send.d.ts +1 -1
- package/dist/http/send.js +2 -2
- package/dist/http/server.d.ts +14 -27
- package/dist/http/server.js +2 -2
- package/dist/http/sse-log.d.ts +3 -8
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +3 -1
- package/dist/identity-source.d.ts +1 -1
- package/dist/identity-source.js +1 -1
- package/dist/instruction-source-notice.d.ts +7 -0
- package/dist/instruction-source-notice.js +6 -6
- 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/mcp-assertion-header.d.ts +4 -0
- package/dist/mcp-assertion-header.js +5 -0
- 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-posture.d.ts +1 -1
- 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 +33 -9
- package/dist/memory-scope.js +17 -5
- package/dist/memory-sync-client.d.ts +34 -9
- package/dist/memory-sync-client.js +108 -28
- package/dist/observability/fail-open.d.ts +30 -2
- package/dist/observability/fail-open.js +30 -2
- package/dist/observability/metrics.js +0 -1
- package/dist/observability/run-terminal.d.ts +6 -2
- package/dist/observability/run-terminal.js +25 -26
- package/dist/observability/secret-env-scrub.d.ts +20 -1
- package/dist/observability/secret-env-scrub.js +14 -0
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -2
- package/dist/orchestration/workflow-completion-inbox.js +13 -5
- package/dist/orchestration/workflow-notify-journal.d.ts +5 -3
- package/dist/orchestration/workflow-notify-journal.js +2 -1
- package/dist/permission-mode-admission.d.ts +68 -0
- package/dist/permission-mode-admission.js +23 -0
- package/dist/permission-rule-list.d.ts +55 -6
- package/dist/permission-rule-list.js +52 -5
- package/dist/plugins/adoption-log-sql.js +4 -4
- package/dist/plugins/approval-ask-store-sql.js +14 -14
- 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 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-index-columns.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +65 -17
- package/dist/plugins/checkpoint-store-sql.js +90 -19
- package/dist/plugins/device-store-sql.d.ts +5 -2
- package/dist/plugins/device-store-sql.js +23 -20
- package/dist/plugins/ensure-index.d.ts +1 -2
- package/dist/plugins/execution-placement-sql.d.ts +19 -9
- package/dist/plugins/execution-placement-sql.js +20 -9
- package/dist/plugins/file-history-store-sql.d.ts +2 -2
- package/dist/plugins/file-history-store-sql.js +4 -4
- package/dist/plugins/file-run-store.d.ts +2 -2
- package/dist/plugins/file-run-store.js +1 -1
- package/dist/plugins/fork-routing-session-store.d.ts +10 -32
- package/dist/plugins/fork-routing-session-store.js +2 -61
- package/dist/plugins/image-bake-store-sql.js +9 -9
- package/dist/plugins/image-index-sql.js +11 -11
- package/dist/plugins/k8s-exec-protocol.d.ts +4 -1
- package/dist/plugins/k8s-exec-protocol.js +6 -1
- package/dist/plugins/leader-run-store-sql.js +4 -4
- package/dist/plugins/local-checkpoint-store.d.ts +10 -1
- package/dist/plugins/local-checkpoint-store.js +22 -0
- package/dist/plugins/local-session-store.d.ts +227 -35
- package/dist/plugins/local-session-store.js +518 -37
- package/dist/plugins/local-session-window.d.ts +17 -3
- package/dist/plugins/local-session-window.js +7 -13
- 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-optout-grant-store-sql.js +1 -1
- package/dist/plugins/memory-run-store.d.ts +2 -2
- package/dist/plugins/memory-run-store.js +1 -1
- package/dist/plugins/memory-sync-store-tidb.js +3 -3
- package/dist/plugins/one-time-migrations.d.ts +52 -11
- package/dist/plugins/one-time-migrations.js +101 -39
- package/dist/plugins/org-delivery-quota-store.js +2 -2
- package/dist/plugins/outcome-ledger-sql.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +4 -4
- package/dist/plugins/permission-rule-store-sql.js +16 -16
- package/dist/plugins/pg-pool.js +3 -2
- package/dist/plugins/pg-session-storage.d.ts +2 -0
- package/dist/plugins/pg-session-storage.js +3 -2
- package/dist/plugins/remote-env-adb.d.ts +7 -24
- package/dist/plugins/remote-env-adb.js +22 -5
- package/dist/plugins/remote-env-device.d.ts +2 -28
- package/dist/plugins/remote-env-device.js +2 -1
- package/dist/plugins/remote-env-e2b.d.ts +40 -27
- package/dist/plugins/remote-env-e2b.js +180 -20
- package/dist/plugins/remote-env-file-error.d.ts +1 -1
- package/dist/plugins/remote-env-file-error.js +8 -2
- package/dist/plugins/remote-env-host.d.ts +19 -35
- package/dist/plugins/remote-env-host.js +26 -3
- package/dist/plugins/remote-env-k8s.d.ts +22 -14
- package/dist/plugins/remote-env-k8s.js +150 -26
- package/dist/plugins/remote-env-local-docker.d.ts +27 -24
- package/dist/plugins/remote-env-local-docker.js +91 -24
- package/dist/plugins/remote-env-ssh.d.ts +4 -24
- package/dist/plugins/remote-env-ssh.js +4 -3
- 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 +1 -1
- package/dist/plugins/retention-store-sql.js +6 -6
- package/dist/plugins/run-store-sql.d.ts +3 -3
- package/dist/plugins/run-store-sql.js +19 -18
- package/dist/plugins/sandbox-read-floor.d.ts +273 -0
- package/dist/plugins/sandbox-read-floor.js +608 -0
- package/dist/plugins/schema-consistency.d.ts +6 -6
- package/dist/plugins/schema-consistency.js +1 -1
- 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-ownership.d.ts +2 -1
- package/dist/plugins/session-placement.d.ts +17 -4
- package/dist/plugins/session-placement.js +2 -1
- 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 +18 -20
- package/dist/plugins/session-store.js +4 -12
- 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 +35 -6
- package/dist/plugins/store-backend.js +27 -10
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/task-list-store-sql.js +11 -11
- package/dist/plugins/tidb-pool.js +7 -6
- package/dist/plugins/tidb-session-storage.js +4 -4
- package/dist/plugins/tidb-session-store.d.ts +4 -1
- package/dist/plugins/tidb-session-store.js +16 -15
- package/dist/plugins/tool-result-store-sql.d.ts +2 -2
- package/dist/plugins/tool-result-store-sql.js +3 -3
- 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/resource-suspend.d.ts +14 -12
- package/dist/resource-suspend.js +1 -1
- package/dist/resource-window.d.ts +4 -3
- package/dist/run-cancel-context.d.ts +34 -0
- package/dist/run-cancel-context.js +7 -0
- package/dist/run-local.js +14 -19
- package/dist/runs.d.ts +47 -16
- package/dist/runs.js +112 -113
- package/dist/security.d.ts +2 -2
- package/dist/server-secret-env.d.ts +2 -1
- package/dist/server-secret-env.js +2 -4
- package/dist/session-error-wire.d.ts +22 -4
- package/dist/session-error-wire.js +16 -0
- package/dist/session-incarnation.d.ts +10 -4
- package/dist/session-incarnation.js +1 -1
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-sync-kernel.d.ts +42 -1
- package/dist/session-sync-kernel.js +17 -0
- package/dist/session-sync.js +16 -4
- package/dist/sql-engine-coordinates.d.ts +49 -0
- package/dist/sql-engine-coordinates.js +27 -0
- package/dist/sql-engine-posture.d.ts +1 -1
- package/dist/sql-engine-posture.js +1 -1
- package/dist/standing-answer-grant.d.ts +31 -2
- package/dist/standing-answer-grant.js +14 -2
- package/dist/store-posture.d.ts +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-mcp.d.ts +21 -8
- package/dist/task-mcp.js +17 -9
- package/dist/task-settings.d.ts +52 -25
- package/dist/task-settings.js +91 -28
- package/dist/terminal.d.ts +103 -19
- package/dist/terminal.js +72 -20
- package/dist/trace/core-keyset-guard.d.ts +79 -2
- package/dist/trace/exit-seat.d.ts +7 -0
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/model-segment.d.ts +2 -2
- package/dist/trace/redact.d.ts +3 -1
- package/dist/trace/redact.js +395 -110
- package/dist/trace/self-minted-wire.d.ts +226 -2
- package/dist/trace/self-minted-wire.js +79 -0
- package/dist/verifier-pause-card.d.ts +8 -0
- package/dist/verifier-pause-card.js +19 -0
- package/dist/web-admin-client/apply-effective.d.ts +5 -7
- package/dist/web-admin-client/apply-effective.js +2 -2
- 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/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -2
- package/package.json +2 -2
- package/wire-changes/server@7.140.0.json +184 -0
- package/wire-changes/server@7.141.0.json +367 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { resolveMemoryScopeStore } from "@sema-agent/core";
|
|
2
|
+
import { memoryScopeContractOf } from "./memory-scope.js";
|
|
3
|
+
export function createMemoryScopeStores(input) {
|
|
4
|
+
const byPlane = new Map();
|
|
5
|
+
const resolve = (layer, backend) => resolveMemoryScopeStore({
|
|
6
|
+
scope: layer,
|
|
7
|
+
scopeContract: memoryScopeContractOf([layer]),
|
|
8
|
+
backend,
|
|
9
|
+
memoryEngineDir: input.root,
|
|
10
|
+
...(input.onIncident !== undefined ? { onIncident: input.onIncident } : {}),
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
backend: input.backend,
|
|
14
|
+
root: input.root,
|
|
15
|
+
namedLayers: input.namedLayers,
|
|
16
|
+
storeOf(layer) {
|
|
17
|
+
const { plane } = resolve(layer, PLANE_PROBE);
|
|
18
|
+
const known = byPlane.get(plane);
|
|
19
|
+
if (known !== undefined)
|
|
20
|
+
return known;
|
|
21
|
+
const store = resolve(layer, input.backend).backend;
|
|
22
|
+
byPlane.set(plane, store);
|
|
23
|
+
return store;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const PLANE_PROBE = Object.freeze({});
|
|
28
|
+
export class MemoryLayerRefusal extends Error {
|
|
29
|
+
code = "request.memory_layer";
|
|
30
|
+
constructor(message, cause) {
|
|
31
|
+
super(message, cause !== undefined ? { cause } : undefined);
|
|
32
|
+
this.name = "MemoryLayerRefusal";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const quoted = (layers) => layers.map((l) => JSON.stringify(l)).join(", ");
|
|
36
|
+
function storeOfNamed(stores, layer, whose, seat) {
|
|
37
|
+
try {
|
|
38
|
+
return stores.storeOf(layer);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err?.code !== "config.memory_scope_key")
|
|
42
|
+
throw err;
|
|
43
|
+
const why = err instanceof Error ? err.message : String(err);
|
|
44
|
+
throw new MemoryLayerRefusal(whose === "request"
|
|
45
|
+
? `the memory layer ${JSON.stringify(layer)} cannot be read: ${why}`
|
|
46
|
+
: `this deployment's configured memory layer ${JSON.stringify(layer)} cannot be read (${why}), so the store a request that names no layer belongs to cannot be told — name the layer in the request (${seat})`, err);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function memoryStoreForRequest(stores, layers, seat = "`scope`") {
|
|
50
|
+
if (layers.length > 0) {
|
|
51
|
+
const groups = new Map();
|
|
52
|
+
for (const layer of layers) {
|
|
53
|
+
const store = storeOfNamed(stores, layer, "request", seat);
|
|
54
|
+
const group = groups.get(store);
|
|
55
|
+
if (group === undefined)
|
|
56
|
+
groups.set(store, [layer]);
|
|
57
|
+
else
|
|
58
|
+
group.push(layer);
|
|
59
|
+
}
|
|
60
|
+
if (groups.size === 1)
|
|
61
|
+
return [...groups.keys()][0];
|
|
62
|
+
const split = [...groups.values()].map((g) => `[${quoted(g)}]`).join(" / ");
|
|
63
|
+
throw new MemoryLayerRefusal(`the memory layers ${quoted(layers)} live in ${groups.size} different memory stores on this deployment and one request reaches one store — send one request per store: ${split}`);
|
|
64
|
+
}
|
|
65
|
+
const groups = deploymentStoreGroups(stores, seat);
|
|
66
|
+
if (groups.size === 1)
|
|
67
|
+
return stores.backend;
|
|
68
|
+
const elsewhere = [...groups.entries()].filter(([store]) => store !== stores.backend).flatMap(([, g]) => g);
|
|
69
|
+
throw new MemoryLayerRefusal(`this deployment keeps memory layers in ${groups.size} stores — the project layers${groups.get(stores.backend).length > 0 ? ` and ${quoted(groups.get(stores.backend))}` : ""} in the deployment's store, ${quoted(elsewhere)} in another — so a request that names no layer cannot be told which store it means; name the layer in the request (${seat})`);
|
|
70
|
+
}
|
|
71
|
+
export function memoryStoresOfDeployment(stores, seat = "`scope`") {
|
|
72
|
+
return [...deploymentStoreGroups(stores, seat).keys()];
|
|
73
|
+
}
|
|
74
|
+
function deploymentStoreGroups(stores, seat) {
|
|
75
|
+
const groups = new Map([[stores.backend, []]]);
|
|
76
|
+
for (const layer of stores.namedLayers()) {
|
|
77
|
+
const store = storeOfNamed(stores, layer, "deployment", seat);
|
|
78
|
+
const group = groups.get(store);
|
|
79
|
+
if (group === undefined)
|
|
80
|
+
groups.set(store, [layer]);
|
|
81
|
+
else
|
|
82
|
+
group.push(layer);
|
|
83
|
+
}
|
|
84
|
+
return groups;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=memory-scope-stores.js.map
|
package/dist/memory-scope.d.ts
CHANGED
|
@@ -8,11 +8,35 @@ export declare const V2_SCOPE_PREFIXES: readonly string[];
|
|
|
8
8
|
* 一个 scope 键是否带 v2 typed 前缀。
|
|
9
9
|
*
|
|
10
10
|
* ⚠️ 判据刻意停在**前缀**层,不升级成 core 的 `parseScopeKey`:后者对「带可识别前缀但结构坏」的键
|
|
11
|
-
* 是 fail-loud
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* 是 fail-loud 抛错,而本谓词的两个读者都只是在决定「要不要挂 v2 标({@link memoryScopeContractOf})/
|
|
12
|
+
* 同步层要不要另给(`config.ts`)」——把抛点挪到这里会把 core 的校验时机整体前移(行为面改动,不在本条案的射程内)。
|
|
13
|
+
* 坏键仍由 core 在它自己的校验点 fail-loud。
|
|
14
14
|
*/
|
|
15
15
|
export declare function isV2ScopeKey(key: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 本服务给一组记忆层盖的**作用域契约**(core `TaskSpec.memory.scopeContract`)—— 一个判据、两个读者:
|
|
18
|
+
* · {@link memorySpecForRequest} 铸 spec 时盖在会话上的那一个(读列表 = 全局层 + 登记簿种子);
|
|
19
|
+
* · 同步执行器替一层问「这一层在哪只店」时报给引擎的那一个(`resolveMemoryScopeStore` 的 `scopeContract`,`memory-sync-client.ts`)。
|
|
20
|
+
* 契约决定**类型化键落哪个平面**(v2 下 `user:` / `userproj:` / `org:` 在个人平面,不透明键在项目平面;不声明则一律项目平面),
|
|
21
|
+
* 所以两个读者必须读同一只:会话把一层放在哪只店,同步就同步哪只店。
|
|
22
|
+
*
|
|
23
|
+
* 判据:列表里有类型化键(前缀表 {@link V2_SCOPE_PREFIXES})⇒ `"v2"`;没有 ⇒ `undefined`(不声明;显式 opt-in,不嗅探调用方数据 ——
|
|
24
|
+
* 这些键只来自本服务自己的铸点与运维的登记簿,见 {@link memorySpecForRequest} 的 142-S1.5 注)。
|
|
25
|
+
* 按层问与按会话问答同一只店:平面按**每把键自己的种类**分,契约只挪类型化键,而本判据下带类型化键的会话恒盖 `"v2"`。
|
|
26
|
+
* 🚧 前提是键为规范形:引擎读键前去掉首尾空白,本判据看原串 —— 带首尾空白的类型化键(`" user:alice"`,配置边界今天放行)本判据不认、引擎在 v2 下读得出,
|
|
27
|
+
* 于是同一把键在带类型化种子的会话里落个人平面、在不带种子的会话与同步执行器里落项目平面。这是会话面先有的不一致(不同项目的会话对同一层就答两只店,
|
|
28
|
+
* 同步执行器无从同时对上两者),收口点在配置边界(拒或规范化这一形,后者会挪存量记忆的平面),不在本谓词;未闭环。
|
|
29
|
+
*/
|
|
30
|
+
export declare function memoryScopeContractOf(scopes: readonly string[]): "v2" | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* 本部署**自己点名**的记忆层 —— 配置里写着的每一层(去重、去空串、保序):单用户的全局层(`MEMORY_SCOPE`,{@link memoryScopeFor} 单用户臂的答案)、
|
|
33
|
+
* 同步层(`MEMORY_SYNC_SCOPE`,缺省即全局层)、项目登记簿的种子层(`config.projects[*].defaultScopes`,{@link memorySpecForRequest} 盖进会话的读列表)、
|
|
34
|
+
* 整理层(`MEMORY_CONSOLIDATION_SCOPES`)。项目层(`userrepo:…`)按任务根铸、不在配置里,也就不在这里 —— 它们住部署的店。
|
|
35
|
+
*
|
|
36
|
+
* 唯一读者:记忆作用域店的「不点名层的动词答在哪只店」(`memory-scope-stores.ts` 的 {@link memoryStoreForRequest} 无层那一臂)。
|
|
37
|
+
* 登记簿随中心配置热换(`applyEffective` 在同一只 `config.projects` 对象上原地整表换内容),所以读者每次现调本函数,不在起服时冻一份。
|
|
38
|
+
*/
|
|
39
|
+
export declare function memoryLayersNamedBy(config: Pick<ServiceConfig, "requirePrincipal" | "memoryScope" | "memorySync" | "projects" | "memoryConsolidationDriver">): string[];
|
|
16
40
|
/** Resolve the memory scope for a request —— {@link memoryScopeFor} 的三形(按实现逐臂写;[ref] 合并前主会话点名:
|
|
17
41
|
* 旧版这里写「多租户恒 `undefined` ⇒ 记忆暗」,与下面 DB 后端那一臂正好相反,test [ref] 就是被这句带偏的)。
|
|
18
42
|
*
|
|
@@ -22,7 +46,7 @@ export declare function isV2ScopeKey(key: string): boolean;
|
|
|
22
46
|
* **读列表的第二层**,不是写家:写家是该任务的**项目层**,由 {@link memoryProjectKeyFor} 按任务根派生、经
|
|
23
47
|
* {@link memorySpecForRequest} 的 `projectKey` 位铸进 spec(读 = [项目, 本 scope],`writeScope` 显式 = 项目)。
|
|
24
48
|
* 本函数在授权期调(还不知道任务根),所以项目层不在这里。
|
|
25
|
-
* ② MULTI-TENANT + DB 后端(`requirePrincipal === true` ∧ `memoryEngineBackend` ∈ {`pg`, `
|
|
49
|
+
* ② MULTI-TENANT + DB 后端(`requirePrincipal === true` ∧ `memoryEngineBackend` ∈ {`pg`, `mysql`}):按**验过的**
|
|
26
50
|
* principal 铸 v2 typed 键 —— 无 projectId ⇒ `user:<tenant>`(core `formatUserScope`),有形状合法的
|
|
27
51
|
* projectId ⇒ `proj:<tenant>/<projectId>`(core `formatProjScope`);principal 缺席(匿名 run)⇒ `undefined`
|
|
28
52
|
* (无记忆)。scope 列是租户分区,授权就是「验过的 principal 进、一个租户的 scope 出」这一绑定;身份永远不来自
|
|
@@ -93,8 +117,8 @@ export declare function replicatesProjectLayersOf(coverage: MemoryReplicationCov
|
|
|
93
117
|
* Pure posture probe (memoryEngineBackendFor consumes it; main.ts logs it): `undefined` = no plane split
|
|
94
118
|
* (the lane's file plane IS this worker's fs — `in-process` / `host`), `dark` = split + fail-closed, `forced` =
|
|
95
119
|
* split + operator override.
|
|
96
|
-
*
|
|
97
|
-
* `lane === undefined || lane === "host"
|
|
120
|
+
* The plane question reads the execution environment block (`hostLocalPaths`, sole author `capabilitiesFor`) — it used to be the
|
|
121
|
+
* hand-written synonym `lane === undefined || lane === "host"`; readings are unchanged lane by lane.
|
|
98
122
|
*/
|
|
99
123
|
export declare function memoryEngineRemoteLanePosture(config: ServiceConfig): {
|
|
100
124
|
lane: ExecutionLane;
|
|
@@ -104,7 +128,7 @@ export declare function memoryEngineRemoteLanePosture(config: ServiceConfig): {
|
|
|
104
128
|
* N0 启动告警的**文案**(纯,可单测;boot/stores.ts 的装配点只负责喂「引擎这一腿真的接上了吗」并打日志)。
|
|
105
129
|
*
|
|
106
130
|
* 为什么要按引擎真身分腿(2026-08-12 复扫,已核真):{@link memoryEngineRemoteLanePosture} 的 `dark` 只对
|
|
107
|
-
* **file** 引擎腿有裁决权 —— 车道门的唯一消费者是 {@link memoryEngineBackendFor}。`MEMORY_ENGINE_BACKEND=pg|
|
|
131
|
+
* **file** 引擎腿有裁决权 —— 车道门的唯一消费者是 {@link memoryEngineBackendFor}。`MEMORY_ENGINE_BACKEND=pg|mysql`
|
|
108
132
|
* 的两条腿在 boot/stores.ts 里**先于**任何车道判断就被点亮(库是持久真身,与手的文件平面无关),于是同一次
|
|
109
133
|
* 启动会先打 `memory_engine_enabled {enabled:true, backend:"pg"}`,几行之后再打一句「memory dark
|
|
110
134
|
* (fail-closed) … Set MEMORY_ENGINE_REMOTE_LANE=allow」—— 两句直接对撞,而且把运维指向一个在该腿上**不接线**
|
|
@@ -148,13 +172,13 @@ engineWired: boolean): {
|
|
|
148
172
|
* (壳布局 `~/.sema/engine-data`)」时分家 ⇒ 记忆库每一次 Write 被硬拒(7.58.0 放行 ⇒ 回归);反向
|
|
149
173
|
* (`CONFIG_LOCAL_DIR=~/.sema` 当 dataRoot)则让配置根自身免检。两根本就是两件事:店根归誊本门,引擎根归本函数。
|
|
150
174
|
*
|
|
151
|
-
* 形与 boot/stores.ts 逐字同律:DB 记忆腿(pg/
|
|
175
|
+
* 形与 boot/stores.ts 逐字同律:DB 记忆腿(pg/mysql)的根 = `<localDataRoot ?? localRoot>/memory-work`
|
|
152
176
|
* (per-worker 物化区);file 腿 = boot file 后端的链 `resolveMemoryEngineRoot(memoryEngineDir)`。**不判引擎是否
|
|
153
177
|
* 启用**——纯路径推导,禁用态下豁免根仍以「若开引擎会写到哪」为根(boot 的 `engineDataRoot` 恒铸,经共享基座交每只 Runner)。
|
|
154
178
|
*/
|
|
155
179
|
export declare function memoryEngineRootFor(config: Pick<ServiceConfig, "memoryEngineBackend" | "memoryEngineDir" | "localDataRoot">, localRoot?: string): string;
|
|
156
180
|
/** file 后端的根链(`MEMORY_ENGINE_DIR ?? fallback ?? $AGENT_DATA_DIR ?? ~/.ai-agent`)—— 与后端选择器**无关**:
|
|
157
|
-
* `memoryEngineBackendFor` 恒造 file 后端(run-local 从 env 继承 `MEMORY_ENGINE_BACKEND=pg|
|
|
181
|
+
* `memoryEngineBackendFor` 恒造 file 后端(run-local 从 env 继承 `MEMORY_ENGINE_BACKEND=pg|mysql` 时仍是 file),
|
|
158
182
|
* 所以它的根必须走这条链而不是 `memoryEngineRootFor` 的分派(codex 修复验证轮 [medium]:分派会把 run-local 的
|
|
159
183
|
* 记忆搬到 `<localDataRoot>/memory-work`,存量记忆与控制面升级后隐形)。 */
|
|
160
184
|
export declare function fileMemoryEngineRoot(config: Pick<ServiceConfig, "memoryEngineDir">, fallbackRoot?: string): string;
|
package/dist/memory-scope.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { FileMemoryEngineBackend, resolveMemoryEngineRoot, deriveControlPlaneDir, formatUserScope, formatProjScope, normalizeMemorySpec } from "@sema-agent/core";
|
|
3
|
-
import {
|
|
3
|
+
import { capabilitiesFor, executionLaneOf } from "./execution-lane-caps.js";
|
|
4
4
|
import { assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
|
|
5
5
|
import { projectLayerForTaskRoot } from "./project-identity.js";
|
|
6
6
|
import { parseProjectLayerKey, projectLayerOwnerOf } from "./project-layer-key.js";
|
|
@@ -14,10 +14,22 @@ export const V2_SCOPE_PREFIXES = Object.freeze(Object.values(V2_SCOPE_PREFIX_BY_
|
|
|
14
14
|
export function isV2ScopeKey(key) {
|
|
15
15
|
return V2_SCOPE_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
16
16
|
}
|
|
17
|
+
export function memoryScopeContractOf(scopes) {
|
|
18
|
+
return scopes.some(isV2ScopeKey) ? "v2" : undefined;
|
|
19
|
+
}
|
|
20
|
+
export function memoryLayersNamedBy(config) {
|
|
21
|
+
const named = [
|
|
22
|
+
...(config.requirePrincipal === true ? [] : [config.memoryScope]),
|
|
23
|
+
...(config.memorySync !== undefined ? [config.memorySync.scope] : []),
|
|
24
|
+
...Object.values(config.projects ?? {}).flatMap((p) => p.defaultScopes ?? []),
|
|
25
|
+
...config.memoryConsolidationDriver.scopes,
|
|
26
|
+
];
|
|
27
|
+
return named.filter((s, i) => typeof s === "string" && s.length > 0 && named.indexOf(s) === i);
|
|
28
|
+
}
|
|
17
29
|
export { MEMORY_SCOPE_COLUMN_CHARS, assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
|
|
18
30
|
export function memoryScopeFor(config, principal, projectId) {
|
|
19
31
|
if (config.requirePrincipal === true) {
|
|
20
|
-
if (config.memoryEngineBackend === "pg" || config.memoryEngineBackend === "
|
|
32
|
+
if (config.memoryEngineBackend === "pg" || config.memoryEngineBackend === "mysql") {
|
|
21
33
|
if (!principal)
|
|
22
34
|
return undefined;
|
|
23
35
|
const scope = projectId ? formatProjScope(principal, projectId) : formatUserScope(principal);
|
|
@@ -64,7 +76,7 @@ export function replicatesProjectLayersOf(coverage, owner) {
|
|
|
64
76
|
}
|
|
65
77
|
export function memoryEngineRemoteLanePosture(config) {
|
|
66
78
|
const lane = executionLaneOf(config.remoteExec?.provider);
|
|
67
|
-
if (
|
|
79
|
+
if (capabilitiesFor(config).hostLocalPaths)
|
|
68
80
|
return undefined;
|
|
69
81
|
return { lane, posture: config.memoryEngineRemoteLaneAllowed === true ? "forced" : "dark" };
|
|
70
82
|
}
|
|
@@ -158,14 +170,14 @@ export function memorySpecForRequest(scope, projectKey, memoryWrite, defaultScop
|
|
|
158
170
|
throw new Error(`memory project scope ${JSON.stringify(projectScope)} equals the global memory scope (MEMORY_SCOPE) — the project layer and the global layer would be one directory. ` +
|
|
159
171
|
"Set MEMORY_SCOPE to a name outside the server-minted project-key families (see src/project-layer-key.ts).");
|
|
160
172
|
}
|
|
161
|
-
const isV2 = isV2ScopeKey;
|
|
162
173
|
const extras = (defaultScopes ?? []).filter((s, i, arr) => typeof s === "string" && s.length > 0 && s !== scope && s !== projectScope && arr.indexOf(s) === i);
|
|
163
174
|
const layered = projectKey !== undefined
|
|
164
175
|
? { scopes: projectScope !== undefined ? [projectScope, scope, ...extras] : [scope, ...extras], writeScope: projectScope ?? scope, projectKey }
|
|
165
176
|
: extras.length > 0
|
|
166
177
|
? { scopes: [scope, ...extras], writeScope: scope }
|
|
167
178
|
: { scopes: [scope] };
|
|
168
|
-
const
|
|
179
|
+
const contract = memoryScopeContractOf([scope, ...extras]);
|
|
180
|
+
const v2 = contract !== undefined ? { scopeContract: contract } : {};
|
|
169
181
|
const stamped = {};
|
|
170
182
|
if (originPolicy !== undefined) {
|
|
171
183
|
if (scope.startsWith("org:"))
|
|
@@ -1,4 +1,6 @@
|
|
|
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";
|
|
2
4
|
import { type OneTimeMigrationWindow } from "./plugins/one-time-migrations.js";
|
|
3
5
|
/** 结构化日志面(src/observability/logger.ts Logger 的结构子集——测试可注入捕获器)。 */
|
|
4
6
|
export interface MemorySyncLog {
|
|
@@ -18,7 +20,7 @@ export interface MemorySyncLog {
|
|
|
18
20
|
*/
|
|
19
21
|
export declare function memorySyncPeerOf(url: string): string;
|
|
20
22
|
/**
|
|
21
|
-
* 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer`
|
|
23
|
+
* 起服日志行 `memory_sync_enabled` 的字段(两处起服 —— `boot/stores.ts` 与 `run-local.ts` —— 同读这一只)。`peer` 是这一腿的对端身份(游标绑的就是它;排障时与停驻行的轴对读):
|
|
22
24
|
* 它已过 {@link memorySyncPeerOf} 的两道判(带凭据的身份起不来);仍过展示铸点({@link buildDisplaySafeUrl}),日志里的 URL 值一律经它 —— 两只判官
|
|
23
25
|
* (引擎的出口脱敏与本仓的展示形状律)不是同一份实现,本仓的那一只若遮了引擎放行的一段,这一行带遮记号而不是逐字(响亮,不漏)。
|
|
24
26
|
*/
|
|
@@ -41,9 +43,19 @@ export declare function createMemorySyncTransport(opts: {
|
|
|
41
43
|
/** The one call shape this transport makes (`fetch` itself fits it). */
|
|
42
44
|
fetchImpl?: (url: string, init: RequestInit) => Promise<Response>;
|
|
43
45
|
}): MemorySyncTransport;
|
|
44
|
-
/**
|
|
46
|
+
/** 一层的游标文件路径:引擎根 `memoryEngineDir`(= `RunnerDeps.memoryEngineDir` 的那一个值)下 `sync-cursors/<scope 摘要>.json`。一层一只,不随对端变、不随店变。
|
|
45
47
|
* 落在子目录里:与更早的根下一层文件名 `sync-cursor-<peer>.json` 的空间不相交。 */
|
|
46
|
-
export declare function memorySyncCursorPath(
|
|
48
|
+
export declare function memorySyncCursorPath(memoryEngineDir: string, scope: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* ⑦ 停驻层的出路(闭集;今天唯一成员)。`delete_cursor` = 删掉这一层的游标文件,下一轮从空基线重收敛。读面 `memorySync.parked[].wayOut` 与运维行同读这只词;
|
|
51
|
+
* 加一个词 ⇒ {@link wayOutSentence} 的穷尽 switch 编译红(句子先写好,词才上得了 wire)。
|
|
52
|
+
*/
|
|
53
|
+
export type MemorySyncWayOut = "delete_cursor";
|
|
54
|
+
/** ⑦ 一层停驻的读数(读面与运维行同读这一只记录;不带盘路径 —— 路径只进运维行)。 */
|
|
55
|
+
export interface MemorySyncParkedLayer {
|
|
56
|
+
readonly scope: string;
|
|
57
|
+
readonly wayOut: MemorySyncWayOut;
|
|
58
|
+
}
|
|
47
59
|
/** M31 一次的读数(测试按它断言)。`window: "closed"` ⇒ 一行都没读。 */
|
|
48
60
|
export interface SyncCursorRelocationM31Report {
|
|
49
61
|
readonly window: "open" | "closed";
|
|
@@ -54,12 +66,12 @@ export interface SyncCursorRelocationM31Report {
|
|
|
54
66
|
* ⑤ 一次性迁移 M31:把升级前的游标搬到各自那一层的路径(`rename`,字节不变;只搬不改,判归引擎)。`today` = UTC 日历日;窗关 ⇒ 不搬。
|
|
55
67
|
* · 上一版的分区目录 `sync-cursors/central/<scope 摘要>.json` ⇒ `sync-cursors/<同名>`(两种布局的文件名是同一只 scope 摘要);搬空的目录删掉;
|
|
56
68
|
* 名字不是摘要形的(写游标的 staging 残片)原地不动。
|
|
57
|
-
* · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(
|
|
69
|
+
* · 更早的 `sync-cursor-central.json` ⇒ 它记的那一层的路径;读不成对象 / 没有 scope ⇒ 原地不动、一行 warn(没法说它是哪一层的基线;句子只点名删掉它,⑦ 一出路律)。
|
|
58
70
|
* · 目标路径上已有游标 ⇒ 不搬、不删,一行 warn(这一层用路径上的那一份;留下的那只没有读者)。
|
|
59
71
|
* 与同一数据根上并跑的另一个进程(server + run-local)同搬:源已被对方搬走(`ENOENT`)⇒ 跳过。残余(如实):先查目标再 `rename` 之间若有写者在目标路径上
|
|
60
72
|
* 落下游标,会被盖掉 —— 写者只有本模块的 runner,而 runner 写任何游标之前先跑完本步。别的读写错 ⇒ 抛(这一轮整轮失败、下一轮再搬)。
|
|
61
73
|
*/
|
|
62
|
-
export declare function relocateSyncCursorsM31(
|
|
74
|
+
export declare function relocateSyncCursorsM31(memoryEngineDir: string, log: MemorySyncLog, opts: {
|
|
63
75
|
readonly today: string;
|
|
64
76
|
readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
|
|
65
77
|
}): Promise<SyncCursorRelocationM31Report>;
|
|
@@ -84,6 +96,8 @@ export interface MemorySyncRunner {
|
|
|
84
96
|
exclusive<T>(work: () => Promise<T>): Promise<T>;
|
|
85
97
|
/** 某一层的 cursor sidecar 路径(boot 日志/测试可见性)。 */
|
|
86
98
|
cursorPathFor(scope: string): string;
|
|
99
|
+
/** ⑦ 停驻的层(最近一轮被拒在游标上的那些;按轮序:同步层在前、项目层按键)。停驻事实的唯一读口 —— 读面与运维行读同一份。 */
|
|
100
|
+
parked(): readonly MemorySyncParkedLayer[];
|
|
87
101
|
/** 排队+在飞的轮数(测试的确定性等待面;0 = 完全空闲)。 */
|
|
88
102
|
pending(): number;
|
|
89
103
|
}
|
|
@@ -91,13 +105,13 @@ export interface MemorySyncRunner {
|
|
|
91
105
|
export declare const MAX_SYNC_ROUNDS = 32;
|
|
92
106
|
/**
|
|
93
107
|
* 组装同步执行器:同步层 `scope` + 同属主的项目层,逐层一轮(cursor 持久化 + core `syncMemoryScope` + conflicts 上报),串行化/节流。
|
|
94
|
-
*
|
|
108
|
+
* 每一层同步的店由记忆作用域店答(⑥);同步层建时就问(坏的层键 ⇒ 这里抛引擎的 `config.memory_scope_key`,原样)。
|
|
95
109
|
*/
|
|
96
110
|
export declare function createMemorySyncRunner(opts: {
|
|
97
|
-
backend
|
|
111
|
+
/** 记忆作用域店(本进程那一只):每一层的店问它的 `storeOf`;项目层的发现列它的 `backend`(部署的店本身);游标的根 = 它的 `root`。 */
|
|
112
|
+
stores: MemoryScopeStores;
|
|
98
113
|
/** 同步层(`config.memorySync.scope`);它同属主的项目层随它一起同步。 */
|
|
99
114
|
scope: string;
|
|
100
|
-
memoryRoot: string;
|
|
101
115
|
/** 对端 = 它的 `peer`(这一轮的对端只有这一个来源;游标绑的对端由引擎对它判)。 */
|
|
102
116
|
transport: MemorySyncTransport;
|
|
103
117
|
log: MemorySyncLog;
|
|
@@ -106,4 +120,15 @@ export declare function createMemorySyncRunner(opts: {
|
|
|
106
120
|
maxPushEntries?: number;
|
|
107
121
|
maxPullEntries?: number;
|
|
108
122
|
}): MemorySyncRunner;
|
|
123
|
+
/**
|
|
124
|
+
* 两个入口(HTTP 服务 `boot/stores.ts` 与 `run-local.ts`)起同步执行器的**唯一一处**:建 transport(对端身份在这里铸一次)、建执行器、
|
|
125
|
+
* 起服日志一行 `memory_sync_enabled`、fire-and-forget 起服那一轮。两处此前各手抄一份同样的装配(上限两键漏过一次);
|
|
126
|
+
* 店只经入口装配出的那只记忆作用域店取 —— 它的两件事实就是交给 Runner 的那两只,会话把一层放在哪只店由同样的两件事实决定。
|
|
127
|
+
*/
|
|
128
|
+
export declare function startMemorySyncRunner(deps: {
|
|
129
|
+
sync: NonNullable<ServiceConfig["memorySync"]>;
|
|
130
|
+
/** 入口装配出的记忆作用域店(= 本入口的记忆引擎对象)。 */
|
|
131
|
+
stores: MemoryScopeStores;
|
|
132
|
+
log: MemorySyncLog;
|
|
133
|
+
}): MemorySyncRunner;
|
|
109
134
|
//# sourceMappingURL=memory-sync-client.d.ts.map
|
|
@@ -50,13 +50,57 @@ const SYNC_CURSOR_DIR = "sync-cursors";
|
|
|
50
50
|
function scopeDigest(scope) {
|
|
51
51
|
return createHash("sha256").update(scope, "utf8").digest("hex").slice(0, 32);
|
|
52
52
|
}
|
|
53
|
-
export function memorySyncCursorPath(
|
|
54
|
-
return join(
|
|
53
|
+
export function memorySyncCursorPath(memoryEngineDir, scope) {
|
|
54
|
+
return join(memoryEngineDir, SYNC_CURSOR_DIR, `${scopeDigest(scope)}.json`);
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const CURSOR_AXES = { scope: true, peer: true, store: true };
|
|
57
|
+
const CURSOR_FOUND = { absent: true, other: true };
|
|
58
|
+
function wayOutSentence(wayOut, cursorPath) {
|
|
59
|
+
switch (wayOut) {
|
|
60
|
+
case "delete_cursor":
|
|
61
|
+
return (`one way out: delete the cursor file ${cursorPath} — the next round re-converges from an empty baseline: it pulls what the center holds for this layer, ` +
|
|
62
|
+
"pushes what this store holds and pushes no deletes; entries changed on both sides are reported as conflicts, and an entry deleted on one side since the last round comes back from the other side");
|
|
63
|
+
default: {
|
|
64
|
+
const unhandled = wayOut;
|
|
65
|
+
throw new Error(`memory sync: no sentence for way out ${String(unhandled)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function parkCauseSentence(cause) {
|
|
70
|
+
switch (cause.kind) {
|
|
71
|
+
case "cursor_refused":
|
|
72
|
+
return cause.detail === undefined
|
|
73
|
+
? "the sync engine refused its cursor"
|
|
74
|
+
: `the sync engine refused its cursor on the ${cause.detail.axis} axis (${cause.detail.found === "absent" ? `the cursor has no ${cause.detail.axis} binding` : `the cursor is bound to another ${cause.detail.axis}`})`;
|
|
75
|
+
case "cursor_unusable":
|
|
76
|
+
return `the cursor file cannot be read as a sync cursor (${cause.why})`;
|
|
77
|
+
default: {
|
|
78
|
+
const unhandled = cause;
|
|
79
|
+
throw new Error(`memory sync: unknown park cause ${JSON.stringify(unhandled)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
class MemorySyncLayerParked extends Error {
|
|
84
|
+
scope;
|
|
85
|
+
cursorPath;
|
|
86
|
+
parkCause;
|
|
87
|
+
wayOut;
|
|
88
|
+
constructor(scope, cursorPath, parkCause, wayOut = "delete_cursor") {
|
|
89
|
+
super(`memory sync: layer ${JSON.stringify(scope)} parked — ${parkCauseSentence(parkCause)}; ${wayOutSentence(wayOut, cursorPath)}`);
|
|
90
|
+
this.scope = scope;
|
|
91
|
+
this.cursorPath = cursorPath;
|
|
92
|
+
this.parkCause = parkCause;
|
|
93
|
+
this.wayOut = wayOut;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function cursorRefusalOf(err) {
|
|
97
|
+
if (err === null || typeof err !== "object" || err.code !== "config.memory_sync_cursor_mismatch")
|
|
98
|
+
return undefined;
|
|
99
|
+
const d = err.detail;
|
|
100
|
+
const axis = d?.axis;
|
|
101
|
+
const found = d?.found;
|
|
102
|
+
const known = typeof axis === "string" && Object.hasOwn(CURSOR_AXES, axis) && typeof found === "string" && Object.hasOwn(CURSOR_FOUND, found);
|
|
103
|
+
return { detail: known ? { axis, found } : undefined };
|
|
60
104
|
}
|
|
61
105
|
function cursorOf(parsed) {
|
|
62
106
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
@@ -86,11 +130,11 @@ async function loadLayerBaseline(path, scope) {
|
|
|
86
130
|
parsed = JSON.parse(raw);
|
|
87
131
|
}
|
|
88
132
|
catch {
|
|
89
|
-
throw
|
|
133
|
+
throw new MemorySyncLayerParked(scope, path, { kind: "cursor_unusable", why: "not JSON" });
|
|
90
134
|
}
|
|
91
135
|
const cursor = cursorOf(parsed);
|
|
92
136
|
if (typeof cursor === "string")
|
|
93
|
-
throw
|
|
137
|
+
throw new MemorySyncLayerParked(scope, path, { kind: "cursor_unusable", why: cursor });
|
|
94
138
|
return { kind: "cursor", cursor };
|
|
95
139
|
}
|
|
96
140
|
async function saveCursor(path, cursor) {
|
|
@@ -120,7 +164,7 @@ function scopeOfLegacyCursor(raw) {
|
|
|
120
164
|
const scope = parsed["scope"];
|
|
121
165
|
return typeof scope === "string" && scope.length > 0 ? scope : undefined;
|
|
122
166
|
}
|
|
123
|
-
export async function relocateSyncCursorsM31(
|
|
167
|
+
export async function relocateSyncCursorsM31(memoryEngineDir, log, opts) {
|
|
124
168
|
if (!migrationWindowOpen(opts.window ?? m31Window(), opts.today))
|
|
125
169
|
return { window: "closed", moved: 0, left: 0 };
|
|
126
170
|
let moved = 0;
|
|
@@ -155,7 +199,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
|
|
|
155
199
|
moved += 1;
|
|
156
200
|
log.info("memory_sync_cursor_relocated", { from, to });
|
|
157
201
|
};
|
|
158
|
-
const legacyDir = join(
|
|
202
|
+
const legacyDir = join(memoryEngineDir, SYNC_CURSOR_DIR, LEGACY_PEER_SEGMENT);
|
|
159
203
|
let names = [];
|
|
160
204
|
try {
|
|
161
205
|
names = await readdir(legacyDir);
|
|
@@ -166,7 +210,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
|
|
|
166
210
|
}
|
|
167
211
|
for (const name of [...names].sort()) {
|
|
168
212
|
if (/^[0-9a-f]{32}\.json$/.test(name))
|
|
169
|
-
await move(join(legacyDir, name), join(
|
|
213
|
+
await move(join(legacyDir, name), join(memoryEngineDir, SYNC_CURSOR_DIR, name));
|
|
170
214
|
}
|
|
171
215
|
if (names.length > 0) {
|
|
172
216
|
try {
|
|
@@ -177,7 +221,7 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
|
|
|
177
221
|
throw err;
|
|
178
222
|
}
|
|
179
223
|
}
|
|
180
|
-
const legacyFile = join(
|
|
224
|
+
const legacyFile = join(memoryEngineDir, LEGACY_ROOT_CURSOR_FILE);
|
|
181
225
|
let raw;
|
|
182
226
|
try {
|
|
183
227
|
raw = await readFile(legacyFile, "utf8");
|
|
@@ -190,10 +234,10 @@ export async function relocateSyncCursorsM31(memoryRoot, log, opts) {
|
|
|
190
234
|
const scope = scopeOfLegacyCursor(raw);
|
|
191
235
|
if (scope === undefined) {
|
|
192
236
|
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
|
|
237
|
+
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 and no round reads it; delete it" });
|
|
194
238
|
}
|
|
195
239
|
else {
|
|
196
|
-
await move(legacyFile, memorySyncCursorPath(
|
|
240
|
+
await move(legacyFile, memorySyncCursorPath(memoryEngineDir, scope));
|
|
197
241
|
}
|
|
198
242
|
}
|
|
199
243
|
return { window: "open", moved, left };
|
|
@@ -209,8 +253,11 @@ export function createMemorySyncRunner(opts) {
|
|
|
209
253
|
const tracked = new Set();
|
|
210
254
|
let enumerationWarned = false;
|
|
211
255
|
const isCoveredProjectLayer = (scope) => scope !== opts.scope && parseProjectLayerKey(scope) !== undefined && replicationCovers(coverage, scope);
|
|
256
|
+
const storeOf = (scope) => opts.stores.storeOf(scope);
|
|
257
|
+
const memoryEngineDir = opts.stores.root;
|
|
258
|
+
storeOf(opts.scope);
|
|
212
259
|
const layersOfRound = () => {
|
|
213
|
-
const local = enumerateScopes(opts.backend);
|
|
260
|
+
const local = enumerateScopes(opts.stores.backend);
|
|
214
261
|
if (!local.supported && !enumerationWarned) {
|
|
215
262
|
enumerationWarned = true;
|
|
216
263
|
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" });
|
|
@@ -219,22 +266,30 @@ export function createMemorySyncRunner(opts) {
|
|
|
219
266
|
return [opts.scope, ...[...projectLayers].sort()];
|
|
220
267
|
};
|
|
221
268
|
const syncLayer = async (scope, reason) => {
|
|
222
|
-
const cursorPath = memorySyncCursorPath(
|
|
269
|
+
const cursorPath = memorySyncCursorPath(memoryEngineDir, scope);
|
|
223
270
|
const baseline = await loadLayerBaseline(cursorPath, scope);
|
|
224
271
|
let cursor = baseline.kind === "cursor" ? baseline.cursor : undefined;
|
|
225
272
|
let result;
|
|
226
273
|
let subRound = 0;
|
|
227
274
|
for (;;) {
|
|
228
275
|
subRound += 1;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
276
|
+
try {
|
|
277
|
+
result = await syncMemoryScope({
|
|
278
|
+
backend: storeOf(scope),
|
|
279
|
+
scope,
|
|
280
|
+
transport: opts.transport,
|
|
281
|
+
...(cursor !== undefined ? { cursor } : {}),
|
|
282
|
+
...(opts.now !== undefined ? { now: opts.now } : {}),
|
|
283
|
+
...(opts.maxPushEntries !== undefined ? { maxPushEntries: opts.maxPushEntries } : {}),
|
|
284
|
+
...(opts.maxPullEntries !== undefined ? { maxPullEntries: opts.maxPullEntries } : {}),
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
const refusal = cursorRefusalOf(err);
|
|
289
|
+
if (refusal !== undefined)
|
|
290
|
+
throw new MemorySyncLayerParked(scope, cursorPath, { kind: "cursor_refused", detail: refusal.detail });
|
|
291
|
+
throw err;
|
|
292
|
+
}
|
|
238
293
|
for (const w of result.warnings ?? [])
|
|
239
294
|
opts.log.warn("memory_sync_warning", { scope, subRound, warning: w });
|
|
240
295
|
if (!(result.pushTruncated === true || result.pullTruncated === true))
|
|
@@ -286,22 +341,32 @@ export function createMemorySyncRunner(opts) {
|
|
|
286
341
|
return result;
|
|
287
342
|
};
|
|
288
343
|
let relocated = false;
|
|
344
|
+
let parkedLayers = [];
|
|
289
345
|
const round = async (reason) => {
|
|
290
346
|
if (!relocated) {
|
|
291
|
-
await relocateSyncCursorsM31(
|
|
347
|
+
await relocateSyncCursorsM31(memoryEngineDir, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
|
|
292
348
|
relocated = true;
|
|
293
349
|
}
|
|
294
350
|
const layers = [];
|
|
295
351
|
const failures = [];
|
|
352
|
+
const parkedNow = [];
|
|
296
353
|
for (const scope of layersOfRound()) {
|
|
297
354
|
try {
|
|
298
355
|
layers.push({ scope, result: await syncLayer(scope, reason) });
|
|
299
356
|
}
|
|
300
357
|
catch (err) {
|
|
301
358
|
failures.push({ scope, err });
|
|
302
|
-
|
|
359
|
+
if (err instanceof MemorySyncLayerParked) {
|
|
360
|
+
parkedNow.push({ scope, wayOut: err.wayOut });
|
|
361
|
+
const refused = err.parkCause.kind === "cursor_refused" ? err.parkCause.detail : undefined;
|
|
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
|
+
}
|
|
303
367
|
}
|
|
304
368
|
}
|
|
369
|
+
parkedLayers = Object.freeze(parkedNow);
|
|
305
370
|
if (failures.length > 0) {
|
|
306
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("; ")}`);
|
|
307
372
|
}
|
|
@@ -339,6 +404,21 @@ export function createMemorySyncRunner(opts) {
|
|
|
339
404
|
tracked.add(scope);
|
|
340
405
|
queueRound("track");
|
|
341
406
|
};
|
|
342
|
-
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(
|
|
407
|
+
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), parked: () => parkedLayers, pending: () => pending };
|
|
408
|
+
}
|
|
409
|
+
export function startMemorySyncRunner(deps) {
|
|
410
|
+
const { sync } = deps;
|
|
411
|
+
const transport = createMemorySyncTransport({ url: sync.url, token: sync.token });
|
|
412
|
+
const runner = createMemorySyncRunner({
|
|
413
|
+
stores: deps.stores,
|
|
414
|
+
scope: sync.scope,
|
|
415
|
+
transport,
|
|
416
|
+
log: deps.log,
|
|
417
|
+
...(sync.maxPushEntries !== undefined ? { maxPushEntries: sync.maxPushEntries } : {}),
|
|
418
|
+
...(sync.maxPullEntries !== undefined ? { maxPullEntries: sync.maxPullEntries } : {}),
|
|
419
|
+
});
|
|
420
|
+
deps.log.info("memory_sync_enabled", buildMemorySyncEnabledFields(transport.peer, sync.scope, runner.cursorPathFor(sync.scope)));
|
|
421
|
+
runner.trigger("boot");
|
|
422
|
+
return runner;
|
|
343
423
|
}
|
|
344
424
|
//# sourceMappingURL=memory-sync-client.js.map
|