@sema-agent/server 7.141.0 → 7.143.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MIGRATION.md +657 -0
- package/USAGE.md +102 -30
- package/dist/adoption/plan.js +22 -0
- package/dist/approval-ask-audit-store.d.ts +61 -43
- package/dist/approval-ask-audit-store.js +69 -44
- package/dist/approval-ask-machine.d.ts +15 -7
- package/dist/approval-ask-machine.js +10 -4
- package/dist/approval-card.d.ts +126 -27
- package/dist/approval-card.js +103 -23
- package/dist/approval-crash-convergence.d.ts +17 -7
- package/dist/approval-crash-convergence.js +94 -22
- package/dist/approval-deny-reasons.d.ts +34 -30
- package/dist/approval-deny-reasons.js +21 -6
- package/dist/approval-policy-settlement.d.ts +1 -1
- package/dist/approval-queue-kind.d.ts +27 -0
- package/dist/approval-queue-kind.js +6 -0
- package/dist/approval-reconciler.d.ts +41 -9
- package/dist/approval-reconciler.js +45 -42
- package/dist/audit.js +4 -2
- package/dist/auth-bridge.d.ts +9 -6
- package/dist/auth-bridge.js +9 -3
- package/dist/boot/adoption.js +2 -1
- package/dist/boot/budget-tracing.d.ts +17 -1
- package/dist/boot/budget-tracing.js +10 -6
- package/dist/boot/device-lane.d.ts +1 -1
- package/dist/boot/execution-env.d.ts +24 -4
- package/dist/boot/execution-env.js +26 -23
- package/dist/boot/execution-lane.js +2 -1
- package/dist/boot/exit-boundary.js +5 -4
- package/dist/boot/governance-seams.d.ts +26 -41
- package/dist/boot/governance-seams.js +61 -28
- package/dist/boot/late-binds.d.ts +7 -4
- package/dist/boot/late-binds.js +3 -1
- package/dist/boot/leader.js +2 -4
- package/dist/boot/memory-consolidation.d.ts +0 -8
- package/dist/boot/memory-consolidation.js +5 -4
- package/dist/boot/parked-revive-gate.js +2 -1
- package/dist/boot/permission-rules-audit.d.ts +0 -28
- package/dist/boot/permission-rules-audit.js +2 -1
- package/dist/boot/read-deny-anchor-audit.js +2 -1
- package/dist/boot/reapers.js +9 -7
- package/dist/boot/resolve-spec.d.ts +1 -1
- package/dist/boot/resolve-spec.js +19 -10
- package/dist/boot/retention-lane.js +5 -3
- package/dist/boot/runner-deps.d.ts +10 -7
- package/dist/boot/runner-deps.js +20 -29
- package/dist/boot/runtime-caps.js +3 -2
- package/dist/boot/session-faces.js +4 -3
- package/dist/boot/shutdown.d.ts +9 -0
- package/dist/boot/shutdown.js +19 -4
- package/dist/boot/stage-02-web-admin-pull.js +3 -0
- package/dist/boot/stage-03-store-backend.d.ts +4 -1
- package/dist/boot/stage-03-store-backend.js +1 -2
- package/dist/boot/stage-04-stores.d.ts +5 -2
- package/dist/boot/stage-04-stores.js +6 -7
- package/dist/boot/stage-05-execution-env.d.ts +6 -3
- package/dist/boot/stage-05-execution-env.js +8 -6
- package/dist/boot/stage-06-runners.d.ts +6 -3
- package/dist/boot/stage-06-runners.js +3 -4
- package/dist/boot/stage-07-capability-layer.d.ts +6 -3
- package/dist/boot/stage-07-capability-layer.js +7 -2
- package/dist/boot/stage-08-reapers.d.ts +6 -3
- package/dist/boot/stage-08-reapers.js +3 -2
- package/dist/boot/stage-09-leader.d.ts +6 -3
- package/dist/boot/stage-10-http-server.d.ts +6 -3
- package/dist/boot/stage-10-http-server.js +14 -5
- package/dist/boot/stores.d.ts +5 -2
- package/dist/boot/stores.js +10 -9
- package/dist/boot/web-admin.d.ts +5 -1
- package/dist/boot/web-admin.js +48 -36
- package/dist/boot/webfetch-summarize-lane.js +2 -1
- package/dist/boot/workflow-orchestration.js +3 -2
- package/dist/capabilities/builtin-tools.d.ts +2 -1
- package/dist/capabilities/builtin-tools.js +2 -4
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/hands-lane.d.ts +9 -39
- package/dist/capabilities/hands-lane.js +8 -13
- package/dist/capabilities/mcp-probe.d.ts +3 -2
- package/dist/capabilities/scenarios.d.ts +11 -1
- package/dist/capabilities/scenarios.js +1 -0
- package/dist/capabilities/skills.d.ts +1 -17
- package/dist/capabilities/skills.js +0 -8
- package/dist/capability-facts.d.ts +31 -7
- package/dist/capability-facts.js +27 -4
- package/dist/card-void.d.ts +6 -9
- package/dist/config-catalog.js +2 -1
- package/dist/config-dir-trust.js +58 -29
- package/dist/config-invariants.d.ts +7 -1
- package/dist/config-invariants.js +7 -0
- package/dist/config-provider.d.ts +10 -0
- package/dist/config-provider.js +15 -4
- package/dist/config-root-migration.d.ts +13 -0
- package/dist/config-root-migration.js +99 -0
- package/dist/config-types.d.ts +19 -8
- package/dist/config.d.ts +10 -3
- package/dist/config.js +18 -4
- package/dist/delegated-session-owner.d.ts +5 -3
- package/dist/delegated-session-owner.js +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/env-model-table-gate.d.ts +53 -0
- package/dist/env-model-table-gate.js +53 -0
- package/dist/execution-lane-caps.d.ts +2 -1
- package/dist/fleet/fleet-bus.d.ts +34 -55
- package/dist/fleet/fleet-bus.js +35 -100
- package/dist/fleet/fleet-reconciler.d.ts +4 -4
- package/dist/fleet/fleet-reconciler.js +1 -1
- package/dist/fleet/subagent-tail-bus.d.ts +14 -11
- package/dist/fleet/subagent-tail-bus.js +15 -28
- package/dist/hooks/hook-runner.d.ts +6 -0
- package/dist/hooks/hook-runner.js +58 -8
- package/dist/host-decision.d.ts +54 -20
- package/dist/host-decision.js +42 -14
- package/dist/http/admission.d.ts +1 -1
- package/dist/http/admission.js +7 -3
- package/dist/http/approval-sweeps.js +11 -8
- package/dist/http/caller-trust.d.ts +1 -1
- package/dist/http/dispatch.js +4 -2
- package/dist/http/durable-run-leg.js +2 -2
- package/dist/http/engine-refusal.d.ts +16 -0
- package/dist/http/engine-refusal.js +17 -0
- package/dist/http/execution-context.d.ts +2 -2
- package/dist/http/idempotency.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
- package/dist/http/memory-compliance-failure-reply.js +4 -6
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +13 -0
- package/dist/http/principal-gate.d.ts +3 -1
- package/dist/http/principal-gate.js +17 -8
- package/dist/http/resume-legs.d.ts +21 -7
- package/dist/http/resume-legs.js +76 -58
- package/dist/http/resume-restatement.d.ts +39 -0
- package/dist/http/resume-restatement.js +25 -0
- package/dist/http/route-ctx.d.ts +35 -10
- package/dist/http/route-ctx.js +1 -1
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.d.ts +9 -1
- package/dist/http/routes/approvals-assistant.js +142 -31
- package/dist/http/routes/attachments.js +18 -0
- package/dist/http/routes/capabilities.js +16 -4
- package/dist/http/routes/memory-policy.d.ts +2 -2
- package/dist/http/routes/memory-policy.js +27 -7
- package/dist/http/routes/notify-wake.js +23 -25
- package/dist/http/routes/rules.js +48 -13
- package/dist/http/routes/runs.js +12 -11
- package/dist/http/routes/session-memory-sync.d.ts +91 -0
- package/dist/http/routes/session-memory-sync.js +174 -0
- package/dist/http/routes/session-sync.js +13 -20
- package/dist/http/routes/sessions-list.d.ts +2 -1
- package/dist/http/routes/sessions.d.ts +2 -2
- package/dist/http/routes/sessions.js +22 -23
- package/dist/http/routes/tasks.js +34 -54
- package/dist/http/run-read-face.d.ts +7 -0
- package/dist/http/run-read-face.js +9 -3
- package/dist/http/send.d.ts +5 -0
- package/dist/http/send.js +9 -2
- package/dist/http/server.d.ts +27 -15
- package/dist/http/server.js +4 -6
- package/dist/http/stored-body-replay.js +15 -4
- package/dist/http/submit-replay.d.ts +4 -3
- package/dist/http/submit-replay.js +2 -2
- package/dist/http/sync-submit-body.d.ts +21 -4
- package/dist/http/sync-submit-body.js +13 -0
- package/dist/http/wire-types.d.ts +17 -7
- package/dist/identity-source.d.ts +21 -2
- package/dist/identity-source.js +6 -0
- package/dist/leader/leader.js +5 -2
- package/dist/leader/wire.d.ts +7 -1
- package/dist/leader/wire.js +6 -5
- package/dist/leg-facts.d.ts +37 -0
- package/dist/leg-facts.js +15 -0
- package/dist/memory-bundle-engine.d.ts +1 -4
- package/dist/memory-bundle-engine.js +2 -2
- package/dist/memory-incident-sink.d.ts +19 -6
- package/dist/memory-incident-sink.js +8 -4
- package/dist/memory-operator-faces.d.ts +30 -66
- package/dist/memory-operator-faces.js +17 -88
- package/dist/memory-scope-stores.d.ts +38 -40
- package/dist/memory-scope-stores.js +51 -36
- package/dist/memory-scope.d.ts +0 -9
- package/dist/memory-scope.js +0 -9
- package/dist/memory-sync-client.d.ts +21 -21
- package/dist/memory-sync-client.js +62 -43
- package/dist/memory-sync-identity.d.ts +24 -0
- package/dist/memory-sync-identity.js +57 -0
- package/dist/memory-sync.d.ts +27 -5
- package/dist/memory-sync.js +29 -7
- package/dist/model-plane-face.d.ts +13 -7
- package/dist/model-plane-face.js +2 -1
- package/dist/observability/fail-open.d.ts +47 -14
- package/dist/observability/fail-open.js +37 -11
- package/dist/observability/run-terminal.d.ts +92 -34
- package/dist/observability/run-terminal.js +171 -162
- package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
- package/dist/orchestration/workflow-completion-inbox.js +0 -12
- package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
- package/dist/orchestration/workflow-park-decision.d.ts +8 -3
- package/dist/orchestration/workflow-park-decision.js +5 -3
- package/dist/org-delivery-gate.d.ts +11 -0
- package/dist/org-delivery-gate.js +19 -1
- package/dist/parked-decide.d.ts +13 -9
- package/dist/parked-decide.js +7 -7
- package/dist/parked-revive-surface.js +1 -1
- package/dist/peer-inbound-face.d.ts +127 -0
- package/dist/peer-inbound-face.js +158 -0
- package/dist/permission-rule-list.d.ts +13 -9
- package/dist/permission-rule-list.js +7 -3
- package/dist/plugins/approval-ask-store-file.d.ts +14 -4
- package/dist/plugins/approval-ask-store-file.js +64 -11
- package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
- package/dist/plugins/approval-ask-store-memory.js +17 -14
- package/dist/plugins/approval-ask-store-sql.d.ts +70 -23
- package/dist/plugins/approval-ask-store-sql.js +79 -30
- package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
- package/dist/plugins/checkpoint-store-sql.js +9 -7
- package/dist/plugins/file-history-store-sql.d.ts +24 -18
- package/dist/plugins/file-history-store-sql.js +24 -19
- package/dist/plugins/file-run-store.d.ts +35 -1
- package/dist/plugins/file-run-store.js +108 -9
- package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
- package/dist/plugins/imported-layer-root-retirement.js +96 -0
- package/dist/plugins/local-checkpoint-store.d.ts +1 -1
- package/dist/plugins/local-checkpoint-store.js +2 -2
- package/dist/plugins/local-session-store.d.ts +42 -9
- package/dist/plugins/local-session-store.js +83 -33
- package/dist/plugins/local-task-attachment-store.d.ts +12 -2
- package/dist/plugins/local-task-attachment-store.js +67 -43
- package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
- package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
- package/dist/plugins/mailbox-store-sql.d.ts +12 -8
- package/dist/plugins/mailbox-store-sql.js +76 -7
- package/dist/plugins/memory-key-guards.d.ts +3 -3
- package/dist/plugins/memory-run-store.d.ts +6 -1
- package/dist/plugins/memory-run-store.js +11 -1
- package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
- package/dist/plugins/memory-sync-store-pg.js +21 -0
- package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
- package/dist/plugins/memory-sync-store-tidb.js +13 -1
- package/dist/plugins/one-time-migrations.d.ts +58 -2
- package/dist/plugins/one-time-migrations.js +161 -4
- package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
- package/dist/plugins/org-delivery-quota-store.js +2 -12
- package/dist/plugins/park-commit-watch.d.ts +1 -1
- package/dist/plugins/permission-rule-store-file.d.ts +10 -1
- package/dist/plugins/permission-rule-store-file.js +15 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
- package/dist/plugins/permission-rule-store-sql.js +35 -2
- package/dist/plugins/remote-env-device.d.ts +8 -11
- package/dist/plugins/remote-env-device.js +6 -3
- package/dist/plugins/remote-env-e2b.d.ts +23 -4
- package/dist/plugins/remote-env-e2b.js +111 -28
- package/dist/plugins/remote-env-k8s.d.ts +61 -22
- package/dist/plugins/remote-env-k8s.js +133 -33
- package/dist/plugins/remote-env-local-docker.d.ts +15 -3
- package/dist/plugins/remote-env-local-docker.js +40 -12
- package/dist/plugins/retention-store-sql.d.ts +2 -2
- package/dist/plugins/run-store-sql.d.ts +9 -1
- package/dist/plugins/run-store-sql.js +9 -1
- package/dist/plugins/sandbox-read-floor.d.ts +5 -7
- package/dist/plugins/sandbox-read-floor.js +2 -7
- package/dist/plugins/sql-errors.d.ts +10 -1
- package/dist/plugins/sql-errors.js +13 -0
- package/dist/plugins/store-backend.d.ts +33 -35
- package/dist/plugins/store-backend.js +10 -1
- package/dist/plugins/store-contracts.d.ts +50 -1
- package/dist/plugins/store-contracts.js +6 -0
- package/dist/plugins/task-attachment-store.d.ts +112 -14
- package/dist/plugins/task-attachment-store.js +207 -29
- package/dist/plugins/tool-result-store-sql.d.ts +9 -8
- package/dist/plugins/tool-result-store-sql.js +23 -2
- package/dist/plugins/wiring-section-codec.d.ts +32 -0
- package/dist/plugins/wiring-section-codec.js +55 -0
- package/dist/plugins/workspace-fill.d.ts +60 -0
- package/dist/plugins/workspace-fill.js +106 -0
- package/dist/project-bundle.d.ts +38 -0
- package/dist/project-bundle.js +609 -0
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +5 -1
- package/dist/rules-consent.d.ts +28 -5
- package/dist/rules-consent.js +40 -28
- package/dist/run-local.js +11 -13
- package/dist/runs.d.ts +12 -8
- package/dist/runs.js +6 -8
- package/dist/runtime-governance.d.ts +1 -1
- package/dist/sandbox-egress-policy.d.ts +4 -1
- package/dist/sandbox-egress-policy.js +3 -1
- package/dist/sandbox-git-plane.d.ts +31 -12
- package/dist/sandbox-git-plane.js +24 -4
- package/dist/security.d.ts +9 -0
- package/dist/security.js +8 -0
- package/dist/server-secret-env.d.ts +4 -0
- package/dist/session-error-wire.d.ts +36 -0
- package/dist/session-error-wire.js +13 -0
- package/dist/session-incarnation.d.ts +1 -1
- package/dist/session-notify-door.d.ts +106 -0
- package/dist/session-notify-door.js +93 -0
- package/dist/session-sync.d.ts +9 -14
- package/dist/session-sync.js +5 -5
- package/dist/session-workspace.d.ts +3 -13
- package/dist/session-workspace.js +0 -9
- package/dist/standing-answer-grant.d.ts +5 -0
- package/dist/standing-answer-grant.js +10 -0
- package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
- package/dist/stores-sql/session-policy-store-sql.js +3 -13
- package/dist/task-mcp.d.ts +1 -1
- package/dist/task-settings.d.ts +21 -10
- package/dist/task-settings.js +46 -13
- package/dist/terminal.d.ts +39 -19
- package/dist/terminal.js +65 -22
- package/dist/tool-approval.d.ts +56 -37
- package/dist/tool-approval.js +78 -71
- package/dist/trace/core-keyset-guard.d.ts +134 -14
- package/dist/trace/engine-notice-wire.d.ts +9 -3
- package/dist/trace/engine-notice-wire.js +7 -3
- package/dist/trace/exit-seat.d.ts +9 -2
- package/dist/trace/exit-seat.js +1 -0
- package/dist/trace/ledger-sink.d.ts +10 -6
- package/dist/trace/ledger-sink.js +2 -2
- package/dist/trace/model-segment.d.ts +55 -25
- package/dist/trace/model-segment.js +101 -33
- package/dist/trace/project.d.ts +25 -5
- package/dist/trace/project.js +22 -0
- package/dist/trace/redact.d.ts +61 -9
- package/dist/trace/redact.js +1490 -468
- package/dist/trace/self-minted-wire.d.ts +745 -24
- package/dist/trace/self-minted-wire.js +239 -9
- package/dist/trace/wire-projection-faces.d.ts +2 -2
- package/dist/trace/wire-projection-faces.js +2 -2
- package/dist/verifier-pause-card.d.ts +33 -5
- package/dist/verifier-pause-card.js +15 -10
- package/dist/web-admin-client/apply-effective.d.ts +12 -16
- package/dist/web-admin-client/apply-effective.js +109 -9
- package/dist/web-admin-client/effective-keys.js +2 -0
- package/dist/web-admin-client/types.d.ts +4 -0
- package/dist/workspace-space.d.ts +19 -3
- package/dist/workspace-space.js +8 -4
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
- package/package.json +3 -2
- package/wire-changes/server@7.142.0.json +639 -0
- package/wire-changes/server@7.143.0.json +1167 -0
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type { IncomingMessage, ServerResponse } from "node:http";
|
|
|
18
18
|
import type { HostDecision } from "../host-decision.js";
|
|
19
19
|
import type { EntryProblem } from "../entry-problems.js";
|
|
20
20
|
import type { SessionOwnerCell } from "../security.js";
|
|
21
|
+
import type { SessionNotifyDoor } from "../session-notify-door.js";
|
|
21
22
|
import type { TaskStream, TaskSpec, TaskResult, QuestionAnswer, Checkpoint, CheckpointToken, ResumeOutcome, RunInternals, Runner, EngineNotice } from "@sema-agent/core";
|
|
22
23
|
import type { FlatServiceDeps, RequestAuth } from "./server.js";
|
|
23
24
|
import type { TaskRequestBody, DecideBinding } from "./wire-types.js";
|
|
@@ -34,6 +35,7 @@ import type { RunIdentity } from "../identity-source.js";
|
|
|
34
35
|
import type { PeerLease } from "../peer-seat.js";
|
|
35
36
|
import type { RootedTaskSpec } from "./execution-context.js";
|
|
36
37
|
import type { GateCommitment, GateSettlementEvidence } from "../gate-settlement.js";
|
|
38
|
+
import type { ResumeRestatement } from "./resume-restatement.js";
|
|
37
39
|
/** 跨域可变状态 = lens1 §C2 实测的那 8 条「运行期登记簿」,一个对象。
|
|
38
40
|
* [ref]:`cancelledViaVerb`(按 taskId 键控的「这次是取消动词」旁表)删除 —— 中止成因改挂在每条腿自己的
|
|
39
41
|
* {@link RunAbort} 句柄上(`inflightRuns` 的值),同寿同删,不再有一张与腿不同寿的旁表。 */
|
|
@@ -123,6 +125,11 @@ export interface PreparedTaskSubmission {
|
|
|
123
125
|
/** [ref]:本次提交的执行 Runner —— 场景 hands=none 时是无手孪生(不挂 executionEnvFactory)。
|
|
124
126
|
* 两个消费域一律用它,不再直接摸 `deps.runner`(那正是「场景表态了、执行点没听」的缝)。 */
|
|
125
127
|
runner: Runner;
|
|
128
|
+
/**
|
|
129
|
+
* 这条腿的**部署事实**(执行车道 · run 技能名册 · 受理见证;`leg-facts.ts`):漏斗在复制之前按装配输出读出的那一份,此后作普通值走 ——
|
|
130
|
+
* 不在 spec 上(引擎的 TaskSpec 一读门拒任何非引擎成员)。缺席 = 嵌入方自带的装配器没铸(名册段 / 会话行记录座随之缺席;车道已在选路时响亮拒)。
|
|
131
|
+
*/
|
|
132
|
+
legFacts: import("../leg-facts.js").LegFacts | undefined;
|
|
126
133
|
/** authorizer 交出的身份与根(必填席,恒在;续跑家族的内部车道交它从 checkpoint 重建的那一只)。 */
|
|
127
134
|
auth: RequestAuth;
|
|
128
135
|
verify?: VerifyRoundsSpec;
|
|
@@ -257,9 +264,10 @@ export interface DriveResumeArgs {
|
|
|
257
264
|
*
|
|
258
265
|
* 🔴 写失败**响亮但不致命**(codex r1 两条 [high] 验真后定):core 的 `resumeStream` 是 **EAGER** 的 ——
|
|
259
266
|
* 写点这一拍 run 已经在执行,裸抛会把一条活腿判成 failed + 释放 claim 而执行还在继续。⇒ 一个臂、
|
|
260
|
-
* 四条腿同守:计数 `resume_ctx_not_persisted_total` + 一行 warn
|
|
261
|
-
*
|
|
262
|
-
*
|
|
267
|
+
* 四条腿同守:计数 `resume_ctx_not_persisted_total` + 一行 warn,本次续跑照常。写失败 ⇒ 耐久体保持**上一次**的值:
|
|
268
|
+
* 模式词那一格(`drivePlanReview` 只会放宽)落盘的恒不比本次续跑更宽;续跑请求的重述键(`resume-restatement.ts`)
|
|
269
|
+
* 可能收窄对端表,写失败时落盘的仍是上一份表 ⇒ 下一次**不带重述**的续跑按上一份重建 —— 这一形是收不紧的,所以
|
|
270
|
+
* 那一行 warn 如实点名它(响亮,不静默)。旧的 `plan_approve_mode_not_persisted`(plan 专用)随 [ref] 删除,
|
|
263
271
|
* 由上面这只家族级的取代。全文见 `resume-legs.ts` 的 `commitResumeCtx` 顶注。
|
|
264
272
|
*/
|
|
265
273
|
resumeCtx: {
|
|
@@ -274,6 +282,8 @@ export interface DriveResumeArgs {
|
|
|
274
282
|
/** [ref]:续跑腿的执行 Runner。resume 会用同一条 resolveSpec 重解析场景,故与首跑选同一只 —— 否则
|
|
275
283
|
* 一次续跑就把手带 band 重新 mount 回来(收窄只在首跑成立 = 没成立)。 */
|
|
276
284
|
runner: Runner;
|
|
285
|
+
/** 这条腿的部署事实(`leg-facts.ts`):续跑漏斗在复制之前读出的那一份(与 {@link PreparedTaskSubmission.legFacts} 同一律)。读者 = 本腿的 `wiring_manifest.skills` 段与会话行记录座。 */
|
|
286
|
+
legFacts: import("../leg-facts.js").LegFacts | undefined;
|
|
277
287
|
sessionId: string;
|
|
278
288
|
principal: string | undefined;
|
|
279
289
|
fleetScope: string;
|
|
@@ -418,7 +428,7 @@ export interface RouteLegs {
|
|
|
418
428
|
/** [ref] 件C:**腿身份**(不是能力表态)。`liveStream: true` 只由 `POST /v1/tasks/stream` 的装配点按
|
|
419
429
|
* URL 传 —— 那是全树唯一挂 HITL 活流投递面的腿,逐腿 `interactionPosture` 判据的第一条合取项。
|
|
420
430
|
* 缺席 ⇒ 无人腿(bg / A2A / resume),行为字节不变。 */
|
|
421
|
-
|
|
431
|
+
legRoute?: {
|
|
422
432
|
liveStream?: boolean;
|
|
423
433
|
}): Promise<PreparedTaskSubmission | null>;
|
|
424
434
|
/** 同步腿的终局记账(计费/配额/指标),tasks 域用。 */
|
|
@@ -429,7 +439,10 @@ export interface RouteLegs {
|
|
|
429
439
|
* 「漏报出处」变成编译错,不是一条下游把「没人答」渲染成「有人拒」的静默假出处。
|
|
430
440
|
* 🔴 [ref](core 7.6.0 S6-A):宿主报事实、core 铸词 —— server 不再传结算词(旧形传 core 的三词之一,
|
|
431
441
|
* 于是「哪个词配哪个 decision」这条相容规则要 server 自己记,而记错的方向恰是上面那句要防的)。 */
|
|
432
|
-
resumeCheckpoint(sessionId: string, decision: "approve" | "deny", reason: string | undefined, hostDecision: HostDecision, req
|
|
442
|
+
resumeCheckpoint(sessionId: string, decision: "approve" | "deny", reason: string | undefined, hostDecision: HostDecision, req: IncomingMessage | undefined, answer: QuestionAnswer | undefined,
|
|
443
|
+
/** 这次决定落在哪一个动作上:调用方点名它看到的那一对(`boundCallId` + `boundInputHash`);缺任一 ⇒ 400 `decide_requires_binding`
|
|
444
|
+
* (判在门型守卫之后,判官 `decidedActionOf`)。本函数从不拿当前停驻行补。 */
|
|
445
|
+
binding: DecideBinding,
|
|
433
446
|
/** [ref] codex R4(验真后修):remember 豁免的 grant 挂点。**工具名不再由路由自己读**(那是与本函数
|
|
434
447
|
* 载入行分歧的第三次 scope-less 读,三读分歧下会给他人工具落豁免=提权)——由本函数把**已属主校验的
|
|
435
448
|
* 载入 cp** 的可记工具名(`rememberableToolName`,非 tool_approval / AskUserQuestion / legacy null / [ref] 强制卡 ⇒ `null`
|
|
@@ -446,7 +459,9 @@ export interface RouteLegs {
|
|
|
446
459
|
decider?: {
|
|
447
460
|
principal?: string;
|
|
448
461
|
explicitOperator: boolean;
|
|
449
|
-
}
|
|
462
|
+
},
|
|
463
|
+
/** 续跑请求的重述键(`mcpServers` / `a2aPeers`),门读出并判过的那一份;缺席 = 冻结读法。语义属主 = `resume-restatement.ts` 顶注。 */
|
|
464
|
+
restatement?: ResumeRestatement): Promise<ResumeLegOutcome>;
|
|
450
465
|
/** 全 resume 家族的共同下半场(lease admission + 写 run log + fleet 发布)。 */
|
|
451
466
|
driveResumeIntoRunLog(args: DriveResumeArgs): Promise<ResumeLegOutcome>;
|
|
452
467
|
/** assistant 抢占腿的复位(gate-kind 守卫 + 行级属主复核 + 驱动)。 */
|
|
@@ -458,7 +473,9 @@ export interface RouteLegs {
|
|
|
458
473
|
decider?: {
|
|
459
474
|
principal?: string;
|
|
460
475
|
explicitOperator: boolean;
|
|
461
|
-
}
|
|
476
|
+
},
|
|
477
|
+
/** 续跑请求的重述键(同 {@link RouteLegs.resumeCheckpoint} 末参)。 */
|
|
478
|
+
restatement?: ResumeRestatement): Promise<ResumeLegOutcome>;
|
|
462
479
|
/** notify-wake 域:唤醒一条 parked checkpoint。 */
|
|
463
480
|
resumeWake(sessionId: string, message: string | undefined, caller: string | undefined, req: IncomingMessage | undefined,
|
|
464
481
|
/** [ref] 件 S-1:透传给 {@link DriveResumeArgs.acceptEarly}(200 受理语义)。HTTP `/wake` 腿传 `true`
|
|
@@ -466,7 +483,11 @@ export interface RouteLegs {
|
|
|
466
483
|
acceptEarly?: boolean,
|
|
467
484
|
/** 透传给 {@link DriveResumeArgs.workflowDecision}。HTTP `/wake` 腿**不设**此位 —— 它是 `/decide` 第三条车道投递腿的专用席,
|
|
468
485
|
* 而两条腿共用这一条驱动路径正是 `notify-wake.ts` 那条「wake 会烧模型,所以复用 resume 家族的同一条腿」的成文纪律。 */
|
|
469
|
-
workflowDecision?: WorkflowDecisionDelivery
|
|
486
|
+
workflowDecision?: WorkflowDecisionDelivery,
|
|
487
|
+
/** 付费方身份(`/decide` workflow 车道专用;HTTP `/wake` 腿不设)。语义属主 = `resume-legs.ts` 同名形参顶注。 */
|
|
488
|
+
ownerLeaseAdmittedFor?: string,
|
|
489
|
+
/** 续跑请求的重述键(同 {@link RouteLegs.resumeCheckpoint} 末参)。 */
|
|
490
|
+
restatement?: ResumeRestatement): Promise<ResumeLegOutcome>;
|
|
470
491
|
/**
|
|
471
492
|
* assistant plan_review 三态腿的**纯读段**([ref],7.97.0):选卡 → 属主轻读 → 载入 → 行级 scope → 门类 → 这条决断
|
|
472
493
|
* 唯一的一次 ctx 读。任何一步拒 ⇒ `{ok:false, answer}`(不改任何状态;端点在消费一次性 nonce **之前**把它答出去);
|
|
@@ -503,7 +524,11 @@ export interface RouteLegs {
|
|
|
503
524
|
acceptEarly?: boolean,
|
|
504
525
|
/** [ref]:批准之后这条会话继续用的 permission mode(闭集 `PLAN_APPROVE_PERMISSION_MODES_AFTER`;只在把计划带出 plan 的决定
|
|
505
526
|
* —— `approve` / `edit` —— 上有意义,缺席 ⇒ `"default"`)。语义与判据的唯一属主 = `resume-legs.ts` 的同名形参顶注。 */
|
|
506
|
-
permissionModeAfter?: PlanApprovePermissionModeAfter
|
|
527
|
+
permissionModeAfter?: PlanApprovePermissionModeAfter,
|
|
528
|
+
/** 续跑请求的重述键(同 {@link RouteLegs.resumeCheckpoint} 末参)。 */
|
|
529
|
+
restatement?: ResumeRestatement): Promise<ResumeLegOutcome>;
|
|
530
|
+
/** 会话通知门(`POST /v1/sessions/:id/notify` 与 Runner 之外的后台子代完成共用的那一只;活流登记簿是本服务的,门随服务建一次)。 */
|
|
531
|
+
sessionNotify: SessionNotifyDoor;
|
|
507
532
|
}
|
|
508
533
|
/**
|
|
509
534
|
* [ref] 件 S-2 —— resume 族**受理点的断连留痕**(裁定:维持 detach,补观察面)。
|
|
@@ -729,7 +754,7 @@ export declare const RUN_NOT_FOUND_MESSAGE = "run not found \u2014 the id belong
|
|
|
729
754
|
* 走一只常量 + `runOwnerOk(..., "session")` 的会话拼法,「改一处」就是改全域。
|
|
730
755
|
*/
|
|
731
756
|
export declare const SESSION_NOT_FOUND_MESSAGE = "session not found";
|
|
732
|
-
/** [ref] 前缀=域判别子既成契约:a\* 与 wa\* 形 id
|
|
757
|
+
/** [ref] 前缀=域判别子既成契约:a\* 与 wa\* 形 id 给任务句柄读腿 / journal 指路;其余给 {@link RUN_NOT_FOUND_MESSAGE}。 */
|
|
733
758
|
export declare function runNotFoundMessage(id: string): string;
|
|
734
759
|
export {};
|
|
735
760
|
//# sourceMappingURL=route-ctx.d.ts.map
|
package/dist/http/route-ctx.js
CHANGED
|
@@ -33,7 +33,7 @@ export const SESSION_NOT_FOUND_MESSAGE = "session not found";
|
|
|
33
33
|
export function runNotFoundMessage(id) {
|
|
34
34
|
const aStar = /^(?:a|wa)[0-9a-f]{4,}$/i.test(id);
|
|
35
35
|
return aStar
|
|
36
|
-
? `run not found — "${id}" looks like an agent handle (a*/wa* domain), which is never in the run store;
|
|
36
|
+
? `run not found — "${id}" looks like an agent handle (a*/wa* domain), which is never in the run store; read it through its run's task-handle surface (GET /v1/runs/:id/tasks/:handle/output, or the workflow journal / subagents surface), and use the run's durable taskId (X-Task-Id) for this endpoint`
|
|
37
37
|
: RUN_NOT_FOUND_MESSAGE;
|
|
38
38
|
}
|
|
39
39
|
//# sourceMappingURL=route-ctx.js.map
|
package/dist/http/route-table.js
CHANGED
|
@@ -32,6 +32,7 @@ import { handleRunMemoryCapture, RUN_MEMORY_CAPTURE_ROUTES } from "./routes/run-
|
|
|
32
32
|
import { handlePermissionMode, RUN_PERMISSION_MODE_ROUTES } from "./routes/run-permission-mode.js";
|
|
33
33
|
import { handleMcpReconnect, MCP_RECONNECT_ROUTES } from "./routes/mcp-reconnect.js";
|
|
34
34
|
import { handleBackground, BACKGROUND_ROUTES } from "./routes/background.js";
|
|
35
|
+
import { handleSessionMemorySync, SESSION_MEMORY_SYNC_ROUTES } from "./routes/session-memory-sync.js";
|
|
35
36
|
import { handleSideQuery, SIDE_QUERY_ROUTES } from "./routes/side-query.js";
|
|
36
37
|
import { handleTasks, TASKS_ROUTES } from "./routes/tasks.js";
|
|
37
38
|
import { handleLeader, LEADER_ROUTES } from "./routes/leader.js";
|
|
@@ -55,6 +56,7 @@ export const ROUTES = [
|
|
|
55
56
|
row({ handler: handlePermissionMode, decls: RUN_PERMISSION_MODE_ROUTES, position: "post-credential", label: "permission-mode" }),
|
|
56
57
|
row({ handler: handleMcpReconnect, decls: MCP_RECONNECT_ROUTES, position: "post-credential", label: "mcp-reconnect" }),
|
|
57
58
|
row({ handler: handleBackground, decls: BACKGROUND_ROUTES, position: "post-credential", label: "background" }),
|
|
59
|
+
row({ handler: handleSessionMemorySync, decls: SESSION_MEMORY_SYNC_ROUTES, position: "post-credential", label: "session-memory-sync" }),
|
|
58
60
|
row({ handler: handleLeader, decls: LEADER_ROUTES, position: "post-credential", label: "leader" }),
|
|
59
61
|
row({ handler: handleImages, decls: IMAGES_ROUTES, position: "post-credential", label: "images" }),
|
|
60
62
|
row({ handler: handleApprovalsAssistant, decls: APPROVALS_ASSISTANT_ROUTES, position: "post-credential", label: "approvals-assistant" }),
|
|
@@ -6,6 +6,7 @@ import { type GovernancePosture } from "../../runtime-governance.js";
|
|
|
6
6
|
import { type GateIntentView } from "../../approval.js";
|
|
7
7
|
import { type RouteCtx, type RouteDecl, type RouteMatch, type RouteIdsOf } from "../route-ctx.js";
|
|
8
8
|
import { type KeyClosureIssue } from "../../request-key-closure.js";
|
|
9
|
+
import { type PeerInboundPendingRow } from "../../peer-inbound-face.js";
|
|
9
10
|
/** 族的成员 = 路由表行 id(改名 / 删行 ⇒ 下表的键当场编译红)。 */
|
|
10
11
|
export type DecisionDoorId = Extract<ApprovalsAssistantRouteId, "approval-decide" | "assistant-task-plan-review">;
|
|
11
12
|
/** 一道门的静态描述 —— 只写**这道门自己**的东西;路径、路径段、别门的键一概不在这里写(从来源读)。 */
|
|
@@ -137,7 +138,14 @@ export declare function streamApprovals(req: IncomingMessage, res: ServerRespons
|
|
|
137
138
|
* 缺席,流的字节与 [ref] 之前逐字相同(能力缺席 = 零变化,不是空数组)。**位置在 `pollMs` 之前**
|
|
138
139
|
* 与 `governance` 同一条理由:`pollMs` 只有测试传,而本参数是每个生产调用点都必须表态的那一个。
|
|
139
140
|
*/
|
|
140
|
-
live: Pick<ToolApprovalCoordinator, "listLivePending"> | undefined,
|
|
141
|
+
live: Pick<ToolApprovalCoordinator, "listLivePending"> | undefined,
|
|
142
|
+
/**
|
|
143
|
+
* 🔴 [ref]:`pending` 车道的**第二个半场** —— 被扣的跨会话来信(`peer_inbound` 行),与 `GET /v1/approvals` 的 `pending[]`
|
|
144
|
+
* **同一只**闭包({@link peerInboundRowsFor},已绑调用方自己的信箱 scope)。与停驻行同一条 delta 车道、同一对事件名
|
|
145
|
+
* (`pending` / `resolved`):两族同一扇决断口,帧上的 `kind` 判别。`undefined` = 本腿没有这一半场(流字节只差每帧的 `kind`)。
|
|
146
|
+
* 位置在 `scope` 之前、与 `live` 并列:每个生产调用点都必须表态。
|
|
147
|
+
*/
|
|
148
|
+
peerInbound: (() => readonly PeerInboundPendingRow[]) | undefined, scope: string | undefined,
|
|
141
149
|
/** [ref]②:出身归因的部署侧一半 + 三张审批名单(问的属主)。缺席 ⇒ 归不出治理出身(键缺席,与 best-effort 同方向)、没有一行读作
|
|
142
150
|
* 部署名单的问(记不住,方向是多问)。**位置在 `pollMs` 之前**是刻意的:`pollMs` 只有测试传,而本参数是每个生产调用点都必须给的那一个。 */
|
|
143
151
|
governance?: PendingProjectionDeployment, pollMs?: number): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { screenedGate } from "../../trace/project.js";
|
|
2
2
|
import { skillCardFacts } from "../../approval-card.js";
|
|
3
|
-
import { hostDecisionFacts,
|
|
3
|
+
import { hostDecisionFacts, readDecider, retiredDecisionKeyFacts, DECIDED_BY_NOT_IN_PROOF } from "../../host-decision.js";
|
|
4
4
|
import { sameExecutionRecord } from "@sema-agent/core";
|
|
5
5
|
import { findParkedAgentForCheckpoint } from "../../parked-decide.js";
|
|
6
6
|
import { DECIDED_REPLAY_SCAN_CAP, approvalPayloadFingerprint } from "../../plugins/checkpoint-store-sql.js";
|
|
@@ -32,16 +32,29 @@ import { MAX_CHECKPOINT_ID_CHARS, durableRowIdentity } from "../../checkpoint-id
|
|
|
32
32
|
import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "../../request-key-closure.js";
|
|
33
33
|
import { fieldConflictFacts } from "../../field-conflict.js";
|
|
34
34
|
import { buildSettlementEvidence, decisionCommittedOf } from "../../gate-settlement.js";
|
|
35
|
+
import { seatText } from "../../trace/exit-seat.js";
|
|
36
|
+
import { approvalQueueKindOfDoorId } from "../../approval-queue-kind.js";
|
|
37
|
+
import { defaultPeerInboundFace, parsePeerInboundId, peerBoxScope } from "../../peer-inbound-face.js";
|
|
38
|
+
import { RESUME_RESTATABLE_KEYS, readResumeRestatement } from "../resume-restatement.js";
|
|
35
39
|
const DECIDE_BODY_DECLARED_KEYS = [
|
|
36
|
-
"answer", "boundCallId", "boundInputHash", "checkpointToken", "decidedBy", "decision", "feedback", "reason", "remember", "updatedInput",
|
|
40
|
+
"answer", "boundCallId", "boundInputHash", "checkpointToken", "decidedBy", "decidedVia", "decision", "feedback", "reason", "remember", "updatedInput",
|
|
41
|
+
...RESUME_RESTATABLE_KEYS,
|
|
37
42
|
];
|
|
38
43
|
const DECIDE_PROOF_COVERED_KEYS = ["answer", "boundCallId", "boundInputHash", "decision", "reason"];
|
|
39
|
-
const DECIDE_NOT_IN_PROOF_KEYS = ["checkpointToken", "decidedBy", "feedback", "remember", "updatedInput"];
|
|
44
|
+
const DECIDE_NOT_IN_PROOF_KEYS = ["checkpointToken", "decidedBy", "decidedVia", "feedback", "remember", "updatedInput", ...RESUME_RESTATABLE_KEYS];
|
|
45
|
+
function readDecisionDoorRestatement(res, body, directDoorActive) {
|
|
46
|
+
if (directDoorActive && RESUME_RESTATABLE_KEYS.some((key) => body[key] !== undefined)) {
|
|
47
|
+
sendError(res, 400, "restatement_not_in_proof", "mcpServers / a2aPeers (the resume request's restatement of its MCP / A2A peer tables) are not supported on a direct-door worker (not covered by the decision proof)");
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return { restatement: readResumeRestatement(body) };
|
|
51
|
+
}
|
|
40
52
|
const PLAN_REVIEW_BODY_DECLARED_KEYS = [
|
|
41
|
-
"boundCallId", "checkpointId", "decidedBy", "decision", "editedPlan", "permissionModeAfter", "reason",
|
|
53
|
+
"boundCallId", "checkpointId", "decidedBy", "decidedVia", "decision", "editedPlan", "permissionModeAfter", "reason",
|
|
54
|
+
...RESUME_RESTATABLE_KEYS,
|
|
42
55
|
];
|
|
43
56
|
const PLAN_REVIEW_PROOF_COVERED_KEYS = ["boundCallId", "checkpointId", "decision", "editedPlan", "permissionModeAfter", "reason"];
|
|
44
|
-
const PLAN_REVIEW_NOT_IN_PROOF_KEYS = ["decidedBy"];
|
|
57
|
+
const PLAN_REVIEW_NOT_IN_PROOF_KEYS = ["decidedBy", "decidedVia", ...RESUME_RESTATABLE_KEYS];
|
|
45
58
|
const PROOF_SLOT_OF = {
|
|
46
59
|
answer: "answer",
|
|
47
60
|
boundCallId: "boundCallId",
|
|
@@ -230,7 +243,7 @@ async function decideIdempotentReplay(cs, args) {
|
|
|
230
243
|
return undefined;
|
|
231
244
|
}
|
|
232
245
|
}
|
|
233
|
-
if (binding.boundInputHash
|
|
246
|
+
if (binding.boundInputHash === undefined || row.boundInputHash !== binding.boundInputHash)
|
|
234
247
|
return undefined;
|
|
235
248
|
if (row.decision !== decision)
|
|
236
249
|
return undefined;
|
|
@@ -300,15 +313,15 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
300
313
|
const operator = explicitOperatorOk(principal, deps.config);
|
|
301
314
|
const ownScope = principal ?? CHECKPOINT_PUBLIC_SCOPE;
|
|
302
315
|
if (match.decl.id === "approvals-stream") {
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
await streamApprovals(req, res, cs, deps.toolApproval, scope, deps.config);
|
|
316
|
+
const ownerParam = operator ? (new URLSearchParams(match.search).get("owner") ?? undefined) : undefined;
|
|
317
|
+
const scope = operator ? ownerParam : ownScope;
|
|
318
|
+
await streamApprovals(req, res, cs, deps.toolApproval, peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam), scope, deps.config);
|
|
307
319
|
return;
|
|
308
320
|
}
|
|
309
321
|
if (match.decl.id === "approvals-list") {
|
|
322
|
+
const ownerParam = operator ? (new URLSearchParams(match.search).get("owner") ?? undefined) : undefined;
|
|
310
323
|
const scope = operator
|
|
311
|
-
?
|
|
324
|
+
? ownerParam
|
|
312
325
|
: ownScope;
|
|
313
326
|
let durableRows;
|
|
314
327
|
try {
|
|
@@ -318,8 +331,9 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
318
331
|
sendApprovalsReadFailure(req, res, deps.logger, err);
|
|
319
332
|
return;
|
|
320
333
|
}
|
|
334
|
+
const pending = [...durableRows.map((r) => projectPendingForWire(r, deps.config)), ...peerInboundRowsFor(defaultPeerInboundFace, principal, ownerParam)()];
|
|
321
335
|
sendJson(res, 200, {
|
|
322
|
-
pending
|
|
336
|
+
pending,
|
|
323
337
|
serverNowMs: Date.now(),
|
|
324
338
|
...(deps.toolApproval ? { livePending: deps.toolApproval.listLivePending(scope) } : {}),
|
|
325
339
|
...(deps.approvalAskAudit ? { crashConverged: deps.approvalAskAudit.listCrashConverged(scope) } : {}),
|
|
@@ -528,6 +542,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
528
542
|
sendPrincipalRefusal(res, req, deps.config);
|
|
529
543
|
return;
|
|
530
544
|
}
|
|
545
|
+
const restatement = readResumeRestatement(await readJson(req));
|
|
531
546
|
const taskId = match.params.taskId;
|
|
532
547
|
const run = await runStore.getRun(taskId);
|
|
533
548
|
if (!run) {
|
|
@@ -539,7 +554,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
539
554
|
sendError(res, 404, "not_found.run", "task not found");
|
|
540
555
|
return;
|
|
541
556
|
}
|
|
542
|
-
const out = await resumePreempted(run.sessionId, req, true, { principal, explicitOperator });
|
|
557
|
+
const out = await resumePreempted(run.sessionId, req, true, { principal, explicitOperator }, restatement);
|
|
543
558
|
sendResumeOutcome(res, out, deps.logger);
|
|
544
559
|
return;
|
|
545
560
|
}
|
|
@@ -574,13 +589,13 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
574
589
|
sendError(res, 400, "request.field_conflict", "decision 'edit' requires a non-empty editedPlan (the operator's revised plan)", fieldConflictFacts("plan_edit", ["decision", "editedPlan"]));
|
|
575
590
|
return;
|
|
576
591
|
}
|
|
577
|
-
const decidedByReading =
|
|
592
|
+
const decidedByReading = readDecider(body, deps.config.directDoorActive);
|
|
578
593
|
switch (decidedByReading.kind) {
|
|
579
594
|
case "not_in_proof":
|
|
580
595
|
sendError(res, 400, "decided_by_not_in_proof", DECIDED_BY_NOT_IN_PROOF);
|
|
581
596
|
return;
|
|
582
597
|
case "outside_closed_set":
|
|
583
|
-
sendError(res, 400, "request.field_invalid",
|
|
598
|
+
sendError(res, 400, "request.field_invalid", decidedByReading.message);
|
|
584
599
|
return;
|
|
585
600
|
case "accepted":
|
|
586
601
|
break;
|
|
@@ -589,6 +604,9 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
589
604
|
throw new Error(`unhandled decidedBy reading: ${JSON.stringify(unreachable)}`);
|
|
590
605
|
}
|
|
591
606
|
}
|
|
607
|
+
const restated = readDecisionDoorRestatement(res, body, deps.config.directDoorActive);
|
|
608
|
+
if (restated === null)
|
|
609
|
+
return;
|
|
592
610
|
if (decision === "edit" && !isWellFormedUnicode(body.editedPlan)) {
|
|
593
611
|
sendError(res, 400, "request.field_invalid", "editedPlan must be well-formed Unicode text (a lone surrogate cannot be signed unambiguously)");
|
|
594
612
|
return;
|
|
@@ -704,7 +722,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
704
722
|
return;
|
|
705
723
|
}
|
|
706
724
|
}
|
|
707
|
-
const out = await drivePlanReview(plan.prepared, decision, decision === "edit" ? body.editedPlan : undefined, typeof body.reason === "string" ? body.reason : undefined, hostDecisionFacts(decidedByReading.
|
|
725
|
+
const out = await drivePlanReview(plan.prepared, decision, decision === "edit" ? body.editedPlan : undefined, typeof body.reason === "string" ? body.reason : undefined, hostDecisionFacts(decidedByReading.decider, deciderPrincipal), req, true, isPlanApprovePermissionModeAfter(body.permissionModeAfter) ? body.permissionModeAfter : undefined, restated.restatement);
|
|
708
726
|
sendResumeOutcome(res, out, deps.logger);
|
|
709
727
|
return;
|
|
710
728
|
}
|
|
@@ -716,10 +734,22 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
716
734
|
sendError(res, 400, "request.path_malformed", "malformed approval id (invalid percent-encoding)");
|
|
717
735
|
return;
|
|
718
736
|
}
|
|
737
|
+
const doorKind = approvalQueueKindOfDoorId(sessionId);
|
|
738
|
+
switch (doorKind) {
|
|
739
|
+
case "peer_inbound":
|
|
740
|
+
await decidePeerInbound(req, res, sessionId, { readJson, config: deps.config, logger: deps.logger, principal, face: defaultPeerInboundFace });
|
|
741
|
+
return;
|
|
742
|
+
case "checkpoint":
|
|
743
|
+
break;
|
|
744
|
+
default: {
|
|
745
|
+
const unreachable = doorKind;
|
|
746
|
+
throw new Error(`approval decide: door id of an unknown kind ${String(unreachable)}`);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
719
749
|
const body = (await readJson(req));
|
|
720
750
|
const decision = body?.decision === "approve" ? "approve" : body?.decision === "deny" ? "deny" : undefined;
|
|
721
751
|
if (!decision) {
|
|
722
|
-
sendError(res, 400, "request.body_shape", "body must be { decision: 'approve' | 'deny', reason?, answer?, checkpointToken?,
|
|
752
|
+
sendError(res, 400, "request.body_shape", "body must be { decision: 'approve' | 'deny', boundCallId, boundInputHash, reason?, answer?, checkpointToken?, updatedInput?, remember?, decidedBy?, decidedVia?, feedback? }", retiredDecisionKeyFacts(body)?.extra);
|
|
723
753
|
return;
|
|
724
754
|
}
|
|
725
755
|
{
|
|
@@ -756,10 +786,6 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
756
786
|
sendError(res, 501, "capability.store_required", "approval exemptions need a store backend (DB_BACKEND=mysql|pg|local)");
|
|
757
787
|
return;
|
|
758
788
|
}
|
|
759
|
-
if (!body.checkpointToken && !body.boundCallId) {
|
|
760
|
-
sendError(res, 400, "remember_requires_binding", "remember requires the decision binding (echo checkpointToken and/or boundCallId from the pending approval)");
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
789
|
remember = true;
|
|
764
790
|
}
|
|
765
791
|
let answer;
|
|
@@ -799,13 +825,13 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
799
825
|
sendError(res, 400, "feedback_not_in_proof", "feedback is not supported on a direct-door worker (not covered by the decision proof)");
|
|
800
826
|
return;
|
|
801
827
|
}
|
|
802
|
-
const decidedByReading =
|
|
828
|
+
const decidedByReading = readDecider(body, deps.config.directDoorActive);
|
|
803
829
|
switch (decidedByReading.kind) {
|
|
804
830
|
case "not_in_proof":
|
|
805
831
|
sendError(res, 400, "decided_by_not_in_proof", DECIDED_BY_NOT_IN_PROOF);
|
|
806
832
|
return;
|
|
807
833
|
case "outside_closed_set":
|
|
808
|
-
sendError(res, 400, "request.field_invalid",
|
|
834
|
+
sendError(res, 400, "request.field_invalid", decidedByReading.message);
|
|
809
835
|
return;
|
|
810
836
|
case "accepted":
|
|
811
837
|
break;
|
|
@@ -814,12 +840,15 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
814
840
|
throw new Error(`unhandled decidedBy reading: ${JSON.stringify(unreachable)}`);
|
|
815
841
|
}
|
|
816
842
|
}
|
|
817
|
-
const
|
|
843
|
+
const decider = decidedByReading.decider;
|
|
844
|
+
const restated = readDecisionDoorRestatement(res, body, deps.config.directDoorActive);
|
|
845
|
+
if (restated === null)
|
|
846
|
+
return;
|
|
818
847
|
if (body.feedback !== undefined && decision !== "approve") {
|
|
819
848
|
sendError(res, 400, "request.field_conflict", "feedback is only valid with decision 'approve' — the words of a denial are its reason", fieldConflictFacts("approval_feedback", ["feedback", "decision"]));
|
|
820
849
|
return;
|
|
821
850
|
}
|
|
822
|
-
if (body.feedback !== undefined && decidedBy !== "person") {
|
|
851
|
+
if (body.feedback !== undefined && decider.decidedBy !== "person") {
|
|
823
852
|
sendError(res, 400, "request.field_conflict", "feedback is only valid on a person's decision — the agent reads it as the user's own words, which a policy decision cannot carry", fieldConflictFacts("approval_feedback", ["feedback", "decidedBy"]));
|
|
824
853
|
return;
|
|
825
854
|
}
|
|
@@ -871,7 +900,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
871
900
|
pendingGrant = { sessionId: grant.overrideSessionId ?? sessionId, toolName: canonical };
|
|
872
901
|
}
|
|
873
902
|
: undefined;
|
|
874
|
-
const out = await resumeCheckpoint(sessionId, decision, body.reason ?? undefined, hostDecisionFacts(
|
|
903
|
+
const out = await resumeCheckpoint(sessionId, decision, body.reason ?? undefined, hostDecisionFacts(decider, deciderPrincipal), req, answer, binding, grantOnCommit, true, { principal: deciderPrincipal, explicitOperator }, restated.restatement);
|
|
875
904
|
if (pendingGrant !== undefined && decisionCommittedOf(out) && deps.approvalExemptionStore) {
|
|
876
905
|
try {
|
|
877
906
|
await deps.approvalExemptionStore.grant(pendingGrant.sessionId, pendingGrant.toolName, deciderPrincipal ?? null);
|
|
@@ -907,6 +936,60 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
907
936
|
}
|
|
908
937
|
return assertNever(match.decl.id);
|
|
909
938
|
}
|
|
939
|
+
async function decidePeerInbound(req, res, id, env) {
|
|
940
|
+
const body = (await env.readJson(req));
|
|
941
|
+
const decision = body?.decision === "approve" ? "approve" : body?.decision === "deny" ? "deny" : undefined;
|
|
942
|
+
if (!decision) {
|
|
943
|
+
sendError(res, 400, "request.body_shape", "body must be { decision: 'approve' | 'deny' }", retiredDecisionKeyFacts(body)?.extra);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
{
|
|
947
|
+
const keyIssue = decisionBodyKeyIssue("approval-decide", body);
|
|
948
|
+
if (keyIssue) {
|
|
949
|
+
const retired = retiredDecisionKeyFacts(body);
|
|
950
|
+
sendError(res, 400, "request.body_shape", retired?.error ?? keyIssue.message, { unknownKeys: keyIssue.unknownKeys, unsupportedKeys: keyIssue.unsupportedKeys, ...retired?.extra });
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
const notForThisKind = Object.keys(body).filter((k) => k !== "decision").sort();
|
|
955
|
+
if (notForThisKind.length > 0) {
|
|
956
|
+
sendError(res, 400, "request.body_shape", `body: key(s) ${notForThisKind.join(", ")} do not apply to a held cross-session message (kind peer_inbound) — refused rather than dropped, because a dropped key reads as "in effect" to the caller. This kind takes { decision: 'approve' | 'deny' } only`, { unknownKeys: [], unsupportedKeys: notForThisKind.slice(0, MAX_KEY_ECHO) });
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
let decider = env.principal;
|
|
960
|
+
if (env.config.directDoorActive) {
|
|
961
|
+
const hdr = (n) => {
|
|
962
|
+
const h = req.headers[n];
|
|
963
|
+
return Array.isArray(h) ? h[0] : h;
|
|
964
|
+
};
|
|
965
|
+
const proof = verifyDirectDoorProof({ jwt: hdr(PRINCIPAL_TOKEN_HEADER), mac: hdr(APPROVAL_MAC_HEADER), kid: hdr(APPROVAL_MAC_KID_HEADER) }, buildApprovalEnvelope(id, DECIDE_PROOF_COVERED_KEYS, { answer: undefined, boundCallId: undefined, boundInputHash: undefined, decision, reason: null }), env.config);
|
|
966
|
+
if (!proof.ok) {
|
|
967
|
+
sendError(res, proof.status, proof.errorCode, proof.error);
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
decider = proof.principal;
|
|
971
|
+
}
|
|
972
|
+
const coord = parsePeerInboundId(id);
|
|
973
|
+
const outcome = coord === undefined ? { kind: "not_found" } : await env.face.decide(peerBoxScope(decider), coord, decision);
|
|
974
|
+
switch (outcome.kind) {
|
|
975
|
+
case "not_found":
|
|
976
|
+
sendError(res, 404, "not_found.approval", "approval not found");
|
|
977
|
+
return;
|
|
978
|
+
case "unrecorded":
|
|
979
|
+
env.logger?.warn("peer_inbound_decision_unrecorded", { id, decision, err: seatText("operator-log-text", String(outcome.error)) });
|
|
980
|
+
sendError(res, 503, "state.peer_decision_unrecorded", "the decision on this held cross-session message could not be recorded (the mailbox store refused the move) — nothing was decided; retry the same request");
|
|
981
|
+
return;
|
|
982
|
+
case "settled": {
|
|
983
|
+
const reply = { kind: "peer_inbound", id, outcome: outcome.outcome };
|
|
984
|
+
sendJson(res, 200, reply);
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
default: {
|
|
988
|
+
const unreachable = outcome;
|
|
989
|
+
throw new Error(`peer inbound decide: unhandled result ${JSON.stringify(unreachable)}`);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
910
993
|
const APPROVALS_STREAM_POLL_MS = 3000;
|
|
911
994
|
function approvalsReadFailureOf(err) {
|
|
912
995
|
const fault = storeFaultClassOf(err);
|
|
@@ -946,6 +1029,12 @@ function redactPendingDisclosures(row) {
|
|
|
946
1029
|
const next = redactRiskDescriptor(rd);
|
|
947
1030
|
return next === rd ? row : { ...row, riskDescriptor: next };
|
|
948
1031
|
}
|
|
1032
|
+
function peerInboundRowsFor(face, principal, operatorOwnerParam) {
|
|
1033
|
+
if (operatorOwnerParam !== undefined && operatorOwnerParam !== principal)
|
|
1034
|
+
return () => [];
|
|
1035
|
+
const scope = peerBoxScope(principal);
|
|
1036
|
+
return () => face.list(scope);
|
|
1037
|
+
}
|
|
949
1038
|
function projectPendingForWire(row, deployment) {
|
|
950
1039
|
const governance = deployment;
|
|
951
1040
|
const redacted = redactPendingDisclosures(row);
|
|
@@ -959,7 +1048,7 @@ function projectPendingForWire(row, deployment) {
|
|
|
959
1048
|
classes: row.classes,
|
|
960
1049
|
deploymentListQuestion,
|
|
961
1050
|
});
|
|
962
|
-
return { ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}) };
|
|
1051
|
+
return { kind: "checkpoint", ...redacted, ...(governanceForced ? { governanceForced } : {}), ...(rememberRefused ? { rememberRefused: true } : {}) };
|
|
963
1052
|
}
|
|
964
1053
|
function createSseDeltaLane(res, spec) {
|
|
965
1054
|
let prev = new Map();
|
|
@@ -978,8 +1067,19 @@ function createSseDeltaLane(res, spec) {
|
|
|
978
1067
|
},
|
|
979
1068
|
};
|
|
980
1069
|
}
|
|
981
|
-
export async function streamApprovals(req, res, cs, live, scope, governance, pollMs = APPROVALS_STREAM_POLL_MS) {
|
|
982
|
-
const keyOf = (p) =>
|
|
1070
|
+
export async function streamApprovals(req, res, cs, live, peerInbound, scope, governance, pollMs = APPROVALS_STREAM_POLL_MS) {
|
|
1071
|
+
const keyOf = (p) => {
|
|
1072
|
+
switch (p.kind) {
|
|
1073
|
+
case "checkpoint":
|
|
1074
|
+
return JSON.stringify([p.kind, p.sessionId, p.toolCallId ?? null]);
|
|
1075
|
+
case "peer_inbound":
|
|
1076
|
+
return JSON.stringify([p.kind, p.id]);
|
|
1077
|
+
default: {
|
|
1078
|
+
const unreachable = p;
|
|
1079
|
+
throw new Error(`approvals stream: pending row of an unknown kind ${JSON.stringify(unreachable)}`);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
983
1083
|
sseHeaders(res);
|
|
984
1084
|
res.write(`event: meta\ndata: ${JSON.stringify({ type: "meta", version: 1, mode: "approvals-delta", pollMs: APPROVALS_STREAM_POLL_MS })}\n\n`);
|
|
985
1085
|
let closed = false;
|
|
@@ -992,7 +1092,18 @@ export async function streamApprovals(req, res, cs, live, scope, governance, pol
|
|
|
992
1092
|
keyOf,
|
|
993
1093
|
bytesOf: (p) => JSON.stringify({ type: "pending", ...p }),
|
|
994
1094
|
upsertData: (p, nowMs) => ({ type: "pending", ...p, serverNowMs: nowMs }),
|
|
995
|
-
exitData: (p, nowMs) =>
|
|
1095
|
+
exitData: (p, nowMs) => {
|
|
1096
|
+
switch (p.kind) {
|
|
1097
|
+
case "checkpoint":
|
|
1098
|
+
return { type: "resolved", kind: p.kind, sessionId: p.sessionId, toolCallId: p.toolCallId, serverNowMs: nowMs };
|
|
1099
|
+
case "peer_inbound":
|
|
1100
|
+
return { type: "resolved", kind: p.kind, id: p.id, serverNowMs: nowMs };
|
|
1101
|
+
default: {
|
|
1102
|
+
const unreachable = p;
|
|
1103
|
+
throw new Error(`approvals stream: pending row of an unknown kind ${JSON.stringify(unreachable)}`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
996
1107
|
});
|
|
997
1108
|
const liveLane = createSseDeltaLane(res, {
|
|
998
1109
|
upsertEvent: "live_pending",
|
|
@@ -1006,7 +1117,7 @@ export async function streamApprovals(req, res, cs, live, scope, governance, pol
|
|
|
1006
1117
|
while (!closed) {
|
|
1007
1118
|
let snapshot;
|
|
1008
1119
|
try {
|
|
1009
|
-
const pending = (await cs.listPending(scope)).map((r) => projectPendingForWire(r, governance));
|
|
1120
|
+
const pending = [...(await cs.listPending(scope)).map((r) => projectPendingForWire(r, governance)), ...(peerInbound?.() ?? [])];
|
|
1010
1121
|
snapshot = { pending, livePending: live ? live.listLivePending(scope) : [] };
|
|
1011
1122
|
}
|
|
1012
1123
|
catch (err) {
|
|
@@ -4,6 +4,7 @@ import { HttpError } from "../../security.js";
|
|
|
4
4
|
import { sendJson, sendError } from "../send.js";
|
|
5
5
|
import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
|
|
6
6
|
import { assertNever } from "../route-ctx.js";
|
|
7
|
+
import { attachmentKindOf, readProjectBundle } from "../../project-bundle.js";
|
|
7
8
|
export async function handleAttachments(req, res, match, ctx) {
|
|
8
9
|
const { deps } = ctx;
|
|
9
10
|
const { rateLimited, quotaExceeded, safeDecode, readRawBody } = ctx.helpers;
|
|
@@ -55,6 +56,23 @@ export async function handleAttachments(req, res, match, ctx) {
|
|
|
55
56
|
}
|
|
56
57
|
throw err;
|
|
57
58
|
}
|
|
59
|
+
const kind = attachmentKindOf(mime);
|
|
60
|
+
switch (kind) {
|
|
61
|
+
case "project_bundle": {
|
|
62
|
+
const reading = await readProjectBundle(body);
|
|
63
|
+
if (!reading.ok) {
|
|
64
|
+
sendError(res, 400, "attachment.bundle_invalid", `not a valid project bundle (${reading.reason}): ${reading.detail}`, { reason: reading.reason, ...(reading.path !== undefined ? { path: reading.path } : {}) });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "attachment":
|
|
70
|
+
break;
|
|
71
|
+
default: {
|
|
72
|
+
const unreachable = kind;
|
|
73
|
+
throw new Error(`unhandled attachment kind: ${String(unreachable)}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
58
76
|
const rec = {
|
|
59
77
|
id: uuidv7(),
|
|
60
78
|
owner,
|
|
@@ -16,6 +16,8 @@ import { buildEmptyMcpProbeFace, buildMcpInjectionRefusal, buildMcpProbeCacheKey
|
|
|
16
16
|
import { mcpForScenario } from "../../web-admin-client/facade.js";
|
|
17
17
|
import { sendScenarioNotFound } from "../scenario-read-face.js";
|
|
18
18
|
import { sendJson, sendError } from "../send.js";
|
|
19
|
+
import { codeFieldOf } from "../../terminal.js";
|
|
20
|
+
import { engineRefusalAnswerOf } from "../engine-refusal.js";
|
|
19
21
|
import { assertNever } from "../route-ctx.js";
|
|
20
22
|
import { fileHistoryCaptureOf } from "../../file-history-capture.js";
|
|
21
23
|
import { projectMemoryEngineCapability } from "../../memory-posture.js";
|
|
@@ -124,6 +126,9 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
124
126
|
memoryOrigin: facts.memoryOrigin(deps).available,
|
|
125
127
|
memoryConsolidationDriver: facts.memoryConsolidation(deps).available,
|
|
126
128
|
memoryOptOutGrant: facts.memoryOptOutGrant(deps).available,
|
|
129
|
+
sessionMemorySync: facts.sessionMemorySync(deps).available,
|
|
130
|
+
memorySyncServe: ((f) => (f.available ? { identity: f.identity } : false))(facts.memorySyncServe(deps)),
|
|
131
|
+
memoryExport: facts.memoryExport(deps).available,
|
|
127
132
|
outcomeLedger: facts.outcomeLedger(deps).available,
|
|
128
133
|
permissionRules: rulesLaneForCaller,
|
|
129
134
|
permissionRulesWrite: rulesLaneForCaller,
|
|
@@ -142,8 +147,8 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
142
147
|
scratchpadDir: true,
|
|
143
148
|
subagentSteer: facts.subagentSteer(deps).available,
|
|
144
149
|
subagentResume: facts.subagentSteer(deps).available,
|
|
145
|
-
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: DEPLOYMENT_SHAPE_PREDICATES.callerCwd.holds(deps.config), hooks: taskHooksHonored(deps.config), pluginHooks: taskHooksHonored(deps.config), managedHookGroups: taskHooksHonored(deps.config), tightenOnlySeats: true, approvalFrameFamilies: true },
|
|
146
|
-
settings: { permissions: { allowGrant: true, allowLayer: true, layers: true, callerTrust: callerTrust(req, deps.config).trust } },
|
|
150
|
+
taskSettings: { permissions: true, permissionMode: true, model: true, outputStyle: true, env: DEPLOYMENT_SHAPE_PREDICATES.callerCwd.holds(deps.config), hooks: taskHooksHonored(deps.config), pluginHooks: taskHooksHonored(deps.config), managedHookGroups: taskHooksHonored(deps.config), httpHookAllowlists: taskHooksHonored(deps.config), tightenOnlySeats: true, approvalFrameFamilies: true },
|
|
151
|
+
settings: { permissions: { allowGrant: true, allowLayer: true, layers: true, layeredAllow: true, callerTrust: callerTrust(req, deps.config).trust } },
|
|
147
152
|
permissionModeAuto: {
|
|
148
153
|
accepted: true,
|
|
149
154
|
classifierSeat: deps.autoModeFace?.seatMounted === true,
|
|
@@ -190,6 +195,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
190
195
|
approvalFrameFamilies: [...facts.approvalFrameFamilies(streamApprovalFact)],
|
|
191
196
|
approvalDecisionNote: true,
|
|
192
197
|
approvalDecidedBy: true,
|
|
198
|
+
approvalDecidedVia: true,
|
|
193
199
|
planReviewCheckpointId: true,
|
|
194
200
|
permissionModes: [...PERMISSION_MODES],
|
|
195
201
|
sessionReadRedaction: true,
|
|
@@ -235,7 +241,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
235
241
|
configCatalog: facts.configCatalog(deps).available,
|
|
236
242
|
sealedKey: projectSealedKeyCapability(deps.sealedKeyPublic),
|
|
237
243
|
workspaceSync: ((space, cache) => space.available || cache.available ? { ...(space.available ? space.policy : {}), ...(cache.available ? { cache: cache.cache } : {}) } : undefined)(facts.workspaceSpace(deps.config), facts.workspaceCache(deps.config)),
|
|
238
|
-
credentialPlane: { sandboxGit: deps.sandboxGitPlane ?? judgeSandboxGitPlane(sandboxGitPlanePremisesOf(deps.config, {})) },
|
|
244
|
+
credentialPlane: { sandboxGit: deps.sandboxGitPlane?.() ?? judgeSandboxGitPlane(sandboxGitPlanePremisesOf(deps.config, {})) },
|
|
239
245
|
}));
|
|
240
246
|
return;
|
|
241
247
|
}
|
|
@@ -304,7 +310,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
304
310
|
modelPlane: buildModelPlaneFace(deps.config, {
|
|
305
311
|
envTableIsPlaceholder: isRosterGateArmed(deps.config.webAdmin),
|
|
306
312
|
candidateRejection,
|
|
307
|
-
|
|
313
|
+
latestPlaneRefusal: deps.latestPlaneRefusalState?.(),
|
|
308
314
|
}),
|
|
309
315
|
aliases: selectableAliasNames(deps.config.models, atCatalog, deps.config.modelRefusals).map((name) => ({ name, ...mentionBit(name) })),
|
|
310
316
|
});
|
|
@@ -371,6 +377,12 @@ async function answerMcpProbe(res, ctx, answer, outcome, declaredBy) {
|
|
|
371
377
|
sendMcpProbeRefusal(res, ctx, buildMcpProbeDeclarationRefusal(), "failed");
|
|
372
378
|
return;
|
|
373
379
|
}
|
|
380
|
+
const engineRefusal = engineRefusalAnswerOf(err);
|
|
381
|
+
if (engineRefusal !== undefined) {
|
|
382
|
+
deps.logger?.warn("mcp_status_face_declaration_refused", { ...codeFieldOf(err), error: engineRefusal.refusal.message });
|
|
383
|
+
sendError(res, engineRefusal.status, engineRefusal.refusal.errorCode, engineRefusal.refusal.message);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
374
386
|
throw err;
|
|
375
387
|
}
|
|
376
388
|
}
|