@sema-agent/server 7.148.0 → 7.150.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 +551 -9
- package/README.md +17 -2
- package/README.zh-CN.md +5 -1
- package/USAGE.md +126 -68
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +33 -33
- package/dist/approval-gate-currency.d.ts +1 -1
- package/dist/approval-leg-origin.d.ts +33 -0
- package/dist/approval-leg-origin.js +4 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.js +2 -2
- package/dist/auth-bridge.d.ts +9 -1
- package/dist/auth-bridge.js +4 -4
- package/dist/auth-keys.d.ts +10 -1
- package/dist/auth-keys.js +8 -0
- package/dist/background-session-reap.d.ts +1 -1
- package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
- package/dist/boot/deferred-sandbox-path-env.js +1 -2
- package/dist/boot/execution-env.js +10 -11
- package/dist/boot/execution-lane.d.ts +6 -4
- package/dist/boot/execution-lane.js +3 -1
- package/dist/boot/org-delivery.d.ts +3 -2
- package/dist/boot/parked-revive-gate.d.ts +5 -5
- package/dist/boot/parked-revive-gate.js +2 -3
- package/dist/boot/resolve-spec.d.ts +4 -5
- package/dist/boot/resolve-spec.js +66 -28
- package/dist/boot/retention-lane.d.ts +1 -1
- package/dist/boot/runner-deps.js +3 -1
- package/dist/boot/session-faces.js +21 -21
- package/dist/boot/stage-01-config.d.ts +1 -0
- package/dist/boot/stage-01-config.js +4 -3
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -0
- package/dist/boot/stage-02-web-admin-pull.js +10 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -0
- package/dist/boot/stage-03-store-backend.js +4 -4
- package/dist/boot/stage-04-stores.d.ts +1 -0
- package/dist/boot/stage-05-execution-env.d.ts +1 -0
- package/dist/boot/stage-06-runners.d.ts +2 -1
- package/dist/boot/stage-06-runners.js +1 -2
- package/dist/boot/stage-07-capability-layer.d.ts +2 -1
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +2 -1
- package/dist/boot/stage-09-leader.d.ts +2 -1
- package/dist/boot/stage-10-http-server.d.ts +35 -2
- package/dist/boot/stage-10-http-server.js +9 -4
- package/dist/boot/stores.d.ts +3 -0
- package/dist/boot/stores.js +22 -19
- package/dist/boot/web-admin.d.ts +1 -1
- package/dist/capabilities/tool-defer.d.ts +1 -1
- package/dist/capability-facts.d.ts +70 -60
- package/dist/capability-facts.js +45 -49
- package/dist/card-settle-stored-scope.d.ts +56 -0
- package/dist/card-settle-stored-scope.js +27 -0
- package/dist/card-void.d.ts +7 -12
- package/dist/card-void.js +2 -7
- package/dist/config-catalog.d.ts +12 -0
- package/dist/config-catalog.js +37 -33
- package/dist/config-invariants.d.ts +5 -4
- package/dist/config-invariants.js +4 -4
- package/dist/config-types.d.ts +49 -16
- package/dist/config.d.ts +6 -5
- package/dist/config.js +35 -24
- package/dist/context-window-notice.d.ts +31 -0
- package/dist/context-window-notice.js +28 -0
- package/dist/deployment-governance.d.ts +7 -28
- package/dist/deployment-governance.js +1 -14
- package/dist/deployment-shape.d.ts +20 -11
- package/dist/deployment-shape.js +1 -0
- package/dist/device-ws-hub.d.ts +1 -0
- package/dist/device-ws-hub.js +64 -28
- package/dist/device-ws-protocol.d.ts +20 -6
- package/dist/device-ws-protocol.js +9 -2
- package/dist/engine-identity.d.ts +7 -1
- package/dist/engine-identity.js +5 -0
- package/dist/env-name-allowlist-knobs.d.ts +6 -5
- package/dist/env-name-allowlist-knobs.js +5 -0
- package/dist/execution-lane-caps.d.ts +20 -5
- package/dist/execution-lane-caps.js +18 -4
- package/dist/field-conflict.d.ts +2 -2
- package/dist/fleet/engine-signal.d.ts +14 -1
- package/dist/fleet/engine-signal.js +5 -1
- package/dist/fleet/fleet-reconciler.d.ts +1 -1
- package/dist/fleet/serving-lease.d.ts +18 -3
- package/dist/fleet/serving-lease.js +81 -35
- package/dist/fleet-client.d.ts +13 -11
- package/dist/fleet-client.js +53 -33
- package/dist/history-anchor.js +1 -1
- package/dist/hitl-delivery.d.ts +1 -3
- package/dist/hitl-delivery.js +1 -1
- package/dist/hooks/command-spawn.d.ts +7 -2
- package/dist/hooks/command-spawn.js +13 -6
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +11 -7
- package/dist/http/active-run-conflict.d.ts +3 -7
- package/dist/http/active-run-conflict.js +2 -2
- package/dist/http/admission.js +5 -5
- package/dist/http/approval-sweeps.js +5 -3
- package/dist/http/caller-trust.d.ts +2 -2
- package/dist/http/caller-trust.js +3 -3
- package/dist/http/checkpoint-ctx.d.ts +18 -63
- package/dist/http/checkpoint-ctx.js +1 -24
- package/dist/http/claim-heartbeat.d.ts +6 -6
- package/dist/http/claim-heartbeat.js +6 -6
- package/dist/http/claimed-run.d.ts +16 -9
- package/dist/http/claimed-run.js +8 -0
- package/dist/http/dispatch.js +7 -5
- package/dist/http/durable-run-leg.d.ts +8 -1
- package/dist/http/durable-run-leg.js +34 -15
- package/dist/http/execution-context.d.ts +115 -14
- package/dist/http/execution-context.js +130 -40
- package/dist/http/owner-access.d.ts +246 -21
- package/dist/http/owner-access.js +186 -8
- package/dist/http/principal-gate.d.ts +18 -11
- package/dist/http/principal-gate.js +3 -1
- package/dist/http/readiness.d.ts +4 -2
- package/dist/http/resume-ctx-read.d.ts +14 -3
- package/dist/http/resume-ctx-read.js +24 -3
- package/dist/http/resume-legs.d.ts +35 -3
- package/dist/http/resume-legs.js +262 -106
- package/dist/http/route-ctx.d.ts +29 -11
- package/dist/http/routes/a2a-serve.js +17 -14
- package/dist/http/routes/approvals-assistant.d.ts +3 -2
- package/dist/http/routes/approvals-assistant.js +11 -11
- package/dist/http/routes/capabilities.js +5 -4
- package/dist/http/routes/config-catalog.d.ts +1 -1
- package/dist/http/routes/diagnostics.d.ts +3 -1
- package/dist/http/routes/diagnostics.js +30 -5
- package/dist/http/routes/mcp-reconnect.js +2 -2
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -3
- package/dist/http/routes/principal-inbox.d.ts +3 -1
- package/dist/http/routes/principal-inbox.js +3 -2
- package/dist/http/routes/projects.d.ts +1 -0
- package/dist/http/routes/projects.js +5 -3
- package/dist/http/routes/retention-ops.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.d.ts +1 -1
- package/dist/http/routes/run-permission-mode.js +2 -2
- package/dist/http/routes/runs.d.ts +1 -1
- package/dist/http/routes/runs.js +33 -37
- package/dist/http/routes/session-memory-sync.js +4 -5
- package/dist/http/routes/session-sync.d.ts +24 -5
- package/dist/http/routes/session-sync.js +45 -37
- package/dist/http/routes/sessions-list.d.ts +1 -1
- package/dist/http/routes/sessions.d.ts +1 -1
- package/dist/http/routes/sessions.js +18 -44
- package/dist/http/routes/tasks.js +136 -101
- package/dist/http/routes/trace-usage.js +14 -14
- package/dist/http/routes/workflows.d.ts +1 -0
- package/dist/http/routes/workflows.js +20 -38
- package/dist/http/server.d.ts +12 -9
- package/dist/http/server.js +35 -6
- package/dist/http/stored-body-replay.d.ts +3 -24
- package/dist/http/stored-body-replay.js +2 -12
- package/dist/http/submit-ctx.d.ts +2 -2
- package/dist/http/submit-ctx.js +5 -5
- package/dist/http/submit-replay.d.ts +15 -2
- package/dist/http/submit-replay.js +10 -4
- package/dist/http/wire-types.d.ts +6 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/leader/endpoint.js +9 -8
- package/dist/leg-facts.d.ts +13 -2
- package/dist/leg-facts.js +2 -0
- package/dist/local-store-declaration.d.ts +54 -0
- package/dist/local-store-declaration.js +59 -0
- package/dist/mcp-assertion-header.d.ts +1 -1
- package/dist/mcp-assertion-header.js +1 -1
- package/dist/mcp-authorization-face.d.ts +18 -14
- package/dist/mcp-authorization-face.js +3 -2
- package/dist/memory-project-layer.js +1 -1
- package/dist/memory-scope.d.ts +4 -4
- package/dist/memory-scope.js +4 -4
- package/dist/memory-store-refusal.d.ts +1 -1
- package/dist/memory-sync-client.js +2 -2
- package/dist/model-context-window.d.ts +50 -11
- package/dist/model-context-window.js +20 -1
- package/dist/model-entry-refusal.d.ts +5 -4
- package/dist/model-entry-refusal.js +17 -3
- package/dist/model-reach-scope.d.ts +2 -1
- package/dist/observability/corrupt-read-seat.d.ts +1 -1
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/logger.d.ts +7 -0
- package/dist/observability/logger.js +3 -1
- package/dist/observability/metrics.js +1 -1
- package/dist/observability/run-terminal.d.ts +1 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/org-delivery-gate.d.ts +31 -6
- package/dist/org-delivery-gate.js +7 -4
- package/dist/owner-faces.d.ts +30 -5
- package/dist/owner-faces.js +2 -0
- package/dist/park-row-commit.d.ts +67 -0
- package/dist/park-row-commit.js +76 -0
- package/dist/parked-decide.d.ts +9 -7
- package/dist/parked-decide.js +25 -6
- package/dist/peer-directory.d.ts +1 -1
- package/dist/peer-seat.d.ts +1 -1
- package/dist/peer-seat.js +52 -59
- package/dist/plugins/adoption-log-sql.d.ts +3 -3
- package/dist/plugins/adoption-log-sql.js +19 -19
- package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
- package/dist/plugins/approval-ask-store-sql.js +3 -5
- package/dist/plugins/approval-exemption-skill-cutoff.d.ts +64 -0
- package/dist/plugins/approval-exemption-skill-cutoff.js +122 -0
- package/dist/plugins/approval-exemption-skill-m46.d.ts +0 -6
- package/dist/plugins/approval-exemption-skill-m46.js +6 -10
- package/dist/plugins/approval-exemption-store.d.ts +26 -1
- package/dist/plugins/approval-exemption-store.js +28 -19
- package/dist/plugins/ensure-schema-lock-policy.d.ts +36 -3
- package/dist/plugins/ensure-schema-lock-policy.js +75 -9
- package/dist/plugins/exec-path-law.d.ts +34 -0
- package/dist/plugins/exec-path-law.js +13 -0
- package/dist/plugins/file-run-store.d.ts +22 -9
- package/dist/plugins/file-run-store.js +51 -23
- package/dist/plugins/local-checkpoint-store.d.ts +8 -1
- package/dist/plugins/local-checkpoint-store.js +5 -1
- package/dist/plugins/local-data-root-admission.d.ts +52 -0
- package/dist/plugins/local-data-root-admission.js +100 -0
- package/dist/plugins/local-engine-high-water.d.ts +28 -7
- package/dist/plugins/local-engine-high-water.js +27 -22
- package/dist/plugins/local-session-store.d.ts +134 -49
- package/dist/plugins/local-session-store.js +149 -108
- package/dist/plugins/memory-run-store.d.ts +14 -9
- package/dist/plugins/memory-run-store.js +48 -22
- package/dist/plugins/one-time-migration-marker.d.ts +19 -3
- package/dist/plugins/one-time-migration-marker.js +7 -1
- package/dist/plugins/one-time-migrations.d.ts +110 -59
- package/dist/plugins/one-time-migrations.js +224 -136
- package/dist/plugins/org-delivery-quota-store.d.ts +34 -11
- package/dist/plugins/org-delivery-quota-store.js +29 -14
- package/dist/plugins/permission-rule-store-sql.d.ts +1 -1
- package/dist/plugins/permission-rule-store-sql.js +5 -9
- package/dist/plugins/pg-pool.d.ts +1 -1
- package/dist/plugins/pg-pool.js +14 -12
- package/dist/plugins/posix-shell-fs.js +4 -2
- package/dist/plugins/remote-env-adb.js +3 -2
- package/dist/plugins/remote-env-device.d.ts +3 -2
- package/dist/plugins/remote-env-device.js +8 -6
- package/dist/plugins/remote-env-e2b.d.ts +4 -8
- package/dist/plugins/remote-env-e2b.js +21 -15
- package/dist/plugins/remote-env-host.d.ts +51 -10
- package/dist/plugins/remote-env-host.js +97 -31
- package/dist/plugins/remote-env-k8s.js +7 -4
- package/dist/plugins/remote-env-local-docker.d.ts +6 -3
- package/dist/plugins/remote-env-local-docker.js +11 -25
- package/dist/plugins/remote-env-ssh.js +4 -3
- package/dist/plugins/remote-shell.d.ts +3 -1
- package/dist/plugins/remote-shell.js +3 -3
- package/dist/plugins/replica-registration.d.ts +35 -0
- package/dist/plugins/replica-registration.js +48 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
- package/dist/plugins/run-store-sql.d.ts +27 -8
- package/dist/plugins/run-store-sql.js +46 -18
- package/dist/plugins/scenario-prefix-switch-m42.d.ts +8 -12
- package/dist/plugins/scenario-prefix-switch-m42.js +9 -6
- package/dist/plugins/schema-consistency.d.ts +91 -5
- package/dist/plugins/schema-consistency.js +189 -7
- package/dist/plugins/schema-drift-sentence.d.ts +6 -5
- package/dist/plugins/schema-drift-sentence.js +13 -5
- package/dist/plugins/serving-requirement.d.ts +49 -0
- package/dist/plugins/serving-requirement.js +53 -0
- package/dist/plugins/session-placement.d.ts +5 -4
- package/dist/plugins/shell-sentinel-owner-m48.d.ts +22 -0
- package/dist/plugins/shell-sentinel-owner-m48.js +6 -0
- package/dist/plugins/stop-then-start-sentence.d.ts +55 -0
- package/dist/plugins/stop-then-start-sentence.js +51 -0
- package/dist/plugins/store-backend.d.ts +23 -9
- package/dist/plugins/store-backend.js +58 -24
- package/dist/plugins/store-contracts.d.ts +45 -5
- package/dist/plugins/store-contracts.js +19 -0
- package/dist/plugins/tidb-pool.d.ts +11 -6
- package/dist/plugins/tidb-pool.js +22 -12
- package/dist/plugins/workspace-custody.d.ts +44 -0
- package/dist/plugins/workspace-custody.js +47 -0
- package/dist/process-instance.d.ts +3 -0
- package/dist/process-instance.js +7 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.js +70 -0
- package/dist/project-identity.d.ts +8 -7
- package/dist/project-identity.js +40 -17
- package/dist/project-layer-key.d.ts +13 -2
- package/dist/project-layer-key.js +9 -1
- package/dist/question.d.ts +9 -21
- package/dist/question.js +28 -19
- package/dist/read-deny-floor-notice.d.ts +36 -0
- package/dist/read-deny-floor-notice.js +40 -0
- package/dist/{plain-string-record.d.ts → request-declaration-disposition.d.ts} +9 -23
- package/dist/request-declaration-disposition.js +39 -0
- package/dist/run-cancel-card.d.ts +28 -0
- package/dist/run-cancel-card.js +20 -0
- package/dist/run-cancel-context.d.ts +10 -0
- package/dist/run-cancel-context.js +1 -0
- package/dist/run-local.js +28 -13
- package/dist/runs.d.ts +31 -7
- package/dist/runs.js +34 -16
- package/dist/security.d.ts +3 -3
- package/dist/server-secret-env.d.ts +34 -20
- package/dist/server-secret-env.js +61 -18
- package/dist/session-agent.d.ts +4 -12
- package/dist/session-agent.js +0 -6
- package/dist/session-children-retire.d.ts +6 -3
- package/dist/session-children-retire.js +2 -5
- package/dist/session-incarnation.d.ts +18 -4
- package/dist/session-incarnation.js +4 -1
- package/dist/session-notify-door.d.ts +4 -4
- package/dist/session-notify-door.js +5 -6
- package/dist/session-rules-seat.d.ts +4 -3
- package/dist/session-rules-seat.js +3 -11
- package/dist/session-sync.js +3 -3
- package/dist/session-workspace.d.ts +3 -3
- package/dist/store-admission.d.ts +33 -0
- package/dist/store-admission.js +49 -0
- package/dist/store-posture.d.ts +5 -0
- package/dist/store-posture.js +18 -1
- package/dist/stores-sql/checkpoint-store-sql.d.ts +22 -3
- package/dist/stores-sql/checkpoint-store-sql.js +109 -23
- package/dist/stores-sql/file-history-store-sql.d.ts +1 -1
- package/dist/stores-sql/file-history-store-sql.js +3 -5
- package/dist/stores-sql/retention-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +4 -6
- package/dist/task-a2a.d.ts +3 -8
- package/dist/task-a2a.js +25 -27
- package/dist/task-cwd.d.ts +27 -10
- package/dist/task-cwd.js +3 -0
- package/dist/task-mcp.d.ts +3 -3
- package/dist/task-mcp.js +1 -1
- package/dist/task-settings.d.ts +13 -0
- package/dist/task-settings.js +14 -7
- package/dist/terminal.d.ts +2 -0
- package/dist/terminal.js +21 -6
- package/dist/tool-approval.d.ts +50 -41
- package/dist/tool-approval.js +96 -73
- package/dist/trace/engine-notice-wire.d.ts +38 -21
- package/dist/trace/engine-notice-wire.js +25 -4
- package/dist/trace/ledger-sink.d.ts +3 -1
- package/dist/trace/project.d.ts +8 -5
- package/dist/trace/project.js +11 -9
- package/dist/trace/projection-table.d.ts +33 -2
- package/dist/trace/redact.d.ts +12 -0
- package/dist/trace/redact.js +5 -1
- package/dist/trace/self-minted-wire.d.ts +268 -56
- package/dist/trace/self-minted-wire.js +110 -40
- package/dist/trace/tool-card.d.ts +3 -0
- package/dist/trace/tool-card.js +24 -0
- package/dist/trace/tool-output-media.d.ts +2 -1
- package/dist/unattended-permission-mode.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.d.ts +1 -1
- package/dist/web-admin-client/apply-effective.js +22 -9
- package/dist/web-admin-client/effective-keys.d.ts +1 -1
- package/dist/web-admin-client/skills-mcp.d.ts +1 -1
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
- package/docs/error-text-lexicon.json +317 -0
- package/package.json +4 -2
- package/scripts/embed-minimal.mjs +183 -0
- package/wire-changes/server@7.149.0.json +372 -0
- package/wire-changes/server@7.150.0.json +335 -0
- package/dist/plain-string-record.js +0 -70
package/dist/store-posture.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { StoreBackend } from "./plugins/store-backend.js";
|
|
|
4
4
|
import type { SqlEngineFacts } from "./plugins/sql-driver.js";
|
|
5
5
|
import { type SqlEngineCapability } from "./sql-engine-posture.js";
|
|
6
6
|
import { type MountRow, type MountTable } from "./mountinfo.js";
|
|
7
|
+
import type { LocalStoreDeclaration } from "./local-store-declaration.js";
|
|
7
8
|
/**
|
|
8
9
|
* 降级词闭集(**按后端族**,不按失败发生在哪一步):
|
|
9
10
|
* · `local_unwritable` —— **缺省推导**的 local 文件店在**空 / 不存在**的数据根上构造期失败(mkdir / 权限 / 只读挂载 / 磁盘满)。
|
|
@@ -124,10 +125,13 @@ export interface StorePosture {
|
|
|
124
125
|
readonly durability: StoreDurability;
|
|
125
126
|
/** 逐根明细:恒以数据根开头;有起服证据时再接封签钥匙目录与(接线时的)file 记忆根。 */
|
|
126
127
|
readonly roots: readonly StoreRootDurability[];
|
|
128
|
+
/** local 店交给两半店的声明(present-iff 装配出 local 后端;值 = 后端构造时 `localStoreDeclarationOf` 的那一份,装配方从后端读回,不重算)。 */
|
|
129
|
+
readonly localDeclaration?: LocalStoreDeclaration;
|
|
127
130
|
}
|
|
128
131
|
/**
|
|
129
132
|
* `build*`(纯数据)。`assembled` = 装配出的 `StoreBackend.kind`;缺席 = 没装出后端 ⇒ `memory`。`evidence` 缺席 = 这个宿主没有起服证据(测试夹具 / 桩):
|
|
130
133
|
* 只判数据根、且只按后端规则判(local 无证据即抛)。`memoryRootWired` = file 记忆引擎在本部署上真接了线(配了才算;没接线的记忆根不进最弱律)。
|
|
134
|
+
* `localDeclaration` = 装配出的 local 后端交给两半店的那一份声明(装配出 local 而缺席 = 装配缺陷,响亮抛;别的后端递了也不收)。
|
|
131
135
|
*/
|
|
132
136
|
export declare function buildStorePosture(a: {
|
|
133
137
|
readonly declared: StoreBackendWord;
|
|
@@ -135,6 +139,7 @@ export declare function buildStorePosture(a: {
|
|
|
135
139
|
readonly degradedReason: StoreDegradedReason | undefined;
|
|
136
140
|
readonly evidence: StoreEvidence | undefined;
|
|
137
141
|
readonly memoryRootWired: boolean;
|
|
142
|
+
readonly localDeclaration: LocalStoreDeclaration | undefined;
|
|
138
143
|
}): StorePosture;
|
|
139
144
|
/** 读面桩的诚实下限(没有装配值的宿主:声明 = 配置词,后端 = memory ⇒ `ephemeral`)。caps 与 `/health` 同调这一只。 */
|
|
140
145
|
export declare function unwiredStorePosture(declared: StoreBackendWord): StorePosture;
|
package/dist/store-posture.js
CHANGED
|
@@ -176,10 +176,27 @@ export function buildStorePosture(a) {
|
|
|
176
176
|
...(a.degradedReason !== undefined ? { degradedReason: a.degradedReason } : {}),
|
|
177
177
|
durability: weakestDurability(roots.map((r) => r.verdict)),
|
|
178
178
|
roots,
|
|
179
|
+
...localDeclarationOf(backend, a.localDeclaration),
|
|
179
180
|
};
|
|
180
181
|
}
|
|
182
|
+
function localDeclarationOf(backend, declaration) {
|
|
183
|
+
switch (backend) {
|
|
184
|
+
case "local":
|
|
185
|
+
if (declaration === undefined)
|
|
186
|
+
throw new Error("store posture: a local backend was assembled without the declaration it handed its stores — wiring defect (read it back from the backend)");
|
|
187
|
+
return { localDeclaration: declaration };
|
|
188
|
+
case "memory":
|
|
189
|
+
case "mysql":
|
|
190
|
+
case "pg":
|
|
191
|
+
return {};
|
|
192
|
+
default: {
|
|
193
|
+
const unhandled = backend;
|
|
194
|
+
throw new Error(`store posture: unknown backend kind ${String(unhandled)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
181
198
|
export function unwiredStorePosture(declared) {
|
|
182
|
-
return buildStorePosture({ declared, assembled: undefined, degradedReason: undefined, evidence: undefined, memoryRootWired: false });
|
|
199
|
+
return buildStorePosture({ declared, assembled: undefined, degradedReason: undefined, evidence: undefined, memoryRootWired: false, localDeclaration: undefined });
|
|
183
200
|
}
|
|
184
201
|
export function buildStoreFreshStartLine(posture, evidence) {
|
|
185
202
|
if (posture.backend !== "local" || evidence.dataRootHeldData)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Pool as MySqlPool } from "mysql2/promise";
|
|
2
2
|
import type { Pool as PgPool } from "pg";
|
|
3
|
-
import { type ExecutionOutcomeRecordWord, type GateOutcome, type Checkpoint, type CheckpointState, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type PendingSteerInput, type ResumeOutcome, type ResolveExpectation, type ReopenReason, type StoreDurability, type StoreFidelity } from "@sema-agent/core";
|
|
3
|
+
import { type CheckpointPage, type CheckpointRowRead, type CheckpointSessionCursor, type CheckpointSessionQuery, type ExecutionOutcomeRecordWord, type GateOutcome, type Checkpoint, type CheckpointState, type CheckpointStore, type CheckpointSummary, type CheckpointToken, type PendingSteerInput, type ResumeOutcome, type ResolveExpectation, type ReopenReason, type StoreDurability, type StoreFidelity } from "@sema-agent/core";
|
|
4
4
|
import { type SqlDialect, type SqlDriver } from "../plugins/sql-driver.js";
|
|
5
5
|
import type { CtxWriteClaim } from "../plugins/store-contracts.js";
|
|
6
6
|
import type { RetentionDeclaration } from "@sema-agent/core";
|
|
@@ -40,7 +40,7 @@ export declare const CHECKPOINT_ADDED_COLUMNS: ReadonlyArray<{
|
|
|
40
40
|
*
|
|
41
41
|
* 判据 = **能力探测**(恒空的 `WHERE 1=0` 读)+ **方言的缺列错误码**({@link isMissingColumnError},
|
|
42
42
|
* 判据属主 `sql-errors.ts`);其余错误**原样 rethrow**,一个字都不加工 —— 把一次瞬时故障诊断成「缺列」的拒因
|
|
43
|
-
* 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link
|
|
43
|
+
* 比它要挡的缺陷更贵([ref] 两轮收窄的结论逐字继承)。拒因的出路是**闭集三条**(与整库对账同一份,{@link probedMissingColumnRefusal}),
|
|
44
44
|
* 没有销毁性的一条:这张表里是在飞的停驻,出路是原表上加列。
|
|
45
45
|
*
|
|
46
46
|
* 位置:与三条先例同款,不在 `ensureSchema` 里(那条通道的契约是「只发 CREATE」),放在 boot 的
|
|
@@ -72,7 +72,6 @@ interface SteerColumnRead {
|
|
|
72
72
|
* 列有内容却读不出 ⇒ `lossy`(两个调用点对它的处置见 {@link SteerColumnRead})。
|
|
73
73
|
*/
|
|
74
74
|
export declare function readPendingSteerQueueColumn(queueRaw: unknown): SteerColumnRead;
|
|
75
|
-
/** SQL checkpoint 店的装配选项:托管留存声明。(ctx 栅栏写口的认领由调用方每次交进来 —— [ref]:栅栏的来源是认领本身,店不另绑 run 店。) */
|
|
76
75
|
export interface SqlCheckpointStoreOptions {
|
|
77
76
|
readonly retention?: RetentionDeclaration;
|
|
78
77
|
}
|
|
@@ -162,7 +161,27 @@ export declare class SqlCheckpointStore implements CheckpointStore {
|
|
|
162
161
|
private pendingRowQuery;
|
|
163
162
|
/** Create-once. core mints the token (`mintCheckpointToken`) and calls this during suspend. */
|
|
164
163
|
put(token: CheckpointToken, cp: Checkpoint): Promise<void>;
|
|
164
|
+
/** 一行的列组 —— `get` 与按会话列表读同一份投影、交同一只解码({@link readStoredCheckpoint})。 */
|
|
165
|
+
private storedCheckpointColumns;
|
|
165
166
|
get(token: CheckpointToken): Promise<Checkpoint | null>;
|
|
167
|
+
/**
|
|
168
|
+
* 一行字节 → 检查点 —— **`get` 与按会话列表({@link listBySession})的唯一解码**(列组 = {@link storedCheckpointColumns})。读不成的格交回 `damaged`
|
|
169
|
+
* (`get` 原样抛那只拒绝,列表把这一行答 `unreadable`);非体问题(读腿漏投影 `not-text` / 连接)原样抛 —— 码坏不冒充一行数据坏。
|
|
170
|
+
*/
|
|
171
|
+
private readStoredCheckpoint;
|
|
172
|
+
/**
|
|
173
|
+
* core `CheckpointStore.listBySession`(可选动词,按在不在探;core 7.66.0 契约):一只会话的行,任何状态,按查询的成员收窄、按 `createdAt` 再按令牌排、键集分页。
|
|
174
|
+
* **读法只有 core 那一只**:查询经 `readCheckpointSessionQuery` 读(坏查询在任何 SQL 之前带码拒),过滤 / 排序 / 游标 / 截断 / 损坏判 全由 core 的 `selectSessionPage` 答
|
|
175
|
+
* (内存店与文件店同调它);本店只交「这只会话的行」—— 按会话列(`session_id`,本店每一只按会话的读口同读这一列)取行,经 `get` 的同一只解码({@link readStoredCheckpoint})成检查点。
|
|
176
|
+
* SQL 只按会话收窄,别的成员(状态 / scope / 门型 / 工具调用 / 建行时刻)一律由 core 判 —— 列上的比较不是 core 的比较(MySQL 协议的 `utf8mb4_bin` 按补空格比,
|
|
177
|
+
* 带尾空格的集外状态词会被 SQL 当成三词之一排除,而 core 要把它答成 `unreadable`),所以 SQL 不预筛任何一格,预筛不可能排除 core 会留下的行。
|
|
178
|
+
* 读成本:这只会话的每一行读一份字节(检查点体 + 旁格)—— 只在页里的行交出去;会话的行数由留存扫车道界住。
|
|
179
|
+
* 一行读不成(体不是一只检查点 / 决议或执行记录那一格读不出 / 体没说出它归在的这只会话)⇒ 这一行**照列**为 `unreadable`(令牌 / scope / 会话 / 状态 / 建行时刻取本店在字节之外
|
|
180
|
+
* 握着的列组;体读成、归属对而旁格坏 ⇒ 过滤器照读体上的成员),不跳过、不让整页失败;拿它的令牌去 `get` 撞见同一只拒。体是对象而成员形读不动 ⇒ core 自己答 `unreadable`。
|
|
181
|
+
* 页里每一只这样的行计一次 `server.approvals.park-row-blob-unreadable`(本店停驻行读者的那一条律:一行的字节只坏它自己那一行):core 判的经它给的座(句子去掉令牌后作探针),
|
|
182
|
+
* 本店判、core 看着读得动的那几行由本店计。只读:列表不改任何一行;交出的行是这一次解码的新对象,改它不改库。
|
|
183
|
+
*/
|
|
184
|
+
listBySession(query: CheckpointSessionQuery): Promise<CheckpointPage<CheckpointRowRead, CheckpointSessionCursor>>;
|
|
166
185
|
/**
|
|
167
186
|
* Atomic CAS: pending → resolved, scoped. Returns true for the single winner, false if already
|
|
168
187
|
* resolved/expired (AlreadyResolved → caller no-ops, never re-executes). `outcome` is persisted atomically
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { parkedApprovalCard } from "../approval-content-kind.js";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { CHECKPOINT_FACET_SQL, CHECKPOINT_SWEEP_ORDER_SQL, CheckpointIndexRefusedError, checkpointSweepAfterSql, hostSessionOf, judgeCheckpointIndexCells } from "../plugins/checkpoint-index-columns.js";
|
|
4
|
-
import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, } from "@sema-agent/core";
|
|
4
|
+
import { CheckpointError, validatePendingSteer, appendPendingSteer, winnerFromOutcome, summarizeCheckpoint, checkpointRevOf, judgeCheckpointRow, CONTRACT_KIT_ENGINE_VERSION, ExecutionOutcomeConflictError, executionRecordDisposition, executionVerdict, readCheckpointSessionQuery, selectSessionPage, } from "@sema-agent/core";
|
|
5
5
|
import { parkRowFacts, skillCardFacts } from "../approval-card.js";
|
|
6
|
-
import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
|
|
6
|
+
import { assertJsonColumnWritable, createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, isJsonObject, jsonColumnText, jsonColumnTextSha256, readJsonArrayColumn, readJsonColumnTextSha256, readNonNullJsonValueColumn, StoreCorruptError, } from "../plugins/sql-json-column.js";
|
|
7
7
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
8
8
|
import { durableRowIdentity, withoutCheckpointToken } from "../checkpoint-identity.js";
|
|
9
9
|
import { mysqlDriver, pgDriver, dialectProtocolJsonEncoder } from "../plugins/sql-driver.js";
|
|
10
10
|
import { isDupKeyError, isMissingColumnError, rowTooLargeOrSelf, StoreRowTooLargeError } from "../plugins/sql-errors.js";
|
|
11
|
-
import {
|
|
11
|
+
import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
|
|
12
12
|
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
|
|
13
13
|
import { approvalPayloadFingerprint } from "../plugins/approval-payload-fingerprint.js";
|
|
14
14
|
import { approvalDecisionOfWinner, boundedRuleOffers, boundedToolInput, candidateIdentityOf, DECIDED_REPLAY_SCAN_CAP, DENY_SWEEP_PAGE_ROWS, isUnreadableSummaryForm, parkedLegTaskId, queueRowGateKind, queueTriageOrder, readRiskDescriptorBody, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS, UnreadableCheckpointRowError, } from "../plugins/checkpoint-store-face.js";
|
|
@@ -77,12 +77,10 @@ export async function assertCheckpointColumnSchema(query, dialect) {
|
|
|
77
77
|
catch (err) {
|
|
78
78
|
if (!isMissingColumnError(err, dialect))
|
|
79
79
|
throw err;
|
|
80
|
-
throw
|
|
80
|
+
throw await probedMissingColumnRefusal(query, dialect, ["checkpoint"], `checkpoint is missing the column ${column} — refusing to start. ` +
|
|
81
81
|
`On this table ${breaksAt} — at runtime, after a healthy-looking boot. ` +
|
|
82
82
|
"The table holds IN-FLIGHT durable parks (suspended approvals/reviews awaiting a decision), so the way forward is an in-place " +
|
|
83
|
-
"ALTER that keeps them; `checkpoint_ctx`, run rows and every other persistent face are untouched
|
|
84
|
-
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
|
|
85
|
-
(await describeProbedTableDrift(query, dialect, ["checkpoint"])));
|
|
83
|
+
"ALTER that keeps them; `checkpoint_ctx`, run rows and every other persistent face are untouched.", err);
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
86
|
}
|
|
@@ -244,6 +242,12 @@ export function readPendingSteerQueueColumn(queueRaw) {
|
|
|
244
242
|
}
|
|
245
243
|
const STEER_CAS_ATTEMPTS = 8;
|
|
246
244
|
const EXECUTION_RECORD_CAS_ATTEMPTS = 8;
|
|
245
|
+
const STORED_ROW_DAMAGE = {
|
|
246
|
+
body: "its stored body is not a checkpoint object",
|
|
247
|
+
decision: "its stored decision record cannot be read",
|
|
248
|
+
execution: "its stored execution record cannot be read",
|
|
249
|
+
filing: "its stored body does not name the session the row is filed under",
|
|
250
|
+
};
|
|
247
251
|
export class SqlCheckpointStore {
|
|
248
252
|
db;
|
|
249
253
|
logger;
|
|
@@ -339,21 +343,62 @@ export class SqlCheckpointStore {
|
|
|
339
343
|
throw rowTooLargeOrSelf(this.db.dialect, e, `the checkpoint row for session ${JSON.stringify(cp.sessionId)}`);
|
|
340
344
|
}
|
|
341
345
|
}
|
|
346
|
+
storedCheckpointColumns() {
|
|
347
|
+
const d = this.db.dialect;
|
|
348
|
+
return (`status, ${jsonColumnText(d, "checkpoint")}, ${jsonColumnText(d, "outcome")}, reopen_reason, rev, ` +
|
|
349
|
+
`${jsonColumnText(d, "pending_steer_queue")}, ${jsonColumnText(d, "execution_outcome")}, execution_at_ms`);
|
|
350
|
+
}
|
|
342
351
|
async get(token) {
|
|
343
|
-
const { rows } = await this.db.query(
|
|
344
|
-
`${jsonColumnText("mysql", "pending_steer_queue")}, ${jsonColumnText("mysql", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = ?`, `SELECT status, ${jsonColumnText("pg", "checkpoint")}, ${jsonColumnText("pg", "outcome")}, reopen_reason, rev, ` +
|
|
345
|
-
`${jsonColumnText("pg", "pending_steer_queue")}, ${jsonColumnText("pg", "execution_outcome")}, execution_at_ms FROM checkpoint WHERE token = $1`), [token]);
|
|
352
|
+
const { rows } = await this.db.query(`SELECT ${this.storedCheckpointColumns()} FROM checkpoint WHERE token = ${this.ph(1)}`, [token]);
|
|
346
353
|
const r = rows[0];
|
|
347
354
|
if (!r)
|
|
348
355
|
return null;
|
|
349
|
-
const
|
|
356
|
+
const read = this.readStoredCheckpoint(r, token);
|
|
357
|
+
if (read.kind === "damaged")
|
|
358
|
+
throw read.error;
|
|
359
|
+
const cp = read.checkpoint;
|
|
360
|
+
this.logger?.info?.("checkpoint_get", {
|
|
361
|
+
tokenFp: tokenFingerprint(token),
|
|
362
|
+
status: cp.status,
|
|
363
|
+
hasWorkspaceHandle: !!cp.state?.workspaceHandle,
|
|
364
|
+
snapshotId: cp.state?.workspaceHandle?.snapshotId,
|
|
365
|
+
});
|
|
366
|
+
return cp;
|
|
367
|
+
}
|
|
368
|
+
readStoredCheckpoint(r, token) {
|
|
369
|
+
const where = `for token ${tokenFingerprint(token)}`;
|
|
370
|
+
let cp;
|
|
371
|
+
try {
|
|
372
|
+
cp = readCheckpointBodyOrRefuse(r.checkpoint, where);
|
|
373
|
+
}
|
|
374
|
+
catch (e) {
|
|
375
|
+
if (e instanceof StoreCorruptError)
|
|
376
|
+
return { kind: "damaged", member: "body", error: e };
|
|
377
|
+
throw e;
|
|
378
|
+
}
|
|
350
379
|
cp.status = r.status;
|
|
351
|
-
|
|
380
|
+
let outcomeRaw;
|
|
381
|
+
try {
|
|
382
|
+
outcomeRaw = refusing("a decision record nobody can read", () => decodeOutcomeColumn(r.outcome, where));
|
|
383
|
+
}
|
|
384
|
+
catch (e) {
|
|
385
|
+
if (e instanceof StoreCorruptError)
|
|
386
|
+
return { kind: "damaged", member: "decision", error: e, body: cp };
|
|
387
|
+
throw e;
|
|
388
|
+
}
|
|
352
389
|
if (outcomeRaw)
|
|
353
390
|
cp.resolvedOutcome = winnerFromOutcome(outcomeRaw);
|
|
354
391
|
cp.reopenReason = r.reopen_reason ?? undefined;
|
|
355
392
|
cp.rev = r.rev == null ? 0 : Number(r.rev);
|
|
356
|
-
|
|
393
|
+
let executionOutcome;
|
|
394
|
+
try {
|
|
395
|
+
executionOutcome = refusing("an execution record nobody can read", () => decodeExecutionOutcomeColumn(r.execution_outcome, where));
|
|
396
|
+
}
|
|
397
|
+
catch (e) {
|
|
398
|
+
if (e instanceof StoreCorruptError)
|
|
399
|
+
return { kind: "damaged", member: "execution", error: e, body: cp };
|
|
400
|
+
throw e;
|
|
401
|
+
}
|
|
357
402
|
if (executionOutcome !== undefined) {
|
|
358
403
|
cp.executionOutcome = executionOutcome;
|
|
359
404
|
if (r.execution_at_ms != null)
|
|
@@ -364,17 +409,58 @@ export class SqlCheckpointStore {
|
|
|
364
409
|
recordFailOpen("server.sql-json.checkpoint.pending-steer-queue-lossy");
|
|
365
410
|
this.logger?.info?.("checkpoint_pending_steer_unreadable", { tokenFp: tokenFingerprint(token) });
|
|
366
411
|
}
|
|
367
|
-
if (cp.state)
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
412
|
+
if (isJsonObject(cp.state) && steer.state.pendingSteerQueue !== undefined)
|
|
413
|
+
cp.state.pendingSteerQueue = steer.state.pendingSteerQueue;
|
|
414
|
+
return { kind: "row", checkpoint: cp };
|
|
415
|
+
}
|
|
416
|
+
async listBySession(query) {
|
|
417
|
+
const read = readCheckpointSessionQuery(query);
|
|
418
|
+
const { rows } = await this.db.query(`SELECT token, scope, session_id, created_at_ms, ${this.storedCheckpointColumns()} FROM checkpoint WHERE session_id = ${this.ph(1)}`, [read.sessionId]);
|
|
419
|
+
const tokens = rows.map((r) => String(r.token));
|
|
420
|
+
const withoutTokens = (sentence) => tokens.reduce((out, t) => withoutCheckpointToken(out, t), sentence);
|
|
421
|
+
const damaged = new Map();
|
|
422
|
+
const candidates = [];
|
|
423
|
+
rows.forEach((r, i) => {
|
|
424
|
+
const token = tokens[i];
|
|
425
|
+
const got = this.readStoredCheckpoint(r, token);
|
|
426
|
+
const body = got.kind === "row" ? got.checkpoint : got.member === "body" ? undefined : got.body;
|
|
427
|
+
const filedHere = body !== undefined && body.sessionId === String(r.session_id);
|
|
428
|
+
if (got.kind === "row" && filedHere) {
|
|
429
|
+
candidates.push([token, body]);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
damaged.set(token, { problem: STORED_ROW_DAMAGE[got.kind === "damaged" ? got.member : "filing"], r, counted: filedHere });
|
|
433
|
+
if (filedHere) {
|
|
434
|
+
candidates.push([token, body]);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const filing = { token: token, sessionId: String(r.session_id), scope: String(r.scope), status: String(r.status), createdAt: Number(r.created_at_ms) };
|
|
438
|
+
candidates.push([token, filing]);
|
|
376
439
|
});
|
|
377
|
-
|
|
440
|
+
const page = selectSessionPage(candidates, read, (cp) => cp, (sentence) => recordFailOpen("server.approvals.park-row-blob-unreadable", withoutTokens(sentence)));
|
|
441
|
+
if (damaged.size === 0)
|
|
442
|
+
return page;
|
|
443
|
+
return {
|
|
444
|
+
...page,
|
|
445
|
+
rows: page.rows.map((row) => {
|
|
446
|
+
const token = row.kind === "row" ? row.checkpoint.token : row.token;
|
|
447
|
+
const hit = damaged.get(token);
|
|
448
|
+
if (hit === undefined)
|
|
449
|
+
return row;
|
|
450
|
+
if (hit.counted && row.kind === "row")
|
|
451
|
+
recordFailOpen("server.approvals.park-row-blob-unreadable", `session listing, session ${JSON.stringify(String(hit.r.session_id))}: ${hit.problem} — listed as unreadable`);
|
|
452
|
+
const createdAt = Number(hit.r.created_at_ms);
|
|
453
|
+
return {
|
|
454
|
+
kind: "unreadable",
|
|
455
|
+
token: token,
|
|
456
|
+
scope: String(hit.r.scope),
|
|
457
|
+
sessionId: String(hit.r.session_id),
|
|
458
|
+
status: String(hit.r.status),
|
|
459
|
+
createdAt: Number.isFinite(createdAt) ? createdAt : null,
|
|
460
|
+
problem: hit.problem,
|
|
461
|
+
};
|
|
462
|
+
}),
|
|
463
|
+
};
|
|
378
464
|
}
|
|
379
465
|
async resolve(token, scope, outcome, expect) {
|
|
380
466
|
const params = [this.json(outcome, "checkpoint outcome"), Date.now(), token, scope];
|
|
@@ -208,7 +208,7 @@ export declare class PgFileHistoryStore extends SqlFileHistoryStore {
|
|
|
208
208
|
* ONLY a provable missing-column error gets the missing-column refusal — everything else
|
|
209
209
|
* (timeout / reset / permission / missing table) rethrows untouched: diagnosing a connection blip as a
|
|
210
210
|
* schema drift is more expensive than the defect this gate exists to stop. The refusal's ways out are the
|
|
211
|
-
* closed set of three shared with the whole-database check (`
|
|
211
|
+
* closed set of three shared with the whole-database check (`probedMissingColumnRefusal`); none of them
|
|
212
212
|
* is destructive. Honest cost statement: the history lives in these tables (per-file REWIND history,
|
|
213
213
|
* working-tree convenience state) — session transcripts and every other durable subsystem are untouched.
|
|
214
214
|
*/
|
|
@@ -3,7 +3,7 @@ import { captureFileBackupViaEnv, applyFileRestoreViaEnv, fileHistoryModeProvenD
|
|
|
3
3
|
import { BlobTooLargeError, SqlBlobIndex } from "./blob-index-sql.js";
|
|
4
4
|
import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
|
|
5
5
|
import { isMissingColumnError, retryOnDeadlock } from "../plugins/sql-errors.js";
|
|
6
|
-
import {
|
|
6
|
+
import { probedMissingColumnRefusal } from "../plugins/schema-drift-sentence.js";
|
|
7
7
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
8
8
|
import { foldCaughtFailure } from "../file-history-disclosure.js";
|
|
9
9
|
export const FILE_HISTORY_BLOB_TABLE = "file_history_blob";
|
|
@@ -844,12 +844,10 @@ export async function assertFileHistorySchema(query, dialect) {
|
|
|
844
844
|
catch (err) {
|
|
845
845
|
if (!isMissingColumnError(err, dialect))
|
|
846
846
|
throw err;
|
|
847
|
-
throw
|
|
847
|
+
throw await probedMissingColumnRefusal(query, dialect, [table], `${table} is missing required columns — refusing to start. This build's per-file rewind history (S-15) ` +
|
|
848
848
|
`references every probed column unconditionally; a partial table would fail mid-write and could publish a ` +
|
|
849
849
|
`half-shaped history graph. The file_history_* tables hold the per-file REWIND history (working-tree convenience ` +
|
|
850
|
-
`state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history
|
|
851
|
-
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
|
|
852
|
-
(await describeProbedTableDrift(query, dialect, [table])));
|
|
850
|
+
`state); session transcripts and all other durable subsystems are untouched, and an in-place ALTER keeps the history.`, err);
|
|
853
851
|
}
|
|
854
852
|
}
|
|
855
853
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 托管留存(managed retention)的**执行器** —— 引擎座表那一半(阶段三逐字搬进 `@sema-agent/core-stores-sql` 的那一半)。
|
|
3
3
|
* SINGLE-FILE DUAL-DIALECT([ref] A12 定型半场;先例 = `checkpoint-store-sql.ts`)。[ref] 车1,设计稿
|
|
4
|
-
* `docs/
|
|
4
|
+
* `docs/[ref]-retention-lane.md` v1.2。上游契约真源 = `@sema-agent/core` 的 `core/retention.d.ts`
|
|
5
5
|
* (`ManagedRetentionCapability` / `RetentionReceipt`);事务内钩表的形 = core-stores-sql 的 `SqlTransactionHooks`(本文件的同名型是它的形,见下)。
|
|
6
6
|
*
|
|
7
7
|
* ── 一个事务、两个属主:执行器 + 部署钩 ─────────────────────────────────────────────────────────────
|
|
@@ -22,7 +22,7 @@ export declare function ensureSchema(pool: PgPool | PgPoolClient): Promise<void>
|
|
|
22
22
|
* 一句看不出根因的话。这正是「响亮或 fail-closed,禁静默降级」那条要挡的形态。
|
|
23
23
|
*
|
|
24
24
|
* 判据用**能力探测**而不是版本号/information_schema:发一条恒空的 `WHERE 1=0` 读,列不在就报错。
|
|
25
|
-
* 探针读抛且是**可证**的缺列 ⇒ 拒启(其余错误原样上抛,与兄弟探针同一条规则)。拒因的出路是**闭集三条**(与整库对账同一份,{@link
|
|
25
|
+
* 探针读抛且是**可证**的缺列 ⇒ 拒启(其余错误原样上抛,与兄弟探针同一条规则)。拒因的出路是**闭集三条**(与整库对账同一份,{@link probedMissingColumnRefusal} / {@link schemaWaysOut}),没有销毁性的一条:
|
|
26
26
|
* 缺的出处两列由对账同一套件逐条打印 `ALTER TABLE tool_result ADD COLUMN …`;键宽不够则第 (2) 条是部署者自己把 `ref` 加宽到本构建的定义。
|
|
27
27
|
*/
|
|
28
28
|
export declare function assertToolResultProvenanceSchema(query: (sql: string) => Promise<{
|
|
@@ -3,7 +3,7 @@ import { pgSanitizeText } from "../plugins/pg-safe-json.js";
|
|
|
3
3
|
import { mysqlDriver, pgDriver } from "../plugins/sql-driver.js";
|
|
4
4
|
import { MANAGED_RETENTION, UNMANAGED_RETENTION } from "../plugins/retention-declaration.js";
|
|
5
5
|
import { ensureIndexes } from "../plugins/ensure-index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { probedMissingColumnRefusal, schemaMismatchRefusal, schemaWaysOut } from "../plugins/schema-drift-sentence.js";
|
|
7
7
|
import { isMissingColumnError } from "../plugins/sql-errors.js";
|
|
8
8
|
export const PG_TOOL_RESULT_SCHEMA = [
|
|
9
9
|
`CREATE TABLE IF NOT EXISTS tool_result (
|
|
@@ -29,7 +29,7 @@ export async function assertToolResultProvenanceSchema(query, dialect) {
|
|
|
29
29
|
: "SELECT character_maximum_length AS len FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'tool_result' AND column_name = 'ref'").catch(() => ({ rows: [] }));
|
|
30
30
|
const width = Number(widthRow.rows[0]?.len ?? NaN);
|
|
31
31
|
if (Number.isFinite(width) && width < MAX_MINTED_TOOL_RESULT_REF_CHARS) {
|
|
32
|
-
throw
|
|
32
|
+
throw schemaMismatchRefusal(`tool_result.ref is VARCHAR(${width}) but core 5.26.0 mints refs up to ${MAX_MINTED_TOOL_RESULT_REF_CHARS} characters ` +
|
|
33
33
|
`(#119 made the ref injective: four \`~\`-separated segments) — refusing to start. A truncating primary key aliases two distinct refs onto ` +
|
|
34
34
|
`one row — one run's tool output reads back from another run's ref. ` +
|
|
35
35
|
`The ${dialect === "pg" ? "PostgreSQL" : "MySQL-protocol"} database is behind this build on that key column; offloaded results are a recoverable-window cache, ` +
|
|
@@ -46,13 +46,11 @@ export async function assertToolResultProvenanceSchema(query, dialect) {
|
|
|
46
46
|
catch (err) {
|
|
47
47
|
if (!isMissingColumnError(err, dialect))
|
|
48
48
|
throw err;
|
|
49
|
-
throw
|
|
49
|
+
throw await probedMissingColumnRefusal(query, dialect, ["tool_result"], `tool_result is missing the #119 provenance columns (owner_session_id / owner_task_id) — refusing to start. ` +
|
|
50
50
|
`This release picks up @sema-agent/core 5.26.0, which changed how an offloaded tool result is addressed ` +
|
|
51
51
|
`(the ref grew past the old VARCHAR(190) key) and made the store record who wrote each row. Without the columns every offload write ` +
|
|
52
52
|
`fails with unknown column and core's failure arm turns it into an inline placeholder — the service keeps running and the tool output is lost. ` +
|
|
53
|
-
`Offloaded tool results are a recoverable-window cache (the inline previews in transcripts are unaffected)
|
|
54
|
-
`Underlying probe error: ${err instanceof Error ? err.message : String(err)}\n\n` +
|
|
55
|
-
(await describeProbedTableDrift(query, dialect, ["tool_result"])));
|
|
53
|
+
`Offloaded tool results are a recoverable-window cache (the inline previews in transcripts are unaffected).`, err);
|
|
56
54
|
}
|
|
57
55
|
}
|
|
58
56
|
function toolResultAgeCutoff(now, opts) {
|
package/dist/task-a2a.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ export declare const A2A_PEER_DISPOSITION: {
|
|
|
57
57
|
readonly toolAxes: "engine";
|
|
58
58
|
};
|
|
59
59
|
/** 请求体 `a2aPeers[i]` 的键闭集 = 处置表的键(书写序即判序)。 */
|
|
60
|
-
export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" | "
|
|
60
|
+
export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" | "toolAxes" | "principalHeader" | "cardUrl" | "allowSkills")[];
|
|
61
61
|
/**
|
|
62
62
|
* **请求入口的一次读**(`boot/resolve-spec.ts` 在腿首铸一份,受理门与读器——续跑重放腿只有读器——都吃这一份):`body.a2aPeers` 若是数组,返回同形数组
|
|
63
63
|
* (每个对象条目换成它的 {@link PeerSnapshot};非对象条目原样;长度保真 —— 界外的条数仍是受理门撞帽拒与读器丢弃留声的依据,只快照界内的条目);不是数组(缺席 / `null` / 别的形)原样返回。
|
|
@@ -68,8 +68,6 @@ export declare const A2A_PEER_ENTRY_KEYS: readonly ("url" | "name" | "headers" |
|
|
|
68
68
|
export declare function snapshotRequestA2a(raw: unknown, options: {
|
|
69
69
|
readonly reread: boolean;
|
|
70
70
|
}): unknown;
|
|
71
|
-
/** 读这条对端的是哪条腿:`fresh` = 新提交;`resume` = 续跑重放升级前存下的请求体(`boot/resolve-spec.ts` 的 `opts.leg` 分流,同一个读法)。 */
|
|
72
|
-
type A2aRequestLeg = "fresh" | "resume";
|
|
73
71
|
/** 请求面对端 → 草稿:按 {@link A2A_PEER_DISPOSITION} 拆(`headers` 进件,其余进声明;拆法与 MCP 请求腿同一只 `splitDeclaration`)。件缺席或空 ⇒ 草稿不带件。纯函数,不登记。 */
|
|
74
72
|
export declare function toA2aDraft(peer: RequestA2aPeer): A2aDraft;
|
|
75
73
|
/**
|
|
@@ -81,8 +79,8 @@ export declare function validateRequestA2a(raw: unknown): {
|
|
|
81
79
|
dropped: A2aInjectionDrop[];
|
|
82
80
|
} | null;
|
|
83
81
|
/**
|
|
84
|
-
* 一份请求的 `a2aPeers` 里,读器挂得上、`headers` 却剔掉了成员的那些坐标(`a2aPeers[<下标>].headers
|
|
85
|
-
* 「请求成员读不出」那一只判据(`unread-request-members.ts`)的 A2A 一座。读器与挂载同一只({@link normalizeA2aPeer}
|
|
82
|
+
* 一份请求的 `a2aPeers` 里,读器挂得上、`headers` 却剔掉了成员的那些坐标(`a2aPeers[<下标>].headers.<保留名>`:成员名只会是契约包保留成员名表里的名字,不带值、不带调用方写的对端名)——
|
|
83
|
+
* 「请求成员读不出」那一只判据(`unread-request-members.ts`)的 A2A 一座。读器与挂载同一只({@link normalizeA2aPeer},只有一种读法;新提交腿上同一形先由判官表 400)。
|
|
86
84
|
* 挂不上的对端不算(整条不进,有自己的留声)。`storedToolAxes` = 续跑重放腿递的 M19 重读座。不是数组 ⇒ 空。
|
|
87
85
|
*/
|
|
88
86
|
export declare function requestA2aHeadersUnread(raw: unknown, storedToolAxes?: StoredToolAxesRereadM19): string[];
|
|
@@ -123,9 +121,6 @@ desk: Pick<PeerDesk, "declare">, lease: PeerLease, logger?: {
|
|
|
123
121
|
info?(m: string, meta?: unknown): void;
|
|
124
122
|
warn(m: string, meta?: unknown): void;
|
|
125
123
|
},
|
|
126
|
-
/** 哪条腿在读(`boot/resolve-spec.ts` 按 `opts.leg` 递;缺省 = 新提交,最严的读法):只决定 `headers` 里 `__proto__` 名成员的处置(见 {@link normalizeA2aPeer})。 */
|
|
127
|
-
leg?: A2aRequestLeg,
|
|
128
124
|
/** 一次性迁移 M19 的重读座(窗口见 `plugins/one-time-migrations.ts` 登记表):只在续跑重放腿、只在窗开着时由 `boot/resolve-spec.ts` 递进来 —— 与 MCP 请求腿同一份。 */
|
|
129
125
|
storedToolAxes?: StoredToolAxesRereadM19): A2aServerSpec[] | undefined;
|
|
130
|
-
export {};
|
|
131
126
|
//# sourceMappingURL=task-a2a.d.ts.map
|
package/dist/task-a2a.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { types } from "node:util";
|
|
2
2
|
import { resolveComplianceDenies } from "@sema-agent/core";
|
|
3
|
-
import { describeNotStringArray, describeValueShape, findUrlCredential, isStringArray } from "@sema-agent/settings-schema";
|
|
3
|
+
import { describeNotStringArray, describeValueShape, findUrlCredential, isStringArray, readPlainStringRecord } from "@sema-agent/settings-schema";
|
|
4
4
|
import { errorKindOf } from "./observability/err-text.js";
|
|
5
|
-
import { declarationKeys,
|
|
5
|
+
import { declarationKeys, splitDeclaration } from "./request-declaration-disposition.js";
|
|
6
6
|
import { mintDeclaration, peerSentence } from "./peer-seat.js";
|
|
7
7
|
import { readRequestToolAxes } from "./request-tool-axes.js";
|
|
8
|
-
import { MAX_KEY_ECHO,
|
|
8
|
+
import { MAX_KEY_ECHO, keyClosureClause, scanKeyNames } from "./request-key-closure.js";
|
|
9
9
|
import { urlCredentialHeaderExit, urlCredentialRefusalReason } from "./url-credential-refusal.js";
|
|
10
10
|
import { createMountSeat, declarationEntryNotObject, declarationIssueFacts, declarationNameIssue, declarationOverCapFacts, declaredName, judgeDeclarationKeys, requestDeclarationRefusal, throwDeclarationRefusal, } from "./request-declaration-refusal.js";
|
|
11
11
|
import { sendHeaderOwnershipIssue } from "./task-mcp.js";
|
|
@@ -53,7 +53,12 @@ function peerUrlIssue(key) {
|
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
55
|
const A2A_SEND_HEADER_PATHS = { headers: "headers", principalHeader: "principalHeader" };
|
|
56
|
-
|
|
56
|
+
function readPeerHeaders(raw) {
|
|
57
|
+
const read = readPlainStringRecord(raw);
|
|
58
|
+
if (!read.ok)
|
|
59
|
+
return { record: undefined, issues: [{ path: "headers", reason: `must be a plain object of string values — ${read.detail}` }] };
|
|
60
|
+
return { record: read.record, issues: read.reservedMembers.map((name) => ({ path: `headers.${name}`, reason: `no member of a declaration may be named "${name}"` })) };
|
|
61
|
+
}
|
|
57
62
|
const PEER_SNAPSHOT = Symbol("a2aPeerSnapshot");
|
|
58
63
|
function openCloneNode(v, ancestors) {
|
|
59
64
|
if (v === null || v === undefined || typeof v === "string" || typeof v === "number" || typeof v === "boolean")
|
|
@@ -134,7 +139,7 @@ function mintPeerSnapshot(entry, reread) {
|
|
|
134
139
|
cardUrl: source.cardUrl,
|
|
135
140
|
principalHeader: source.principalHeader,
|
|
136
141
|
allowSkills: Array.isArray(allowSkills) ? Array.from(allowSkills) : allowSkills,
|
|
137
|
-
headers: headers === undefined ? undefined :
|
|
142
|
+
headers: headers === undefined ? undefined : readPeerHeaders(headers),
|
|
138
143
|
toolAxes: toolAxesRead,
|
|
139
144
|
toolAxesDeclared: reread && toolAxesRead !== undefined && !toolAxesRead.ok ? cloneDeclaration(toolAxes) : undefined,
|
|
140
145
|
unknownKeys: Object.keys(source).filter((name) => !A2A_PEER_ENTRY_KEY_SET.has(name)),
|
|
@@ -162,18 +167,10 @@ const A2A_PEER_KEY_JUDGES = {
|
|
|
162
167
|
name: declarationNameIssue,
|
|
163
168
|
url: peerUrlIssue("url"),
|
|
164
169
|
cardUrl: peerUrlIssue("cardUrl"),
|
|
165
|
-
headers: (o) =>
|
|
166
|
-
const read = o.headers;
|
|
167
|
-
if (read === undefined)
|
|
168
|
-
return undefined;
|
|
169
|
-
if (!read.ok)
|
|
170
|
-
return { path: "headers", reason: `must be a plain object of string values — ${read.member === undefined ? read.fault : `its member "${echoKeyName("", read.member)}" ${read.fault}`}` };
|
|
171
|
-
const reserved = read.stripped[0];
|
|
172
|
-
return reserved === undefined ? undefined : { path: HEADERS_RESERVED_MEMBER_PATH, reason: reserved.fault };
|
|
173
|
-
},
|
|
170
|
+
headers: (o) => o.headers?.issues[0],
|
|
174
171
|
principalHeader: (o) => o.principalHeader !== undefined && typeof o.principalHeader !== "string"
|
|
175
172
|
? { path: "principalHeader", reason: `must be a string (a header name) — got ${describeValueShape(o.principalHeader)}` }
|
|
176
|
-
: sendHeaderOwnershipIssue({ headers: o.headers?.
|
|
173
|
+
: sendHeaderOwnershipIssue({ headers: o.headers?.record, principalHeader: o.principalHeader }, "a2a", A2A_SEND_HEADER_PATHS),
|
|
177
174
|
allowSkills: (o) => o.allowSkills === undefined || isStringArray(o.allowSkills)
|
|
178
175
|
? undefined
|
|
179
176
|
: { path: "allowSkills", reason: `must be an array of strings (omit the key to leave the peer's skills unrestricted) — got ${describeNotStringArray(o.allowSkills)}` },
|
|
@@ -182,19 +179,20 @@ const A2A_PEER_KEY_JUDGES = {
|
|
|
182
179
|
return read === undefined || read.ok ? undefined : toolAxesIssue(read);
|
|
183
180
|
},
|
|
184
181
|
};
|
|
185
|
-
function normalizeA2aPeer(e,
|
|
182
|
+
function normalizeA2aPeer(e, at, storedToolAxes) {
|
|
186
183
|
const dropBecause = (issue) => ({ draft: null, because: declarationIssueFacts(at, issue) });
|
|
187
184
|
const notObject = declarationEntryNotObject(A2A_SEAT, e, at);
|
|
188
185
|
if (notObject !== undefined)
|
|
189
186
|
return { draft: null, because: { field: at, reason: notObject } };
|
|
190
187
|
const o = snapshotPeer(e, storedToolAxes !== undefined);
|
|
188
|
+
const lost = o.headers?.record === undefined ? [] : o.headers.issues;
|
|
191
189
|
for (const key of A2A_PEER_ENTRY_KEYS) {
|
|
192
190
|
const issue = A2A_PEER_KEY_JUDGES[key](o, at);
|
|
193
191
|
if (issue === undefined)
|
|
194
192
|
continue;
|
|
195
193
|
if (issue.credential !== undefined)
|
|
196
194
|
continue;
|
|
197
|
-
if (
|
|
195
|
+
if (lost.includes(issue))
|
|
198
196
|
continue;
|
|
199
197
|
if (key === "toolAxes" && storedToolAxes !== undefined)
|
|
200
198
|
continue;
|
|
@@ -204,9 +202,9 @@ function normalizeA2aPeer(e, leg, at, storedToolAxes) {
|
|
|
204
202
|
if (o.cardUrl !== undefined)
|
|
205
203
|
spec.cardUrl = o.cardUrl;
|
|
206
204
|
const { headers, toolAxes: axes } = o;
|
|
207
|
-
if (headers?.
|
|
205
|
+
if (headers?.record !== undefined)
|
|
208
206
|
spec.headers = headers.record;
|
|
209
|
-
const
|
|
207
|
+
const headersLost = lost.length > 0 ? { headersLost: lost.map((issue) => issue.path) } : {};
|
|
210
208
|
if (o.principalHeader !== undefined)
|
|
211
209
|
spec.principalHeader = o.principalHeader;
|
|
212
210
|
if (o.allowSkills !== undefined)
|
|
@@ -227,22 +225,22 @@ function normalizeA2aPeer(e, leg, at, storedToolAxes) {
|
|
|
227
225
|
return unread;
|
|
228
226
|
if (again.toolAxes !== undefined)
|
|
229
227
|
spec.toolAxes = again.toolAxes;
|
|
230
|
-
return { draft: toA2aDraft(spec), toolAxesReread: peerSentence(axes.problem), ...
|
|
228
|
+
return { draft: toA2aDraft(spec), toolAxesReread: peerSentence(axes.problem), ...headersLost };
|
|
231
229
|
}
|
|
232
230
|
if (axes.toolAxes !== undefined)
|
|
233
231
|
spec.toolAxes = axes.toolAxes;
|
|
234
232
|
}
|
|
235
|
-
return { draft: toA2aDraft(spec), ...
|
|
233
|
+
return { draft: toA2aDraft(spec), ...headersLost };
|
|
236
234
|
}
|
|
237
235
|
export function toA2aDraft(peer) {
|
|
238
236
|
const split = splitDeclaration(peer, A2A_PEER_DISPOSITION);
|
|
239
237
|
return { spec: split.engine, ...(split.pieces === undefined ? {} : { pieces: split.pieces }) };
|
|
240
238
|
}
|
|
241
239
|
export function validateRequestA2a(raw) {
|
|
242
|
-
const v = validateRequestA2aEntries(raw
|
|
240
|
+
const v = validateRequestA2aEntries(raw);
|
|
243
241
|
return v === null ? null : { ok: v.ok, dropped: v.dropped };
|
|
244
242
|
}
|
|
245
|
-
function validateRequestA2aEntries(raw,
|
|
243
|
+
function validateRequestA2aEntries(raw, storedToolAxes) {
|
|
246
244
|
if (raw === undefined || raw === null)
|
|
247
245
|
return { ok: [], dropped: [], toolAxesReread: [] };
|
|
248
246
|
if (!Array.isArray(raw))
|
|
@@ -251,7 +249,7 @@ function validateRequestA2aEntries(raw, leg, storedToolAxes) {
|
|
|
251
249
|
const dropped = [];
|
|
252
250
|
const toolAxesReread = [];
|
|
253
251
|
for (const [i, e] of raw.slice(0, MAX_REQUEST_A2A_PEERS).entries()) {
|
|
254
|
-
const read = normalizeA2aPeer(e,
|
|
252
|
+
const read = normalizeA2aPeer(e, `${A2A_SEAT.list}[${i}]`, storedToolAxes);
|
|
255
253
|
if (read.draft !== null) {
|
|
256
254
|
ok.push(read.draft);
|
|
257
255
|
if (read.toolAxesReread !== undefined)
|
|
@@ -271,7 +269,7 @@ export function requestA2aHeadersUnread(raw, storedToolAxes) {
|
|
|
271
269
|
const out = [];
|
|
272
270
|
for (const [i, e] of raw.slice(0, MAX_REQUEST_A2A_PEERS).entries()) {
|
|
273
271
|
const at = `${A2A_SEAT.list}[${i}]`;
|
|
274
|
-
const read = normalizeA2aPeer(e,
|
|
272
|
+
const read = normalizeA2aPeer(e, at, storedToolAxes);
|
|
275
273
|
if (read.draft !== null)
|
|
276
274
|
for (const member of read.headersLost ?? [])
|
|
277
275
|
out.push(`${at}.${member}`);
|
|
@@ -307,14 +305,14 @@ export function mergeRequestA2a(baseline, drafts, desk, lease) {
|
|
|
307
305
|
const winners = drafts.filter((d) => !taken.has(d.spec.name));
|
|
308
306
|
return winners.length === 0 ? baseline : [...(baseline ?? []), ...winners.map((d) => mintDeclaration(desk, d, lease))];
|
|
309
307
|
}
|
|
310
|
-
export function resolveRequestA2a(baseline, bodyA2a, config, desk, lease, logger,
|
|
308
|
+
export function resolveRequestA2a(baseline, bodyA2a, config, desk, lease, logger, storedToolAxes) {
|
|
311
309
|
if (bodyA2a === undefined || bodyA2a === null)
|
|
312
310
|
return baseline;
|
|
313
311
|
if (!a2aInjectionHonored(config)) {
|
|
314
312
|
logger?.warn("a2a_injection_ignored", { reason: "gate_closed (per-request A2A peers honored only on a single-user, unlocked, compliance-permitted deployment)" });
|
|
315
313
|
return baseline;
|
|
316
314
|
}
|
|
317
|
-
const v = validateRequestA2aEntries(bodyA2a,
|
|
315
|
+
const v = validateRequestA2aEntries(bodyA2a, storedToolAxes);
|
|
318
316
|
if (v === null) {
|
|
319
317
|
logger?.warn("a2a_injection_ignored", { reason: "a2aPeers not an array" });
|
|
320
318
|
return baseline;
|