@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
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { isAbsolute } from "node:path";
|
|
2
|
+
import { seatText } from "./trace/exit-seat.js";
|
|
3
|
+
const DECLARABLE_TIERS = { prepend: true, user: true, append: true };
|
|
4
|
+
const TIER_WORDS = Object.keys(DECLARABLE_TIERS);
|
|
5
|
+
const isDeclarableTier = (v) => typeof v === "string" && Object.hasOwn(DECLARABLE_TIERS, v);
|
|
6
|
+
const ENTRY_KEYS = new Set(["name", "dir", "tier", "options"]);
|
|
7
|
+
const REFUSAL_WORDS = {
|
|
8
|
+
config_invalid: true,
|
|
9
|
+
root_missing: true,
|
|
10
|
+
name_missing: true,
|
|
11
|
+
duplicate_name: true,
|
|
12
|
+
root_unreadable: true,
|
|
13
|
+
dir_unreadable: true,
|
|
14
|
+
outside_root: true,
|
|
15
|
+
not_a_directory: true,
|
|
16
|
+
package_refused: true,
|
|
17
|
+
name_mismatch: true,
|
|
18
|
+
admission_refused: true,
|
|
19
|
+
};
|
|
20
|
+
export const DEPLOYMENT_MODS_REFUSALS = Object.keys(REFUSAL_WORDS);
|
|
21
|
+
const seatLabel = (seat) => (typeof seat === "string" ? seat : `MODS_PACKAGES[${seat.entry}]`);
|
|
22
|
+
const entryLabel = (entry) => seatLabel({ entry });
|
|
23
|
+
const nameLabel = (name) => JSON.stringify(name);
|
|
24
|
+
function refusalText(r) {
|
|
25
|
+
const line = (subject, detail) => `deployment mods refused: ${r.word}${subject !== undefined ? `(${subject})` : ""}${detail !== undefined ? `: ${detail}` : ""}`;
|
|
26
|
+
switch (r.word) {
|
|
27
|
+
case "config_invalid":
|
|
28
|
+
return line(seatLabel(r.seat), r.why);
|
|
29
|
+
case "root_missing":
|
|
30
|
+
return line("MODS_ROOT", "MODS_PACKAGES declares packages but MODS_ROOT is not set — every declared package must resolve under that root");
|
|
31
|
+
case "name_missing":
|
|
32
|
+
return line(entryLabel(r.entry), "name must be a non-empty string — the package's own name (its .claude-plugin/plugin.json `name`), spelled exactly");
|
|
33
|
+
case "duplicate_name":
|
|
34
|
+
return line(entryLabel(r.entry), `same name as ${entryLabel(r.first)} — each declared package is one registration and a name is its identity`);
|
|
35
|
+
case "root_unreadable":
|
|
36
|
+
return line("MODS_ROOT", `its real path cannot be resolved (${r.cause})`);
|
|
37
|
+
case "dir_unreadable":
|
|
38
|
+
return line(entryLabel(r.entry), `dir's real path cannot be resolved (${r.cause})`);
|
|
39
|
+
case "outside_root":
|
|
40
|
+
return line(entryLabel(r.entry), "dir does not resolve strictly under MODS_ROOT (real paths, compared by path components; the root itself is not a package directory)");
|
|
41
|
+
case "not_a_directory":
|
|
42
|
+
return line(entryLabel(r.entry), "dir does not resolve to a directory");
|
|
43
|
+
case "package_refused":
|
|
44
|
+
return line(r.code !== undefined ? `${nameLabel(r.name)}, ${r.code}` : nameLabel(r.name), r.reason);
|
|
45
|
+
case "name_mismatch":
|
|
46
|
+
return line(nameLabel(r.name), `the package names itself ${nameLabel(r.packageName)} — the declared name must be the package's own name, spelled exactly`);
|
|
47
|
+
case "admission_refused":
|
|
48
|
+
return line(r.code, r.reason);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function buildDeploymentModsRefusalLine(r) {
|
|
52
|
+
return seatText("boot-refusal-line", refusalText(r));
|
|
53
|
+
}
|
|
54
|
+
export class DeploymentModsRefusedError extends Error {
|
|
55
|
+
refusal;
|
|
56
|
+
constructor(refusal) {
|
|
57
|
+
super(buildDeploymentModsRefusalLine(refusal));
|
|
58
|
+
this.refusal = refusal;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function refuse(r) {
|
|
62
|
+
throw new DeploymentModsRefusedError(r);
|
|
63
|
+
}
|
|
64
|
+
const isPlainRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
65
|
+
export function parseModsDeclaration(rootRaw, packagesRaw) {
|
|
66
|
+
const root = rootRaw === undefined || rootRaw === "" ? undefined : rootRaw;
|
|
67
|
+
if (root !== undefined && !isAbsolute(root))
|
|
68
|
+
refuse({ word: "config_invalid", seat: "MODS_ROOT", why: "must be an absolute path" });
|
|
69
|
+
const undeclared = root !== undefined ? { root } : {};
|
|
70
|
+
if (packagesRaw === undefined || packagesRaw.trim() === "")
|
|
71
|
+
return undeclared;
|
|
72
|
+
let parsed;
|
|
73
|
+
try {
|
|
74
|
+
parsed = JSON.parse(packagesRaw);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
refuse({ word: "config_invalid", seat: "MODS_PACKAGES", why: "is not valid JSON" });
|
|
78
|
+
}
|
|
79
|
+
if (!Array.isArray(parsed))
|
|
80
|
+
refuse({ word: "config_invalid", seat: "MODS_PACKAGES", why: "must be a JSON array of { name, dir, tier, options? }" });
|
|
81
|
+
const packages = [];
|
|
82
|
+
const firstIndexOf = new Map();
|
|
83
|
+
for (const [entry, item] of parsed.entries()) {
|
|
84
|
+
if (!isPlainRecord(item))
|
|
85
|
+
refuse({ word: "config_invalid", seat: { entry }, why: "must be an object { name, dir, tier, options? }" });
|
|
86
|
+
const unknown = Object.keys(item).filter((k) => !ENTRY_KEYS.has(k));
|
|
87
|
+
if (unknown.length > 0)
|
|
88
|
+
refuse({ word: "config_invalid", seat: { entry }, why: `has keys outside { name, dir, tier, options }: ${unknown.map((k) => JSON.stringify(k)).join(", ")}` });
|
|
89
|
+
const { name, dir, tier } = item;
|
|
90
|
+
if (typeof name !== "string" || name === "")
|
|
91
|
+
refuse({ word: "name_missing", entry });
|
|
92
|
+
if (typeof dir !== "string" || !isAbsolute(dir))
|
|
93
|
+
refuse({ word: "config_invalid", seat: { entry }, why: "dir must be an absolute path" });
|
|
94
|
+
if (!isDeclarableTier(tier))
|
|
95
|
+
refuse({ word: "config_invalid", seat: { entry }, why: `tier must be one of ${TIER_WORDS.join(", ")} — the host declares it, a package never does` });
|
|
96
|
+
const first = firstIndexOf.get(name);
|
|
97
|
+
if (first !== undefined)
|
|
98
|
+
refuse({ word: "duplicate_name", entry, first });
|
|
99
|
+
firstIndexOf.set(name, entry);
|
|
100
|
+
packages.push({ name, dir, tier, ...(Object.hasOwn(item, "options") ? { options: item.options } : {}) });
|
|
101
|
+
}
|
|
102
|
+
const [head, ...tail] = packages;
|
|
103
|
+
if (head === undefined)
|
|
104
|
+
return undeclared;
|
|
105
|
+
if (root === undefined)
|
|
106
|
+
refuse({ word: "root_missing" });
|
|
107
|
+
return { root, packages: [head, ...tail] };
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=mods-declaration.js.map
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* batch; stacking a second wide-fanout move on it in one session was the higher-risk option for no added
|
|
11
11
|
* clarity.)
|
|
12
12
|
*
|
|
13
|
-
* The engine prices ONLY LLM tokens (core's `Stats.costBreakdown` = llmRoot / nestedSubagent /
|
|
14
|
-
*
|
|
13
|
+
* The engine prices ONLY LLM tokens (core's `Stats.costBreakdown` = llmRoot / nestedSubagent / compaction, plus the
|
|
14
|
+
* suggestions and side-call lines). It has NO $ for tool calls, sandbox wall-clock, or network egress — those are INFRA/service costs
|
|
15
15
|
* the service owns the data + rates for. This module is the thin report layer: it turns a
|
|
16
16
|
* per-run infra USAGE + the deployment's RATES into an infra breakdown, then composes it with the core LLM
|
|
17
17
|
* breakdown into one supervisor-facing total. Pure + rate-driven (rates default 0 ⇒ infra reads $0, exactly like
|
|
@@ -9,4 +9,9 @@ export declare function errorText(e: unknown): string;
|
|
|
9
9
|
* `name` 的 getter 抛、或它交回的不是字符串(拼进模板串会抛的 Symbol 等)⇒ `<unreadable>`。
|
|
10
10
|
*/
|
|
11
11
|
export declare function errorKindOf(err: unknown): string;
|
|
12
|
+
/**
|
|
13
|
+
* 错误自带的机器码(`err.code` 串:Node 的 `ENOENT`、引擎的 `config.mods_invalid` …)—— 第三只读口,同一处兜底 {@link readOr}:`code` 的 getter 抛 / 代理抛 /
|
|
14
|
+
* 不是串 ⇒ `undefined`(没有码,不编一个)。调用方按自己的形判(什么样的码配进一句拒句)再用它。
|
|
15
|
+
*/
|
|
16
|
+
export declare function errorCodeOf(err: unknown): string | undefined;
|
|
12
17
|
//# sourceMappingURL=err-text.d.ts.map
|
|
@@ -21,4 +21,13 @@ export function errorKindOf(err) {
|
|
|
21
21
|
return typeof name === "string" ? name : KIND_UNREADABLE;
|
|
22
22
|
}, KIND_UNREADABLE);
|
|
23
23
|
}
|
|
24
|
+
export function errorCodeOf(err) {
|
|
25
|
+
const code = readOr(() => {
|
|
26
|
+
if (!(err instanceof Error) || !("code" in err))
|
|
27
|
+
return "";
|
|
28
|
+
const c = err.code;
|
|
29
|
+
return typeof c === "string" ? c : "";
|
|
30
|
+
}, "");
|
|
31
|
+
return code === "" ? undefined : code;
|
|
32
|
+
}
|
|
24
33
|
//# sourceMappingURL=err-text.js.map
|
|
@@ -87,6 +87,10 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
87
87
|
readonly cls: "P-DEBT";
|
|
88
88
|
readonly note: "件6(#43 案 [4281]):`FileRunStore.hydrate()` 的 `readdirSync(runs/)` 失败 ⇒ 引擎以**空账**起动。旧形是空 catch + return:data root 不可读(权限/挂载丢失/路径被换走)时零告警,此后每一次 getRun 恒 miss —— 而「恒 miss」正是 #43 现场看到的形,一次环境故障被伪装成「本来就没有这条 run」。方向问题在于吞掉之后被读成的是「证不出有行 ⇒ 没有行」,两句话不等价。**缺省不拒启是刻意保留的行为**,所以这条留作债:响亮化(error 级启动日志带 runs 绝对路径+异常文案)+ 本计数,让它在遥测里显形而不是只活在一行日志里。⚠️ 2026-08-29(S-46 件B)起终局格已兑现:部署可用 `RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 **fail-closed 启动门**(拒启,遗言同样带绝对路径+异常文案);本计数因此只在**缺省 off** 档上产生 —— 读遥测时别把「计数为零」直接读成「没有部署踩到这条路」,也可能是它们开了拒启门、根本起不来。";
|
|
89
89
|
};
|
|
90
|
+
readonly "server.run-store.file-active-claims-unreadable": {
|
|
91
|
+
readonly cls: "P-DEBT";
|
|
92
|
+
readonly note: "`FileRunStore.hydrate()` 的第二处 `readdirSync(runs/active/)`(claim 平面 = 单活索引)失败,且 `RUN_STORE_STRICT_HYDRATE` 关(缺省)⇒ 服务照常起动、单活索引为**空**。放行的后果两条,都不报错:① 已有 run 行仍可读(`GET /v1/runs/:id` 照常答),但此后对这些会话的审批决定在请求里同步跑完、却找不到要更新的 run 行 —— run 行停在决前的 `suspended`;② 读单活索引的各处读到「没人占用」—— `getActiveTaskId` 恒答空,例如会话同步推送前的「会话有在飞 run」检查(与 `server.run-store.file-hydrate-unreadable` 同一句错话:「证不出有占用 ⇒ 没有占用」,只是换了一个盘面);新提交不读这张索引,claim 文件完好时仍按盘上的 claim 裁定,不受影响。同一条债的另一个盘面:缺省不拒启是刻意保留的行为,所以留作 P-DEBT —— error 级启动日志(`file_run_store_active_claims_unreadable`,带 `runs/active/` 绝对路径)早已在,本计数补的是遥测轴(此前 `runs/` 臂有计数、这一臂没有)。`RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 fail-closed 启动门,本计数因此只在缺省 off 档的真放行时产生(拒启那一形不记)—— 读遥测时「计数为零」也可能是部署开了拒启门、根本起不来。";
|
|
93
|
+
};
|
|
90
94
|
readonly "server.fork-routing.placed-probe-host-error": {
|
|
91
95
|
readonly cls: "F";
|
|
92
96
|
readonly note: "#292 placement 半场:hostPlacedProbe 对 host 店的 requireExisting 探针失败且**不是 not_found**(host 读故障)时折叠为 absent —— 路由回旧规则(claim-create 会在 transient 建新会话)。放行的是「host 暂不可达时 transient 腿保持独立可用」;最坏后果=故障窗内对一个真 placed id 铸出 transient 影子(host 恢复后双身,requireExisting 冷探针会重新指回 host 真身)。not_found 是常态路径不计数;只有真故障臂进本计数。";
|
|
@@ -193,7 +197,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
193
197
|
};
|
|
194
198
|
readonly "server.approvals.durable-row-checkpoint-id-missing": {
|
|
195
199
|
readonly cls: "F";
|
|
196
|
-
readonly note: "7.133.0(`checkpoint-identity.ts` 的 `durableRowIdentity`;两只店的 `listPending` + `/v1/assistant/inbox` handler):三处 durable 读面(运维队列行 `GET /v1/approvals` 与 `/v1/approvals/stream` 的 `pending` 帧;停驻收件 `GET /v1/assistant/inbox` 的行)读到一行停驻门**没有可用的 `checkpointId`** ⇒ 这一行**照列、`checkpointId` 键缺席**(消费方按「这一行没有可 join 的身份」处理)。detail = `face=<approvals-queue|assistant-inbox> why=<absent|untrusted>`:`absent` = 行上没有这一格(core 5.42.0 之前停驻的老行 —— core 明写不回填);`untrusted` = 在场却没过信任边界(非串 / 空串 / 超长 / 含 resume token —— 数据污染;没过边界的值不上 wire)。放行(列、键缺席)的最坏后果 = 消费方对这一行做不了跨面 join(三面对账少一行)—— 行本身、决断口(`/v1/approvals/:sessionId/decide`,按会话)、到期收割都照常。不放行有两种形:整只拒的代价是一行老 / 坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧);把这一行藏起来的代价更糟 —— 人看不到要批的东西(审批面上的 fail-open,方向反了)。故 F 类;必须留痕(两种成因从此可分)。计数非零 = 有老 / 坏行在飞(列着、没身份);`/stream` 每 3s 一拍重读,同一行每拍计一次(量值 = 行 × 拍数,在场才是信号)。 7.137.0 起第四面:park 账本行(`GET /v1/runs/:id/events` 的 `suspended` / `needs_review`;唯一构造器 `trace/project.ts` 的 `parkEventData`,写时判一次,detail `face=run-events-park`)—— 暂停因由上的门身份没过信任边界 ⇒ 行照写、`checkpointId` 键缺席(决断方回显不了这道门,退回不带键的决断或从 run 结果取原值;行本身、门、待批调用身份照常)。";
|
|
200
|
+
readonly note: "7.133.0(`checkpoint-identity.ts` 的 `durableRowIdentity`;两只店的 `listPending` + `/v1/assistant/inbox` handler):三处 durable 读面(运维队列行 `GET /v1/approvals` 与 `/v1/approvals/stream` 的 `pending` 帧;停驻收件 `GET /v1/assistant/inbox` 的行)读到一行停驻门**没有可用的 `checkpointId`** ⇒ 这一行**照列、`checkpointId` 键缺席**(消费方按「这一行没有可 join 的身份」处理)。detail = `face=<approvals-queue|assistant-inbox> why=<absent|untrusted>`:`absent` = 行上没有这一格(core 5.42.0 之前停驻的老行 —— core 明写不回填);`untrusted` = 在场却没过信任边界(非串 / 空串 / 超长 / 含 resume token —— 数据污染;没过边界的值不上 wire)。放行(列、键缺席)的最坏后果 = 消费方对这一行做不了跨面 join(三面对账少一行)—— 行本身、决断口(`/v1/approvals/:sessionId/decide`,按会话)、到期收割都照常。不放行有两种形:整只拒的代价是一行老 / 坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧);把这一行藏起来的代价更糟 —— 人看不到要批的东西(审批面上的 fail-open,方向反了)。故 F 类;必须留痕(两种成因从此可分)。计数非零 = 有老 / 坏行在飞(列着、没身份);`/stream` 每 3s 一拍重读,同一行每拍计一次(量值 = 行 × 拍数,在场才是信号)。 7.137.0 起第四面:park 账本行(`GET /v1/runs/:id/events` 的 `suspended` / `needs_review`;唯一构造器 `trace/project.ts` 的 `parkEventData`,写时判一次,detail `face=run-events-park`)—— 暂停因由上的门身份没过信任边界 ⇒ 行照写、`checkpointId` 键缺席(决断方回显不了这道门,退回不带键的决断或从 run 结果取原值;行本身、门、待批调用身份照常)。 第五面:回决门族的 200 体(`/decide` / `/resume` / `/plan_review` 执行之前从选中的那一行捕获一次,`http/resume-legs.ts` 三处;detail `face=decision-receipt`)—— 选中那一行的门身份没过信任边界 ⇒ 结清证据照铸、体上 `checkpointId` 键缺席(决断方只是对不上身份;位、决定、续跑都照常)。每次走到捕获点的决断计一次(计的是选中那一行的事实,与这次决断随后走哪条车道无关)。";
|
|
197
201
|
};
|
|
198
202
|
readonly "server.approvals.queue-row-gate-kind-missing": {
|
|
199
203
|
readonly cls: "F";
|
|
@@ -279,6 +283,10 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
279
283
|
readonly cls: "F";
|
|
280
284
|
readonly note: "S-690(`instruction-source-notice.ts announceInstructionSource`,项目指令换源通告的唯一铸点;detail 的 `op` 点名哪一步):基线**读**失败或超期(会话店抖动 / 列形坏 / 2s 期限到,detail 带 `timeout=`)⇒ 本 run 不比也不写;基线**写**失败或超期 ⇒ 本 run 的换源帧照发(它对本 run 为真),下一 run 可能再说一遍同一次换源(超期后才落地的写由 CAS 挡住,盖不掉更新的基线);通告**出口**抛 ⇒ 本 run 缺这一帧。放行的最坏后果 = 一次换源没被说出来或被多说一次 —— 通告是**披露**面,不参与任何门 / 裁决,模型读到的指令本身不受影响。不放行的代价严重不对称:铸点跑在 core 的 `loadProjectMemory` 席内,抛回去 core 会把这次 prepare 的项目记忆整块丢掉(不注入),为一条通告赔上指令本身。计数让「某台部署的会话店一直读不出基线」与「这台部署从来没换过源」在遥测上分得开。";
|
|
281
285
|
};
|
|
286
|
+
readonly "server.session-workspace.record-degraded": {
|
|
287
|
+
readonly cls: "F";
|
|
288
|
+
readonly note: "会话行的位置与项目那一格(`session_meta.workspace` / local `session-workspaces.json`;唯一写者 `session-workspace.ts` 的腿记录座,唯一列属主 `plugins/session-workspace-cell.ts`;detail 的 `op` 点名哪一步):`op=write` 一次写失败(库抖 / 盘满 / 锁等待超时)⇒ 行停在上一帧,下一帧照写;`op=read` 一格读不出(被带外改坏)⇒ 读作没有记录,下一帧整格重写;`op=frame` 一帧投不成存储形(引擎不会铸出这种帧)⇒ 这一帧不记。这一格是宿主对**引擎判过的答案**的记录,不是判据 —— 引擎每条腿都重判项目(有记号就判记号,声明不等响亮拒)。放行的最坏后果 = 这只会话的下一条不带 `projectId` 的提交续声明的是旧的项目或不声明(没有记号的检出里这只会话少一次续声明);不放行的代价是把一条在跑的腿、或这只会话往后的每一次提交,为一份记录摔掉。计数非零 = 这台部署的会话店写不动 / 有人改过库。";
|
|
289
|
+
};
|
|
282
290
|
readonly "server.engine-notice.wire-sink-threw": {
|
|
283
291
|
readonly cls: "F";
|
|
284
292
|
readonly note: "#310:`engine_notice` 分流器把一条白名单通告投给某条 run 腿的登记口时,那只口抛了(live 口写向已断/已撕裂的 SSE socket,或 durable 口的账本写同步抛)。放行的最坏后果 = **这一条通告的这一个终点**缺席:①日志终点在分流之前已经逐字打过(事实一条不丢);②两个终点注册成两只独立 sink ⇒ live 抛不牵连 durable(断连后仍看得见的那半保住);③同会话其余口照投。故 F 类。不放行的代价是把异常回抛给 core 的 `deliverEngineNotice` —— 它会吞掉,于是同一次失败**既没有留痕也没有第二只口**,正是本 tag 要根除的形。留痕是承重的:静默吞掉之后「wire 腿为什么总有几条通告不到」在遥测里与「core 本来就没发」同形。";
|
|
@@ -545,6 +553,14 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
545
553
|
readonly cls: "P-DEBT";
|
|
546
554
|
readonly note: "S-163(`config-dir-trust.ts` `judgeWin32`,本地配置源 `config.d` 读之前的目录信任判官在 win32 上的那一半):Node 在 win32 上 `uid` 恒 0、模式位是只读属性的投影 ⇒ 「属主 ∈ {本服务用户, root, CONFIG_LOCAL_OWNER_UID}」与「不得组 / 他人可写」两判不可做;结构判(常规文件 / 16 MiB 上界 / 链接成环)照做。每一次读照常进行(起服一次 + 每拍一次)记一次。放行的最坏后果 = 一台多用户 Windows 机器上,别的本机用户若能写这台服务的 `config.d`(缺省的用户目录 ACL 不允许),改一份 `mcp.json` 就能让本服务以它的身份起命令 —— 与 7.132.x 逐字同。照 POSIX 拒启会让每个 Windows 本机引擎(含 cli 起的)升级即起不来,而让 cli 替用户设豁免键是中间层替部署者设安全旋钮。收口 = 有了 ACL 判据或一次裁定(改 R 或改拒启)那一版,本 tag 删或改类。detail = `config.d=<物理路径>`。";
|
|
547
555
|
};
|
|
556
|
+
readonly "server.remote-write.staging-dir-left": {
|
|
557
|
+
readonly cls: "F";
|
|
558
|
+
readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备 / 提交脚本删一只私有暂存目录(`.<名>.stage-<十六进制>/`,生来 0700;只删这次写放进去的 `data` 与这只空目录,从不递归删)没删掉、在 stdout 写一条记录点名 —— 五处:`mv` 已把暂存件换到目标上之后删当前那只;跟一跳到别的目录重新暂存成功之后删原来那只;重新暂存却没用上(收紧不成 / 拷不成)的那一只;就地拷入 / 追加时删暂存名失败、或写完关掉 fd 后删私有目录失败(有的文件系统为仍打开着的文件留隐藏项:NFS / libfuse / SMB);准备时收不成私有 / 里面已有 `data` 的那一只。目录里多出了不是这次写放进去的东西(同一身份或 root 的别的进程往里写过 / 收紧之前别人放进来的)、目录被换成了符号链接、或文件系统拒删。写照它自己的结局答(提交已成 ⇒ 成功:答失败会让调用方以为目标没变),残留只在这里留痕。放行的最坏后果 = 目标(或链接所指)目录里多一只隐藏目录(空的,或装着别人放进去的东西;重新暂存没用上的那只可能装着这次写的半份新内容),只有写入方进得去;不是门、不是凭据、不碰目标内容。计数非零 = 那台目标机上有东西在往别人的暂存目录里写,或删不动,去看那只目录。detail = 残留目录的路径(一次写最多两只,逐只计)。";
|
|
559
|
+
};
|
|
560
|
+
readonly "server.remote-write.stage-seal-fallback": {
|
|
561
|
+
readonly cls: "F";
|
|
562
|
+
readonly note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。";
|
|
563
|
+
};
|
|
548
564
|
};
|
|
549
565
|
/** 词表键推导的闭集类型——未登记的 tag 传不进 {@link recordFailOpen}(编译期拒)。 */
|
|
550
566
|
export type FailOpenTag = keyof typeof FAIL_OPEN_TAGS;
|
|
@@ -68,6 +68,10 @@ export const FAIL_OPEN_TAGS = {
|
|
|
68
68
|
cls: "P-DEBT",
|
|
69
69
|
note: "件6(#43 案 [4281]):`FileRunStore.hydrate()` 的 `readdirSync(runs/)` 失败 ⇒ 引擎以**空账**起动。旧形是空 catch + return:data root 不可读(权限/挂载丢失/路径被换走)时零告警,此后每一次 getRun 恒 miss —— 而「恒 miss」正是 #43 现场看到的形,一次环境故障被伪装成「本来就没有这条 run」。方向问题在于吞掉之后被读成的是「证不出有行 ⇒ 没有行」,两句话不等价。**缺省不拒启是刻意保留的行为**,所以这条留作债:响亮化(error 级启动日志带 runs 绝对路径+异常文案)+ 本计数,让它在遥测里显形而不是只活在一行日志里。⚠️ 2026-08-29(S-46 件B)起终局格已兑现:部署可用 `RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 **fail-closed 启动门**(拒启,遗言同样带绝对路径+异常文案);本计数因此只在**缺省 off** 档上产生 —— 读遥测时别把「计数为零」直接读成「没有部署踩到这条路」,也可能是它们开了拒启门、根本起不来。",
|
|
70
70
|
},
|
|
71
|
+
"server.run-store.file-active-claims-unreadable": {
|
|
72
|
+
cls: "P-DEBT",
|
|
73
|
+
note: "`FileRunStore.hydrate()` 的第二处 `readdirSync(runs/active/)`(claim 平面 = 单活索引)失败,且 `RUN_STORE_STRICT_HYDRATE` 关(缺省)⇒ 服务照常起动、单活索引为**空**。放行的后果两条,都不报错:① 已有 run 行仍可读(`GET /v1/runs/:id` 照常答),但此后对这些会话的审批决定在请求里同步跑完、却找不到要更新的 run 行 —— run 行停在决前的 `suspended`;② 读单活索引的各处读到「没人占用」—— `getActiveTaskId` 恒答空,例如会话同步推送前的「会话有在飞 run」检查(与 `server.run-store.file-hydrate-unreadable` 同一句错话:「证不出有占用 ⇒ 没有占用」,只是换了一个盘面);新提交不读这张索引,claim 文件完好时仍按盘上的 claim 裁定,不受影响。同一条债的另一个盘面:缺省不拒启是刻意保留的行为,所以留作 P-DEBT —— error 级启动日志(`file_run_store_active_claims_unreadable`,带 `runs/active/` 绝对路径)早已在,本计数补的是遥测轴(此前 `runs/` 臂有计数、这一臂没有)。`RUN_STORE_STRICT_HYDRATE=on` 把这一格换成 fail-closed 启动门,本计数因此只在缺省 off 档的真放行时产生(拒启那一形不记)—— 读遥测时「计数为零」也可能是部署开了拒启门、根本起不来。",
|
|
74
|
+
},
|
|
71
75
|
"server.fork-routing.placed-probe-host-error": {
|
|
72
76
|
cls: "F",
|
|
73
77
|
note: "#292 placement 半场:hostPlacedProbe 对 host 店的 requireExisting 探针失败且**不是 not_found**(host 读故障)时折叠为 absent —— 路由回旧规则(claim-create 会在 transient 建新会话)。放行的是「host 暂不可达时 transient 腿保持独立可用」;最坏后果=故障窗内对一个真 placed id 铸出 transient 影子(host 恢复后双身,requireExisting 冷探针会重新指回 host 真身)。not_found 是常态路径不计数;只有真故障臂进本计数。",
|
|
@@ -174,7 +178,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
174
178
|
},
|
|
175
179
|
"server.approvals.durable-row-checkpoint-id-missing": {
|
|
176
180
|
cls: "F",
|
|
177
|
-
note: "7.133.0(`checkpoint-identity.ts` 的 `durableRowIdentity`;两只店的 `listPending` + `/v1/assistant/inbox` handler):三处 durable 读面(运维队列行 `GET /v1/approvals` 与 `/v1/approvals/stream` 的 `pending` 帧;停驻收件 `GET /v1/assistant/inbox` 的行)读到一行停驻门**没有可用的 `checkpointId`** ⇒ 这一行**照列、`checkpointId` 键缺席**(消费方按「这一行没有可 join 的身份」处理)。detail = `face=<approvals-queue|assistant-inbox> why=<absent|untrusted>`:`absent` = 行上没有这一格(core 5.42.0 之前停驻的老行 —— core 明写不回填);`untrusted` = 在场却没过信任边界(非串 / 空串 / 超长 / 含 resume token —— 数据污染;没过边界的值不上 wire)。放行(列、键缺席)的最坏后果 = 消费方对这一行做不了跨面 join(三面对账少一行)—— 行本身、决断口(`/v1/approvals/:sessionId/decide`,按会话)、到期收割都照常。不放行有两种形:整只拒的代价是一行老 / 坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧);把这一行藏起来的代价更糟 —— 人看不到要批的东西(审批面上的 fail-open,方向反了)。故 F 类;必须留痕(两种成因从此可分)。计数非零 = 有老 / 坏行在飞(列着、没身份);`/stream` 每 3s 一拍重读,同一行每拍计一次(量值 = 行 × 拍数,在场才是信号)。 7.137.0 起第四面:park 账本行(`GET /v1/runs/:id/events` 的 `suspended` / `needs_review`;唯一构造器 `trace/project.ts` 的 `parkEventData`,写时判一次,detail `face=run-events-park`)—— 暂停因由上的门身份没过信任边界 ⇒ 行照写、`checkpointId` 键缺席(决断方回显不了这道门,退回不带键的决断或从 run 结果取原值;行本身、门、待批调用身份照常)。",
|
|
181
|
+
note: "7.133.0(`checkpoint-identity.ts` 的 `durableRowIdentity`;两只店的 `listPending` + `/v1/assistant/inbox` handler):三处 durable 读面(运维队列行 `GET /v1/approvals` 与 `/v1/approvals/stream` 的 `pending` 帧;停驻收件 `GET /v1/assistant/inbox` 的行)读到一行停驻门**没有可用的 `checkpointId`** ⇒ 这一行**照列、`checkpointId` 键缺席**(消费方按「这一行没有可 join 的身份」处理)。detail = `face=<approvals-queue|assistant-inbox> why=<absent|untrusted>`:`absent` = 行上没有这一格(core 5.42.0 之前停驻的老行 —— core 明写不回填);`untrusted` = 在场却没过信任边界(非串 / 空串 / 超长 / 含 resume token —— 数据污染;没过边界的值不上 wire)。放行(列、键缺席)的最坏后果 = 消费方对这一行做不了跨面 join(三面对账少一行)—— 行本身、决断口(`/v1/approvals/:sessionId/decide`,按会话)、到期收割都照常。不放行有两种形:整只拒的代价是一行老 / 坏行打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远停在旧帧);把这一行藏起来的代价更糟 —— 人看不到要批的东西(审批面上的 fail-open,方向反了)。故 F 类;必须留痕(两种成因从此可分)。计数非零 = 有老 / 坏行在飞(列着、没身份);`/stream` 每 3s 一拍重读,同一行每拍计一次(量值 = 行 × 拍数,在场才是信号)。 7.137.0 起第四面:park 账本行(`GET /v1/runs/:id/events` 的 `suspended` / `needs_review`;唯一构造器 `trace/project.ts` 的 `parkEventData`,写时判一次,detail `face=run-events-park`)—— 暂停因由上的门身份没过信任边界 ⇒ 行照写、`checkpointId` 键缺席(决断方回显不了这道门,退回不带键的决断或从 run 结果取原值;行本身、门、待批调用身份照常)。 第五面:回决门族的 200 体(`/decide` / `/resume` / `/plan_review` 执行之前从选中的那一行捕获一次,`http/resume-legs.ts` 三处;detail `face=decision-receipt`)—— 选中那一行的门身份没过信任边界 ⇒ 结清证据照铸、体上 `checkpointId` 键缺席(决断方只是对不上身份;位、决定、续跑都照常)。每次走到捕获点的决断计一次(计的是选中那一行的事实,与这次决断随后走哪条车道无关)。",
|
|
178
182
|
},
|
|
179
183
|
"server.approvals.queue-row-gate-kind-missing": {
|
|
180
184
|
cls: "F",
|
|
@@ -260,6 +264,10 @@ export const FAIL_OPEN_TAGS = {
|
|
|
260
264
|
cls: "F",
|
|
261
265
|
note: "S-690(`instruction-source-notice.ts announceInstructionSource`,项目指令换源通告的唯一铸点;detail 的 `op` 点名哪一步):基线**读**失败或超期(会话店抖动 / 列形坏 / 2s 期限到,detail 带 `timeout=`)⇒ 本 run 不比也不写;基线**写**失败或超期 ⇒ 本 run 的换源帧照发(它对本 run 为真),下一 run 可能再说一遍同一次换源(超期后才落地的写由 CAS 挡住,盖不掉更新的基线);通告**出口**抛 ⇒ 本 run 缺这一帧。放行的最坏后果 = 一次换源没被说出来或被多说一次 —— 通告是**披露**面,不参与任何门 / 裁决,模型读到的指令本身不受影响。不放行的代价严重不对称:铸点跑在 core 的 `loadProjectMemory` 席内,抛回去 core 会把这次 prepare 的项目记忆整块丢掉(不注入),为一条通告赔上指令本身。计数让「某台部署的会话店一直读不出基线」与「这台部署从来没换过源」在遥测上分得开。",
|
|
262
266
|
},
|
|
267
|
+
"server.session-workspace.record-degraded": {
|
|
268
|
+
cls: "F",
|
|
269
|
+
note: "会话行的位置与项目那一格(`session_meta.workspace` / local `session-workspaces.json`;唯一写者 `session-workspace.ts` 的腿记录座,唯一列属主 `plugins/session-workspace-cell.ts`;detail 的 `op` 点名哪一步):`op=write` 一次写失败(库抖 / 盘满 / 锁等待超时)⇒ 行停在上一帧,下一帧照写;`op=read` 一格读不出(被带外改坏)⇒ 读作没有记录,下一帧整格重写;`op=frame` 一帧投不成存储形(引擎不会铸出这种帧)⇒ 这一帧不记。这一格是宿主对**引擎判过的答案**的记录,不是判据 —— 引擎每条腿都重判项目(有记号就判记号,声明不等响亮拒)。放行的最坏后果 = 这只会话的下一条不带 `projectId` 的提交续声明的是旧的项目或不声明(没有记号的检出里这只会话少一次续声明);不放行的代价是把一条在跑的腿、或这只会话往后的每一次提交,为一份记录摔掉。计数非零 = 这台部署的会话店写不动 / 有人改过库。",
|
|
270
|
+
},
|
|
263
271
|
"server.engine-notice.wire-sink-threw": {
|
|
264
272
|
cls: "F",
|
|
265
273
|
note: "#310:`engine_notice` 分流器把一条白名单通告投给某条 run 腿的登记口时,那只口抛了(live 口写向已断/已撕裂的 SSE socket,或 durable 口的账本写同步抛)。放行的最坏后果 = **这一条通告的这一个终点**缺席:①日志终点在分流之前已经逐字打过(事实一条不丢);②两个终点注册成两只独立 sink ⇒ live 抛不牵连 durable(断连后仍看得见的那半保住);③同会话其余口照投。故 F 类。不放行的代价是把异常回抛给 core 的 `deliverEngineNotice` —— 它会吞掉,于是同一次失败**既没有留痕也没有第二只口**,正是本 tag 要根除的形。留痕是承重的:静默吞掉之后「wire 腿为什么总有几条通告不到」在遥测里与「core 本来就没发」同形。",
|
|
@@ -505,6 +513,14 @@ export const FAIL_OPEN_TAGS = {
|
|
|
505
513
|
cls: "P-DEBT",
|
|
506
514
|
note: "S-163(`config-dir-trust.ts` `judgeWin32`,本地配置源 `config.d` 读之前的目录信任判官在 win32 上的那一半):Node 在 win32 上 `uid` 恒 0、模式位是只读属性的投影 ⇒ 「属主 ∈ {本服务用户, root, CONFIG_LOCAL_OWNER_UID}」与「不得组 / 他人可写」两判不可做;结构判(常规文件 / 16 MiB 上界 / 链接成环)照做。每一次读照常进行(起服一次 + 每拍一次)记一次。放行的最坏后果 = 一台多用户 Windows 机器上,别的本机用户若能写这台服务的 `config.d`(缺省的用户目录 ACL 不允许),改一份 `mcp.json` 就能让本服务以它的身份起命令 —— 与 7.132.x 逐字同。照 POSIX 拒启会让每个 Windows 本机引擎(含 cli 起的)升级即起不来,而让 cli 替用户设豁免键是中间层替部署者设安全旋钮。收口 = 有了 ACL 判据或一次裁定(改 R 或改拒启)那一版,本 tag 删或改类。detail = `config.d=<物理路径>`。",
|
|
507
515
|
},
|
|
516
|
+
"server.remote-write.staging-dir-left": {
|
|
517
|
+
cls: "F",
|
|
518
|
+
note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备 / 提交脚本删一只私有暂存目录(`.<名>.stage-<十六进制>/`,生来 0700;只删这次写放进去的 `data` 与这只空目录,从不递归删)没删掉、在 stdout 写一条记录点名 —— 五处:`mv` 已把暂存件换到目标上之后删当前那只;跟一跳到别的目录重新暂存成功之后删原来那只;重新暂存却没用上(收紧不成 / 拷不成)的那一只;就地拷入 / 追加时删暂存名失败、或写完关掉 fd 后删私有目录失败(有的文件系统为仍打开着的文件留隐藏项:NFS / libfuse / SMB);准备时收不成私有 / 里面已有 `data` 的那一只。目录里多出了不是这次写放进去的东西(同一身份或 root 的别的进程往里写过 / 收紧之前别人放进来的)、目录被换成了符号链接、或文件系统拒删。写照它自己的结局答(提交已成 ⇒ 成功:答失败会让调用方以为目标没变),残留只在这里留痕。放行的最坏后果 = 目标(或链接所指)目录里多一只隐藏目录(空的,或装着别人放进去的东西;重新暂存没用上的那只可能装着这次写的半份新内容),只有写入方进得去;不是门、不是凭据、不碰目标内容。计数非零 = 那台目标机上有东西在往别人的暂存目录里写,或删不动,去看那只目录。detail = 残留目录的路径(一次写最多两只,逐只计)。",
|
|
519
|
+
},
|
|
520
|
+
"server.remote-write.stage-seal-fallback": {
|
|
521
|
+
cls: "F",
|
|
522
|
+
note: "远端五条车道的写(`plugins/remote-shell.ts` 的暂存编排):准备脚本把刚建的私有暂存目录收成 0700 并读回核实(`chmod 700` 退出 0 ∧ `stat` 读回的模式低三位恰是 700 ∧ 属主 = 本身份),任一步不成 —— `chmod` 被文件系统 / 内核拒;退出 0 却没生效(不认权限位的文件系统);父目录带缺省 ACL 而掩码收不下来;目标机上没有 `stat` —— 准备退出 4、这次写**不暂存**,两只文件面、五条腿一律改为直接写目标(与「目标目录不收新名字」同一条回落;能追加写的腿直接追加,e2b / adb 的追加 = 读旧 + 拼 + 整份写目标 —— adb 的这一形就是 7.138.x 的追加,push 没有追加写)。修前这一步是 `chmod 700 … || :`:加固失败照样放行上传,字节落在一只别的身份可能读得到的目录里(P 轴 fail-open),那一臂已删,字节从不进一只没核成私有的目录。放行的最坏后果 = 这一次写失去「整旧或整新」(非原子:中途断 / 取消 ⇒ 目标可能只写了一半,整份写目标的 e2b / adb 上可能没了或只剩半份;写目标那一步因取消中断答 `unknown`,那一步开始之前取消答 `aborted`、目标未碰,那一步不收取消(ssh 的 sftp)照样写完);ssh / local-docker / k8s 写进原 inode、权限位不变,e2b 的 `files.write` 与 adb 的 push 整份写目标、inode / 权限位由对端定(adbd 不保,与 7.138.x 同形);不是门、不是凭据。Android 共享存储(`/sdcard` 一类:权限位由挂载派生、不认 chmod)上 adb 的写按 sdcardfs / MediaProvider FUSE 的权限语义**推断**每一次都走这里(未上真机,真机读数候 test;结构性,运维修不掉;工作区放在 `/data/local/tmp` 一类认权限位的目录就不走)。计数非零 = 这台目标机的这个目录上私有目录收不成私有(文件系统不认权限位 / 缺省 ACL 掩码收不下来 / 缺 `stat`),写在走非原子的退路,去看那个目录。detail = `<目标路径>: <准备脚本写的原因句>`。",
|
|
523
|
+
},
|
|
508
524
|
};
|
|
509
525
|
export function failOpenTagForDroppedFrame(frameType) {
|
|
510
526
|
switch (frameType) {
|
|
@@ -593,7 +593,6 @@ const STATS_KEY_TREATMENT = {
|
|
|
593
593
|
humanReview: "human-review",
|
|
594
594
|
};
|
|
595
595
|
const STATS_MECHANISMS_TREATMENT = {
|
|
596
|
-
finalVerifyInjected: "verbatim",
|
|
597
596
|
finalVerifyInjections: "verbatim",
|
|
598
597
|
attachmentsInjected: "verbatim",
|
|
599
598
|
approachNoticesSent: "verbatim",
|
|
@@ -62,7 +62,7 @@ export declare const SECRET_ENV_SCRUB_SITES: {
|
|
|
62
62
|
readonly remedy: null;
|
|
63
63
|
};
|
|
64
64
|
readonly "server.hooks.spawn-env": {
|
|
65
|
-
readonly note: "settings hooks 的命令腿(请求带来的命令跑在宿主上)的 env —— 严格名单(`PATH` / `HOME` / 终端 / locale 等十二名)
|
|
65
|
+
readonly note: "settings hooks 的命令腿(请求带来的命令跑在宿主上)的 env —— 严格名单(`PATH` / `HOME` / 终端 / locale 等十二名)经引擎的 `hostChildEnv` 组合:名单外一律不交,本服务自有凭据任何情况都不交;请求的有效环境层(`settings.env`)作授予叠在上面;请求给工具 shell 的继承词对它不作用";
|
|
66
66
|
readonly remedy: null;
|
|
67
67
|
};
|
|
68
68
|
readonly "server.project-memory.git-subprocess": {
|
|
@@ -14,7 +14,7 @@ export const SECRET_ENV_SCRUB_SITES = {
|
|
|
14
14
|
remedy: null,
|
|
15
15
|
},
|
|
16
16
|
"server.hooks.spawn-env": {
|
|
17
|
-
note: "settings hooks 的命令腿(请求带来的命令跑在宿主上)的 env —— 严格名单(`PATH` / `HOME` / 终端 / locale 等十二名)
|
|
17
|
+
note: "settings hooks 的命令腿(请求带来的命令跑在宿主上)的 env —— 严格名单(`PATH` / `HOME` / 终端 / locale 等十二名)经引擎的 `hostChildEnv` 组合:名单外一律不交,本服务自有凭据任何情况都不交;请求的有效环境层(`settings.env`)作授予叠在上面;请求给工具 shell 的继承词对它不作用",
|
|
18
18
|
remedy: null,
|
|
19
19
|
},
|
|
20
20
|
"server.project-memory.git-subprocess": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
2
|
+
import { AppendLog, REDACTION_MARKS, ensureDir } from "@sema-agent/core";
|
|
3
3
|
import { buildFleetWorkflowRow } from "../fleet/fleet-bus.js";
|
|
4
4
|
import { isTerminalWorkflowRowStatus, seedTerminalWorkflowRow } from "../fleet/fleet-terminal-window.js";
|
|
5
5
|
import { buildParkSessionRows } from "./workflow-agent-session-index.js";
|
|
@@ -26,11 +26,11 @@ export function recoverySummary(status, error) {
|
|
|
26
26
|
return "workflow completed";
|
|
27
27
|
const base = error ? `workflow failed: ${error}` : "workflow failed";
|
|
28
28
|
const redacted = base
|
|
29
|
-
.replace(/https?:\/\/[^\s"'`]+/gi,
|
|
29
|
+
.replace(/https?:\/\/[^\s"'`]+/gi, REDACTION_MARKS.url)
|
|
30
30
|
.replace(/\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+/gi, "[redacted-token]")
|
|
31
31
|
.replace(/\b(?:api[_-]?key|secret|token|password|authorization)\b\s*[:=]\s*"?[^\s"',}]+/gi, "[redacted-secret]")
|
|
32
|
-
.replace(/(?:\/[A-Za-z0-9_.-]+){2,}/g,
|
|
33
|
-
.replace(/(?:[A-Za-z]:)?(?:\\{1,2}[A-Za-z0-9_.$-]+){2,}/g,
|
|
32
|
+
.replace(/(?:\/[A-Za-z0-9_.-]+){2,}/g, REDACTION_MARKS.path)
|
|
33
|
+
.replace(/(?:[A-Za-z]:)?(?:\\{1,2}[A-Za-z0-9_.$-]+){2,}/g, REDACTION_MARKS.path);
|
|
34
34
|
return redacted.length > SUMMARY_MAX ? `${redacted.slice(0, SUMMARY_MAX)}…[+${redacted.length - SUMMARY_MAX} chars]` : redacted;
|
|
35
35
|
}
|
|
36
36
|
export class WorkflowNotifyGate {
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
* core 侧契约(RunnerDeps.memoryScopeAdmission,core types.d.ts):resolver 返回 {ok:false} ⇒ core 铸终局码
|
|
13
13
|
* memory.admission_denied;resolver throw 一个带 `retryAfterMs`(ms)属性的 Error ⇒ core 铸瞬时码
|
|
14
14
|
* memory.admission_required 并透传 retryAfterMs;{ok:true}.scopes 只能 ⊆ requested(多给 = resolver fault
|
|
15
|
-
* 会被 core 整拒)
|
|
16
|
-
*
|
|
15
|
+
* 会被 core 整拒)。「谁在问」只有引擎一处读法:入参按调用方在不在场分两形,无调用方那一形只装部署来源的 scope
|
|
16
|
+
* (请求来源的 scope 没有调用方 ⇒ 引擎在自己的门上先拒,不问本解析器)—— 本模块按这一判别式分支,不另判一次缺席;
|
|
17
|
+
* 引擎型不可达的那一组合(无调用方 ∧ 请求来源 scope)若仍到了这里 ⇒ 抛,不放行。
|
|
17
18
|
*/
|
|
18
19
|
import type { MemoryScopeAdmission } from "@sema-agent/core";
|
|
19
20
|
/**
|
|
@@ -72,7 +73,7 @@ export interface MemoryScopeAdmissionOptions {
|
|
|
72
73
|
/** "audit": 判决照算但从不真拒——会拒的结果(终局 ok:false 或瞬时 throw)一律换成全额放行 + 观测记录
|
|
73
74
|
* would-deny;真通过的判决(含 write 收窄为 null)照常返回。"enforce": 判决即结果。 */
|
|
74
75
|
mode: "audit" | "enforce";
|
|
75
|
-
/** 观测 hook。outcome ∈ "ok" | "denied" | "
|
|
76
|
+
/** 观测 hook。outcome ∈ "ok" | "denied" | "directory_absent" | "directory_stale" |
|
|
76
77
|
* "directory_malformed"。audit 模式下的 would-deny 也走这里,`details.audited === true`。hook 本身绝不
|
|
77
78
|
* 允许打断准入判决——一律吞掉它可能抛出的异常。 */
|
|
78
79
|
onOutcome?: (outcome: string, details?: Record<string, unknown>) => void;
|
|
@@ -164,8 +164,9 @@ export function createMemoryScopeAdmission(directory, opts) {
|
|
|
164
164
|
catch {
|
|
165
165
|
}
|
|
166
166
|
};
|
|
167
|
-
return async (
|
|
168
|
-
const
|
|
167
|
+
return async (input) => {
|
|
168
|
+
const { requested, requestedWriteScope } = input;
|
|
169
|
+
const attributed = (details) => input.principal === undefined ? details : { principal: input.principal, ...details };
|
|
169
170
|
const requestOriginRequested = requested.filter((r) => r.origin === "request");
|
|
170
171
|
const writeIsRequestOrigin = requestedWriteScope?.origin === "request";
|
|
171
172
|
const hasRequestOrigin = requestOriginRequested.length > 0 || writeIsRequestOrigin;
|
|
@@ -175,10 +176,9 @@ export function createMemoryScopeAdmission(directory, opts) {
|
|
|
175
176
|
async function decide() {
|
|
176
177
|
if (!hasRequestOrigin)
|
|
177
178
|
return { kind: "admit", verdict: admitFull() };
|
|
178
|
-
if (principal === undefined)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const lookup = await directory.lookup(principal);
|
|
179
|
+
if (input.principal === undefined)
|
|
180
|
+
throw new Error("org memory admission was asked with no caller yet holds a request-origin scope — the engine's caller-less arm carries deployment-origin scopes only, so this input is an engine contract break and is refused, never admitted");
|
|
181
|
+
const lookup = await directory.lookup(input.principal);
|
|
182
182
|
if (lookup.kind === "unavailable") {
|
|
183
183
|
const outcome = lookup.reason === "stale_generation" ? "directory_stale" : lookup.reason === "malformed" ? "directory_malformed" : "directory_absent";
|
|
184
184
|
return { kind: "unavailable", retryAfterMs: lookup.retryAfterMs, outcome, lookupReason: lookup.reason, details: { reason: lookup.reason } };
|
package/dist/parked-decide.d.ts
CHANGED
|
@@ -15,7 +15,15 @@ import type { ParkedReviveSurfaceFactory } from "./parked-revive-surface.js";
|
|
|
15
15
|
import type { RebuiltInheritedGate } from "./boot/parked-revive-gate.js";
|
|
16
16
|
import { type WorkflowRunParkKey, type WorkflowAgentSessionIndex } from "./orchestration/workflow-agent-session-index.js";
|
|
17
17
|
import type { WorkflowParkDecisionStore, WorkflowParkOutcome } from "./orchestration/workflow-park-decision.js";
|
|
18
|
-
import {
|
|
18
|
+
import type { SessionFace } from "./session-incarnation.js";
|
|
19
|
+
import { type ExecutionCommitDeps, type RootedExecutionContext } from "./http/execution-context.js";
|
|
20
|
+
import type { GateIdentity } from "./checkpoint-identity.js";
|
|
21
|
+
/**
|
|
22
|
+
* 本腿的**装配前提**(`createHttpServer` 起服拒的拒句;规则见 {@link ParkedDecideDeps} 的 `execution`):接了本腿(`parkedReviveSurface` 在场)的部署,
|
|
23
|
+
* 会话店必须带执行席 —— 认领之后对根会话住客的唯一判官是执行席上的写口,无席 ⇒ 认领之后没有任何判官(安全轴上不留静默放开的臂,所以拒启,
|
|
24
|
+
* 不留「无席时照旧两读」的臂)。
|
|
25
|
+
*/
|
|
26
|
+
export declare const PARKED_REVIVE_EXECUTION_SEAT_REQUIRED = "the parked-agent revive leg (parkedReviveSurface) needs a session store with an execution placement seat (sessionStorage.executionPlacement) \u2014 after the claim, the root session's occupant is judged only by the write on that seat; every built-in session store carries one (the MySQL-protocol and PostgreSQL stores and the local store, i.e. the session store a StoreBackend hands out; the caching and fork-routing wrappers forward it), and an in-memory test or embedding passes InMemoryExecutionPlacementSeat; refusing to start rather than revive parked agents with nobody judging the root session's occupant after the claim";
|
|
19
27
|
export interface ParkedAgentMatch {
|
|
20
28
|
handle: string;
|
|
21
29
|
row: BackgroundAgentRecord;
|
|
@@ -150,14 +158,6 @@ export interface ParkedDecideDeps {
|
|
|
150
158
|
* checkpointStore 同实例,[ref] 设计稿 §4),以及与它同出一张列表的继承座 `mountsCallerTool`(一层行才有;属主与全部判据
|
|
151
159
|
* 在 `parked-revive-surface.ts`)。修前是 boot 期的一只裸 Agent 工具、从不交座 ⇒ 7.32.0 起停在继承工具上的 allow 恒 422。 */
|
|
152
160
|
reviveSurface: ParkedReviveSurfaceFactory;
|
|
153
|
-
/**
|
|
154
|
-
* [ref]([ref] §4)—— 会话店按 id 读一行的唯一读口(`sessionStorage.headOf`:属主 + 化身 + 叶子一次读;活住客经 `sessionFaceOfHead`
|
|
155
|
-
* 投影,空化身响亮拒)。**必填**:本腿对根会话读两次 ——
|
|
156
|
-
* claim 之前一次、claim 成功之后一次 —— 会话不在或两次不是同一位住客 ⇒ 回滚 claim、答既有的 `409 decide.not_parked`;赎回面只在
|
|
157
|
-
* 属主相等且两次是同一位住客时交继承座。读抛 ⇒ claim 前原样上抛(没有东西要回滚),claim 后先回滚再上抛。
|
|
158
|
-
* ([ref] 那只可选的 `sessionOwnerOf` 退役:可选 ⇒ 缺席时静默不复核,正是 [ref] 那个窗的形。)
|
|
159
|
-
*/
|
|
160
|
-
headOf: HeadRead;
|
|
161
161
|
/** revive args 的 `subagent_type` 成员性判定集([ref] Q1 修正:非 roster 成员必须省略,否则
|
|
162
162
|
* unknown subagent_type 拒;字面比对 general-purpose 在 roster 撞名时会错省)。缺省空集=恒省略。 */
|
|
163
163
|
knownAgentTypes?: ReadonlySet<string>;
|
|
@@ -186,7 +186,33 @@ export interface ParkedDecideDeps {
|
|
|
186
186
|
owner: string | null;
|
|
187
187
|
taskId: string;
|
|
188
188
|
sessionId: string;
|
|
189
|
+
executionLane: import("./execution-lane-caps.js").ExecutionLane;
|
|
189
190
|
}) => NonNullable<ToolExecuteContext["onAsk"]>;
|
|
191
|
+
/**
|
|
192
|
+
* 边界 9 的执行上下文(设计 §3.2;与提交腿 / 续跑家族同一只解析器、同一只写口)—— 本腿对根会话的住客**一读一判**:
|
|
193
|
+
* · `resolve` —— **认领之前**按行的根取上下文:解析器那一次行读(执行席:住客 + 格)就是本腿对根会话的**唯一一读**;根行不在 ⇒ `gone`
|
|
194
|
+
* (本腿答既有的 `decide.not_parked`);其余拒(mirror / 车道不再服务 / 主体的车道裁定)按续跑家族的折叠律答好交回(`answer`)—— 不认领、不驱动。
|
|
195
|
+
* 成功形的 `occupant` **必有**:接本腿的部署必须带执行席(无席的会话店交不出这一读;`createHttpServer` 在装配时拒无席形);
|
|
196
|
+
* · `commitDeps` —— 认领之后 `commitExecutionContext` 读的部署件:唯一写口按那一读的住客定址写 / 再读(化身 ∧ 属主),这就是认领之后对根住客的
|
|
197
|
+
* **唯一一判**(拒 ⇒ 先回滚认领)。写口放行的那一位就是赎回面的根住客见证;写口之后本腿不再读根。
|
|
198
|
+
* 复活子代的审批席带这只上下文的车道(`approverSeatFor` 的 `executionLane`)。
|
|
199
|
+
*/
|
|
200
|
+
execution: {
|
|
201
|
+
resolve: (root: string) => Promise<{
|
|
202
|
+
readonly ok: true;
|
|
203
|
+
readonly ctx: RootedExecutionContext & {
|
|
204
|
+
readonly occupant: SessionFace;
|
|
205
|
+
};
|
|
206
|
+
} | {
|
|
207
|
+
readonly ok: false;
|
|
208
|
+
readonly gone: true;
|
|
209
|
+
} | {
|
|
210
|
+
readonly ok: false;
|
|
211
|
+
readonly gone: false;
|
|
212
|
+
readonly answer: ParkedDecideResult;
|
|
213
|
+
}>;
|
|
214
|
+
commitDeps: ExecutionCommitDeps;
|
|
215
|
+
};
|
|
190
216
|
warn?: (event: string, fields: Record<string, unknown>) => void;
|
|
191
217
|
}
|
|
192
218
|
export interface ParkedDecideRequest {
|
|
@@ -224,6 +250,18 @@ export interface ParkedDecideRequest {
|
|
|
224
250
|
* 的挂点(调用方自吞错,绝不抛落赎回)。claim 败/判别 miss 恒不触发。 */
|
|
225
251
|
onDecideCommitted?: () => Promise<void>;
|
|
226
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* 后台子代赎回腿({@link decideParkedAgent})收的请求 = 两条车道共用的那一份 + 选中这张卡的**门身份片段**。workflow 车道是投递、不收它
|
|
255
|
+
* (那条车道的回执不带结清证据,身份在那里不适用 —— 不捕获,也就不为它记「缺身份」)。
|
|
256
|
+
*/
|
|
257
|
+
export interface ParkedAgentDecideRequest extends ParkedDecideRequest {
|
|
258
|
+
/**
|
|
259
|
+
* 调用方在执行之前从选中的那一行捕获、已过信任边界(`checkpoint-identity.ts` 的 `durableRowIdentity`,face `decision-receipt`;读不出 ⇒ `{}`)。
|
|
260
|
+
* 本腿的两个结清边界(复活成功 / 不可复活 deny 作废)把它随结清证据上体(`gate-settlement.ts`)。必填:身份只有这一个来源 ——
|
|
261
|
+
* agent 行上没有这一格,本腿也不另读「当前待决门」(受理之后它可能已是下一张卡);认领到的 token 不是选中的那一张 ⇒ 拒,不替它作证。
|
|
262
|
+
*/
|
|
263
|
+
readonly selectedGate: GateIdentity;
|
|
264
|
+
}
|
|
227
265
|
export interface ParkedDecideResult {
|
|
228
266
|
status: number;
|
|
229
267
|
body: Record<string, unknown>;
|
|
@@ -341,8 +379,8 @@ export declare function planWorkflowParkedDecide(origin: Extract<ParkOrigin, {
|
|
|
341
379
|
* ([ref] Q2:isError 入参级早退 + throw 两形,已核幂等/双滚=无害 lost)。
|
|
342
380
|
*
|
|
343
381
|
* 200 是**受理**语义(drive 是 core 内 fire-and-forget):失败经行/notification 面 + REOPENED
|
|
344
|
-
* re-park 诚实可见,操作员可再决。
|
|
382
|
+
* re-park 诚实可见,操作员可再决。200 体 `{ taskId, status: "resuming", decision }` + 结清证据(`gate-settlement.ts` 的边界 ④)。
|
|
345
383
|
*/
|
|
346
|
-
export declare function decideParkedAgent(deps: ParkedDecideDeps, req:
|
|
384
|
+
export declare function decideParkedAgent(deps: ParkedDecideDeps, req: ParkedAgentDecideRequest, match: ParkedAgentMatch): Promise<ParkedDecideResult>;
|
|
347
385
|
export {};
|
|
348
386
|
//# sourceMappingURL=parked-decide.d.ts.map
|
package/dist/parked-decide.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { compareOccupants } from "@sema-agent/core";
|
|
2
1
|
import { readClosedWordSeat } from "./approval-card.js";
|
|
3
2
|
import { routeWorkflowPark } from "./orchestration/workflow-agent-session-index.js";
|
|
4
3
|
import { recordFailOpen } from "./observability/fail-open.js";
|
|
5
4
|
import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
|
|
6
5
|
import { backgroundScopesForCheckpointScope, decodeCheckpointScope, ownerOfCheckpointScope } from "./security.js";
|
|
7
6
|
import { ASK_USER_QUESTION_TOOL_NAME, isEngineQuestionRow } from "./approval-content-kind.js";
|
|
8
|
-
import { sessionFaceOfHead } from "./session-incarnation.js";
|
|
9
7
|
import { refusalBody, resumeRefusalFacts } from "./http/refusal-body.js";
|
|
8
|
+
import { backgroundAgentNameOf } from "./background-agent-name.js";
|
|
9
|
+
import { buildSettlementEvidence } from "./gate-settlement.js";
|
|
10
|
+
import { commitExecutionContext } from "./http/execution-context.js";
|
|
11
|
+
import { EXECUTION_PLACEMENT_SEAT_CARRIERS } from "./execution-placement.js";
|
|
12
|
+
export const PARKED_REVIVE_EXECUTION_SEAT_REQUIRED = `the parked-agent revive leg (parkedReviveSurface) needs a session store with an execution placement seat (sessionStorage.executionPlacement) — after the claim, the root session's occupant is judged only by the write on that seat; ${EXECUTION_PLACEMENT_SEAT_CARRIERS}; refusing to start rather than revive parked agents with nobody judging the root session's occupant after the claim`;
|
|
10
13
|
export async function findParkOrigin(sources, cp) {
|
|
11
14
|
if (sources.agentStore !== undefined) {
|
|
12
15
|
let match;
|
|
@@ -111,7 +114,7 @@ function mintReviveArgs(row, knownAgentTypes) {
|
|
|
111
114
|
return {
|
|
112
115
|
prompt: row.description ?? "resume parked agent",
|
|
113
116
|
run_in_background: true,
|
|
114
|
-
name: row
|
|
117
|
+
name: backgroundAgentNameOf(row),
|
|
115
118
|
description: row.description ?? "revived agent",
|
|
116
119
|
...(row.agentType !== undefined && knownAgentTypes.has(row.agentType) ? { subagent_type: row.agentType } : {}),
|
|
117
120
|
};
|
|
@@ -232,10 +235,10 @@ function sessionGone(taskId) {
|
|
|
232
235
|
}),
|
|
233
236
|
};
|
|
234
237
|
}
|
|
238
|
+
function gateMoved(taskId) {
|
|
239
|
+
return { status: 409, body: refusalBody({ errorCode: "decide.gate_moved", message: "the approval gate moved under this decide (re-parked or already settled) — re-check the pending list", facts: { taskId } }) };
|
|
240
|
+
}
|
|
235
241
|
function revivabilityOf(row, taskId) {
|
|
236
|
-
if (row.name === undefined) {
|
|
237
|
-
return { kind: "unrevivable", approveRefusal: { status: 409, body: refusalBody({ errorCode: "decide.row_unrevivable", message: "parked row carries no agent name — the revive path requires a named agent, so this approval cannot be approved; deny it to close the card (the agent then ends as failed)", facts: { taskId } }) } };
|
|
238
|
-
}
|
|
239
242
|
if (row.rootSessionId === undefined) {
|
|
240
243
|
return { kind: "unrevivable", approveRefusal: { status: 409, body: refusalBody({ errorCode: "decide.row_unrevivable", message: "this agent's row carries no root session anchor — the revive path cannot tell which session this approval belongs to, so it cannot be approved; deny it to close the card (the agent then ends as failed)", facts: { taskId } }) } };
|
|
241
244
|
}
|
|
@@ -244,7 +247,7 @@ function revivabilityOf(row, taskId) {
|
|
|
244
247
|
async function voidUnrevivableCard(deps, req, match) {
|
|
245
248
|
const voided = await deps.checkpointStore.expire(req.token, req.scope);
|
|
246
249
|
if (!voided) {
|
|
247
|
-
return
|
|
250
|
+
return gateMoved(match.handle);
|
|
248
251
|
}
|
|
249
252
|
try {
|
|
250
253
|
await deps.registry.reconcileParkedAgents({ agentStore: deps.agentStore, checkpointStore: deps.checkpointStore }, match.row.scope, Date.now());
|
|
@@ -252,7 +255,7 @@ async function voidUnrevivableCard(deps, req, match) {
|
|
|
252
255
|
catch (e) {
|
|
253
256
|
quiet(deps.warn, "parked_unrevivable_reconcile_failed", { taskId: match.handle, error: e instanceof Error ? e.message : String(e) });
|
|
254
257
|
}
|
|
255
|
-
return { status: 200, body: { taskId: match.handle, status: "failed", decision: "deny" } };
|
|
258
|
+
return { status: 200, body: { taskId: match.handle, status: "failed", decision: "deny", ...buildSettlementEvidence({ at: "card_voided", gate: req.selectedGate }) } };
|
|
256
259
|
}
|
|
257
260
|
export async function decideParkedAgent(deps, req, match) {
|
|
258
261
|
const row = match.row;
|
|
@@ -268,9 +271,10 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
268
271
|
if (revivability.kind === "unrevivable")
|
|
269
272
|
return req.decision === "deny" ? voidUnrevivableCard(deps, req, match) : revivability.approveRefusal;
|
|
270
273
|
const root = revivability.root;
|
|
271
|
-
const
|
|
272
|
-
if (
|
|
273
|
-
return sessionGone(match.handle);
|
|
274
|
+
const resolved = await deps.execution.resolve(root);
|
|
275
|
+
if (!resolved.ok)
|
|
276
|
+
return resolved.gone ? sessionGone(match.handle) : resolved.answer;
|
|
277
|
+
const execution = resolved.ctx;
|
|
274
278
|
const inheritedGate = deps.rebuildInheritedGate ? await deps.rebuildInheritedGate(row, { principal: decodeCheckpointScope(req.scope) }) : undefined;
|
|
275
279
|
const stores = { agentStore: deps.agentStore, checkpointStore: deps.checkpointStore };
|
|
276
280
|
const claim = await deps.registry.claimParkedAgent(stores, match.handle, { scope: row.scope, owner: row.owner });
|
|
@@ -282,7 +286,7 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
282
286
|
return { status: 409, body: refusalBody({ errorCode: "decide.claim_lost", message: "the parked agent is already claimed — either a concurrent decide won (re-check the pending list), or a stale claim is pending automatic cleanup (retry after the stale-claim window)", facts: { taskId: match.handle } }) };
|
|
283
287
|
case "binding_broken":
|
|
284
288
|
case "checkpoint_not_pending":
|
|
285
|
-
return
|
|
289
|
+
return gateMoved(match.handle);
|
|
286
290
|
default:
|
|
287
291
|
return { status: 409, body: refusalBody({ errorCode: "decide.not_parked", message: "the parked agent moved under this decide (stopped or already decided)", facts: { taskId: match.handle } }) };
|
|
288
292
|
}
|
|
@@ -306,21 +310,16 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
306
310
|
return "retry";
|
|
307
311
|
}
|
|
308
312
|
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
catch (e) {
|
|
314
|
-
await rollback("session_face_unreadable");
|
|
315
|
-
throw e;
|
|
316
|
-
}
|
|
317
|
-
if (compareOccupants(faceBefore, faceAfter) !== "same") {
|
|
318
|
-
await rollback("session_occupant_changed");
|
|
319
|
-
return sessionGone(match.handle);
|
|
313
|
+
if (ticket.token !== req.token) {
|
|
314
|
+
await rollback("claimed_token_moved");
|
|
315
|
+
return gateMoved(match.handle);
|
|
320
316
|
}
|
|
317
|
+
const placementRefusal = await commitExecutionContext(deps.execution.commitDeps, { kind: "parked", taskId: match.handle, rollback, goneAnswer: sessionGone(match.handle) }, execution);
|
|
318
|
+
if (placementRefusal)
|
|
319
|
+
return placementRefusal;
|
|
321
320
|
let surface;
|
|
322
321
|
try {
|
|
323
|
-
surface = deps.reviveSurface(row, { principal: decodeCheckpointScope(req.scope),
|
|
322
|
+
surface = deps.reviveSurface(row, { principal: decodeCheckpointScope(req.scope), rootOccupant: execution.occupant });
|
|
324
323
|
}
|
|
325
324
|
catch (e) {
|
|
326
325
|
await rollback("revive_surface_threw");
|
|
@@ -344,7 +343,7 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
344
343
|
toolCallId: `drv-${ticket.claimId}`,
|
|
345
344
|
...(surface.mountsCallerTool !== undefined ? { mountsCallerTool: surface.mountsCallerTool } : {}),
|
|
346
345
|
...(deps.approverSeatFor !== undefined && seatRoot !== undefined
|
|
347
|
-
? { onAsk: deps.approverSeatFor({ owner: ownerOfCheckpointScope(req.scope), taskId: match.handle, sessionId: seatRoot }) }
|
|
346
|
+
? { onAsk: deps.approverSeatFor({ owner: ownerOfCheckpointScope(req.scope), taskId: match.handle, sessionId: seatRoot, executionLane: execution.lane }) }
|
|
348
347
|
: {}),
|
|
349
348
|
reviveClaim: {
|
|
350
349
|
row: { ...ticket.row, parkClaimId: ticket.claimId },
|
|
@@ -408,6 +407,6 @@ export async function decideParkedAgent(deps, req, match) {
|
|
|
408
407
|
}),
|
|
409
408
|
};
|
|
410
409
|
}
|
|
411
|
-
return { status: 200, body: { taskId: match.handle, status: "resuming", decision: req.decision } };
|
|
410
|
+
return { status: 200, body: { taskId: match.handle, status: "resuming", decision: req.decision, ...buildSettlementEvidence({ at: "agent_revived", gate: req.selectedGate }) } };
|
|
412
411
|
}
|
|
413
412
|
//# sourceMappingURL=parked-decide.js.map
|