@sema-agent/server 7.141.0 → 7.143.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 +657 -0
- package/USAGE.md +102 -30
- package/dist/adoption/plan.js +22 -0
- package/dist/approval-ask-audit-store.d.ts +61 -43
- package/dist/approval-ask-audit-store.js +69 -44
- package/dist/approval-ask-machine.d.ts +15 -7
- package/dist/approval-ask-machine.js +10 -4
- package/dist/approval-card.d.ts +126 -27
- package/dist/approval-card.js +103 -23
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-queue-kind.d.ts +27 -0
- package/dist/approval-queue-kind.js +6 -0
- package/dist/approval-reconciler.d.ts +41 -9
- package/dist/approval-reconciler.js +45 -42
- package/dist/audit.js +4 -2
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +17 -1
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +26 -23
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/governance-seams.d.ts +26 -41
- package/dist/boot/governance-seams.js +61 -28
- package/dist/boot/late-binds.d.ts +7 -4
- package/dist/boot/late-binds.js +3 -1
- package/dist/boot/leader.js +2 -4
- package/dist/boot/memory-consolidation.d.ts +0 -8
- package/dist/boot/memory-consolidation.js +5 -4
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.js +9 -7
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +19 -10
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.d.ts +10 -7
- package/dist/boot/runner-deps.js +20 -29
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +19 -4
- package/dist/boot/stage-02-web-admin-pull.js +3 -0
- package/dist/boot/stage-03-store-backend.d.ts +4 -1
- package/dist/boot/stage-03-store-backend.js +1 -2
- package/dist/boot/stage-04-stores.d.ts +5 -2
- package/dist/boot/stage-04-stores.js +6 -7
- package/dist/boot/stage-05-execution-env.d.ts +6 -3
- package/dist/boot/stage-05-execution-env.js +8 -6
- package/dist/boot/stage-06-runners.d.ts +6 -3
- package/dist/boot/stage-06-runners.js +3 -4
- package/dist/boot/stage-07-capability-layer.d.ts +6 -3
- package/dist/boot/stage-07-capability-layer.js +7 -2
- package/dist/boot/stage-08-reapers.d.ts +6 -3
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +6 -3
- package/dist/boot/stage-10-http-server.d.ts +6 -3
- package/dist/boot/stage-10-http-server.js +14 -5
- package/dist/boot/stores.d.ts +5 -2
- package/dist/boot/stores.js +10 -9
- package/dist/boot/web-admin.d.ts +5 -1
- package/dist/boot/web-admin.js +48 -36
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/hands-lane.d.ts +9 -39
- package/dist/capabilities/hands-lane.js +8 -13
- package/dist/capabilities/mcp-probe.d.ts +3 -2
- package/dist/capabilities/scenarios.d.ts +11 -1
- package/dist/capabilities/scenarios.js +1 -0
- package/dist/capabilities/skills.d.ts +1 -17
- package/dist/capabilities/skills.js +0 -8
- package/dist/capability-facts.d.ts +31 -7
- package/dist/capability-facts.js +27 -4
- package/dist/card-void.d.ts +6 -9
- package/dist/config-catalog.js +2 -1
- package/dist/config-dir-trust.js +58 -29
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +7 -0
- package/dist/config-provider.d.ts +10 -0
- package/dist/config-provider.js +15 -4
- package/dist/config-root-migration.d.ts +13 -0
- package/dist/config-root-migration.js +99 -0
- package/dist/config-types.d.ts +19 -8
- package/dist/config.d.ts +10 -3
- package/dist/config.js +18 -4
- package/dist/delegated-session-owner.d.ts +5 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- package/dist/execution-lane-caps.d.ts +2 -1
- package/dist/fleet/fleet-bus.d.ts +34 -55
- package/dist/fleet/fleet-bus.js +35 -100
- package/dist/fleet/fleet-reconciler.d.ts +4 -4
- package/dist/fleet/fleet-reconciler.js +1 -1
- package/dist/fleet/subagent-tail-bus.d.ts +14 -11
- package/dist/fleet/subagent-tail-bus.js +15 -28
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +58 -8
- package/dist/host-decision.d.ts +54 -20
- package/dist/host-decision.js +42 -14
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/approval-sweeps.js +11 -8
- package/dist/http/caller-trust.d.ts +1 -1
- package/dist/http/dispatch.js +4 -2
- package/dist/http/durable-run-leg.js +2 -2
- package/dist/http/engine-refusal.d.ts +16 -0
- package/dist/http/engine-refusal.js +17 -0
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
- package/dist/http/memory-compliance-failure-reply.js +4 -6
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +13 -0
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +76 -58
- package/dist/http/resume-restatement.d.ts +39 -0
- package/dist/http/resume-restatement.js +25 -0
- package/dist/http/route-ctx.d.ts +35 -10
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.d.ts +9 -1
- package/dist/http/routes/approvals-assistant.js +142 -31
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +16 -4
- package/dist/http/routes/memory-policy.d.ts +2 -2
- package/dist/http/routes/memory-policy.js +27 -7
- package/dist/http/routes/notify-wake.js +23 -25
- package/dist/http/routes/rules.js +48 -13
- package/dist/http/routes/runs.js +12 -11
- package/dist/http/routes/session-memory-sync.d.ts +91 -0
- package/dist/http/routes/session-memory-sync.js +174 -0
- package/dist/http/routes/session-sync.js +13 -20
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/sessions.d.ts +2 -2
- package/dist/http/routes/sessions.js +22 -23
- package/dist/http/routes/tasks.js +34 -54
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +9 -3
- package/dist/http/send.d.ts +5 -0
- package/dist/http/send.js +9 -2
- package/dist/http/server.d.ts +27 -15
- package/dist/http/server.js +4 -6
- package/dist/http/stored-body-replay.js +15 -4
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/http/wire-types.d.ts +17 -7
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +6 -5
- package/dist/leg-facts.d.ts +37 -0
- package/dist/leg-facts.js +15 -0
- package/dist/memory-bundle-engine.d.ts +1 -4
- package/dist/memory-bundle-engine.js +2 -2
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-operator-faces.d.ts +30 -66
- package/dist/memory-operator-faces.js +17 -88
- package/dist/memory-scope-stores.d.ts +38 -40
- package/dist/memory-scope-stores.js +51 -36
- package/dist/memory-scope.d.ts +0 -9
- package/dist/memory-scope.js +0 -9
- package/dist/memory-sync-client.d.ts +21 -21
- package/dist/memory-sync-client.js +62 -43
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +27 -5
- package/dist/memory-sync.js +29 -7
- package/dist/model-plane-face.d.ts +13 -7
- package/dist/model-plane-face.js +2 -1
- package/dist/observability/fail-open.d.ts +47 -14
- package/dist/observability/fail-open.js +37 -11
- package/dist/observability/run-terminal.d.ts +92 -34
- package/dist/observability/run-terminal.js +171 -162
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
- package/dist/orchestration/workflow-completion-inbox.js +0 -12
- package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
- package/dist/orchestration/workflow-park-decision.d.ts +8 -3
- package/dist/orchestration/workflow-park-decision.js +5 -3
- package/dist/org-delivery-gate.d.ts +11 -0
- package/dist/org-delivery-gate.js +19 -1
- package/dist/parked-decide.d.ts +13 -9
- package/dist/parked-decide.js +7 -7
- package/dist/parked-revive-surface.js +1 -1
- package/dist/peer-inbound-face.d.ts +127 -0
- package/dist/peer-inbound-face.js +158 -0
- package/dist/permission-rule-list.d.ts +13 -9
- package/dist/permission-rule-list.js +7 -3
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +64 -11
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +17 -14
- package/dist/plugins/approval-ask-store-sql.d.ts +70 -23
- package/dist/plugins/approval-ask-store-sql.js +79 -30
- package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
- package/dist/plugins/checkpoint-store-sql.js +9 -7
- package/dist/plugins/file-history-store-sql.d.ts +24 -18
- package/dist/plugins/file-history-store-sql.js +24 -19
- package/dist/plugins/file-run-store.d.ts +35 -1
- package/dist/plugins/file-run-store.js +108 -9
- package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
- package/dist/plugins/imported-layer-root-retirement.js +96 -0
- package/dist/plugins/local-checkpoint-store.d.ts +1 -1
- package/dist/plugins/local-checkpoint-store.js +2 -2
- package/dist/plugins/local-session-store.d.ts +42 -9
- package/dist/plugins/local-session-store.js +83 -33
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
- package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
- package/dist/plugins/mailbox-store-sql.d.ts +12 -8
- package/dist/plugins/mailbox-store-sql.js +76 -7
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +6 -1
- package/dist/plugins/memory-run-store.js +11 -1
- package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
- package/dist/plugins/memory-sync-store-pg.js +21 -0
- package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
- package/dist/plugins/memory-sync-store-tidb.js +13 -1
- package/dist/plugins/one-time-migrations.d.ts +58 -2
- package/dist/plugins/one-time-migrations.js +161 -4
- package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
- package/dist/plugins/org-delivery-quota-store.js +2 -12
- package/dist/plugins/park-commit-watch.d.ts +1 -1
- package/dist/plugins/permission-rule-store-file.d.ts +10 -1
- package/dist/plugins/permission-rule-store-file.js +15 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
- package/dist/plugins/permission-rule-store-sql.js +35 -2
- package/dist/plugins/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-e2b.d.ts +23 -4
- package/dist/plugins/remote-env-e2b.js +111 -28
- package/dist/plugins/remote-env-k8s.d.ts +61 -22
- package/dist/plugins/remote-env-k8s.js +133 -33
- package/dist/plugins/remote-env-local-docker.d.ts +15 -3
- package/dist/plugins/remote-env-local-docker.js +40 -12
- package/dist/plugins/retention-store-sql.d.ts +2 -2
- package/dist/plugins/run-store-sql.d.ts +9 -1
- package/dist/plugins/run-store-sql.js +9 -1
- package/dist/plugins/sandbox-read-floor.d.ts +5 -7
- package/dist/plugins/sandbox-read-floor.js +2 -7
- package/dist/plugins/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/store-backend.d.ts +33 -35
- package/dist/plugins/store-backend.js +10 -1
- package/dist/plugins/store-contracts.d.ts +50 -1
- package/dist/plugins/store-contracts.js +6 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +207 -29
- package/dist/plugins/tool-result-store-sql.d.ts +9 -8
- package/dist/plugins/tool-result-store-sql.js +23 -2
- package/dist/plugins/wiring-section-codec.d.ts +32 -0
- package/dist/plugins/wiring-section-codec.js +55 -0
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +5 -1
- package/dist/rules-consent.d.ts +28 -5
- package/dist/rules-consent.js +40 -28
- package/dist/run-local.js +11 -13
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +6 -8
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +9 -0
- package/dist/security.js +8 -0
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +36 -0
- package/dist/session-error-wire.js +13 -0
- package/dist/session-incarnation.d.ts +1 -1
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +93 -0
- package/dist/session-sync.d.ts +9 -14
- package/dist/session-sync.js +5 -5
- package/dist/session-workspace.d.ts +3 -13
- package/dist/session-workspace.js +0 -9
- package/dist/standing-answer-grant.d.ts +5 -0
- package/dist/standing-answer-grant.js +10 -0
- package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
- package/dist/stores-sql/session-policy-store-sql.js +3 -13
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +21 -10
- package/dist/task-settings.js +46 -13
- package/dist/terminal.d.ts +39 -19
- package/dist/terminal.js +65 -22
- package/dist/tool-approval.d.ts +56 -37
- package/dist/tool-approval.js +78 -71
- package/dist/trace/core-keyset-guard.d.ts +134 -14
- package/dist/trace/engine-notice-wire.d.ts +9 -3
- package/dist/trace/engine-notice-wire.js +7 -3
- package/dist/trace/exit-seat.d.ts +9 -2
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +10 -6
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/model-segment.d.ts +55 -25
- package/dist/trace/model-segment.js +101 -33
- package/dist/trace/project.d.ts +25 -5
- package/dist/trace/project.js +22 -0
- package/dist/trace/redact.d.ts +61 -9
- package/dist/trace/redact.js +1490 -468
- package/dist/trace/self-minted-wire.d.ts +745 -24
- package/dist/trace/self-minted-wire.js +239 -9
- package/dist/trace/wire-projection-faces.d.ts +2 -2
- package/dist/trace/wire-projection-faces.js +2 -2
- package/dist/verifier-pause-card.d.ts +33 -5
- package/dist/verifier-pause-card.js +15 -10
- package/dist/web-admin-client/apply-effective.d.ts +12 -16
- package/dist/web-admin-client/apply-effective.js +109 -9
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/types.d.ts +4 -0
- package/dist/workspace-space.d.ts +19 -3
- package/dist/workspace-space.js +8 -4
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
- package/package.json +3 -2
- package/wire-changes/server@7.142.0.json +639 -0
- package/wire-changes/server@7.143.0.json +1167 -0
|
@@ -1,30 +1,63 @@
|
|
|
1
|
-
import { resolveMemoryScopeStore } from "@sema-agent/core";
|
|
1
|
+
import { createMemoryDeployment, normalizeMemorySpec, resolveMemoryScopeStore, scopePlanesOf } from "@sema-agent/core";
|
|
2
2
|
import { memoryScopeContractOf } from "./memory-scope.js";
|
|
3
|
+
export function backendOwnsControlPlane(backend) {
|
|
4
|
+
return controlPlaneRootOf(backend) !== undefined;
|
|
5
|
+
}
|
|
6
|
+
export function controlPlaneRootOf(backend) {
|
|
7
|
+
const root = backend?.controlPlaneRoot;
|
|
8
|
+
return typeof root === "string" && root !== "" ? root : undefined;
|
|
9
|
+
}
|
|
3
10
|
export function createMemoryScopeStores(input) {
|
|
4
|
-
|
|
5
|
-
const resolve = (layer
|
|
11
|
+
let personal;
|
|
12
|
+
const resolve = (layer) => resolveMemoryScopeStore({
|
|
6
13
|
scope: layer,
|
|
7
14
|
scopeContract: memoryScopeContractOf([layer]),
|
|
8
|
-
backend,
|
|
15
|
+
backend: input.backend,
|
|
9
16
|
memoryEngineDir: input.root,
|
|
10
17
|
...(input.onIncident !== undefined ? { onIncident: input.onIncident } : {}),
|
|
11
18
|
});
|
|
19
|
+
const planeOf = (layer) => {
|
|
20
|
+
const contract = memoryScopeContractOf([layer]);
|
|
21
|
+
let spec;
|
|
22
|
+
try {
|
|
23
|
+
spec = normalizeMemorySpec({ scopes: [layer], writeScope: layer, ...(contract !== undefined ? { scopeContract: contract } : {}) });
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
spec = undefined;
|
|
27
|
+
}
|
|
28
|
+
if (spec === undefined || spec.writeScope === null)
|
|
29
|
+
return resolve(layer).plane;
|
|
30
|
+
return scopePlanesOf(spec.scopes, spec.writeScope, spec.scopeContract)?.writePlane ?? "project";
|
|
31
|
+
};
|
|
32
|
+
let deployment;
|
|
12
33
|
return {
|
|
13
34
|
backend: input.backend,
|
|
14
35
|
root: input.root,
|
|
15
|
-
|
|
36
|
+
captureRecords: input.captureRecords,
|
|
37
|
+
planeOf,
|
|
16
38
|
storeOf(layer) {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
39
|
+
const plane = planeOf(layer);
|
|
40
|
+
if (plane === "personal" && personal !== undefined)
|
|
41
|
+
return personal;
|
|
42
|
+
const store = resolve(layer).backend;
|
|
43
|
+
if (plane === "personal")
|
|
44
|
+
personal = store;
|
|
23
45
|
return store;
|
|
24
46
|
},
|
|
47
|
+
deployment() {
|
|
48
|
+
if (deployment !== undefined || !backendOwnsControlPlane(input.backend))
|
|
49
|
+
return deployment;
|
|
50
|
+
deployment = createMemoryDeployment({
|
|
51
|
+
backend: input.backend,
|
|
52
|
+
memoryEngineDir: input.root,
|
|
53
|
+
scopeContract: "v2",
|
|
54
|
+
...(input.onIncident !== undefined ? { onIncident: input.onIncident } : {}),
|
|
55
|
+
...(input.captureRecords !== undefined ? { memoryCaptureRecordStore: input.captureRecords } : {}),
|
|
56
|
+
});
|
|
57
|
+
return deployment;
|
|
58
|
+
},
|
|
25
59
|
};
|
|
26
60
|
}
|
|
27
|
-
const PLANE_PROBE = Object.freeze({});
|
|
28
61
|
export class MemoryLayerRefusal extends Error {
|
|
29
62
|
code = "request.memory_layer";
|
|
30
63
|
constructor(message, cause) {
|
|
@@ -33,7 +66,7 @@ export class MemoryLayerRefusal extends Error {
|
|
|
33
66
|
}
|
|
34
67
|
}
|
|
35
68
|
const quoted = (layers) => layers.map((l) => JSON.stringify(l)).join(", ");
|
|
36
|
-
function storeOfNamed(stores, layer
|
|
69
|
+
function storeOfNamed(stores, layer) {
|
|
37
70
|
try {
|
|
38
71
|
return stores.storeOf(layer);
|
|
39
72
|
}
|
|
@@ -41,16 +74,14 @@ function storeOfNamed(stores, layer, whose, seat) {
|
|
|
41
74
|
if (err?.code !== "config.memory_scope_key")
|
|
42
75
|
throw err;
|
|
43
76
|
const why = err instanceof Error ? err.message : String(err);
|
|
44
|
-
throw new MemoryLayerRefusal(
|
|
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);
|
|
77
|
+
throw new MemoryLayerRefusal(`the memory layer ${JSON.stringify(layer)} cannot be read: ${why}`, err);
|
|
47
78
|
}
|
|
48
79
|
}
|
|
49
80
|
export function memoryStoreForRequest(stores, layers, seat = "`scope`") {
|
|
50
81
|
if (layers.length > 0) {
|
|
51
82
|
const groups = new Map();
|
|
52
83
|
for (const layer of layers) {
|
|
53
|
-
const store = storeOfNamed(stores, layer
|
|
84
|
+
const store = storeOfNamed(stores, layer);
|
|
54
85
|
const group = groups.get(store);
|
|
55
86
|
if (group === undefined)
|
|
56
87
|
groups.set(store, [layer]);
|
|
@@ -62,25 +93,9 @@ export function memoryStoreForRequest(stores, layers, seat = "`scope`") {
|
|
|
62
93
|
const split = [...groups.values()].map((g) => `[${quoted(g)}]`).join(" / ");
|
|
63
94
|
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
95
|
}
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
96
|
+
const census = stores.deployment()?.stores() ?? [];
|
|
97
|
+
if (census.length <= 1)
|
|
67
98
|
return stores.backend;
|
|
68
|
-
|
|
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;
|
|
99
|
+
throw new MemoryLayerRefusal(`this deployment keeps memory in ${census.length} stores (the project store and the personal store under the memory engine root), so a request that names no layer cannot be told which store it means; name the layer in the request (${seat})`);
|
|
85
100
|
}
|
|
86
101
|
//# sourceMappingURL=memory-scope-stores.js.map
|
package/dist/memory-scope.d.ts
CHANGED
|
@@ -28,15 +28,6 @@ export declare function isV2ScopeKey(key: string): boolean;
|
|
|
28
28
|
* 同步执行器无从同时对上两者),收口点在配置边界(拒或规范化这一形,后者会挪存量记忆的平面),不在本谓词;未闭环。
|
|
29
29
|
*/
|
|
30
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[];
|
|
40
31
|
/** Resolve the memory scope for a request —— {@link memoryScopeFor} 的三形(按实现逐臂写;[ref] 合并前主会话点名:
|
|
41
32
|
* 旧版这里写「多租户恒 `undefined` ⇒ 记忆暗」,与下面 DB 后端那一臂正好相反,test [ref] 就是被这句带偏的)。
|
|
42
33
|
*
|
package/dist/memory-scope.js
CHANGED
|
@@ -17,15 +17,6 @@ export function isV2ScopeKey(key) {
|
|
|
17
17
|
export function memoryScopeContractOf(scopes) {
|
|
18
18
|
return scopes.some(isV2ScopeKey) ? "v2" : undefined;
|
|
19
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
|
-
}
|
|
29
20
|
export { MEMORY_SCOPE_COLUMN_CHARS, assertMemoryScopeWidth } from "./plugins/memory-key-guards.js";
|
|
30
21
|
export function memoryScopeFor(config, principal, projectId) {
|
|
31
22
|
if (config.requirePrincipal === true) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type memorySyncReportOf, type MemorySyncTransport } from "@sema-agent/core";
|
|
2
2
|
import type { ServiceConfig } from "./config-types.js";
|
|
3
3
|
import type { MemoryScopeStores } from "./memory-scope-stores.js";
|
|
4
4
|
import { type OneTimeMigrationWindow } from "./plugins/one-time-migrations.js";
|
|
@@ -8,15 +8,9 @@ export interface MemorySyncLog {
|
|
|
8
8
|
warn(msg: string, fields?: Record<string, unknown>): void;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* 不带 userinfo / query / fragment;拒句只点名规则、值只到 origin);
|
|
15
|
-
* ② 身份里有没有凭据 —— 判官是引擎包根的 `carriesCredential`(引擎同步客户端判 `transport.peer` 的同一只:任何 userinfo,或同主体出口脱敏会遮掉的任何一段,
|
|
16
|
-
* 例如路径段里的令牌形),判的就是规范化之后的身份 —— 引擎第一轮判的那个值。身份随每个请求体与落盘游标走,所以带凭据的地址拒而不清洗
|
|
17
|
-
* (清洗过的身份是另一个身份;令牌只走 `MEMORY_SYNC_TOKEN`);拒句不引地址的任何部分。
|
|
18
|
-
* 规范化 = WHATWG URL 的 `origin`(协议与主机小写、缺省端口省掉)+ 路径去尾斜杠;规范化之后装不进中心侧 `peer` 列({@link MEMORY_SYNC_PEER_COLUMN_CHARS},
|
|
19
|
-
* 每一轮都会被中心拒)⇒ 同样拒。
|
|
11
|
+
* 客户端腿的同步地址 `MEMORY_SYNC_URL` → 同步对端的身份(规范化基址)。判与铸只在 `memory-sync-identity.ts` 的唯一铸点({@link readMemorySyncIdentity});
|
|
12
|
+
* 本函数是那只铸点的「客户端腿」座位:值可以不是规范形(铸成规范形用),有缺陷 ⇒ 抛(拒句不引地址的路径与凭据)。`config.ts` 读 env 时调它拒启(引擎关着也拒),
|
|
13
|
+
* transport 建时再调它铸身份 —— 同一只纯函数,同一个答案。
|
|
20
14
|
*/
|
|
21
15
|
export declare function memorySyncPeerOf(url: string): string;
|
|
22
16
|
/**
|
|
@@ -75,20 +69,26 @@ export declare function relocateSyncCursorsM31(memoryEngineDir: string, log: Mem
|
|
|
75
69
|
readonly today: string;
|
|
76
70
|
readonly window?: Pick<OneTimeMigrationWindow, "removableAfter">;
|
|
77
71
|
}): Promise<SyncCursorRelocationM31Report>;
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
/**
|
|
73
|
+
* 一层的一次调用如它落定的那样:`{ scope, result }`(`syncMemoryScope` 兑现)或 `{ scope, thrown }`(它或本服务在这一层上抛的)。
|
|
74
|
+
* 型**从引擎报告的入参派生**(`memorySyncReportOf` 的 `layers` 元素;引擎没有单独导出这只型)—— 本服务不手抄一份。
|
|
75
|
+
*/
|
|
76
|
+
export type MemorySyncLayerCall = Extract<Parameters<typeof memorySyncReportOf>[0], {
|
|
77
|
+
readonly layers: unknown;
|
|
78
|
+
}>["layers"][number];
|
|
79
|
+
/** 一轮:这一轮每一层的每一次调用,按跑的顺序(同步层在前、项目层按键;一层续轮 = 多条;一层失败 = 那一层最后一条是 `thrown`)。 */
|
|
84
80
|
export interface MemorySyncRound {
|
|
85
|
-
readonly
|
|
81
|
+
readonly calls: readonly MemorySyncLayerCall[];
|
|
86
82
|
}
|
|
87
83
|
export interface MemorySyncRunner {
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
84
|
+
/** 这一腿的对端身份(transport 的 `peer`;会话触发口拿请求点名的目标与它比)。 */
|
|
85
|
+
readonly peer: string;
|
|
86
|
+
/** 一轮同步(串行:排在上一轮之后),逐层一轮;**从不 reject**(⑧):一层失败那一层以 `{ scope, thrown }` 收尾、别的层照跑,逐层失败各一行运维 warn。
|
|
87
|
+
* `reading`:会话触发的读集 —— 里面被本腿覆盖的项目层并进这一轮(并登记进跟踪表);不被覆盖的层不进轮。 */
|
|
88
|
+
syncOnce(reason: string, opts?: {
|
|
89
|
+
readonly reading?: readonly string[];
|
|
90
|
+
}): Promise<MemorySyncRound>;
|
|
91
|
+
/** fire-and-forget 一轮:上一轮在飞(排队/执行中)则跳过(inflight 节流)。 */
|
|
92
92
|
trigger(reason: string): void;
|
|
93
93
|
/** 跟踪一只本层属主的项目层(本地还没有条目也每轮拉它);首次跟踪即排一轮。不是本同步腿覆盖的项目层 ⇒ 抛(调用方的错)。 */
|
|
94
94
|
track(scope: string): void;
|
|
@@ -4,24 +4,17 @@ import { dirname, join } from "node:path";
|
|
|
4
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
|
-
import { MEMORY_SYNC_PEER_COLUMN_CHARS } from "./plugins/memory-key-guards.js";
|
|
8
7
|
import { ONE_TIME_MIGRATIONS, migrationWindowOpen } from "./plugins/one-time-migrations.js";
|
|
9
|
-
import {
|
|
8
|
+
import { buildMemorySyncIdentityBootRefusal, readMemorySyncIdentity } from "./memory-sync-identity.js";
|
|
10
9
|
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
10
|
+
import { errorText } from "./observability/err-text.js";
|
|
11
11
|
export function memorySyncPeerOf(url) {
|
|
12
|
-
const
|
|
13
|
-
if (
|
|
12
|
+
const r = readMemorySyncIdentity(url);
|
|
13
|
+
if ("flaw" in r) {
|
|
14
|
+
const refusal = buildMemorySyncIdentityBootRefusal("MEMORY_SYNC_URL", url, r.flaw, "put the token in MEMORY_SYNC_TOKEN and set MEMORY_SYNC_URL to the central base address itself");
|
|
14
15
|
throw new Error(refusal);
|
|
15
|
-
const u = new URL(url);
|
|
16
|
-
const identity = `${u.origin}${u.pathname.replace(/\/+$/, "")}`;
|
|
17
|
-
if (carriesCredential(identity)) {
|
|
18
|
-
throw new Error("memory sync: MEMORY_SYNC_URL holds a credential (a token-shaped part of its path) — the sync peer identity travels verbatim in every request body and " +
|
|
19
|
-
"cursor, so it is refused, never cleaned: put the token in MEMORY_SYNC_TOKEN and set MEMORY_SYNC_URL to the central base address itself");
|
|
20
16
|
}
|
|
21
|
-
|
|
22
|
-
throw new Error(`memory sync: MEMORY_SYNC_URL normalizes to ${identity.length} characters — the peer identity is the central request's peer, which holds at most ${MEMORY_SYNC_PEER_COLUMN_CHARS}: use a shorter base URL`);
|
|
23
|
-
}
|
|
24
|
-
return identity;
|
|
17
|
+
return r.identity;
|
|
25
18
|
}
|
|
26
19
|
export function buildMemorySyncEnabledFields(peer, scope, cursorPath) {
|
|
27
20
|
return { peer: buildDisplaySafeUrl(peer), scope, cursorPath };
|
|
@@ -53,7 +46,7 @@ function scopeDigest(scope) {
|
|
|
53
46
|
export function memorySyncCursorPath(memoryEngineDir, scope) {
|
|
54
47
|
return join(memoryEngineDir, SYNC_CURSOR_DIR, `${scopeDigest(scope)}.json`);
|
|
55
48
|
}
|
|
56
|
-
const CURSOR_AXES = { scope: true, peer: true, store: true };
|
|
49
|
+
const CURSOR_AXES = { scope: true, peer: true, store: true, incarnation: true };
|
|
57
50
|
const CURSOR_FOUND = { absent: true, other: true };
|
|
58
51
|
function wayOutSentence(wayOut, cursorPath) {
|
|
59
52
|
switch (wayOut) {
|
|
@@ -113,7 +106,18 @@ function cursorOf(parsed) {
|
|
|
113
106
|
const updatedAtMs = c["updatedAtMs"];
|
|
114
107
|
if (typeof updatedAtMs !== "number" || !Number.isFinite(updatedAtMs))
|
|
115
108
|
return "updatedAtMs is not a finite number";
|
|
116
|
-
|
|
109
|
+
const peerIncarnation = c["peerIncarnation"];
|
|
110
|
+
if (peerIncarnation !== undefined && (typeof peerIncarnation !== "string" || peerIncarnation.trim().length === 0 || carriesCredential(peerIncarnation))) {
|
|
111
|
+
return "peerIncarnation is not a non-blank string free of credentials";
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
scope: c["scope"],
|
|
115
|
+
peer: c["peer"],
|
|
116
|
+
store: c["store"],
|
|
117
|
+
...(peerIncarnation !== undefined ? { peerIncarnation } : {}),
|
|
118
|
+
baseRevs: { ...baseRevs },
|
|
119
|
+
updatedAtMs,
|
|
120
|
+
};
|
|
117
121
|
}
|
|
118
122
|
async function loadLayerBaseline(path, scope) {
|
|
119
123
|
let raw;
|
|
@@ -123,7 +127,7 @@ async function loadLayerBaseline(path, scope) {
|
|
|
123
127
|
catch (err) {
|
|
124
128
|
if (err.code === "ENOENT")
|
|
125
129
|
return { kind: "none" };
|
|
126
|
-
throw new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — cannot read the cursor file ${path} (${
|
|
130
|
+
throw new Error(`memory sync: layer ${JSON.stringify(scope)} not synced — cannot read the cursor file ${path} (${errorText(err)}); the next round reads it again once it is readable`);
|
|
127
131
|
}
|
|
128
132
|
let parsed;
|
|
129
133
|
try {
|
|
@@ -265,7 +269,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
265
269
|
const projectLayers = new Set([...(local.supported ? local.scopes : []), ...tracked].filter(isCoveredProjectLayer));
|
|
266
270
|
return [opts.scope, ...[...projectLayers].sort()];
|
|
267
271
|
};
|
|
268
|
-
const syncLayer = async (scope, reason) => {
|
|
272
|
+
const syncLayer = async (scope, reason, calls) => {
|
|
269
273
|
const cursorPath = memorySyncCursorPath(memoryEngineDir, scope);
|
|
270
274
|
const baseline = await loadLayerBaseline(cursorPath, scope);
|
|
271
275
|
let cursor = baseline.kind === "cursor" ? baseline.cursor : undefined;
|
|
@@ -290,6 +294,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
290
294
|
throw new MemorySyncLayerParked(scope, cursorPath, { kind: "cursor_refused", detail: refusal.detail });
|
|
291
295
|
throw err;
|
|
292
296
|
}
|
|
297
|
+
calls.push({ scope, result });
|
|
293
298
|
for (const w of result.warnings ?? [])
|
|
294
299
|
opts.log.warn("memory_sync_warning", { scope, subRound, warning: w });
|
|
295
300
|
if (!(result.pushTruncated === true || result.pullTruncated === true))
|
|
@@ -303,7 +308,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
303
308
|
await saveCursor(cursorPath, result.cursor);
|
|
304
309
|
}
|
|
305
310
|
catch (err) {
|
|
306
|
-
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err:
|
|
311
|
+
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err: errorText(err) });
|
|
307
312
|
}
|
|
308
313
|
}
|
|
309
314
|
cursor = result.cursor ?? cursor;
|
|
@@ -324,7 +329,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
324
329
|
await saveCursor(cursorPath, result.cursor);
|
|
325
330
|
}
|
|
326
331
|
catch (err) {
|
|
327
|
-
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err:
|
|
332
|
+
opts.log.warn("memory_sync_cursor_write_failed", { path: cursorPath, err: errorText(err) });
|
|
328
333
|
}
|
|
329
334
|
}
|
|
330
335
|
opts.log.info("memory_sync_round", {
|
|
@@ -342,35 +347,46 @@ export function createMemorySyncRunner(opts) {
|
|
|
342
347
|
};
|
|
343
348
|
let relocated = false;
|
|
344
349
|
let parkedLayers = [];
|
|
350
|
+
const logLayerFailure = (reason, scope, err) => {
|
|
351
|
+
if (err instanceof MemorySyncLayerParked) {
|
|
352
|
+
const refused = err.parkCause.kind === "cursor_refused" ? err.parkCause.detail : undefined;
|
|
353
|
+
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 });
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: errorText(err) });
|
|
357
|
+
}
|
|
358
|
+
};
|
|
345
359
|
const round = async (reason) => {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
360
|
+
const calls = [];
|
|
361
|
+
let layers;
|
|
362
|
+
try {
|
|
363
|
+
if (!relocated) {
|
|
364
|
+
await relocateSyncCursorsM31(memoryEngineDir, opts.log, { today: new Date(now()).toISOString().slice(0, 10) });
|
|
365
|
+
relocated = true;
|
|
366
|
+
}
|
|
367
|
+
layers = layersOfRound();
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
for (const scope of [opts.scope, ...[...tracked].filter(isCoveredProjectLayer).sort()]) {
|
|
371
|
+
calls.push({ scope, thrown: err });
|
|
372
|
+
logLayerFailure(reason, scope, err);
|
|
373
|
+
}
|
|
374
|
+
return { calls };
|
|
349
375
|
}
|
|
350
|
-
const layers = [];
|
|
351
|
-
const failures = [];
|
|
352
376
|
const parkedNow = [];
|
|
353
|
-
for (const scope of
|
|
377
|
+
for (const scope of layers) {
|
|
354
378
|
try {
|
|
355
|
-
|
|
379
|
+
await syncLayer(scope, reason, calls);
|
|
356
380
|
}
|
|
357
381
|
catch (err) {
|
|
358
|
-
|
|
359
|
-
if (err instanceof MemorySyncLayerParked)
|
|
382
|
+
calls.push({ scope, thrown: err });
|
|
383
|
+
if (err instanceof MemorySyncLayerParked)
|
|
360
384
|
parkedNow.push({ scope, wayOut: err.wayOut });
|
|
361
|
-
|
|
362
|
-
opts.log.warn("memory_sync_layer_parked", { reason, scope, cursorPath: err.cursorPath, ...(refused !== undefined ? { axis: refused.axis, found: refused.found } : {}), wayOut: err.wayOut, note: err.message });
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
opts.log.warn("memory_sync_layer_failed", { reason, scope, cursorPath: memorySyncCursorPath(memoryEngineDir, scope), err: String(err) });
|
|
366
|
-
}
|
|
385
|
+
logLayerFailure(reason, scope, err);
|
|
367
386
|
}
|
|
368
387
|
}
|
|
369
388
|
parkedLayers = Object.freeze(parkedNow);
|
|
370
|
-
|
|
371
|
-
throw new Error(`memory sync round failed for ${failures.length} layer(s): ${failures.map((f) => `${f.scope}: ${f.err instanceof Error ? f.err.message : String(f.err)}`).join("; ")}`);
|
|
372
|
-
}
|
|
373
|
-
return { layers };
|
|
389
|
+
return { calls };
|
|
374
390
|
};
|
|
375
391
|
let chain = Promise.resolve();
|
|
376
392
|
let pending = 0;
|
|
@@ -384,11 +400,14 @@ export function createMemorySyncRunner(opts) {
|
|
|
384
400
|
});
|
|
385
401
|
return run;
|
|
386
402
|
};
|
|
387
|
-
const syncOnce = (reason
|
|
403
|
+
const syncOnce = (reason, syncOpts) => {
|
|
404
|
+
for (const scope of syncOpts?.reading ?? [])
|
|
405
|
+
if (isCoveredProjectLayer(scope))
|
|
406
|
+
tracked.add(scope);
|
|
407
|
+
return exclusive(() => round(reason));
|
|
408
|
+
};
|
|
388
409
|
const queueRound = (reason) => {
|
|
389
|
-
void syncOnce(reason)
|
|
390
|
-
opts.log.warn("memory_sync_failed", { reason, scope: opts.scope, err: String(err) });
|
|
391
|
-
});
|
|
410
|
+
void syncOnce(reason);
|
|
392
411
|
};
|
|
393
412
|
const trigger = (reason) => {
|
|
394
413
|
if (pending > 0)
|
|
@@ -404,7 +423,7 @@ export function createMemorySyncRunner(opts) {
|
|
|
404
423
|
tracked.add(scope);
|
|
405
424
|
queueRound("track");
|
|
406
425
|
};
|
|
407
|
-
return { syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), parked: () => parkedLayers, pending: () => pending };
|
|
426
|
+
return { peer: opts.transport.peer, syncOnce, trigger, track, exclusive, cursorPathFor: (scope) => memorySyncCursorPath(memoryEngineDir, scope), parked: () => parkedLayers, pending: () => pending };
|
|
408
427
|
}
|
|
409
428
|
export function startMemorySyncRunner(deps) {
|
|
410
429
|
const { sync } = deps;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 同步身份的拒因词(闭集,判序):形的四词逐字是契约包 `HttpUrlFlaw`;`credential` = 规范化之后带凭据;`too-long` = 装不进中心 `peer` 列;
|
|
3
|
+
* `not-canonical` = 值本身没缺陷、但不是它自己的规范形(只在「声明的身份」座位出现)。wire 面:`request.field_invalid` 拒体的 `reason`。
|
|
4
|
+
*/
|
|
5
|
+
export declare const MEMORY_SYNC_IDENTITY_FLAWS: readonly ["not-http-url", "userinfo", "query", "fragment", "credential", "too-long", "not-canonical"];
|
|
6
|
+
export type MemorySyncIdentityFlaw = (typeof MEMORY_SYNC_IDENTITY_FLAWS)[number];
|
|
7
|
+
/** 铸点的答:身份,或首个缺陷。 */
|
|
8
|
+
export type MemorySyncIdentityReading = {
|
|
9
|
+
readonly identity: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly flaw: Exclude<MemorySyncIdentityFlaw, "not-canonical">;
|
|
12
|
+
};
|
|
13
|
+
/** 唯一铸点(纯函数)。值 → 规范化身份,或首个缺陷。 */
|
|
14
|
+
export declare function readMemorySyncIdentity(raw: string): MemorySyncIdentityReading;
|
|
15
|
+
/** 「声明的身份」座位(中心声明 / 会话触发的 `target` / 中心入站的 `peer`):值必须**就是**它自己的规范化身份。`undefined` = 合格。 */
|
|
16
|
+
export declare function declaredMemorySyncIdentityFlaw(raw: string): MemorySyncIdentityFlaw | undefined;
|
|
17
|
+
/** 一个拒因词的规则句(穷举;新词不补 = 编译红)。只说规则,不含值。 */
|
|
18
|
+
export declare function memorySyncIdentityRule(flaw: MemorySyncIdentityFlaw): string;
|
|
19
|
+
/**
|
|
20
|
+
* 起服座位(env 键)的拒句:`memory sync: <键> <规则句>[: <出路>]`。形的四词走 URL 形拒句的铸点(值只到 origin);其余三词值不回显 ——
|
|
21
|
+
* `not-canonical` 例外地给出**规范形**(它已过凭据判官,是运维要写下的那一串;仍经展示铸点)。
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildMemorySyncIdentityBootRefusal(envKey: string, raw: string, flaw: MemorySyncIdentityFlaw, exit: string): string;
|
|
24
|
+
//# sourceMappingURL=memory-sync-identity.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { carriesCredential } from "@sema-agent/core";
|
|
2
|
+
import { httpUrlRule, pathBaseUrlFlaws } from "@sema-agent/settings-schema";
|
|
3
|
+
import { MEMORY_SYNC_PEER_COLUMN_CHARS } from "./plugins/memory-key-guards.js";
|
|
4
|
+
import { buildHttpUrlRefusal } from "./url-refusal.js";
|
|
5
|
+
import { buildDisplaySafeUrl } from "./trace/redact.js";
|
|
6
|
+
export const MEMORY_SYNC_IDENTITY_FLAWS = ["not-http-url", "userinfo", "query", "fragment", "credential", "too-long", "not-canonical"];
|
|
7
|
+
export function readMemorySyncIdentity(raw) {
|
|
8
|
+
const shape = pathBaseUrlFlaws(raw)[0];
|
|
9
|
+
if (shape !== undefined)
|
|
10
|
+
return { flaw: shape };
|
|
11
|
+
const u = new URL(raw);
|
|
12
|
+
const identity = `${u.origin}${u.pathname.replace(/\/+$/, "")}`;
|
|
13
|
+
if (carriesCredential(identity))
|
|
14
|
+
return { flaw: "credential" };
|
|
15
|
+
if (identity.length > MEMORY_SYNC_PEER_COLUMN_CHARS)
|
|
16
|
+
return { flaw: "too-long" };
|
|
17
|
+
return { identity };
|
|
18
|
+
}
|
|
19
|
+
export function declaredMemorySyncIdentityFlaw(raw) {
|
|
20
|
+
const r = readMemorySyncIdentity(raw);
|
|
21
|
+
if ("flaw" in r)
|
|
22
|
+
return r.flaw;
|
|
23
|
+
return r.identity === raw ? undefined : "not-canonical";
|
|
24
|
+
}
|
|
25
|
+
export function memorySyncIdentityRule(flaw) {
|
|
26
|
+
switch (flaw) {
|
|
27
|
+
case "not-http-url":
|
|
28
|
+
case "userinfo":
|
|
29
|
+
case "query":
|
|
30
|
+
case "fragment":
|
|
31
|
+
return httpUrlRule(flaw);
|
|
32
|
+
case "credential":
|
|
33
|
+
return "carries a credential (a token-shaped part of its path) — a sync identity travels verbatim in every request body and cursor and is published on the capability face, so it is refused, never cleaned";
|
|
34
|
+
case "too-long":
|
|
35
|
+
return `is longer than ${MEMORY_SYNC_PEER_COLUMN_CHARS} characters once normalized (the central peer column holds at most ${MEMORY_SYNC_PEER_COLUMN_CHARS})`;
|
|
36
|
+
case "not-canonical":
|
|
37
|
+
return "is not in canonical form (lower-case scheme and host, no default port, no trailing slash)";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function buildMemorySyncIdentityBootRefusal(envKey, raw, flaw, exit) {
|
|
41
|
+
switch (flaw) {
|
|
42
|
+
case "not-http-url":
|
|
43
|
+
case "userinfo":
|
|
44
|
+
case "query":
|
|
45
|
+
case "fragment":
|
|
46
|
+
return buildHttpUrlRefusal(`memory sync: ${envKey}`, raw, [flaw]);
|
|
47
|
+
case "credential":
|
|
48
|
+
return `memory sync: ${envKey} holds a credential — ${memorySyncIdentityRule(flaw)}: ${exit}`;
|
|
49
|
+
case "too-long":
|
|
50
|
+
return `memory sync: ${envKey} ${memorySyncIdentityRule(flaw)}: use a shorter base URL`;
|
|
51
|
+
case "not-canonical": {
|
|
52
|
+
const r = readMemorySyncIdentity(raw);
|
|
53
|
+
return `memory sync: ${envKey} ${memorySyncIdentityRule(flaw)} — what is declared is what is published and compared, so it is not normalized for you: set it to ${JSON.stringify("identity" in r ? buildDisplaySafeUrl(r.identity) : "")}`;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=memory-sync-identity.js.map
|
package/dist/memory-sync.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { type MemoryBackend, type MemoryEntry, type PatchReport } from "@sema-agent/core";
|
|
2
2
|
import type { MemorySyncStore } from "./plugins/memory-sync-store-pg.js";
|
|
3
|
+
import { type MemorySyncIdentityFlaw } from "./memory-sync-identity.js";
|
|
3
4
|
/** 一轮同步请求(验型后的干净形状——`parseMemorySyncRequest` 是唯一铸造点)。 */
|
|
4
5
|
export interface MemorySyncRequest {
|
|
5
|
-
/**
|
|
6
|
+
/** 主张:客户端游标的 `peerIncarnation` 原样(这份基线是对着本中心哪一份状态取的);缺席 = 没有主张(首轮,或 7.61.0 之前写下的游标)。
|
|
7
|
+
* ⓪ 在任何读写之前与分区化身比。 */
|
|
8
|
+
peerIncarnation?: string;
|
|
9
|
+
/** 对端标识(`CentralSyncCursor.peer`,per-(scope,peer) 游标分区键)= 客户端到达本中心用的同步身份(规范化基址)。验型时已过同步身份的唯一铸点
|
|
10
|
+
* (`memory-sync-identity.ts`,「声明的身份」座位:必须已是规范形、不带凭据);路由随后与本中心声明的身份按串比(不等 ⇒ 409,零写)。 */
|
|
6
11
|
peer: string;
|
|
7
12
|
/** 客户端游标的共同基线:id → rev at last completed sync round(空 map = 首轮)。 */
|
|
8
13
|
baseRevs: Record<string, string>;
|
|
@@ -35,34 +40,51 @@ export interface MemorySyncResponse {
|
|
|
35
40
|
id: string;
|
|
36
41
|
baseRev: string;
|
|
37
42
|
}>;
|
|
38
|
-
/** 本轮落库的新基线(草案偏离②)——客户端应用完 pull
|
|
43
|
+
/** 本轮落库的新基线(草案偏离②)——客户端应用完 pull 半场后把它持久化为自己的游标。`incarnation` = 答这一轮的分区化身(⓪;
|
|
44
|
+
* 有主张时恒等于主张),客户端存成游标的 `peerIncarnation`。 */
|
|
39
45
|
cursor: {
|
|
40
46
|
peer: string;
|
|
47
|
+
incarnation: string;
|
|
41
48
|
baseRevs: Record<string, string>;
|
|
42
49
|
updatedAtMs: number;
|
|
43
50
|
};
|
|
44
51
|
/** S2.5 分批腿:serverEntries 被 pull.limit 截断时为 true(缺席=未截断)。客户端续轮直到缺席。 */
|
|
45
52
|
pullTruncated?: true;
|
|
46
53
|
}
|
|
54
|
+
/** ⓪ 拒答:请求的主张不是本分区的化身,或带非空基线却没有主张 —— 答本分区的化身,什么都没读、什么都没写。路由按 200 原样发(引擎读它成游标拒)。 */
|
|
55
|
+
export interface MemorySyncRefusal {
|
|
56
|
+
refused: "baseline_incarnation";
|
|
57
|
+
incarnation: string;
|
|
58
|
+
}
|
|
59
|
+
/** 一轮的答:结果或拒答(两者都是 2xx 体)。 */
|
|
60
|
+
export type MemorySyncAnswer = MemorySyncResponse | MemorySyncRefusal;
|
|
47
61
|
/** 一轮 push 的条目数上限 = core REMOTE_HARVEST_MAX_FILES 同款口径(一个 scope 的文件面上限 500;
|
|
48
62
|
* 同步一轮打满全集也在此内)。deletes 上限同基数放宽(全集删除也要能一轮表达)。 */
|
|
49
63
|
export declare const MEMORY_SYNC_MAX_ENTRIES = 500;
|
|
50
64
|
export declare const MEMORY_SYNC_MAX_DELETES = 2000;
|
|
65
|
+
/** 验型的答:干净形,或体的形错(HTTP 422),或 `peer` 不是声明的同步身份(HTTP 400 `request.field_invalid`,`field: "peer"`,拒因词 = 铸点的闭集,不回显值)。 */
|
|
51
66
|
type ParseResult = {
|
|
52
67
|
ok: true;
|
|
53
68
|
value: MemorySyncRequest;
|
|
54
69
|
} | {
|
|
55
70
|
ok: false;
|
|
56
71
|
error: string;
|
|
72
|
+
} | {
|
|
73
|
+
ok: false;
|
|
74
|
+
peerFlaw: MemorySyncIdentityFlaw;
|
|
57
75
|
};
|
|
58
76
|
/** Shape-validate + NORMALIZE the request body (picked-not-spread:只挑认识的字段重建,body 上的
|
|
59
77
|
* 多余键绝不透传进数据面)。`scope` = 路径上已过 owner 门的 scope——每个条目的 scope 必须逐字
|
|
60
78
|
* 等于它(否则一个 owner-gated 路由就成了跨租户写门)。 */
|
|
61
79
|
export declare function parseMemorySyncRequest(body: unknown, scope: string): ParseResult;
|
|
80
|
+
/** ⓪ 栅栏收口:写完之后分区化身已不是开头那一只 —— 分区在这一轮中被重置。路由按 500 答(客户端零本地写)。 */
|
|
81
|
+
export declare class MemorySyncPartitionReset extends Error {
|
|
82
|
+
constructor(scope: string);
|
|
83
|
+
}
|
|
62
84
|
/**
|
|
63
|
-
* One full sync round for one (scope, peer). `backend` = 中心侧 entry plane(DB 权威),`
|
|
64
|
-
* per-(scope,peer) 游标面
|
|
85
|
+
* One full sync round for one (scope, peer). `backend` = 中心侧 entry plane(DB 权威),`sync` = 同步店
|
|
86
|
+
* (分区化身 + per-(scope,peer) 游标面)。纯数据参数进(路由已完成鉴权/owner 门/验型),答出(结果或 ⓪ 拒答)。
|
|
65
87
|
*/
|
|
66
|
-
export declare function performMemorySync(backend: MemoryBackend,
|
|
88
|
+
export declare function performMemorySync(backend: MemoryBackend, sync: Pick<MemorySyncStore, "putCursor" | "partitionIncarnation">, scope: string, req: MemorySyncRequest, now?: () => number): Promise<MemorySyncAnswer>;
|
|
67
89
|
export {};
|
|
68
90
|
//# sourceMappingURL=memory-sync.d.ts.map
|