@sema-agent/server 7.137.0 → 7.139.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 +600 -142
- package/README.md +7 -3
- package/README.zh-CN.md +6 -3
- package/USAGE.md +132 -41
- package/deploy/sema-up/README.md +1 -1
- package/deploy/sema-up/lib/lanes.json +3 -2
- package/dist/adoption/wire.d.ts +3 -3
- package/dist/approval-card.d.ts +23 -16
- package/dist/approval-card.js +1 -0
- package/dist/audit.js +1 -0
- package/dist/background-agent-name.d.ts +5 -0
- package/dist/background-agent-name.js +5 -0
- package/dist/bench/s1/arms.d.ts +1 -1
- package/dist/bench/s1/arms.js +1 -3
- package/dist/bench/s1/run-firm.js +2 -2
- package/dist/boot/budget-tracing.d.ts +1 -12
- package/dist/boot/deployment-mods.d.ts +46 -16
- package/dist/boot/deployment-mods.js +82 -6
- package/dist/boot/device-lane.d.ts +8 -3
- package/dist/boot/device-lane.js +7 -2
- package/dist/boot/execution-env.d.ts +4 -2
- package/dist/boot/execution-env.js +16 -24
- package/dist/boot/execution-lane.d.ts +1 -1
- package/dist/boot/governance-seams.d.ts +12 -18
- package/dist/boot/governance-seams.js +10 -24
- package/dist/boot/leader.d.ts +3 -0
- package/dist/boot/leader.js +1 -0
- package/dist/boot/memory-consolidation.js +2 -2
- package/dist/boot/org-delivery.js +15 -2
- package/dist/boot/resolve-spec.d.ts +4 -1
- package/dist/boot/resolve-spec.js +29 -19
- package/dist/boot/retention-lane.d.ts +14 -8
- package/dist/boot/retention-lane.js +2 -8
- package/dist/boot/runner-deps.d.ts +10 -4
- package/dist/boot/runner-deps.js +2 -0
- package/dist/boot/session-faces.d.ts +0 -3
- package/dist/boot/session-faces.js +15 -8
- package/dist/boot/stage-02-web-admin-pull.js +2 -2
- package/dist/boot/stage-04-stores.d.ts +1 -12
- package/dist/boot/stage-05-execution-env.d.ts +3 -12
- package/dist/boot/stage-05-execution-env.js +8 -5
- package/dist/boot/stage-06-runners.d.ts +3 -12
- package/dist/boot/stage-06-runners.js +5 -3
- package/dist/boot/stage-07-capability-layer.d.ts +3 -12
- package/dist/boot/stage-07-capability-layer.js +2 -2
- package/dist/boot/stage-08-reapers.d.ts +3 -12
- package/dist/boot/stage-09-leader.d.ts +3 -12
- package/dist/boot/stage-09-leader.js +2 -3
- package/dist/boot/stage-10-http-server.d.ts +3 -12
- package/dist/boot/stage-10-http-server.js +5 -3
- package/dist/boot/stores.js +4 -4
- package/dist/boot/web-admin.d.ts +5 -2
- package/dist/boot/web-admin.js +5 -5
- package/dist/brain.js +5 -5
- package/dist/budget.d.ts +17 -15
- package/dist/budget.js +2 -2
- package/dist/capabilities/mcp-probe.d.ts +36 -11
- package/dist/capabilities/mcp-probe.js +44 -5
- package/dist/capabilities/scenarios.d.ts +1 -1
- package/dist/capability-facts.d.ts +21 -7
- package/dist/capability-facts.js +13 -3
- package/dist/checkpoint-identity.d.ts +13 -6
- package/dist/config-catalog.js +14 -8
- package/dist/config-invariants.d.ts +23 -3
- package/dist/config-invariants.js +42 -0
- package/dist/config-provider.d.ts +19 -24
- package/dist/config-provider.js +27 -12
- package/dist/config-types.d.ts +25 -12
- package/dist/config.d.ts +33 -0
- package/dist/config.js +52 -20
- package/dist/delegated-session-owner.d.ts +2 -2
- package/dist/deployment-shape.d.ts +7 -7
- package/dist/device-enrollment.d.ts +41 -2
- package/dist/device-enrollment.js +27 -12
- package/dist/device-store.d.ts +58 -85
- package/dist/device-store.js +19 -49
- package/dist/device-ws-hub.js +2 -1
- package/dist/env-name-allowlist-knobs.d.ts +5 -4
- package/dist/exec-cut.d.ts +60 -0
- package/dist/exec-cut.js +36 -0
- package/dist/execution-lane-caps.d.ts +19 -0
- package/dist/execution-lane-caps.js +10 -0
- package/dist/execution-placement.d.ts +259 -0
- package/dist/execution-placement.js +199 -0
- package/dist/field-conflict.d.ts +3 -2
- package/dist/field-conflict.js +1 -0
- package/dist/gate-settlement.d.ts +71 -0
- package/dist/gate-settlement.js +40 -0
- package/dist/hooks/classic-stations.d.ts +1 -0
- package/dist/hooks/classic-stations.js +1 -0
- package/dist/hooks/command-spawn.d.ts +2 -2
- package/dist/hooks/hook-runner.js +14 -14
- package/dist/hosted-posture.d.ts +2 -2
- package/dist/http/admission.js +10 -19
- package/dist/http/checkpoint-ctx.d.ts +21 -12
- package/dist/http/checkpoint-ctx.js +4 -2
- package/dist/http/claimed-run.d.ts +1 -1
- package/dist/http/dispatch.d.ts +4 -0
- package/dist/http/dispatch.js +3 -0
- package/dist/http/durable-run-leg.js +12 -6
- package/dist/http/execution-context.d.ts +218 -0
- package/dist/http/execution-context.js +330 -0
- package/dist/http/memory-compliance-failure-reply.js +3 -0
- package/dist/http/resume-legs.d.ts +5 -11
- package/dist/http/resume-legs.js +179 -104
- package/dist/http/route-ctx.d.ts +44 -59
- package/dist/http/route-ctx.js +2 -5
- package/dist/http/routes/a2a-serve.js +10 -4
- package/dist/http/routes/agents-roster.d.ts +1 -1
- package/dist/http/routes/agents-roster.js +5 -1
- package/dist/http/routes/approvals-assistant.js +6 -6
- package/dist/http/routes/background.d.ts +29 -1
- package/dist/http/routes/background.js +29 -2
- package/dist/http/routes/capabilities.js +12 -10
- package/dist/http/routes/devices.d.ts +13 -19
- package/dist/http/routes/devices.js +21 -26
- package/dist/http/routes/mcp-reconnect.d.ts +3 -2
- package/dist/http/routes/mcp-reconnect.js +7 -5
- package/dist/http/routes/memory-consolidation.d.ts +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/rules.js +3 -3
- package/dist/http/routes/runs.js +11 -7
- package/dist/http/routes/session-sync.js +110 -15
- package/dist/http/routes/sessions.d.ts +6 -0
- package/dist/http/routes/sessions.js +119 -13
- package/dist/http/routes/side-query.d.ts +12 -4
- package/dist/http/routes/side-query.js +6 -8
- package/dist/http/routes/tasks.js +800 -782
- package/dist/http/server.d.ts +61 -39
- package/dist/http/server.js +11 -0
- package/dist/http/session-inherit-env.d.ts +3 -4
- package/dist/http/session-inherit-env.js +1 -1
- package/dist/http/wire-types.d.ts +22 -18
- package/dist/identity-source.d.ts +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/leader/wire.d.ts +7 -0
- package/dist/leader/wire.js +5 -4
- package/dist/mcp-assertion-header.d.ts +51 -40
- package/dist/mcp-assertion-header.js +10 -11
- package/dist/mcp-authorization-face.d.ts +27 -0
- package/dist/mcp-authorization-face.js +15 -0
- package/dist/memory-operator-faces.d.ts +13 -4
- package/dist/memory-operator-faces.js +1 -17
- package/dist/memory-sync-client.d.ts +53 -14
- package/dist/memory-sync-client.js +187 -92
- package/dist/memory-sync.d.ts +1 -1
- package/dist/memory-sync.js +3 -2
- package/dist/model-route-gap.d.ts +6 -22
- package/dist/model-route-gap.js +7 -5
- package/dist/mods-declaration.d.ts +110 -0
- package/dist/mods-declaration.js +109 -0
- package/dist/observability/cost-taxonomy.d.ts +2 -2
- package/dist/observability/err-text.d.ts +5 -0
- package/dist/observability/err-text.js +9 -0
- package/dist/observability/fail-open.d.ts +17 -1
- package/dist/observability/fail-open.js +17 -1
- package/dist/observability/run-terminal.js +0 -1
- package/dist/observability/secret-env-scrub.d.ts +1 -1
- package/dist/observability/secret-env-scrub.js +1 -1
- package/dist/orchestration/workflow-notify-journal.js +4 -4
- package/dist/org-memory-admission.d.ts +4 -3
- package/dist/org-memory-admission.js +6 -6
- package/dist/parked-decide.d.ts +49 -11
- package/dist/parked-decide.js +25 -26
- package/dist/parked-revive-surface.d.ts +12 -15
- package/dist/parked-revive-surface.js +3 -6
- package/dist/peer-seat.d.ts +156 -0
- package/dist/peer-seat.js +273 -0
- package/dist/permission-rule-vocab.d.ts +7 -7
- package/dist/permission-rule-vocab.js +3 -3
- package/dist/plain-string-record.d.ts +38 -0
- package/dist/plain-string-record.js +38 -0
- package/dist/plugins/approval-ask-store-file.d.ts +7 -0
- package/dist/plugins/approval-ask-store-file.js +11 -0
- package/dist/plugins/approval-ask-store-memory.d.ts +6 -0
- package/dist/plugins/approval-ask-store-memory.js +12 -0
- package/dist/plugins/approval-ask-store-sql.d.ts +22 -5
- package/dist/plugins/approval-ask-store-sql.js +30 -12
- package/dist/plugins/background-agent-store-sql.js +6 -3
- package/dist/plugins/caching-session-store.d.ts +11 -2
- package/dist/plugins/caching-session-store.js +15 -3
- package/dist/plugins/checkpoint-store-sql.d.ts +21 -25
- package/dist/plugins/checkpoint-store-sql.js +13 -15
- package/dist/plugins/device-store-sql.d.ts +25 -51
- package/dist/plugins/device-store-sql.js +49 -103
- package/dist/plugins/execution-placement-sql.d.ts +151 -0
- package/dist/plugins/execution-placement-sql.js +223 -0
- package/dist/plugins/file-history-store-sql.d.ts +7 -6
- package/dist/plugins/file-history-store-sql.js +8 -7
- package/dist/plugins/file-run-store.d.ts +6 -1
- package/dist/plugins/file-run-store.js +4 -0
- package/dist/plugins/fork-routing-session-store.d.ts +6 -0
- package/dist/plugins/fork-routing-session-store.js +16 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
- package/dist/plugins/k8s-pod-reaper.js +3 -3
- package/dist/plugins/local-checkpoint-store.d.ts +1 -11
- package/dist/plugins/local-checkpoint-store.js +0 -1
- package/dist/plugins/local-session-store.d.ts +183 -45
- package/dist/plugins/local-session-store.js +412 -105
- package/dist/plugins/mailbox-store-sql.d.ts +11 -3
- package/dist/plugins/mailbox-store-sql.js +103 -15
- package/dist/plugins/memory-key-guards.d.ts +4 -0
- package/dist/plugins/memory-key-guards.js +1 -0
- package/dist/plugins/memory-run-store.d.ts +1 -1
- package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
- package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
- package/dist/plugins/one-time-migrations.d.ts +117 -6
- package/dist/plugins/one-time-migrations.js +383 -14
- package/dist/plugins/outcome-ledger-sql.d.ts +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +13 -15
- package/dist/plugins/permission-rule-store-sql.js +34 -29
- package/dist/plugins/pg-pool.d.ts +7 -3
- package/dist/plugins/pg-session-storage.d.ts +41 -22
- package/dist/plugins/pg-session-storage.js +136 -55
- package/dist/plugins/remote-env-adb.d.ts +22 -4
- package/dist/plugins/remote-env-adb.js +70 -62
- package/dist/plugins/remote-env-device.d.ts +32 -12
- package/dist/plugins/remote-env-device.js +18 -17
- package/dist/plugins/remote-env-e2b.d.ts +19 -3
- package/dist/plugins/remote-env-e2b.js +41 -42
- package/dist/plugins/remote-env-file-error.d.ts +2 -0
- package/dist/plugins/remote-env-file-error.js +1 -1
- package/dist/plugins/remote-env-host.d.ts +23 -8
- package/dist/plugins/remote-env-host.js +66 -70
- package/dist/plugins/remote-env-k8s.d.ts +13 -16
- package/dist/plugins/remote-env-k8s.js +61 -54
- package/dist/plugins/remote-env-local-docker.d.ts +30 -5
- package/dist/plugins/remote-env-local-docker.js +46 -59
- package/dist/plugins/remote-env-ssh.d.ts +18 -4
- package/dist/plugins/remote-env-ssh.js +72 -56
- package/dist/plugins/remote-shell.d.ts +100 -3
- package/dist/plugins/remote-shell.js +166 -2
- package/dist/plugins/retention-store-sql.d.ts +23 -8
- package/dist/plugins/retention-store-sql.js +13 -1
- package/dist/plugins/run-store-sql.d.ts +13 -3
- package/dist/plugins/run-store-sql.js +5 -0
- package/dist/plugins/schema-consistency.d.ts +15 -5
- package/dist/plugins/schema-consistency.js +14 -8
- package/dist/plugins/schema-drift-sentence.d.ts +27 -0
- package/dist/plugins/schema-drift-sentence.js +38 -0
- package/dist/plugins/session-fork.d.ts +63 -12
- package/dist/plugins/session-fork.js +38 -21
- package/dist/plugins/session-ownership.d.ts +7 -0
- package/dist/plugins/session-ownership.js +5 -0
- package/dist/plugins/session-row-sql.d.ts +22 -0
- package/dist/plugins/session-row-sql.js +81 -0
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/plugins/session-workspace-cell.d.ts +17 -0
- package/dist/plugins/session-workspace-cell.js +38 -0
- package/dist/plugins/store-backend.d.ts +1 -1
- package/dist/plugins/store-backend.js +26 -14
- package/dist/plugins/store-contracts.d.ts +25 -5
- package/dist/plugins/tidb-pool.d.ts +12 -7
- package/dist/plugins/tidb-pool.js +15 -3
- package/dist/plugins/tidb-session-store.d.ts +74 -28
- package/dist/plugins/tidb-session-store.js +132 -53
- package/dist/plugins/tool-result-store-sql.d.ts +16 -8
- package/dist/plugins/tool-result-store-sql.js +26 -17
- package/dist/plugins/worktree-isolation.d.ts +3 -2
- package/dist/plugins/worktree-isolation.js +1 -7
- package/dist/project-identity.d.ts +1 -14
- package/dist/project-identity.js +0 -4
- package/dist/project-memory.d.ts +9 -15
- package/dist/project-memory.js +5 -4
- package/dist/request-key-closure.js +1 -1
- package/dist/request-value-closure.d.ts +6 -7
- package/dist/request-value-closure.js +1 -3
- package/dist/rules-consent.js +6 -4
- package/dist/run-local.d.ts +6 -0
- package/dist/run-local.js +36 -26
- package/dist/runs.d.ts +9 -2
- package/dist/runs.js +9 -2
- package/dist/sandbox-git-plane.d.ts +2 -2
- package/dist/security.d.ts +38 -12
- package/dist/security.js +8 -2
- package/dist/server-secret-env.d.ts +15 -8
- package/dist/server-secret-env.js +4 -2
- package/dist/session-children-retire.d.ts +27 -9
- package/dist/session-children-retire.js +9 -5
- package/dist/session-incarnation.d.ts +19 -10
- package/dist/session-incarnation.js +5 -3
- package/dist/session-row.d.ts +114 -5
- package/dist/session-row.js +55 -5
- package/dist/session-titler.d.ts +2 -2
- package/dist/session-workspace.d.ts +130 -0
- package/dist/session-workspace.js +83 -0
- package/dist/spec-fields.d.ts +1 -1
- package/dist/standing-answer-grant.d.ts +2 -13
- package/dist/standing-answer-grant.js +0 -7
- package/dist/task-a2a.d.ts +44 -14
- package/dist/task-a2a.js +46 -20
- package/dist/task-cwd.d.ts +6 -1
- package/dist/task-mcp.d.ts +33 -20
- package/dist/task-mcp.js +46 -22
- package/dist/tool-approval.d.ts +38 -8
- package/dist/tool-approval.js +57 -13
- package/dist/trace/core-keyset-guard.d.ts +33 -18
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +20 -1
- package/dist/trace/exit-seat.js +3 -0
- package/dist/trace/extension-call-face.d.ts +33 -0
- package/dist/trace/extension-call-face.js +39 -0
- package/dist/trace/project.d.ts +26 -7
- package/dist/trace/project.js +30 -3
- package/dist/trace/redact.js +42 -13
- package/dist/trace/self-minted-wire.d.ts +240 -12
- package/dist/trace/self-minted-wire.js +100 -4
- package/dist/trace/wire-projection-faces.d.ts +6 -6
- package/dist/trace/wire-projection-faces.js +6 -5
- package/dist/url-refusal.d.ts +1 -1
- package/dist/user-authored-text.d.ts +2 -2
- package/dist/user-authored-text.js +16 -1
- package/dist/web-admin-client/apply-effective.d.ts +17 -3
- package/dist/web-admin-client/apply-effective.js +20 -10
- package/dist/web-admin-client/center-request.d.ts +3 -2
- package/dist/web-admin-client/center-request.js +2 -3
- package/dist/web-admin-client/skills-mcp.d.ts +8 -4
- package/dist/web-admin-client/skills-mcp.js +32 -18
- package/dist/web-admin-client/types.d.ts +11 -0
- package/package.json +3 -3
- package/wire-changes/server@7.138.0.json +446 -0
- package/wire-changes/server@7.139.0.json +348 -0
- package/dist/http/device-placement.d.ts +0 -175
- package/dist/http/device-placement.js +0 -145
- package/dist/http/mcp-remote-text.d.ts +0 -5
- package/dist/http/mcp-remote-text.js +0 -7
package/dist/session-row.js
CHANGED
|
@@ -1,7 +1,50 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { normalizePeerName, sessionRowMatches } from "@sema-agent/core";
|
|
2
3
|
export const SESSION_TITLE_MAX_CHARS = 120;
|
|
3
|
-
const
|
|
4
|
-
const
|
|
4
|
+
export const SESSION_ROW_CELL_DEFAULTS = { title: null, pinned: false };
|
|
5
|
+
const SESSION_ROW_TAG_DOMAIN = "sema.session-row-tag/1";
|
|
6
|
+
export const SESSION_ROW_TAG_SHAPE = /^[A-Za-z0-9_-]{43}$/;
|
|
7
|
+
export function sessionRowTagOf(row) {
|
|
8
|
+
const keys = Object.keys(SESSION_ROW_CELL_DEFAULTS).sort();
|
|
9
|
+
const cells = keys.filter((k) => row.cells[k] !== SESSION_ROW_CELL_DEFAULTS[k]).map((k) => [k, row.cells[k]]);
|
|
10
|
+
return createHash("sha256").update(JSON.stringify([SESSION_ROW_TAG_DOMAIN, row.sessionId, row.owner, row.incarnation, cells]), "utf8").digest("base64url");
|
|
11
|
+
}
|
|
12
|
+
export function sessionRowTagKey(row) {
|
|
13
|
+
return row.incarnation === undefined ? {} : { rowTag: sessionRowTagOf({ sessionId: row.sessionId, owner: row.owner, incarnation: row.incarnation, cells: row.cells }) };
|
|
14
|
+
}
|
|
15
|
+
export function judgeSessionRowWrite(sessionId, how, locked, logNow) {
|
|
16
|
+
if (locked === undefined || !sessionRowMatches(how.row, { owner: locked.owner, incarnation: locked.incarnation }, logNow) || locked.placed)
|
|
17
|
+
return "absent";
|
|
18
|
+
switch (how.writer) {
|
|
19
|
+
case "replay":
|
|
20
|
+
return "write";
|
|
21
|
+
case "client": {
|
|
22
|
+
if (locked.mirror)
|
|
23
|
+
return "mirror";
|
|
24
|
+
if (how.rowTag === undefined)
|
|
25
|
+
return "write";
|
|
26
|
+
const current = locked.incarnation === undefined ? undefined : sessionRowTagOf({ sessionId, owner: locked.owner, incarnation: locked.incarnation, cells: locked.cells });
|
|
27
|
+
return current === how.rowTag ? "write" : "changed";
|
|
28
|
+
}
|
|
29
|
+
default: {
|
|
30
|
+
const unknownWriter = how;
|
|
31
|
+
throw new Error(`judgeSessionRowWrite: no writer ${JSON.stringify(unknownWriter)}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function settleSessionRowWrite(how, verdict, answer) {
|
|
36
|
+
const judged = how.row.row;
|
|
37
|
+
if (judged === null || answer === undefined)
|
|
38
|
+
return { kind: "absent" };
|
|
39
|
+
if (!sessionRowMatches({ row: judged }, answer.occupant, unreachableLogMark))
|
|
40
|
+
return { kind: "absent" };
|
|
41
|
+
return verdict === "write" ? { kind: "written", row: answer.row } : { kind: "changed", row: answer.row };
|
|
42
|
+
}
|
|
43
|
+
function unreachableLogMark() {
|
|
44
|
+
throw new Error("settleSessionRowWrite: the answer row's occupant check carries no log mark, yet the judge asked for one");
|
|
45
|
+
}
|
|
46
|
+
const BODY_SHAPE_MESSAGE = "the body must be a JSON object carrying at least one of `title` and `pinned` (and, optionally, `expectedRowTag`), and nothing else";
|
|
47
|
+
const ROW_TAG_INVALID_MESSAGE = "expectedRowTag must be the `rowTag` string a session list row carries (43 characters: letters, digits, `-` and `_`)";
|
|
5
48
|
const EMPTY_TITLE_MESSAGE = "title must not be the empty string — send `null` to clear it back to the automatic title";
|
|
6
49
|
const INVISIBLE_TITLE_MESSAGE = "title must contain at least one visible character";
|
|
7
50
|
const C0_CONTROL = /[\u0000-\u001f]/u;
|
|
@@ -25,11 +68,13 @@ export function readSessionTitle(v) {
|
|
|
25
68
|
export function readSessionPinned(v) {
|
|
26
69
|
return typeof v === "boolean" ? { pinned: v } : { problem: { code: "request.field_invalid", message: "pinned must be a boolean" } };
|
|
27
70
|
}
|
|
71
|
+
const EXPECTED_ROW_TAG_KEY = "expectedRowTag";
|
|
28
72
|
export function readSessionRowPatch(body) {
|
|
29
73
|
if (body === null || typeof body !== "object" || Array.isArray(body))
|
|
30
74
|
return { problem: { code: "request.body_shape", message: BODY_SHAPE_MESSAGE } };
|
|
31
75
|
const keys = Object.keys(body);
|
|
32
|
-
|
|
76
|
+
const isCell = (k) => Object.hasOwn(SESSION_ROW_CELL_DEFAULTS, k);
|
|
77
|
+
if (!keys.some(isCell) || keys.some((k) => !isCell(k) && k !== EXPECTED_ROW_TAG_KEY)) {
|
|
33
78
|
return { problem: { code: "request.body_shape", message: BODY_SHAPE_MESSAGE } };
|
|
34
79
|
}
|
|
35
80
|
const b = body;
|
|
@@ -46,6 +91,11 @@ export function readSessionRowPatch(body) {
|
|
|
46
91
|
return read;
|
|
47
92
|
patch = { ...patch, pinned: read.pinned };
|
|
48
93
|
}
|
|
49
|
-
|
|
94
|
+
if (!(EXPECTED_ROW_TAG_KEY in b))
|
|
95
|
+
return { patch };
|
|
96
|
+
const tag = b.expectedRowTag;
|
|
97
|
+
if (typeof tag !== "string" || !SESSION_ROW_TAG_SHAPE.test(tag))
|
|
98
|
+
return { problem: { code: "request.field_invalid", message: ROW_TAG_INVALID_MESSAGE } };
|
|
99
|
+
return { patch, expectedRowTag: tag };
|
|
50
100
|
}
|
|
51
101
|
//# sourceMappingURL=session-row.js.map
|
package/dist/session-titler.d.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* submission's acceptance seat (`run-acceptance.ts`: the objective entered the conversation, or a result-only verify /
|
|
6
6
|
* cascade leg came back having done work). A run the engine refuses before acceptance never reaches the titler (no
|
|
7
7
|
* model call for a refused input). No need to wait for the run to finish, and the web Recents gets the title sooner. Fills a NULL title only (`setTitleIfNull`,
|
|
8
|
-
* store-side `WHERE title IS NULL`) — the user writes the SAME column
|
|
9
|
-
* `
|
|
8
|
+
* store-side `WHERE title IS NULL`) — the user writes the SAME column (`PATCH /v1/sessions/:id` → the session row's write
|
|
9
|
+
* primitive `writeSessionRowCells`, unconditional unless the request carries `expectedRowTag`): a user-set title is never overwritten (the probe below reads "titled" and skips the model
|
|
10
10
|
* call), and a title the user cleared (`title: null` ⇒ NULL) is filled again at the next submit (law: `session-row.ts`).
|
|
11
11
|
* "Has it a title?" is read from the column on every submit (one point read) — the process keeps NO "already titled"
|
|
12
12
|
* memo, because such a memo would be a second answer to that question that a clear turns stale. Generation = one
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **会话的位置与项目** —— 一个事实、一个写者、一种读法(引擎 7.58.0:`workspace_changed` 是腿的整份快照;
|
|
3
|
+
* 会话行 = 该会话上最后一帧)。
|
|
4
|
+
*
|
|
5
|
+
* ## 律(三句)
|
|
6
|
+
* · **判官只有引擎一只**:每条腿起点一帧 `workspace_changed`(`cwd` / `root` / `entered?` / `project?` / `remote`),之后每变一帧;
|
|
7
|
+
* 这条腿属于哪个项目由引擎对检出的 `.sema/project` 记号判一次(带了 `TaskSpec.projectId` 就必须等于记号,
|
|
8
|
+
* 否则整腿拒 `config.project_identity_mismatch`;没有记号时声明就是身份)。本仓**不判**项目、不读记号。
|
|
9
|
+
* · **会话行只存一格**:`workspace` = 该会话上一条腿**自己的**最后一帧(SQL `session_meta.workspace`;local 店
|
|
10
|
+
* `session-workspaces.json` 那一格)。成员由 {@link SESSION_WORKSPACE_DISPOSITIONS} 一张表定(引擎给帧加成员 ⇒ 表 tsc 红,
|
|
11
|
+
* 门在 `trace/core-keyset-guard.ts` ⑰ 读同一张表)。**唯一写者** = 腿的记录座 {@link createSessionWorkspaceRecorder}
|
|
12
|
+
* (持流的四条腿逐帧喂同一只;委派子代转上来的帧 —— 带 `parentToolCallId` / `sourceTaskId` / `bgAgentId` —— 不是这条腿的位置,不认)。
|
|
13
|
+
* 每一笔写**在腿的流循环里等完**(不排队、没有在飞的写:腿从任何出口离开时都没有它的写还悬着 ⇒ 写恒落在这条腿的 run 认领之内,
|
|
14
|
+
* 同一会话的下一条腿不会被一笔晚到的写盖掉),并且**只写受理时见证到的那一位住客**({@link stampLegSessionWitness}:resolveSpec
|
|
15
|
+
* 受理时读会话行的化身那一读,同一只值也是委派树根的见证)—— 会话被删掉又以同一 id 重建之后,上一位住客的腿写不进新住客的行。
|
|
16
|
+
* · **每条腿声明一个值**:`TaskSpec.projectId` = 这次提交自己带的 `projectId`(新提交的请求体;续跑腿重放的是持久化的同一个体)
|
|
17
|
+
* ?? 会话行上最后一帧的 `project.id` —— **只当它是调用方声明过的**(`source: "declared"`:没有记号的检出里声明就是身份,行存它就是为了接着声明);
|
|
18
|
+
* `source: "marker"` 是引擎对**那一次**检出的记号判决,不续声明(引擎每条腿对自己的检出再判 —— 记号改写 / 换检出之后续跑照跑);
|
|
19
|
+
* 都没有 ⇒ 不带(引擎在有记号的检出里自判)。唯一铸点 {@link legProjectIdOf},唯一调用点 `boot/resolve-spec.ts`(新提交与续跑同经那一只装配)。
|
|
20
|
+
*
|
|
21
|
+
* ## 什么因此变少
|
|
22
|
+
* 修前请求键 `projectId` 被提交口受理、却从不交给引擎(keyset 守卫记该引擎座「未接线」,与受理表相反 —— 编译门 ② 红着);
|
|
23
|
+
* 会话在没有记号的检出里续跑时,项目随每一次不带 `projectId` 的提交丢掉。现在:一个值进引擎、一格存引擎的答案、下一条腿读那一格。
|
|
24
|
+
*
|
|
25
|
+
* ## 不在这里的(如实)
|
|
26
|
+
* · **记忆项目层**(单用户 `userrepo:` 键,`project-identity.ts` 的 git 检出身份)不是这个事实:它在装配期(任何帧之前)交给
|
|
27
|
+
* `TaskSpec.memory.projectKey`,按 git 仓库(origin 远端 / 本机路径)键控,与 `.sema/project` 记号无关;引擎 7.58.0 自己也写明
|
|
28
|
+
* 「只声明的身份不改记忆的键」。两者是否该合成一个身份,是另一张设计票。
|
|
29
|
+
* · 多租户记忆分区(`proj:<租户>/<projectId>`)与登记簿种子读的是**这次提交**的 `projectId`(按提交挑盘,修前即如此),不读行。
|
|
30
|
+
* · 行上的 `root` / `cwd` / `entered` / `remote` 今天只存不读(会话面读它们归 wire 半场那一车);fork 出的新会话从「没有记录」起。
|
|
31
|
+
* · **只回结果的腿(verify / cascade,含续跑的 verify 臂)不记行**:引擎在组合入口里自己排空内层腿的流,宿主手里没有流,而引擎给组合入口开的
|
|
32
|
+
* 帧座(`onStatusEvent` / `onWiringManifest`)没有这一帧 ⇒ 这条腿的位置与项目到不了行,行停在更早一条腿的那一帧(没有记号的检出里,
|
|
33
|
+
* 下一条不带 `projectId` 的提交续声明的是那一帧声明过的项目)。本仓不替引擎铸「这条腿站在哪里」;要引擎为组合入口开这一帧的座。
|
|
34
|
+
* · **行读在 run 认领之前**(resolveSpec 的每一读都是):同一会话两次提交交叠、后一次的装配读行之后前一次才落定并放认领 ⇒ 后一次读到的是更早
|
|
35
|
+
* 一条腿的项目(有记号的检出里多一次 409,没有记号的检出里续声明旧项目)。修法要把声明的装配挪到认领之后,不在本模块补第二个写者。
|
|
36
|
+
*/
|
|
37
|
+
import type { TaskEvent, TaskSpec } from "@sema-agent/core";
|
|
38
|
+
import type { DelegationRootWitness } from "./delegated-session-owner.js";
|
|
39
|
+
/**
|
|
40
|
+
* `workspace_changed` 帧在会话行上的样子(**唯一**一张表;表序 = 存储形的键序)。`forward` = 原值存进行;`strip` = 不进行。
|
|
41
|
+
* 腿身份四键(`eventId` / `parentToolCallId` / `sourceTaskId` / `bgAgentId`)是这一帧在流上的身份,不是位置;`type` 是臂名。
|
|
42
|
+
*/
|
|
43
|
+
export declare const SESSION_WORKSPACE_DISPOSITIONS: {
|
|
44
|
+
readonly type: "strip";
|
|
45
|
+
readonly cwd: "forward";
|
|
46
|
+
readonly root: "forward";
|
|
47
|
+
readonly entered: {
|
|
48
|
+
readonly nested: {
|
|
49
|
+
readonly dir: "forward";
|
|
50
|
+
readonly byPath: "forward";
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
readonly project: {
|
|
54
|
+
readonly nested: {
|
|
55
|
+
readonly id: "forward";
|
|
56
|
+
readonly source: "forward";
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
readonly remote: "forward";
|
|
60
|
+
readonly eventId: "strip";
|
|
61
|
+
readonly parentToolCallId: "strip";
|
|
62
|
+
readonly sourceTaskId: "strip";
|
|
63
|
+
readonly bgAgentId: "strip";
|
|
64
|
+
};
|
|
65
|
+
type WorkspaceFrame = Extract<TaskEvent, {
|
|
66
|
+
type: "workspace_changed";
|
|
67
|
+
}>;
|
|
68
|
+
/** 表里存进行的键(`strip` 之外的)—— 存储形由表派生,不另写一份成员清单。 */
|
|
69
|
+
type KeptKeys = {
|
|
70
|
+
[K in keyof typeof SESSION_WORKSPACE_DISPOSITIONS]: (typeof SESSION_WORKSPACE_DISPOSITIONS)[K] extends "strip" ? never : K;
|
|
71
|
+
}[keyof typeof SESSION_WORKSPACE_DISPOSITIONS];
|
|
72
|
+
/** 会话行上那一格的值:最后一帧里本仓存下的成员(型从引擎的帧臂派生)。 */
|
|
73
|
+
export type SessionWorkspace = Pick<WorkspaceFrame, KeptKeys>;
|
|
74
|
+
/** 会话店的这一面(两只方法都在才算 —— 只有一半的店让读写不同源)。 */
|
|
75
|
+
export interface SessionWorkspaceStore {
|
|
76
|
+
/** 这只会话上最后一帧;没有记录 / 没有这一行 ⇒ `undefined`。格读不出 ⇒ 读作没有记录并计一次(`decodeSessionWorkspaceCell`)。 */
|
|
77
|
+
readSessionWorkspace(sessionId: string): Promise<SessionWorkspace | undefined>;
|
|
78
|
+
/**
|
|
79
|
+
* 整格覆写(最后一帧胜),**只写住客是 `incarnation` 那一位的行**:行不在、或此刻坐的是另一位住客 ⇒ 不写、不抛(那一行不是这条腿的)。
|
|
80
|
+
* 写**没有提交**必须抛。SQL 两孪生走店的一般查询面(与腿循环里的账本追加同一形,不设每查询的客户端超时 —— 客户端放弃等待而服务端照跑完的写,
|
|
81
|
+
* 正是能在这条腿离开之后落地的那一形);卡住的上界归服务端(锁等待超时 / 部署的 `DB_QUERY_TIMEOUT_MS`)。
|
|
82
|
+
*/
|
|
83
|
+
recordSessionWorkspace(sessionId: string, incarnation: string, workspace: SessionWorkspace): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
/** 店有没有这一面 —— 结构判(包装店 `CachingSessionStore` 逐方法转发,判的是能力不是名字)。没有 ⇒ 不记、不读(core 的内存 dev 店)。 */
|
|
86
|
+
export declare function sessionWorkspaceStoreOf(store: unknown): SessionWorkspaceStore | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* 这条腿的会话**在受理时**是哪一位住客(会话 id + 化身令牌)的 spec 字段键 —— 与 run 名册 / 执行车道字段同一形:symbol 键随 spread / rest 解构走
|
|
89
|
+
* (续跑腿的 `taskConfig` 是 rest 解构出来的),对引擎的键面不可见(JSON / 结构化克隆跳过 symbol 键)。铸点唯一 = resolveSpec 出口
|
|
90
|
+
* ({@link stampLegSessionWitness}),值 = 受理时读会话行的那**一**读(同一只值也交给场景层作委派树根的见证);读点唯一 = {@link legSessionWitnessOf}。
|
|
91
|
+
*/
|
|
92
|
+
export declare const LEG_SESSION_WITNESS_FIELD: unique symbol;
|
|
93
|
+
/** 铸见证(**单一属主**:只许 resolveSpec 的出口调用)。`writable:false` + `configurable:false`:同一只 spec 被第二次铸 ⇒ 当场 TypeError。缺席 ⇒ 不铸。 */
|
|
94
|
+
export declare function stampLegSessionWitness<T extends TaskSpec>(spec: T, witness: DelegationRootWitness | undefined): T;
|
|
95
|
+
/** 读见证。`undefined` = 这只 spec 不是本仓装配出口产的,或这条腿没有会话 ⇒ 记录座不写(没有住客可认)。入参收 `object`:续跑腿手里是 rest 解构出来的 taskConfig。 */
|
|
96
|
+
export declare function legSessionWitnessOf(spec: object): DelegationRootWitness | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* 一帧 → 会话行上那一格的值。只认这条腿**自己的**帧(腿身份三键任一在场 = 委派子代转上来的,不是这条腿站在哪里);
|
|
99
|
+
* 按表投影后过存储形的同一只读者(写得进的就是读得出的)。读不出(引擎不会铸出这种帧)⇒ 不记、计一次。
|
|
100
|
+
*/
|
|
101
|
+
export declare function sessionWorkspaceOfFrame(ev: TaskEvent): SessionWorkspace | undefined;
|
|
102
|
+
/** 腿的记录座。 */
|
|
103
|
+
export interface SessionWorkspaceRecorder {
|
|
104
|
+
/**
|
|
105
|
+
* 持流的腿**逐帧**调用并等它(在本腿的取消成因闩之后)。这条腿自己的 `workspace_changed` ⇒ 这一笔写写完(或失败计一次)才兑现 ——
|
|
106
|
+
* 帧按到达序一笔一笔写,最后一帧胜;腿从任何出口(`done`、断连 `break`、抛)离开时没有写还悬着。其余帧当场兑现。**从不抛、从不拒**。
|
|
107
|
+
*/
|
|
108
|
+
observe(ev: TaskEvent): Promise<void>;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 记录座的**唯一**铸点(四条持流腿各在开流时铸一只:后台腿 `runs.ts`、活流腿与同步腿 `http/routes/tasks.ts`、续跑腿 `http/resume-legs.ts`),
|
|
112
|
+
* `witness` = 这条腿 spec 上的受理见证({@link legSessionWitnessOf})。店没有这一面、或见证缺席 / 受理时这只会话还没有住客令牌 ⇒
|
|
113
|
+
* 一只什么都不做的座(诚实缺席:认不出住客就不写 —— 写错住客比少记一次坏)。
|
|
114
|
+
*
|
|
115
|
+
* 失败方向(F 类):写失败(库抖 / 盘满 / 锁等待超时)⇒ 行停在上一帧,计一次;下一帧照写。放行的最坏后果 = 下一条不带 `projectId` 的提交读到旧的
|
|
116
|
+
* (或没有)项目 —— 有记号的检出里引擎照判记号(声明不等就响亮拒),没有记号的检出里这只会话少一次续声明。不放行的代价是把一条在跑的腿
|
|
117
|
+
* 为一次记录摔掉。等一笔写的代价 = 这条腿的流晚一次主键 UPDATE 的时延(引擎不等宿主消费,只是帧晚到;一条腿的帧只有起点一帧加每次换位一帧)。
|
|
118
|
+
* 库卡住(池取不到连接)时这条腿交给调用方的结果跟着等:后台腿与续跑腿的循环本来每一帧就等店写(账本追加);同步腿与不落账的活流腿此前循环里没有店写,
|
|
119
|
+
* 现在多等这一笔 —— 但同一只池上引擎自己的会话条目写(每一轮 `appendEntry`,`plugins/store-backend.ts` 的 `session()` 与其余店共用一池)本来就在
|
|
120
|
+
* 这条腿的关键路径上,池饿死时这条腿本来就停;上界归部署(`DB_QUERY_TIMEOUT_MS`、MySQL 池的排队上界)。local 后端的写不经池。
|
|
121
|
+
*/
|
|
122
|
+
export declare function createSessionWorkspaceRecorder(store: SessionWorkspaceStore | undefined, witness: DelegationRootWitness | undefined): SessionWorkspaceRecorder;
|
|
123
|
+
/**
|
|
124
|
+
* **这条腿交给引擎的 `TaskSpec.projectId`**(唯一铸点;见文件头第三句)。`submitted` = 这次提交自己带的、已过形状门且折成小写的值
|
|
125
|
+
* (新提交:鉴权口的 `resolvedProjectId`;续跑:持久化体的同一个键)。它在场就不读行。行读失败(库抖)原样上抛 —— 读不到「这只会话的项目」
|
|
126
|
+
* 不能悄悄折成「没有项目」(那会把一只登记在项目下的会话在没有记号的检出里改成无项目)。
|
|
127
|
+
*/
|
|
128
|
+
export declare function legProjectIdOf(submitted: string | undefined, store: SessionWorkspaceStore | undefined, sessionId: string | undefined): Promise<string | undefined>;
|
|
129
|
+
export {};
|
|
130
|
+
//# sourceMappingURL=session-workspace.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { recordFailOpen } from "./observability/fail-open.js";
|
|
2
|
+
import { readSessionWorkspaceCell, SESSION_WORKSPACE_DEGRADED } from "./plugins/session-workspace-cell.js";
|
|
3
|
+
import { projectByTable } from "./trace/projection-table.js";
|
|
4
|
+
export const SESSION_WORKSPACE_DISPOSITIONS = {
|
|
5
|
+
type: "strip",
|
|
6
|
+
cwd: "forward",
|
|
7
|
+
root: "forward",
|
|
8
|
+
entered: { nested: { dir: "forward", byPath: "forward" } },
|
|
9
|
+
project: { nested: { id: "forward", source: "forward" } },
|
|
10
|
+
remote: "forward",
|
|
11
|
+
eventId: "strip",
|
|
12
|
+
parentToolCallId: "strip",
|
|
13
|
+
sourceTaskId: "strip",
|
|
14
|
+
bgAgentId: "strip",
|
|
15
|
+
};
|
|
16
|
+
export function sessionWorkspaceStoreOf(store) {
|
|
17
|
+
const s = store;
|
|
18
|
+
if (typeof s?.readSessionWorkspace !== "function" || typeof s.recordSessionWorkspace !== "function")
|
|
19
|
+
return undefined;
|
|
20
|
+
return { readSessionWorkspace: s.readSessionWorkspace.bind(s), recordSessionWorkspace: s.recordSessionWorkspace.bind(s) };
|
|
21
|
+
}
|
|
22
|
+
export const LEG_SESSION_WITNESS_FIELD = Symbol("sema-server.legSessionWitness");
|
|
23
|
+
export function stampLegSessionWitness(spec, witness) {
|
|
24
|
+
if (witness !== undefined)
|
|
25
|
+
Object.defineProperty(spec, LEG_SESSION_WITNESS_FIELD, { value: Object.freeze({ ...witness }), enumerable: true, writable: false, configurable: false });
|
|
26
|
+
return spec;
|
|
27
|
+
}
|
|
28
|
+
export function legSessionWitnessOf(spec) {
|
|
29
|
+
return spec[LEG_SESSION_WITNESS_FIELD];
|
|
30
|
+
}
|
|
31
|
+
export function sessionWorkspaceOfFrame(ev) {
|
|
32
|
+
if (ev.type !== "workspace_changed")
|
|
33
|
+
return undefined;
|
|
34
|
+
if (ev.parentToolCallId !== undefined || ev.sourceTaskId !== undefined || ev.bgAgentId !== undefined)
|
|
35
|
+
return undefined;
|
|
36
|
+
const shape = readSessionWorkspaceCell(projectByTable("session_workspace", SESSION_WORKSPACE_DISPOSITIONS, ev, ev));
|
|
37
|
+
if (shape.problem !== undefined) {
|
|
38
|
+
recordFailOpen(SESSION_WORKSPACE_DEGRADED, `op=frame problem=${shape.problem.slice(0, 200)}`);
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return shape.value;
|
|
42
|
+
}
|
|
43
|
+
const SETTLED = Promise.resolve();
|
|
44
|
+
const NO_RECORD = { observe: () => SETTLED };
|
|
45
|
+
export function createSessionWorkspaceRecorder(store, witness) {
|
|
46
|
+
if (store === undefined || witness === undefined || witness.incarnation === null)
|
|
47
|
+
return NO_RECORD;
|
|
48
|
+
const { sessionId, incarnation } = witness;
|
|
49
|
+
return {
|
|
50
|
+
async observe(ev) {
|
|
51
|
+
const snapshot = sessionWorkspaceOfFrame(ev);
|
|
52
|
+
if (snapshot === undefined)
|
|
53
|
+
return;
|
|
54
|
+
try {
|
|
55
|
+
await store.recordSessionWorkspace(sessionId, incarnation, snapshot);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
recordFailOpen(SESSION_WORKSPACE_DEGRADED, `op=write session=${sessionId} ${(e instanceof Error ? e.message : String(e)).slice(0, 200)}`);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export async function legProjectIdOf(submitted, store, sessionId) {
|
|
64
|
+
if (submitted !== undefined)
|
|
65
|
+
return submitted;
|
|
66
|
+
if (store === undefined || sessionId === undefined)
|
|
67
|
+
return undefined;
|
|
68
|
+
const project = (await store.readSessionWorkspace(sessionId))?.project;
|
|
69
|
+
return project === undefined ? undefined : replayedProjectIdOf(project);
|
|
70
|
+
}
|
|
71
|
+
function replayedProjectIdOf(project) {
|
|
72
|
+
switch (project.source) {
|
|
73
|
+
case "declared":
|
|
74
|
+
return project.id;
|
|
75
|
+
case "marker":
|
|
76
|
+
return undefined;
|
|
77
|
+
default: {
|
|
78
|
+
const unknownSource = project.source;
|
|
79
|
+
throw new Error(`session workspace: unknown project source word ${JSON.stringify(unknownSource)} (the row reader admits only marker / declared)`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=session-workspace.js.map
|
package/dist/spec-fields.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ export interface TaskAgentsRefusal {
|
|
|
108
108
|
* 定义被悄悄削(一个丢掉的 isolation 就是放宽),比 400 更危险,故整条拒。`allowTools` / `denyTools` 本门只判形与条数(`toolList`),
|
|
109
109
|
* **名字归引擎一只判**(core 7.49.0 [ref] / [ref] 第二步起与根 run 的 `allowTools` / `excludeTools` 是同一只选择器,开跑前,三来源同判):core 7.51.0 [ref] 起
|
|
110
110
|
* 没有任何工具答应的名字(无人挂的名、大小写孪生 / 退役名 `bash` / `BASH`)**引擎不拒 run** —— 条目原样保留、经 onError(config)告知一次(带该写的拼法);
|
|
111
|
-
* 其中**写错现名的**(`denyTools:["BASH"]` 想禁的照用 = 静默放行;`allowTools:["BASH"]` 想放的什么也没放 = 静默收窄)由 `resolveSpec` 前置拒:**限制侧**(`denyTools`)
|
|
111
|
+
* 其中**写错现名的**(`denyTools:["BASH"]` 想禁的照用 = 静默放行;`allowTools:["BASH"]` 想放的什么也没放 = 静默收窄)由 `resolveSpec` 前置拒:**限制侧**(`denyTools`)三类全拒(近似名 · 退役名 · 文法读不出);名内的 `*` 自 7.137.0 起是整名 glob、受理(孤 `*` = 每一只工具),`Tool(content)` 引擎读成整只工具、同样受理,
|
|
112
112
|
* **放行侧**(`allowTools`)只拒近似名 —— 放行侧的内容形 / 没有后继的退役名 / 文法读不出照 7.130.0 受理、交引擎既有通告(有后继的退役名〔`bash` · `Task`〕与文法读不出的放行条目引擎在子代 prepare 整 run 拒 `config.agent.unknown_tool`,根 `allowTools` 拒 `config.tool_face_control`,同步腿两码同为 400〔`runs.ts` `PREPARE_REFUSAL_HTTP_STATUS`〕;`rule-entry-list.ts` `inertToolListEntryOf`:近似名提交 400 `config.agent.unknown_tool` 点名现名、续跑按方向分流;
|
|
113
113
|
* 判官仍是引擎的条目读法,不是第二个判官);
|
|
114
114
|
* **真未知名**(没有近似也没有退役对应:懒挂载的合法形)照旧只告运维、不拒。
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** 载体:引擎给一次调用盖过绑定词的任何形(活的 `AskRequest`、停驻行的 `tool_approval` 臂、运维队列行、策略层的 `ToolCallRequest` 都结构上满足)。 */
|
|
2
2
|
export interface StandingAnswerCard {
|
|
3
3
|
readonly boundGrantDigest?: unknown;
|
|
4
4
|
readonly boundGrantAbsence?: unknown;
|
|
5
5
|
}
|
|
6
|
-
/** 调用载体:策略层的 `ToolCallRequest`、豁免店的行(只有工具名)。 */
|
|
7
|
-
export interface StandingAnswerCall {
|
|
8
|
-
readonly toolName?: unknown;
|
|
9
|
-
}
|
|
10
6
|
/** 一问的授予绑定(闭集):`none` = 应答只覆盖这一次调用;`bound` = 授予、绑定可读;`unreadable` = 授予或说不清、绑定读不出。 */
|
|
11
7
|
export type GrantBinding = {
|
|
12
8
|
readonly kind: "none";
|
|
@@ -16,13 +12,6 @@ export type GrantBinding = {
|
|
|
16
12
|
} | {
|
|
17
13
|
readonly kind: "unreadable";
|
|
18
14
|
};
|
|
19
|
-
/**
|
|
15
|
+
/** 载体上的授予绑定 —— 只读引擎的词(读法见文件头注):摘要是串 ⇒ bound;摘要不在且词是 `not_bindable` ⇒ none;其余 ⇒ unreadable。 */
|
|
20
16
|
export declare function grantBindingOf(card: StandingAnswerCard): GrantBinding;
|
|
21
|
-
/** 调用上的授予绑定 —— 调用上没有引擎的词,读引擎绑定工厂的那一条规则(见文件头注;🚧 引擎在调用上盖词之后退役)。 */
|
|
22
|
-
export declare function callGrantBindingOf(call: StandingAnswerCall): GrantBinding;
|
|
23
|
-
/**
|
|
24
|
-
* 一问的授予绑定的**唯一折点**:读哪个载体由**问的属主**定 —— 名单自己的问(`deploymentListQuestion`,即 `standingAnswerHome` 的 `server_store` 家)读调用口,
|
|
25
|
-
* 引擎铸的问读卡口(载体与理由见文件头注)。所有把「一张卡」折成 {@link StandingAnswerFacts} 的读者(活的问、待决条目、停驻行、运维队列行)经它取绑定,不各自选口。
|
|
26
|
-
*/
|
|
27
|
-
export declare function questionGrantBindingOf(carrier: StandingAnswerCard & StandingAnswerCall, deploymentListQuestion: boolean): GrantBinding;
|
|
28
17
|
//# sourceMappingURL=standing-answer-grant.d.ts.map
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SKILL_TOOL_NAME } from "@sema-agent/core";
|
|
2
1
|
const NOT_BINDABLE = "not_bindable";
|
|
3
2
|
export function grantBindingOf(card) {
|
|
4
3
|
const digest = card.boundGrantDigest;
|
|
@@ -6,10 +5,4 @@ export function grantBindingOf(card) {
|
|
|
6
5
|
return { kind: "bound", digest };
|
|
7
6
|
return digest === undefined && card.boundGrantAbsence === NOT_BINDABLE ? { kind: "none" } : { kind: "unreadable" };
|
|
8
7
|
}
|
|
9
|
-
export function callGrantBindingOf(call) {
|
|
10
|
-
return call.toolName === SKILL_TOOL_NAME ? { kind: "unreadable" } : { kind: "none" };
|
|
11
|
-
}
|
|
12
|
-
export function questionGrantBindingOf(carrier, deploymentListQuestion) {
|
|
13
|
-
return deploymentListQuestion ? callGrantBindingOf(carrier) : grantBindingOf(carrier);
|
|
14
|
-
}
|
|
15
8
|
//# sourceMappingURL=standing-answer-grant.js.map
|
package/dist/task-a2a.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type A2aServerSpec, type CompliancePosture, type LockedKey } from "@sema-agent/core";
|
|
2
|
+
import { type A2aDraft, type PeerDesk, type PeerLease } from "./peer-seat.js";
|
|
2
3
|
import { type DeclarationRefusal } from "./request-declaration-refusal.js";
|
|
3
4
|
import type { StoredToolAxesRereadM19 } from "./stored-request-tool-axes-m19.js";
|
|
4
5
|
import { type PrincipalAxisInput } from "./deployment-shape.js";
|
|
@@ -21,18 +22,42 @@ export interface A2aInjectionDrop {
|
|
|
21
22
|
readonly reason: string;
|
|
22
23
|
readonly field: string;
|
|
23
24
|
}
|
|
24
|
-
/** The deployment-facing shape this module reads: the principal axis
|
|
25
|
-
export type A2aGateConfig = PrincipalAxisInput
|
|
25
|
+
/** The deployment-facing shape this module reads: the principal axis, the operator locks and the compliance posture. */
|
|
26
|
+
export type A2aGateConfig = PrincipalAxisInput & {
|
|
27
|
+
readonly lockedConfigKeys?: readonly LockedKey[];
|
|
28
|
+
readonly compliancePosture?: CompliancePosture;
|
|
29
|
+
};
|
|
26
30
|
/**
|
|
27
|
-
* Does THIS deployment honor caller-supplied per-request A2A peers?
|
|
28
|
-
*
|
|
31
|
+
* Does THIS deployment honor caller-supplied per-request A2A peers? The same three vetoes as `task-mcp.ts mcpInjectionHonored`, each with its own
|
|
32
|
+
* engine word: multi-tenant (deployment-shape row `multiTenant`: a tenant cannot point the shared worker at an agent), the operator lock `a2a`, and a
|
|
33
|
+
* compliance posture that denies `a2a_peers` (the engine refuses a task that declares a peer under either at prepare — advertising the bit would be
|
|
34
|
+
* "says yes, every use refused"). The capability bit and the request leg both read this predicate, so there is exactly one place where the answer can be wrong.
|
|
35
|
+
* A posture that cannot be resolved throws here (validated twice before this site; loud beats a quiet boolean, same as the MCP gate).
|
|
29
36
|
*/
|
|
30
37
|
export declare function a2aInjectionHonored(config: A2aGateConfig): boolean;
|
|
31
38
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
39
|
+
* 请求面一条 `a2aPeers[]` 对端在 wire 上的形(= `TaskRequestBody.a2aPeers` 的元素型):引擎的 `A2aServerSpec` 去掉凭据引用位、加上 `headers`(件)。
|
|
40
|
+
* 交给引擎之前一律经 {@link toA2aDraft} 拆成草稿 —— 引擎的声明收据对带 `headers` 的声明整条拒,件由部署的对端座在每次发送时作答。
|
|
34
41
|
*/
|
|
35
|
-
export
|
|
42
|
+
export type RequestA2aPeer = Omit<A2aServerSpec, "credentials"> & {
|
|
43
|
+
headers?: Record<string, string>;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* 请求体 `a2aPeers[i]` 的**处置表** —— 同时是键闭集(判序 = 书写序:名 → 对端地址 → 它的 agent card 地址 → 凭据头 → 身份头 → 技能收窄 → 轴覆写)与每个键的去处:
|
|
47
|
+
* `headers` 是件(进草稿,交给引擎的声明里不带),其余进声明。三道编译格:对请求形两向(`satisfies`:请求形多一个键而表没写 / 表里写一个请求形上没有的词 ⇒ 红);
|
|
48
|
+
* 对引擎的 `A2aServerSpec` 两道(引擎长出 / 退役一个键,或退役凭据引用位 ⇒ 红)。于是这张表是从型**派生**出来的数据,而不是谁记得去同步的清单。
|
|
49
|
+
*/
|
|
50
|
+
export declare const A2A_PEER_DISPOSITION: {
|
|
51
|
+
readonly name: "engine";
|
|
52
|
+
readonly url: "engine";
|
|
53
|
+
readonly cardUrl: "engine";
|
|
54
|
+
readonly headers: "seat";
|
|
55
|
+
readonly principalHeader: "engine";
|
|
56
|
+
readonly allowSkills: "engine";
|
|
57
|
+
readonly toolAxes: "engine";
|
|
58
|
+
};
|
|
59
|
+
/** 请求体 `a2aPeers[i]` 的键闭集 = 处置表的键(书写序即判序)。 */
|
|
60
|
+
export declare const A2A_PEER_ENTRY_KEYS: readonly ("name" | "headers" | "principalHeader" | "toolAxes" | "url" | "cardUrl" | "allowSkills")[];
|
|
36
61
|
/**
|
|
37
62
|
* **请求入口的一次读**(`boot/resolve-spec.ts` 在腿首铸一份,受理门与读器——续跑重放腿只有读器——都吃这一份):`body.a2aPeers` 若是数组,返回同形数组
|
|
38
63
|
* (每个对象条目换成它的 {@link PeerSnapshot};非对象条目原样;长度保真 —— 界外的条数仍是受理门撞帽拒与读器丢弃留声的依据,只快照界内的条目);不是数组(缺席 / `null` / 别的形)原样返回。
|
|
@@ -45,12 +70,14 @@ export declare function snapshotRequestA2a(raw: unknown, options: {
|
|
|
45
70
|
}): unknown;
|
|
46
71
|
/** 读这条对端的是哪条腿:`fresh` = 新提交;`resume` = 续跑重放升级前存下的请求体(`boot/resolve-spec.ts` 的 `opts.leg` 分流,同一个读法)。 */
|
|
47
72
|
type A2aRequestLeg = "fresh" | "resume";
|
|
73
|
+
/** 请求面对端 → 草稿:按 {@link A2A_PEER_DISPOSITION} 拆(`headers` 进件,其余进声明;拆法与 MCP 请求腿同一只 `splitDeclaration`)。件缺席或空 ⇒ 草稿不带件。纯函数,不登记。 */
|
|
74
|
+
export declare function toA2aDraft(peer: RequestA2aPeer): A2aDraft;
|
|
48
75
|
/**
|
|
49
76
|
* Shape-check the raw `body.a2aPeers`. `null` → a non-array (caller error; the route may 400). Otherwise the
|
|
50
|
-
* valid subset + the dropped (malformed/over-cap) entries as {@link A2aInjectionDrop} facts (peer name · reason · body coordinate).
|
|
77
|
+
* valid subset as drafts (nothing registered) + the dropped (malformed/over-cap) entries as {@link A2aInjectionDrop} facts (peer name · reason · body coordinate).
|
|
51
78
|
*/
|
|
52
79
|
export declare function validateRequestA2a(raw: unknown): {
|
|
53
|
-
ok:
|
|
80
|
+
ok: A2aDraft[];
|
|
54
81
|
dropped: A2aInjectionDrop[];
|
|
55
82
|
} | null;
|
|
56
83
|
/**
|
|
@@ -69,13 +96,14 @@ export declare function requestA2aDeclarationRefusal(list: unknown): Declaration
|
|
|
69
96
|
/** fresh 提交腿的 `a2aPeers` 门:{@link requestA2aDeclarationRefusal} 有答案 ⇒ 同步 400(码 / 句 / `unknownKeys` / 机读位原样)。 */
|
|
70
97
|
export declare function assertRequestA2aEntries(bodyA2a: unknown): void;
|
|
71
98
|
/**
|
|
72
|
-
* Merge GATED per-request
|
|
99
|
+
* Merge GATED per-request peer drafts OVER the deployment baseline (center/config): **select first, then mint**. The baseline WINS on a name
|
|
73
100
|
* clash — a caller can ADD a peer but can never SHADOW a configured one. This is load-bearing on THIS lane
|
|
74
101
|
* specifically: the peer name is the tool-namespace segment (`a2a__<peer>__<skill>`), so shadowing would let
|
|
75
|
-
* a caller keep the tool NAMES the model was told about while swapping the agent behind them.
|
|
102
|
+
* a caller keep the tool NAMES the model was told about while swapping the agent behind them. A draft that loses to the baseline never reaches
|
|
103
|
+
* the desk; each winner is minted once on `desk` under `lease` (`peer-seat.ts` `mintDeclaration`: the reference at the top level, zero header values).
|
|
76
104
|
* Returns the baseline unchanged when there is nothing to add.
|
|
77
105
|
*/
|
|
78
|
-
export declare function mergeRequestA2a(baseline: A2aServerSpec[] | undefined,
|
|
106
|
+
export declare function mergeRequestA2a(baseline: A2aServerSpec[] | undefined, drafts: readonly A2aDraft[], desk: Pick<PeerDesk, "declare">, lease: PeerLease): A2aServerSpec[] | undefined;
|
|
79
107
|
/**
|
|
80
108
|
* The single entry point the spec-builder calls: compute the effective `TaskSpec.a2a` = deployment baseline
|
|
81
109
|
* (`a2aForScenario(config.a2aPeers, scenario)`) + the caller's gated per-request peers. Off the honored lane
|
|
@@ -83,7 +111,9 @@ export declare function mergeRequestA2a(baseline: A2aServerSpec[] | undefined, g
|
|
|
83
111
|
* peers a closed gate dropped, so a shell never silently believes its declaration took effect (it also reads
|
|
84
112
|
* `capabilities.a2aInjection`).
|
|
85
113
|
*/
|
|
86
|
-
export declare function resolveRequestA2a(baseline: A2aServerSpec[] | undefined, bodyA2a: unknown, config: A2aGateConfig,
|
|
114
|
+
export declare function resolveRequestA2a(baseline: A2aServerSpec[] | undefined, bodyA2a: unknown, config: A2aGateConfig,
|
|
115
|
+
/** 对端座的 desk 与本腿的宿主租约(必填,与 MCP 请求腿同一对):胜出草稿在它下面铸成带引用的声明。 */
|
|
116
|
+
desk: Pick<PeerDesk, "declare">, lease: PeerLease, logger?: {
|
|
87
117
|
info?(m: string, meta?: unknown): void;
|
|
88
118
|
warn(m: string, meta?: unknown): void;
|
|
89
119
|
},
|
package/dist/task-a2a.js
CHANGED
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
import { types } from "node:util";
|
|
2
|
+
import { resolveComplianceDenies } from "@sema-agent/core";
|
|
2
3
|
import { describeNotStringArray, describeValueShape, findUrlCredential, isStringArray } from "@sema-agent/settings-schema";
|
|
3
4
|
import { errorKindOf } from "./observability/err-text.js";
|
|
4
|
-
import { readPlainStringRecord } from "./plain-string-record.js";
|
|
5
|
+
import { declarationKeys, readPlainStringRecord, splitDeclaration } from "./plain-string-record.js";
|
|
6
|
+
import { mintDeclaration, peerSentence } from "./peer-seat.js";
|
|
5
7
|
import { readRequestToolAxes } from "./request-tool-axes.js";
|
|
6
8
|
import { MAX_KEY_ECHO, echoKeyName, keyClosureClause, scanKeyNames } from "./request-key-closure.js";
|
|
7
9
|
import { urlCredentialHeaderExit, urlCredentialRefusalReason } from "./url-credential-refusal.js";
|
|
8
10
|
import { declarationEntryNotObject, declarationIssueFacts, declarationNameIssue, declarationOverCapFacts, declaredName, judgeDeclarationKeys, requestDeclarationRefusal, throwDeclarationRefusal, } from "./request-declaration-refusal.js";
|
|
11
|
+
import { principalHeaderOwnershipIssue } from "./task-mcp.js";
|
|
9
12
|
import { DEPLOYMENT_SHAPE_PREDICATES } from "./deployment-shape.js";
|
|
10
13
|
export const MAX_REQUEST_A2A_PEERS = 32;
|
|
11
14
|
export function a2aInjectionHonored(config) {
|
|
15
|
+
if (config.lockedConfigKeys?.includes(A2A_LOCK_WORD) === true)
|
|
16
|
+
return false;
|
|
17
|
+
if (config.compliancePosture !== undefined && resolveComplianceDenies(config.compliancePosture).has(A2A_COMPLIANCE_WORD))
|
|
18
|
+
return false;
|
|
12
19
|
return !DEPLOYMENT_SHAPE_PREDICATES.multiTenant.holds(config);
|
|
13
20
|
}
|
|
21
|
+
const A2A_LOCK_WORD = "a2a";
|
|
22
|
+
const A2A_COMPLIANCE_WORD = "a2a_peers";
|
|
14
23
|
const HTTP_URL_RE = /^https?:\/\//i;
|
|
15
|
-
export const
|
|
24
|
+
export const A2A_PEER_DISPOSITION = {
|
|
25
|
+
name: "engine",
|
|
26
|
+
url: "engine",
|
|
27
|
+
cardUrl: "engine",
|
|
28
|
+
headers: "seat",
|
|
29
|
+
principalHeader: "engine",
|
|
30
|
+
allowSkills: "engine",
|
|
31
|
+
toolAxes: "engine",
|
|
32
|
+
};
|
|
33
|
+
export const A2A_PEER_ENTRY_KEYS = declarationKeys(A2A_PEER_DISPOSITION);
|
|
16
34
|
const A2A_PEER_ENTRY_KEY_SET = new Set(A2A_PEER_ENTRY_KEYS);
|
|
17
35
|
const NO_TOLERATED_KEYS = new Set();
|
|
18
36
|
const A2A_SEAT = {
|
|
@@ -133,11 +151,11 @@ export function snapshotRequestA2a(raw, options) {
|
|
|
133
151
|
return kept;
|
|
134
152
|
}
|
|
135
153
|
function toolAxesIssue(read) {
|
|
136
|
-
return { path: "toolAxes", reason: read.problem };
|
|
154
|
+
return { path: "toolAxes", reason: peerSentence(read.problem) };
|
|
137
155
|
}
|
|
138
156
|
function toolAxesNotCloneableIssue(read, clone) {
|
|
139
157
|
const because = clone === undefined || clone.ok ? "unavailable" : clone.because;
|
|
140
|
-
return { path: "toolAxes", reason: `declaration not cloneable (${because}) — the one-time migration re-reads the stored declaration as plain JSON data and this one is not; ${read.problem}` };
|
|
158
|
+
return { path: "toolAxes", reason: `declaration not cloneable (${because}) — the one-time migration re-reads the stored declaration as plain JSON data and this one is not; ${peerSentence(read.problem)}` };
|
|
141
159
|
}
|
|
142
160
|
const A2A_PEER_KEY_JUDGES = {
|
|
143
161
|
name: declarationNameIssue,
|
|
@@ -152,7 +170,11 @@ const A2A_PEER_KEY_JUDGES = {
|
|
|
152
170
|
const reserved = read.stripped[0];
|
|
153
171
|
return reserved === undefined ? undefined : { path: HEADERS_RESERVED_MEMBER_PATH, reason: reserved.fault };
|
|
154
172
|
},
|
|
155
|
-
principalHeader: (o) =>
|
|
173
|
+
principalHeader: (o) => o.principalHeader === undefined
|
|
174
|
+
? undefined
|
|
175
|
+
: typeof o.principalHeader !== "string"
|
|
176
|
+
? { path: "principalHeader", reason: `must be a string (a header name) — got ${describeValueShape(o.principalHeader)}` }
|
|
177
|
+
: principalHeaderOwnershipIssue(o.principalHeader, "a2a", "principalHeader"),
|
|
156
178
|
allowSkills: (o) => o.allowSkills === undefined || isStringArray(o.allowSkills)
|
|
157
179
|
? undefined
|
|
158
180
|
: { path: "allowSkills", reason: `must be an array of strings (omit the key to leave the peer's skills unrestricted) — got ${describeNotStringArray(o.allowSkills)}` },
|
|
@@ -162,10 +184,10 @@ const A2A_PEER_KEY_JUDGES = {
|
|
|
162
184
|
},
|
|
163
185
|
};
|
|
164
186
|
function normalizeA2aPeer(e, leg, at, storedToolAxes) {
|
|
165
|
-
const dropBecause = (issue) => ({
|
|
187
|
+
const dropBecause = (issue) => ({ draft: null, because: declarationIssueFacts(at, issue) });
|
|
166
188
|
const notObject = declarationEntryNotObject(A2A_SEAT, e, at);
|
|
167
189
|
if (notObject !== undefined)
|
|
168
|
-
return {
|
|
190
|
+
return { draft: null, because: { field: at, reason: notObject } };
|
|
169
191
|
const o = snapshotPeer(e, storedToolAxes !== undefined);
|
|
170
192
|
for (const key of A2A_PEER_ENTRY_KEYS) {
|
|
171
193
|
const issue = A2A_PEER_KEY_JUDGES[key](o, at);
|
|
@@ -206,12 +228,16 @@ function normalizeA2aPeer(e, leg, at, storedToolAxes) {
|
|
|
206
228
|
return unread;
|
|
207
229
|
if (again.toolAxes !== undefined)
|
|
208
230
|
spec.toolAxes = again.toolAxes;
|
|
209
|
-
return { spec, toolAxesReread: axes.problem, ...lost };
|
|
231
|
+
return { draft: toA2aDraft(spec), toolAxesReread: peerSentence(axes.problem), ...lost };
|
|
210
232
|
}
|
|
211
233
|
if (axes.toolAxes !== undefined)
|
|
212
234
|
spec.toolAxes = axes.toolAxes;
|
|
213
235
|
}
|
|
214
|
-
return { spec, ...lost };
|
|
236
|
+
return { draft: toA2aDraft(spec), ...lost };
|
|
237
|
+
}
|
|
238
|
+
export function toA2aDraft(peer) {
|
|
239
|
+
const split = splitDeclaration(peer, A2A_PEER_DISPOSITION);
|
|
240
|
+
return { spec: split.engine, ...(split.pieces === undefined ? {} : { pieces: split.pieces }) };
|
|
215
241
|
}
|
|
216
242
|
export function validateRequestA2a(raw) {
|
|
217
243
|
const v = validateRequestA2aEntries(raw, "fresh");
|
|
@@ -228,12 +254,12 @@ function validateRequestA2aEntries(raw, leg, storedToolAxes) {
|
|
|
228
254
|
const headersLost = [];
|
|
229
255
|
for (const [i, e] of raw.slice(0, MAX_REQUEST_A2A_PEERS).entries()) {
|
|
230
256
|
const read = normalizeA2aPeer(e, leg, `${A2A_SEAT.list}[${i}]`, storedToolAxes);
|
|
231
|
-
if (read.
|
|
232
|
-
ok.push(read.
|
|
257
|
+
if (read.draft !== null) {
|
|
258
|
+
ok.push(read.draft);
|
|
233
259
|
if (read.toolAxesReread !== undefined)
|
|
234
|
-
toolAxesReread.push({ peer: read.spec.name,
|
|
260
|
+
toolAxesReread.push({ peer: read.draft.spec.name, sentence: read.toolAxesReread });
|
|
235
261
|
if (read.headersLost !== undefined)
|
|
236
|
-
headersLost.push({ peer: read.spec.name, lost: read.headersLost });
|
|
262
|
+
headersLost.push({ peer: read.draft.spec.name, lost: read.headersLost });
|
|
237
263
|
}
|
|
238
264
|
else
|
|
239
265
|
dropped.push({ peer: declaredName(e).slice(0, 128), ...read.because });
|
|
@@ -265,14 +291,14 @@ export function requestA2aDeclarationRefusal(list) {
|
|
|
265
291
|
export function assertRequestA2aEntries(bodyA2a) {
|
|
266
292
|
throwDeclarationRefusal(requestA2aDeclarationRefusal(bodyA2a));
|
|
267
293
|
}
|
|
268
|
-
export function mergeRequestA2a(baseline,
|
|
269
|
-
if (
|
|
294
|
+
export function mergeRequestA2a(baseline, drafts, desk, lease) {
|
|
295
|
+
if (drafts.length === 0)
|
|
270
296
|
return baseline;
|
|
271
297
|
const taken = new Set((baseline ?? []).map((p) => p.name));
|
|
272
|
-
const
|
|
273
|
-
return
|
|
298
|
+
const winners = drafts.filter((d) => !taken.has(d.spec.name));
|
|
299
|
+
return winners.length === 0 ? baseline : [...(baseline ?? []), ...winners.map((d) => mintDeclaration(desk, d, lease))];
|
|
274
300
|
}
|
|
275
|
-
export function resolveRequestA2a(baseline, bodyA2a, config, logger, leg = "fresh", storedToolAxes) {
|
|
301
|
+
export function resolveRequestA2a(baseline, bodyA2a, config, desk, lease, logger, leg = "fresh", storedToolAxes) {
|
|
276
302
|
if (bodyA2a === undefined || bodyA2a === null)
|
|
277
303
|
return baseline;
|
|
278
304
|
if (!a2aInjectionHonored(config)) {
|
|
@@ -286,7 +312,7 @@ export function resolveRequestA2a(baseline, bodyA2a, config, logger, leg = "fres
|
|
|
286
312
|
}
|
|
287
313
|
if (v.dropped.length > 0)
|
|
288
314
|
logger?.warn("a2a_injection_dropped", { dropped: v.dropped });
|
|
289
|
-
const merged = mergeRequestA2a(baseline, v.ok);
|
|
315
|
+
const merged = mergeRequestA2a(baseline, v.ok, desk, lease);
|
|
290
316
|
const baselineNames = new Set((baseline ?? []).map((p) => p.name));
|
|
291
317
|
for (const r of v.toolAxesReread) {
|
|
292
318
|
if (baselineNames.has(r.peer))
|
|
@@ -294,7 +320,7 @@ export function resolveRequestA2a(baseline, bodyA2a, config, logger, leg = "fres
|
|
|
294
320
|
logger?.warn("a2a_tool_axes_read_as_before_upgrade", {
|
|
295
321
|
peer: r.peer,
|
|
296
322
|
migration: "M19",
|
|
297
|
-
problem: r.
|
|
323
|
+
problem: r.sentence,
|
|
298
324
|
note: "this replayed request was stored before the upgrade and the peer's toolAxes declaration does not pass the engine's reader; while one-time migration M19 is open it is read the way the pre-upgrade reader reads it (members that do not fit are dropped) and the peer stays mounted — once the window closes the same request leaves this peer unmounted",
|
|
299
325
|
});
|
|
300
326
|
}
|