@sema-agent/server 7.135.0 → 7.137.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 +237 -2
- package/USAGE.md +146 -41
- package/deploy/sema-up/README.md +15 -1
- package/deploy/sema-up/chart/templates/NOTES.txt +1 -1
- package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +7 -1
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +7 -1
- package/dist/approval-card.d.ts +60 -4
- package/dist/approval-card.js +18 -1
- package/dist/approval-content-kind.d.ts +7 -0
- package/dist/approval-content-kind.js +8 -1
- package/dist/approval-gate-currency.d.ts +48 -6
- package/dist/approval-gate-currency.js +16 -9
- package/dist/approval-hmac.d.ts +13 -1
- package/dist/approval-hmac.js +1 -1
- package/dist/approval.js +3 -3
- package/dist/auth-bridge.d.ts +34 -5
- package/dist/auth-bridge.js +22 -5
- package/dist/boot/budget-tracing.d.ts +1 -2
- package/dist/boot/budget-tracing.js +2 -2
- package/dist/boot/execution-env.d.ts +24 -1
- package/dist/boot/execution-env.js +69 -6
- package/dist/boot/org-memory.js +1 -1
- package/dist/boot/reapers.d.ts +3 -0
- package/dist/boot/reapers.js +15 -1
- package/dist/boot/resolve-spec.d.ts +4 -0
- package/dist/boot/resolve-spec.js +24 -22
- package/dist/boot/runner-deps.js +4 -2
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.d.ts +4 -2
- package/dist/boot/stage-05-execution-env.js +11 -2
- package/dist/boot/stage-06-runners.d.ts +4 -2
- package/dist/boot/stage-06-runners.js +5 -1
- package/dist/boot/stage-07-capability-layer.d.ts +5 -2
- package/dist/boot/stage-07-capability-layer.js +1 -1
- package/dist/boot/stage-08-reapers.d.ts +5 -2
- package/dist/boot/stage-08-reapers.js +2 -1
- package/dist/boot/stage-09-leader.d.ts +5 -2
- package/dist/boot/stage-10-http-server.d.ts +5 -2
- package/dist/boot/stage-10-http-server.js +3 -2
- package/dist/boot/stores.d.ts +1 -1
- package/dist/boot/stores.js +2 -3
- package/dist/boot/web-admin.js +10 -17
- package/dist/brain.js +12 -3
- package/dist/broker/broker.d.ts +89 -0
- package/dist/broker/broker.js +287 -0
- package/dist/broker/constants.d.ts +39 -0
- package/dist/broker/constants.js +15 -0
- package/dist/broker/http1.d.ts +111 -0
- package/dist/broker/http1.js +256 -0
- package/dist/broker/lease-book.d.ts +87 -0
- package/dist/broker/lease-book.js +142 -0
- package/dist/broker/lease-file.d.ts +60 -0
- package/dist/broker/lease-file.js +127 -0
- package/dist/broker/main.d.ts +2 -0
- package/dist/broker/main.js +20 -0
- package/dist/budget.d.ts +3 -3
- package/dist/budget.js +4 -0
- package/dist/capabilities/composite-members.d.ts +2 -1
- package/dist/capabilities/composite-members.js +2 -1
- package/dist/capabilities/engine-capabilities.js +1 -0
- package/dist/capabilities/mcp-probe.d.ts +5 -4
- package/dist/capabilities/scenarios.d.ts +23 -8
- package/dist/capabilities/scenarios.js +13 -8
- package/dist/capabilities/skills.d.ts +28 -5
- package/dist/capabilities/skills.js +8 -0
- package/dist/capability-facts.d.ts +3 -7
- package/dist/capability-facts.js +3 -5
- package/dist/center-credential-renewer.js +5 -1
- package/dist/center-credential.d.ts +18 -4
- package/dist/center-credential.js +18 -3
- package/dist/checkpoint-ctx-seal-m28.d.ts +3 -10
- package/dist/checkpoint-ctx-seal-m28.js +1 -1
- package/dist/checkpoint-identity.d.ts +3 -3
- package/dist/config-catalog.js +21 -16
- package/dist/config-d-migration.js +1 -1
- package/dist/config-invariants.d.ts +1 -1
- package/dist/config-invariants.js +8 -0
- package/dist/config-lkg.d.ts +0 -38
- package/dist/config-lkg.js +1 -83
- package/dist/config-types.d.ts +21 -4
- package/dist/config.d.ts +1 -1
- package/dist/config.js +60 -16
- package/dist/deployment-governance.d.ts +2 -2
- package/dist/deployment-governance.js +7 -9
- package/dist/deployment-shape.d.ts +1 -1
- package/dist/device-ws-protocol.d.ts +1 -0
- package/dist/device-ws-protocol.js +2 -1
- package/dist/elicitation.d.ts +8 -7
- package/dist/elicitation.js +9 -8
- package/dist/file-history-disclosure.d.ts +8 -4
- package/dist/file-history-disclosure.js +24 -6
- package/dist/fleet-client.d.ts +32 -1
- package/dist/fleet-client.js +24 -4
- package/dist/fleet-lease.d.ts +7 -3
- package/dist/fleet-lease.js +31 -23
- package/dist/hooks/classic-stations.d.ts +11 -54
- package/dist/hooks/hook-llm.js +14 -8
- package/dist/hooks/hook-runner.d.ts +4 -4
- package/dist/hooks/hook-runner.js +2 -2
- package/dist/http/admission.js +10 -7
- package/dist/http/approval-sweeps.js +4 -3
- package/dist/http/caller-trust.d.ts +2 -1
- package/dist/http/caller-trust.js +1 -3
- package/dist/http/checkpoint-ctx.d.ts +76 -16
- package/dist/http/checkpoint-ctx.js +45 -1
- package/dist/http/dispatch.js +24 -19
- package/dist/http/durable-run-leg.d.ts +5 -1
- package/dist/http/durable-run-leg.js +1 -1
- package/dist/http/health-self-report.d.ts +2 -2
- package/dist/http/mcp-remote-text.d.ts +5 -0
- package/dist/http/mcp-remote-text.js +7 -0
- package/dist/http/principal-gate.d.ts +15 -10
- package/dist/http/principal-gate.js +48 -9
- package/dist/http/resume-ctx-read.d.ts +2 -16
- package/dist/http/resume-ctx-read.js +2 -3
- package/dist/http/resume-legs.d.ts +2 -20
- package/dist/http/resume-legs.js +52 -38
- package/dist/http/route-ctx.d.ts +8 -2
- package/dist/http/routes/approvals-assistant.d.ts +0 -2
- package/dist/http/routes/approvals-assistant.js +65 -16
- package/dist/http/routes/capabilities.js +11 -1
- package/dist/http/routes/mcp-reconnect.d.ts +1 -1
- package/dist/http/routes/mcp-reconnect.js +5 -7
- package/dist/http/routes/runs.js +4 -3
- package/dist/http/routes/session-sync.js +70 -12
- package/dist/http/routes/sessions.js +20 -15
- package/dist/http/routes/tasks.js +8 -6
- package/dist/http/server.d.ts +4 -0
- package/dist/http/stored-body-replay.d.ts +3 -3
- package/dist/http/stored-body-replay.js +6 -1
- package/dist/http/submit-ctx.js +1 -1
- package/dist/http/wire-types.d.ts +57 -30
- package/dist/identity-source.d.ts +65 -0
- package/dist/identity-source.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory-store-refusal.d.ts +3 -3
- package/dist/model-entry-refusal.d.ts +2 -0
- package/dist/model-entry-refusal.js +11 -0
- package/dist/model-route-gap.d.ts +49 -4
- package/dist/model-route-gap.js +34 -1
- package/dist/model-select.d.ts +6 -17
- package/dist/model-select.js +0 -3
- package/dist/namespaced-rule-name.d.ts +1 -1
- package/dist/object-store-buckets.d.ts +13 -0
- package/dist/object-store-buckets.js +3 -0
- package/dist/observability/fail-open.d.ts +19 -7
- package/dist/observability/fail-open.js +19 -7
- package/dist/observability/metrics.js +2 -2
- package/dist/observability/principal-context.d.ts +4 -1
- package/dist/observability/principal-context.js +6 -1
- package/dist/observability/run-terminal.js +1 -0
- package/dist/operator-ask.d.ts +2 -2
- package/dist/org-delivery-gate.js +1 -1
- package/dist/org-memory-admission.d.ts +2 -2
- package/dist/org-memory-admission.js +39 -2
- package/dist/parked-decide.d.ts +4 -3
- package/dist/parked-decide.js +29 -9
- package/dist/peer-directory.d.ts +2 -1
- package/dist/peer-directory.js +7 -5
- package/dist/permission-rule-list.d.ts +2 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +8 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -0
- package/dist/plugins/blob-backend.js +2 -1
- package/dist/plugins/checkpoint-index-columns.d.ts +50 -1
- package/dist/plugins/checkpoint-index-columns.js +28 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +46 -12
- package/dist/plugins/checkpoint-store-sql.js +64 -35
- package/dist/plugins/file-history-store-sql.d.ts +6 -0
- package/dist/plugins/file-history-store-sql.js +37 -37
- package/dist/plugins/file-run-store.js +8 -2
- package/dist/plugins/k8s-exec-protocol.d.ts +69 -13
- package/dist/plugins/k8s-exec-protocol.js +116 -28
- package/dist/plugins/k8s-pod-delete-failure.d.ts +25 -0
- package/dist/plugins/k8s-pod-delete-failure.js +17 -0
- package/dist/plugins/k8s-pod-reaper.d.ts +112 -0
- package/dist/plugins/k8s-pod-reaper.js +122 -0
- package/dist/plugins/local-checkpoint-store.d.ts +3 -6
- package/dist/plugins/local-checkpoint-store.js +7 -6
- package/dist/plugins/memory-run-store.js +2 -0
- package/dist/plugins/one-time-migrations.d.ts +49 -37
- package/dist/plugins/one-time-migrations.js +87 -128
- package/dist/plugins/permission-rule-store-file.js +2 -2
- package/dist/plugins/permission-rule-store-sql.d.ts +17 -17
- package/dist/plugins/permission-rule-store-sql.js +16 -17
- package/dist/plugins/pg-pool.js +4 -0
- package/dist/plugins/pg-session-storage.js +2 -2
- package/dist/plugins/remote-env-adb.d.ts +3 -0
- package/dist/plugins/remote-env-adb.js +1 -0
- package/dist/plugins/remote-env-device.d.ts +3 -0
- package/dist/plugins/remote-env-device.js +4 -0
- package/dist/plugins/remote-env-e2b.d.ts +3 -0
- package/dist/plugins/remote-env-e2b.js +1 -0
- package/dist/plugins/remote-env-file-error.d.ts +8 -1
- package/dist/plugins/remote-env-file-error.js +7 -1
- package/dist/plugins/remote-env-host.d.ts +7 -1
- package/dist/plugins/remote-env-host.js +7 -1
- package/dist/plugins/remote-env-k8s.d.ts +154 -14
- package/dist/plugins/remote-env-k8s.js +300 -21
- package/dist/plugins/remote-env-local-docker.d.ts +3 -0
- package/dist/plugins/remote-env-local-docker.js +1 -0
- package/dist/plugins/remote-env-ssh.d.ts +3 -0
- package/dist/plugins/remote-env-ssh.js +1 -0
- package/dist/plugins/run-store-sql.js +10 -4
- package/dist/plugins/s3-answer.d.ts +15 -0
- package/dist/plugins/s3-answer.js +5 -0
- package/dist/plugins/session-ownership.d.ts +17 -3
- package/dist/plugins/session-ownership.js +19 -5
- package/dist/plugins/snapshot-bucket-presence.d.ts +39 -4
- package/dist/plugins/snapshot-bucket-presence.js +35 -7
- package/dist/plugins/sql-errors.d.ts +60 -0
- package/dist/plugins/sql-errors.js +64 -1
- package/dist/plugins/store-contracts.d.ts +10 -1
- package/dist/plugins/store-fault.d.ts +4 -0
- package/dist/plugins/store-fault.js +10 -0
- package/dist/plugins/tidb-pool.js +8 -1
- package/dist/plugins/tidb-session-store.js +2 -2
- package/dist/plugins/web-search.d.ts +1 -1
- package/dist/plugins/web-search.js +2 -2
- package/dist/request-declaration-refusal.d.ts +94 -0
- package/dist/request-declaration-refusal.js +78 -0
- package/dist/request-image-urls.d.ts +6 -0
- package/dist/request-image-urls.js +27 -0
- package/dist/request-key-closure.d.ts +5 -0
- package/dist/request-key-closure.js +5 -2
- package/dist/request-value-closure.d.ts +31 -2
- package/dist/request-value-closure.js +7 -1
- package/dist/rule-entry-list.d.ts +3 -2
- package/dist/rule-entry-list.js +2 -2
- package/dist/rules-consent.js +7 -4
- package/dist/run-local.d.ts +2 -2
- package/dist/runs.js +2 -1
- package/dist/runtime-caps-resolver.js +1 -1
- package/dist/runtime-governance.d.ts +5 -5
- package/dist/sandbox-git-lease.d.ts +49 -0
- package/dist/sandbox-git-lease.js +60 -0
- package/dist/sandbox-git-plane.d.ts +84 -0
- package/dist/sandbox-git-plane.js +105 -0
- package/dist/sandbox-pkg-source.d.ts +28 -1
- package/dist/sandbox-pkg-source.js +1 -2
- package/dist/sealed-key.d.ts +19 -0
- package/dist/sealed-key.js +47 -4
- package/dist/security.d.ts +16 -4
- package/dist/security.js +8 -3
- package/dist/server-secret-env.d.ts +6 -0
- package/dist/server-secret-env.js +14 -0
- package/dist/session-error-wire.d.ts +14 -6
- package/dist/session-error-wire.js +2 -1
- package/dist/session-incarnation.d.ts +16 -5
- package/dist/session-incarnation.js +7 -4
- package/dist/session-policy-wire.d.ts +1 -1
- package/dist/session-policy-wire.js +2 -2
- package/dist/session-row.d.ts +3 -17
- package/dist/session-row.js +4 -0
- package/dist/session-sync.d.ts +15 -3
- package/dist/session-sync.js +5 -2
- package/dist/spec-fields.d.ts +43 -13
- package/dist/spec-fields.js +33 -12
- package/dist/stored-request-prompt-profile-m30.d.ts +49 -0
- package/dist/stored-request-prompt-profile-m30.js +25 -0
- package/dist/task-a2a.d.ts +52 -81
- package/dist/task-a2a.js +219 -89
- package/dist/task-mcp.d.ts +7 -15
- package/dist/task-mcp.js +16 -51
- package/dist/task-settings.d.ts +2 -2
- package/dist/tool-approval.d.ts +35 -12
- package/dist/tool-approval.js +59 -39
- package/dist/tool-rule-names.d.ts +5 -0
- package/dist/trace/core-keyset-guard.d.ts +5 -5
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.js +37 -13
- package/dist/trace/ledger-sink.d.ts +4 -5
- package/dist/trace/ledger-sink.js +3 -7
- package/dist/trace/project.d.ts +37 -3
- package/dist/trace/project.js +20 -6
- package/dist/trace/redact.d.ts +40 -12
- package/dist/trace/redact.js +332 -128
- package/dist/trace/self-minted-wire.d.ts +641 -0
- package/dist/trace/self-minted-wire.js +201 -0
- package/dist/trace/wire-projection-faces.d.ts +5 -3
- package/dist/trace/wire-projection-faces.js +7 -3
- package/dist/url-credential-refusal.d.ts +52 -0
- package/dist/url-credential-refusal.js +14 -0
- package/dist/url-refusal.d.ts +13 -4
- package/dist/url-refusal.js +8 -2
- package/dist/web-admin-client/apply-effective.js +0 -1
- package/dist/web-admin-client/center-request.d.ts +20 -2
- package/dist/web-admin-client/center-request.js +40 -5
- package/dist/web-admin-client/http-client.js +6 -6
- package/dist/web-admin-client/types.d.ts +5 -5
- package/dist/workspace-space.d.ts +27 -0
- package/dist/workspace-space.js +26 -0
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +11 -5
- package/package.json +4 -3
- package/scripts/drain-check-local.mjs +2 -1
- package/scripts/drain-check.mysql.sql +3 -1
- package/scripts/drain-check.pg.sql +3 -1
- package/skills/schedule.md +1 -1
- package/wire-changes/server@7.104.0.json +134 -0
- package/wire-changes/server@7.105.0.json +53 -0
- package/wire-changes/server@7.106.0.json +475 -0
- package/wire-changes/server@7.107.0.json +64 -0
- package/wire-changes/server@7.108.0.json +153 -0
- package/wire-changes/server@7.109.0.json +46 -0
- package/wire-changes/server@7.110.0.json +90 -0
- package/wire-changes/server@7.111.0.json +86 -0
- package/wire-changes/server@7.112.0.json +60 -0
- package/wire-changes/server@7.113.0.json +26 -0
- package/wire-changes/server@7.114.0.json +26 -0
- package/wire-changes/server@7.115.0.json +97 -0
- package/wire-changes/server@7.116.0.json +218 -0
- package/wire-changes/server@7.116.1.json +26 -0
- package/wire-changes/server@7.117.0.json +65 -0
- package/wire-changes/server@7.118.0.json +129 -0
- package/wire-changes/server@7.119.0.json +65 -0
- package/wire-changes/server@7.120.0.json +89 -0
- package/wire-changes/server@7.121.0.json +219 -0
- package/wire-changes/server@7.122.0.json +54 -0
- package/wire-changes/server@7.123.0.json +26 -0
- package/wire-changes/server@7.124.0.json +336 -0
- package/wire-changes/server@7.124.1.json +26 -0
- package/wire-changes/server@7.125.0.json +111 -0
- package/wire-changes/server@7.126.0.json +285 -0
- package/wire-changes/server@7.127.0.json +77 -0
- package/wire-changes/server@7.127.1.json +26 -0
- package/wire-changes/server@7.127.2.json +53 -0
- package/wire-changes/server@7.128.0.json +26 -0
- package/wire-changes/server@7.129.0.json +106 -0
- package/wire-changes/server@7.129.1.json +26 -0
- package/wire-changes/server@7.130.0.json +26 -0
- package/wire-changes/server@7.131.0.json +118 -0
- package/wire-changes/server@7.132.0.json +226 -0
- package/wire-changes/server@7.133.0.json +1155 -0
- package/wire-changes/server@7.134.0.json +107 -0
- package/wire-changes/server@7.135.0.json +174 -0
- package/wire-changes/server@7.136.0.json +448 -0
- package/wire-changes/server@7.137.0.json +825 -0
- package/dist/url-credential.d.ts +0 -52
- package/dist/url-credential.js +0 -49
|
@@ -16,6 +16,7 @@ import { CheckpointError, type QuestionAnswer, type CheckpointToken } from "@sem
|
|
|
16
16
|
import type { DecideBinding } from "./wire-types.js";
|
|
17
17
|
import type { CheckpointStoreFull } from "../plugins/store-backend.js";
|
|
18
18
|
import type { HostDecision } from "../host-decision.js";
|
|
19
|
+
import { type GateSighting } from "../approval-gate-currency.js";
|
|
19
20
|
import { type PlanApprovePermissionModeAfter } from "../task-settings.js";
|
|
20
21
|
import type { RouteCtxBase, DriveResumeArgs, HostLegOutcome, PreparedPlanReview, ResumeLegOutcome, WorkflowDecisionDelivery } from "./route-ctx.js";
|
|
21
22
|
import type { RunRegistry, RouteHelpers, RouteLegs } from "./route-ctx.js";
|
|
@@ -28,25 +29,6 @@ import { type ResumeRefusalAnswer } from "./checkpoint-ctx.js";
|
|
|
28
29
|
* ⇒ `"terminal"`:与 `by_reason` 认不得的词同一条 fail-closed 方向(不宽容默认成可重试),也与修前手列串的缺省逐字相同。
|
|
29
30
|
*/
|
|
30
31
|
export declare function resumeRefusalDisposition(e: CheckpointError): "cancelled" | "retriable" | "terminal";
|
|
31
|
-
/**
|
|
32
|
-
* 🔴 **引擎拒一次续跑(`CheckpointError`)的 HTTP 答复 —— 唯一组句器**([ref] / [ref],core 7.52.0;五条续跑腿〔`/decide` · `/resume` · `/wake` · `plan_review` ·
|
|
33
|
-
* workflow 决定在宿主会话上起的新一跑〕同经 `driveResumeLeg` 的 catch 臂到这里,SLA 拒扫腿与 PARKED 赎回席经 `resumeCheckpoint` 同得)。
|
|
34
|
-
*
|
|
35
|
-
* 规则(一条):**一次拒 = 一个答复形,与「谁发现的」无关**。引擎在认领前 / CAS 前拒的,与本服务在重放存量体之前自己拒的(`resume_blocked_by_policy`,
|
|
36
|
-
* `checkpoint-ctx.ts` `resumeBlockedByPolicyAnswer`;续跑入口上卡仍 pending ⇒ 同样带 `retriable:true`)读同一组机读键;体经拒体组句器 `refusalBody` 铸,
|
|
37
|
-
* `retriable` 经续跑家族的唯一写点 `resumeRefusalWithCard` 追加。
|
|
38
|
-
* 后台停驻子代的 parked 赎回不经本函数(引擎交的是赎回回执,不是 `CheckpointError`):`parked-decide.ts` 答 `422 parked_resume.startup_failed`,体带成因词 `cause`,
|
|
39
|
-
* 引擎 7.54.0 起回执的 `details.detail` 交出同三位判别位,经**同一只**读法 `resumeRefusalFacts` 落成同名三键(下面第二条)。键律:
|
|
40
|
-
* · `errorCode` —— 引擎码的原码镜像(`checkpoint.invalid_outcome` 按决断成因改铸,{@link decisionRefusalCauseOf});
|
|
41
|
-
* · 引擎 `detail` 的三个机读判别位原样过境,键名与 `resume_blocked_by_policy` 同义:`detail.code` ⇒ `blockedBy`(挡住它的准入码 = 同一份坏配置在新跑上报的 `errorCode`;
|
|
42
|
-
* 今天只有 `checkpoint.config_refused` 带它)· `detail.field` ⇒ `field`(哪一侧 / 哪一格)· `detail.reason` ⇒ `reason`(同码多臂时哪一臂,闭集 `CheckpointErrorReason`)。
|
|
43
|
-
* 缺席不造键(引擎没给就是没给)。`detail` 的其余成员(`requires` / `differs`)不在这组判别位里,不上 wire;`retryAfterMs` 折成族口径的 `retryAfterSec`(下面);
|
|
44
|
-
* · `retriable` —— 只在处置读口答 `retriable` 时在场(卡仍 pending、行重新 park;**不是**「原样重试会成功」—— 契约附录 A.1 注那一个意思,壳按它决定卡还挂不挂着)。
|
|
45
|
-
* 出路(改那一格再决 / 只能在停它的那一版 worker 上跑完或取消 / 等一会)由 `errorCode` + `field` + `blockedBy` + `reason` 与引擎的句子说;
|
|
46
|
-
* · `error` —— 引擎的句子原样(它点名条目、哪一臂与出路);本层不另铸第二句;
|
|
47
|
-
* · `taskId`(抢到行才在)/ `retryAfterSec`(两码闭集)/ 两版本键(只在 `checkpoint.unsupported_version`:调用方按那一码交 `buildCheckpointVersionKeys` 的产物,本函数原样铺)—— 键律照旧。
|
|
48
|
-
* 纯函数(钉直接调)。`retriable` 由调用方传入 —— 行的落点与 wire 的 `retriable` 读同一次求值(catch 臂一处),本函数不另判。
|
|
49
|
-
*/
|
|
50
32
|
export declare function checkpointRefusalAnswer(e: CheckpointError, at: {
|
|
51
33
|
readonly gate: string;
|
|
52
34
|
readonly retriable: boolean;
|
|
@@ -89,7 +71,7 @@ export declare function createResumeLegs(ctx: {
|
|
|
89
71
|
}, decider?: {
|
|
90
72
|
principal?: string;
|
|
91
73
|
explicitOperator: boolean;
|
|
92
|
-
}) => Promise<{
|
|
74
|
+
}, sighting?: GateSighting) => Promise<{
|
|
93
75
|
readonly ok: false;
|
|
94
76
|
readonly answer: {
|
|
95
77
|
status: number;
|
package/dist/http/resume-legs.js
CHANGED
|
@@ -9,7 +9,7 @@ const headUnavailable = async (sessionId) => {
|
|
|
9
9
|
throw new Error(`parked decide cannot verify which occupant session ${sessionId} belongs to: this deployment's session store has no head read (headOf; every built-in store has one) — refusing rather than resuming unverified`);
|
|
10
10
|
};
|
|
11
11
|
import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
|
|
12
|
-
import { parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
12
|
+
import { graftParkToolCallId, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
|
|
13
13
|
import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
|
|
14
14
|
import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../run-cancel-context.js";
|
|
15
15
|
import { isTerminalRunStatus } from "../plugins/store-contracts.js";
|
|
@@ -18,16 +18,17 @@ import { recordResumeResourceWindow } from "../resource-window.js";
|
|
|
18
18
|
import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "../orchestration/workflow-completion-inbox.js";
|
|
19
19
|
import { fleetRunPublisher, fleetRunLabels, fleetRunResiduals, isFleetAgentTerminalNotification } from "../fleet/fleet-bus.js";
|
|
20
20
|
import { defaultSubagentTailBus } from "../fleet/subagent-tail-bus.js";
|
|
21
|
-
import { currentApprovalGateToken } from "../approval-gate-currency.js";
|
|
22
|
-
import { createApprovalCardEmitter, declaredApprovalFrameFamilies, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerDurablyRecordable } from "../tool-approval.js";
|
|
21
|
+
import { currentApprovalGateToken, currentGateFacesOf, sightsCurrentGate } from "../approval-gate-currency.js";
|
|
22
|
+
import { approverFacingArgs, createApprovalCardEmitter, declaredApprovalFrameFamilies, parkRowStandingAnswerFacts, resolveApprovalLeg, resolveStreamApprovalGate, standingAnswerDurablyRecordable } from "../tool-approval.js";
|
|
23
23
|
import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
|
|
24
24
|
import { APPROVAL_ENDING_CAUSES } from "../approval-deny-reasons.js";
|
|
25
25
|
import { serviceVersion } from "../service-version.js";
|
|
26
26
|
import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
|
|
27
27
|
import { withPrincipal } from "../observability/principal-context.js";
|
|
28
28
|
import { withModelReachScope } from "../model-reach-scope.js";
|
|
29
|
+
import { runSkillsOf } from "../capabilities/skills.js";
|
|
29
30
|
import { createInstructionSelectionLeg } from "../instruction-selection.js";
|
|
30
|
-
import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
31
|
+
import { modelTextEventData, parkEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, compactionStartedEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
|
|
31
32
|
import { redactSecrets } from "../trace/redact.js";
|
|
32
33
|
import { buildRunObjectivePreview } from "../user-authored-text.js";
|
|
33
34
|
import { createForwardAppendFailedNote, createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
|
|
@@ -43,11 +44,11 @@ import { createSessionsLocal } from "./routes/sessions.js";
|
|
|
43
44
|
import { createSessionSyncLocal } from "./routes/session-sync.js";
|
|
44
45
|
import { createImagesLocal } from "./routes/images.js";
|
|
45
46
|
import { createCapabilitiesLocal } from "./routes/capabilities.js";
|
|
46
|
-
import { gatedPrincipal, explicitOperatorOk } from "./principal-gate.js";
|
|
47
|
+
import { gatedPrincipal, explicitOperatorOk, runIdentityOf } from "./principal-gate.js";
|
|
47
48
|
import { buildActiveRunConflict, resumeEntryForGate } from "./active-run-conflict.js";
|
|
48
49
|
import { commitPlacement, devicePlacementErrorBody } from "./device-placement.js";
|
|
49
50
|
import { commitSessionInheritEnv } from "./session-inherit-env.js";
|
|
50
|
-
import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind } from "../approval-content-kind.js";
|
|
51
|
+
import { ASK_USER_QUESTION_TOOL_NAME, isApprovalGateKind, isEngineQuestionRow } from "../approval-content-kind.js";
|
|
51
52
|
import { deploymentListAsks } from "../approval.js";
|
|
52
53
|
import { readResumeCtx, resumeCtxCorruptAnswer, resumeCtxUnsealableAnswer } from "./resume-ctx-read.js";
|
|
53
54
|
import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, resumeRefusalWithCard, sealCheckpointCtx, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
|
|
@@ -79,6 +80,7 @@ const WORKER_SWAP_REDEEMABLE = {
|
|
|
79
80
|
read_face_forged: false,
|
|
80
81
|
write_face_damaged: false,
|
|
81
82
|
skill_grants_damaged: false,
|
|
83
|
+
skill_scopes_damaged: false,
|
|
82
84
|
constraint_chain_missing: false,
|
|
83
85
|
hands_coordinates_damaged: false,
|
|
84
86
|
installation_record_absent: true,
|
|
@@ -111,6 +113,7 @@ const CHECKPOINT_ERROR_DISPOSITION = {
|
|
|
111
113
|
"resume.principal_mismatch": "retriable",
|
|
112
114
|
"resume.placement_mismatch": "retriable",
|
|
113
115
|
"resume.preflight_rejected": "retriable",
|
|
116
|
+
"resume.holder_superseded": "retriable",
|
|
114
117
|
"wake.gate_pending": "retriable",
|
|
115
118
|
"wake.nothing_to_deliver": "retriable",
|
|
116
119
|
"checkpoint.config_refused": "retriable",
|
|
@@ -183,6 +186,26 @@ function decisionRefusalCauseOf(e, gate) {
|
|
|
183
186
|
function notResumableAnswer() {
|
|
184
187
|
return { status: 409, body: refusalBody({ errorCode: "conflict.not_resumable", message: "run is not in a resumable (suspended) state (already resumed, decided, or expired)" }) };
|
|
185
188
|
}
|
|
189
|
+
function approvalStaleAnswer(facts) {
|
|
190
|
+
return {
|
|
191
|
+
status: 409,
|
|
192
|
+
body: refusalBody({
|
|
193
|
+
errorCode: "approval_stale",
|
|
194
|
+
message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
195
|
+
facts: {
|
|
196
|
+
...(facts.terminal !== undefined ? { terminal: facts.terminal } : {}),
|
|
197
|
+
...(facts.currentPending !== undefined ? { currentPending: facts.currentPending } : {}),
|
|
198
|
+
},
|
|
199
|
+
}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function acceptableFormFacts(detail) {
|
|
203
|
+
const rewritten = detail !== null && typeof detail === "object" ? detail.editRewritten : undefined;
|
|
204
|
+
if (rewritten === null || typeof rewritten !== "object" || !Object.hasOwn(rewritten, "acceptableForm"))
|
|
205
|
+
return {};
|
|
206
|
+
const shown = approverFacingArgs(rewritten.acceptableForm);
|
|
207
|
+
return shown === undefined ? {} : { acceptableForm: shown };
|
|
208
|
+
}
|
|
186
209
|
export function checkpointRefusalAnswer(e, at) {
|
|
187
210
|
const refusalCause = e.code === "checkpoint.invalid_outcome" ? decisionRefusalCauseOf(e, at.gate) : undefined;
|
|
188
211
|
const detail = e.detail;
|
|
@@ -198,6 +221,7 @@ export function checkpointRefusalAnswer(e, at) {
|
|
|
198
221
|
? { retryAfterSec: Math.max(1, Math.ceil(detail.retryAfterMs / 1000)) }
|
|
199
222
|
: {}),
|
|
200
223
|
...at.versionKeys,
|
|
224
|
+
...(e.code === "resume.constraint_rejected" ? acceptableFormFacts(detail) : {}),
|
|
201
225
|
},
|
|
202
226
|
}),
|
|
203
227
|
}, at.retriable);
|
|
@@ -209,7 +233,7 @@ export function createResumeLegs(ctx) {
|
|
|
209
233
|
async function resumeCheckpoint(sessionId, decision, reason, hostDecision, req, answer, binding, onResumeCommitted, acceptEarly, decider) {
|
|
210
234
|
const cs = deps.checkpointStore;
|
|
211
235
|
const token = await currentApprovalGateToken(cs, sessionId);
|
|
212
|
-
if (binding?.checkpointToken && binding.checkpointToken
|
|
236
|
+
if (binding?.checkpointToken && !sightsCurrentGate({ face: "checkpointToken", value: binding.checkpointToken }, token ? { checkpointToken: token } : null)) {
|
|
213
237
|
let seenUnreadable = false;
|
|
214
238
|
const seen = await cs.get(binding.checkpointToken).catch((e) => {
|
|
215
239
|
seenUnreadable = true;
|
|
@@ -245,17 +269,7 @@ export function createResumeLegs(ctx) {
|
|
|
245
269
|
};
|
|
246
270
|
}
|
|
247
271
|
}
|
|
248
|
-
return {
|
|
249
|
-
status: 409,
|
|
250
|
-
body: refusalBody({
|
|
251
|
-
errorCode: "approval_stale",
|
|
252
|
-
message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
253
|
-
facts: {
|
|
254
|
-
...(attributable ? { terminal: "resolved" } : {}),
|
|
255
|
-
...(currentPending ? { currentPending } : {}),
|
|
256
|
-
},
|
|
257
|
-
}),
|
|
258
|
-
};
|
|
272
|
+
return approvalStaleAnswer({ ...(attributable ? { terminal: "resolved" } : {}), ...(currentPending ? { currentPending } : {}) });
|
|
259
273
|
}
|
|
260
274
|
if (!token) {
|
|
261
275
|
deps.logger?.info?.("approval_decide_no_pending", { sessionId });
|
|
@@ -330,16 +344,7 @@ export function createResumeLegs(ctx) {
|
|
|
330
344
|
...(cp.pendingAction.boundInputHash ? { boundInputHash: cp.pendingAction.boundInputHash } : {}),
|
|
331
345
|
};
|
|
332
346
|
}
|
|
333
|
-
return {
|
|
334
|
-
status: 409,
|
|
335
|
-
body: refusalBody({
|
|
336
|
-
errorCode: "approval_stale",
|
|
337
|
-
message: "the approval you are deciding is no longer current (already resolved or superseded since you viewed it)",
|
|
338
|
-
facts: {
|
|
339
|
-
...(s57CurrentPending ? { currentPending: s57CurrentPending } : {}),
|
|
340
|
-
},
|
|
341
|
-
}),
|
|
342
|
-
};
|
|
347
|
+
return approvalStaleAnswer(s57CurrentPending ? { currentPending: s57CurrentPending } : {});
|
|
343
348
|
}
|
|
344
349
|
const paForGrant = cp.pendingAction.kind === "tool_approval" ? cp.pendingAction : undefined;
|
|
345
350
|
const rememberableTool = paForGrant !== undefined &&
|
|
@@ -498,7 +503,7 @@ export function createResumeLegs(ctx) {
|
|
|
498
503
|
boundCallId: binding?.boundCallId ?? persistedCallId,
|
|
499
504
|
boundInputHash: binding?.boundInputHash ?? persistedHash,
|
|
500
505
|
decision: decision === "approve" ? "allow" : "deny",
|
|
501
|
-
...(decision === "approve" && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
|
|
506
|
+
...(decision === "approve" && !isEngineQuestionRow(pa) && binding?.updatedInput !== undefined ? { updatedInput: binding.updatedInput } : {}),
|
|
502
507
|
...(reason ? { reason } : {}),
|
|
503
508
|
...(binding?.feedback !== undefined ? { feedback: binding.feedback } : {}),
|
|
504
509
|
...(answer !== undefined ? { answer } : {}),
|
|
@@ -599,6 +604,7 @@ export function createResumeLegs(ctx) {
|
|
|
599
604
|
const freshId = uuidv7();
|
|
600
605
|
const created = await deps.runStore.createRun(freshId, sessionId, principal ?? null, deps.instanceId ?? "default", {
|
|
601
606
|
source: args.mintFreshRun.source,
|
|
607
|
+
identity: args.mintFreshRun.identity,
|
|
602
608
|
objectivePreview: buildRunObjectivePreview(resumeObjective ?? ""),
|
|
603
609
|
});
|
|
604
610
|
if (created.existing) {
|
|
@@ -725,6 +731,7 @@ export function createResumeLegs(ctx) {
|
|
|
725
731
|
let unregisterEngineNotice;
|
|
726
732
|
try {
|
|
727
733
|
const instructionLeg = createInstructionSelectionLeg();
|
|
734
|
+
const legWiringSections = () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(taskConfig) });
|
|
728
735
|
result = await instructionLeg.run(sessionId, () => withModelReachScope({ sessionId, taskId }, () => withPrincipal(principal, async () => {
|
|
729
736
|
const resumeNotifiedKeys = new NotifiedKeys();
|
|
730
737
|
if (deps.runStore && taskId) {
|
|
@@ -748,7 +755,7 @@ export function createResumeLegs(ctx) {
|
|
|
748
755
|
persistThinking: deps.config.traceThinking,
|
|
749
756
|
notifiedKeys: resumeNotifiedKeys,
|
|
750
757
|
...(taskId ? { onActivity: () => recordTurnActivity(taskId) } : {}),
|
|
751
|
-
|
|
758
|
+
legSections: legWiringSections,
|
|
752
759
|
});
|
|
753
760
|
const vAppend = (type, data) => verifySink.append(type, data);
|
|
754
761
|
cancelLedgerAppend = vAppend;
|
|
@@ -820,7 +827,7 @@ export function createResumeLegs(ctx) {
|
|
|
820
827
|
if (reSuspended || vrReopened) {
|
|
821
828
|
await appendModelUsageDelta(ap, deps.modelUsage, taskId);
|
|
822
829
|
const vrParkCallId = vrNested !== undefined ? await parkToolCallId({ terminal: vrNested }, deps.checkpointStore) : undefined;
|
|
823
|
-
await ap(reParkStatus ?? reparkStatus, {
|
|
830
|
+
await ap(reParkStatus ?? reparkStatus, parkEventData({ terminal: vrNested, ...(vrParkCallId !== undefined ? { toolCallId: vrParkCallId } : {}) }, vrReopened ? (failureCodeOf(vr.terminal) ?? null) : undefined));
|
|
824
831
|
}
|
|
825
832
|
else {
|
|
826
833
|
safe = await attachModelUsage(safe, { append: ap, getEvents: (id, a) => rs2.getEvents(id, a), modelUsage: deps.modelUsage, taskId });
|
|
@@ -1017,7 +1024,7 @@ export function createResumeLegs(ctx) {
|
|
|
1017
1024
|
break;
|
|
1018
1025
|
case "wiring_manifest":
|
|
1019
1026
|
await flush();
|
|
1020
|
-
await append("wiring_manifest", wiringManifestEventData(ev,
|
|
1027
|
+
await append("wiring_manifest", wiringManifestEventData(ev, legWiringSections()));
|
|
1021
1028
|
break;
|
|
1022
1029
|
case "tool_roster_delta": {
|
|
1023
1030
|
await flush();
|
|
@@ -1074,20 +1081,19 @@ export function createResumeLegs(ctx) {
|
|
|
1074
1081
|
const r = causeAtDone !== undefined ? mintCancelledResult({ taskId, sessionId, startedAtMs: resumeLegStartedAt, message: "cancelled by user", engineResult: rEngine, cause: causeAtDone }) : rEngine;
|
|
1075
1082
|
const rPark = r.terminal.kind === "paused" ? outwardPlane(r.terminal).status : undefined;
|
|
1076
1083
|
if (rPark === "suspended") {
|
|
1077
|
-
|
|
1078
|
-
await append("suspended", { gate: r.terminal.kind === "paused" ? r.terminal.gate : null, ...(parkCallId !== undefined ? { toolCallId: parkCallId } : {}) });
|
|
1084
|
+
await append("suspended", parkEventData(await graftParkToolCallId(r, deps.checkpointStore)));
|
|
1079
1085
|
await appendModelUsageDelta(append, deps.modelUsage, taskId);
|
|
1080
1086
|
return r;
|
|
1081
1087
|
}
|
|
1082
1088
|
if (rPark === "needs_review") {
|
|
1083
|
-
await append("needs_review",
|
|
1089
|
+
await append("needs_review", parkEventData(r));
|
|
1084
1090
|
await appendModelUsageDelta(append, deps.modelUsage, taskId);
|
|
1085
1091
|
return r;
|
|
1086
1092
|
}
|
|
1087
1093
|
if (await reopenedAfterLeg(r, causeAtDone !== undefined)) {
|
|
1088
1094
|
reopenConfirmedPark = true;
|
|
1089
1095
|
try {
|
|
1090
|
-
await append(reparkStatus,
|
|
1096
|
+
await append(reparkStatus, parkEventData(r, failureCodeOf(r.terminal) ?? null));
|
|
1091
1097
|
await appendModelUsageDelta(append, deps.modelUsage, taskId);
|
|
1092
1098
|
}
|
|
1093
1099
|
catch {
|
|
@@ -1339,6 +1345,7 @@ export function createResumeLegs(ctx) {
|
|
|
1339
1345
|
fleetScope: resumeFleetScope(req, auth),
|
|
1340
1346
|
elicitOwner: principal ?? null,
|
|
1341
1347
|
source: "workflow_parked_decide",
|
|
1348
|
+
identity: runIdentityOf(req, deps.config, principal ?? null),
|
|
1342
1349
|
workflowDecision,
|
|
1343
1350
|
});
|
|
1344
1351
|
if (out.existing) {
|
|
@@ -1424,7 +1431,7 @@ export function createResumeLegs(ctx) {
|
|
|
1424
1431
|
outcome: { gate: "wake", ...(message !== undefined ? { message: { text: message, trusted } } : {}) },
|
|
1425
1432
|
verifyRounds: undefined,
|
|
1426
1433
|
resumeCtx: { sessionId, ctx },
|
|
1427
|
-
mintFreshRun: { source: "wake" },
|
|
1434
|
+
mintFreshRun: { source: "wake", identity: runIdentityOf(req, deps.config, auth.principal ?? null) },
|
|
1428
1435
|
...(workflowDecision !== undefined ? { workflowDecision } : {}),
|
|
1429
1436
|
...(engineNotices.length > 0 ? { engineNotices } : {}),
|
|
1430
1437
|
...(acceptEarly === true ? { acceptEarly: true } : {}),
|
|
@@ -1432,7 +1439,7 @@ export function createResumeLegs(ctx) {
|
|
|
1432
1439
|
...(ownerLeaseAdmittedFor !== undefined && auth.principal === ownerLeaseAdmittedFor ? { ownerLeaseAdmitted: true } : {}),
|
|
1433
1440
|
}));
|
|
1434
1441
|
}
|
|
1435
|
-
async function preparePlanReview(sessionId, log, decider) {
|
|
1442
|
+
async function preparePlanReview(sessionId, log, decider, sighting) {
|
|
1436
1443
|
const cs = deps.checkpointStore;
|
|
1437
1444
|
const refuse = (answer) => ({ ok: false, answer });
|
|
1438
1445
|
const log404 = (r) => deps.logger?.info?.("decide_404", {
|
|
@@ -1490,6 +1497,13 @@ export function createResumeLegs(ctx) {
|
|
|
1490
1497
|
}),
|
|
1491
1498
|
});
|
|
1492
1499
|
}
|
|
1500
|
+
if (sighting !== undefined) {
|
|
1501
|
+
const current = currentGateFacesOf(token, cp);
|
|
1502
|
+
if (!sightsCurrentGate(sighting, current)) {
|
|
1503
|
+
const currentId = decider !== undefined && cp.status === "pending" ? current.checkpointId : undefined;
|
|
1504
|
+
return refuse(approvalStaleAnswer(currentId !== undefined ? { currentPending: { checkpointId: currentId } } : {}));
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1493
1507
|
const ctxRead = await readResumeCtx(cs.getCtx(sessionId), ctxOpening(sessionId));
|
|
1494
1508
|
if (ctxRead.kind === "corrupt")
|
|
1495
1509
|
return refuse(resumeCtxCorruptAnswer(ctxRead.where));
|
package/dist/http/route-ctx.d.ts
CHANGED
|
@@ -27,8 +27,10 @@ import type { CapabilitiesLocal } from "./routes/capabilities.js";
|
|
|
27
27
|
import type { SessionsLocal } from "./routes/sessions.js";
|
|
28
28
|
import type { SessionSyncLocal } from "./routes/session-sync.js";
|
|
29
29
|
import type { PlanApprovePermissionModeAfter } from "../task-settings.js";
|
|
30
|
+
import type { GateSighting } from "../approval-gate-currency.js";
|
|
30
31
|
import type { VerifyRoundsSpec } from "./verify-rounds.js";
|
|
31
32
|
import type { RunAbort } from "../run-cancel-context.js";
|
|
33
|
+
import type { RunIdentity } from "../identity-source.js";
|
|
32
34
|
/** 跨域可变状态 = lens1 §C2 实测的那 8 条「运行期登记簿」,一个对象。
|
|
33
35
|
* [ref]:`cancelledViaVerb`(按 taskId 键控的「这次是取消动词」旁表)删除 —— 中止成因改挂在每条腿自己的
|
|
34
36
|
* {@link RunAbort} 句柄上(`inflightRuns` 的值),同寿同删,不再有一张与腿不同寿的旁表。 */
|
|
@@ -281,11 +283,13 @@ export interface DriveResumeArgs {
|
|
|
281
283
|
/** [ref]/[ref]:调用方声明「本腿的 park 是终局后纯 park,session claim 已随终局 finalize 删除」——
|
|
282
284
|
* getActiveTaskId 缺席时**铸新 canonical taskId + createRun 全生命周期行**(wake 续跑=同 session 新一轮
|
|
283
285
|
* turn),而不是无行裸跑(不可取消/无计费归因/无 durable append/fleet 不可见,且 core `spec.taskId ??
|
|
284
|
-
* sessionId` fallback 会把 lastRunId 铸成 sessionId)。`source` 进 run 行元数据(runMeta
|
|
286
|
+
* sessionId` fallback 会把 lastRunId 铸成 sessionId)。`source` 进 run 行元数据(runMeta 同列,只作归因);`identity` = 新行的身份元组(调用方经
|
|
287
|
+
* `runIdentityOf(req, config, 卡属主)` 读凭据门铸下的那一份:按下按钮的就是卡属主才有,否则空)。
|
|
285
288
|
* decide/answer/plan_review 腿**不设**此位:它们的 claim 在 park 期间特意保留,缺席=行被 reap 的竞态,
|
|
286
289
|
* 维持既有兜底(checkpoint token 同拍过期 ⇒ resumeStream 拒),不铸新行。 */
|
|
287
290
|
mintFreshRun?: {
|
|
288
291
|
source: string;
|
|
292
|
+
identity: RunIdentity | null;
|
|
289
293
|
};
|
|
290
294
|
/** `/decide` 第三条车道交给本腿的那一件决定投递(语义属主 = {@link WorkflowDecisionDelivery} 顶注)。缺席 ⇒ 只摊决定行(其余四条 resume 腿恒缺席)。 */
|
|
291
295
|
workflowDecision?: WorkflowDecisionDelivery;
|
|
@@ -457,7 +461,9 @@ export interface RouteLegs {
|
|
|
457
461
|
decider?: {
|
|
458
462
|
principal?: string;
|
|
459
463
|
explicitOperator: boolean;
|
|
460
|
-
}
|
|
464
|
+
},
|
|
465
|
+
/** [ref]:决断方看到的那道门(决断体的 `checkpointId`);不是当前门 ⇒ `409 approval_stale`(`approval_stale` 律,语义属主 = `resume-legs.ts` 的同名形参顶注)。 */
|
|
466
|
+
sighting?: GateSighting): Promise<{
|
|
461
467
|
readonly ok: false;
|
|
462
468
|
readonly answer: {
|
|
463
469
|
status: number;
|
|
@@ -102,8 +102,6 @@ export type QuestionAnswerCheck = {
|
|
|
102
102
|
* [ref] 起**同时闭键**:词表外的键(拼错 / 未来键 / `__proto__`)拒不丢。
|
|
103
103
|
*/
|
|
104
104
|
export declare function checkQuestionAnswer(v: unknown): QuestionAnswerCheck;
|
|
105
|
-
/** 路由体 = 从 `server.ts` 的 `handle()` 里**整段剪切**过来的原文(唯一改动:统一去缩进两格)。裸 `return;`
|
|
106
|
-
* = 「本域已应答」;走到函数尾才是「没匹配上」。跨域的东西一律在首行解构出来。 */
|
|
107
105
|
export declare function handleApprovalsAssistant(req: IncomingMessage, res: ServerResponse, match: RouteMatch<ApprovalsAssistantRouteId>, ctx: RouteCtx): Promise<void>;
|
|
108
106
|
/**
|
|
109
107
|
* 🔴 [ref]-2 / [ref]②:两条 durable 读面(`GET /v1/approvals` + `/v1/approvals/stream`)的**唯一**
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { screenedGate } from "../../trace/project.js";
|
|
2
|
+
import { skillCardFacts } from "../../approval-card.js";
|
|
2
3
|
import { hostDecisionFacts, readDecidedBy, retiredDecisionKeyFacts, DECIDED_BY_NOT_IN_PROOF, DECIDED_BY_OUTSIDE_CLOSED_SET } from "../../host-decision.js";
|
|
3
4
|
import { sameExecutionRecord } from "@sema-agent/core";
|
|
5
|
+
import { findParkedAgentForCheckpoint } from "../../parked-decide.js";
|
|
4
6
|
import { DECIDED_REPLAY_SCAN_CAP, approvalPayloadFingerprint } from "../../plugins/checkpoint-store-sql.js";
|
|
5
7
|
import { carriesDeploymentListQuestion, standingAnswerDurablyRecordable } from "../../tool-approval.js";
|
|
6
8
|
import { APPROVAL_GATE_KINDS } from "../../approval-content-kind.js";
|
|
@@ -24,10 +26,9 @@ import { callGrantBindingOf, questionGrantBindingOf } from "../../standing-answe
|
|
|
24
26
|
import { sendResumeOutcome } from "../route-ctx.js";
|
|
25
27
|
import { assertNever } from "../route-ctx.js";
|
|
26
28
|
import * as facts from "../../capability-facts.js";
|
|
27
|
-
import {
|
|
28
|
-
import { StoreCorruptError } from "../../plugins/sql-json-column.js";
|
|
29
|
+
import { storeFaultClassOf } from "../../plugins/store-fault.js";
|
|
29
30
|
import { recordFailOpen } from "../../observability/fail-open.js";
|
|
30
|
-
import { durableRowIdentity } from "../../checkpoint-identity.js";
|
|
31
|
+
import { MAX_CHECKPOINT_ID_CHARS, durableRowIdentity } from "../../checkpoint-identity.js";
|
|
31
32
|
import { MAX_KEY_ECHO, keyClosureClause, scanKeyClosure } from "../../request-key-closure.js";
|
|
32
33
|
import { fieldConflictFacts } from "../../field-conflict.js";
|
|
33
34
|
const DECIDE_BODY_DECLARED_KEYS = [
|
|
@@ -36,14 +37,15 @@ const DECIDE_BODY_DECLARED_KEYS = [
|
|
|
36
37
|
const DECIDE_PROOF_COVERED_KEYS = ["answer", "boundCallId", "boundInputHash", "decision", "reason"];
|
|
37
38
|
const DECIDE_NOT_IN_PROOF_KEYS = ["checkpointToken", "decidedBy", "feedback", "remember", "updatedInput"];
|
|
38
39
|
const PLAN_REVIEW_BODY_DECLARED_KEYS = [
|
|
39
|
-
"boundCallId", "decidedBy", "decision", "editedPlan", "permissionModeAfter", "reason",
|
|
40
|
+
"boundCallId", "checkpointId", "decidedBy", "decision", "editedPlan", "permissionModeAfter", "reason",
|
|
40
41
|
];
|
|
41
|
-
const PLAN_REVIEW_PROOF_COVERED_KEYS = ["boundCallId", "decision", "editedPlan", "permissionModeAfter", "reason"];
|
|
42
|
+
const PLAN_REVIEW_PROOF_COVERED_KEYS = ["boundCallId", "checkpointId", "decision", "editedPlan", "permissionModeAfter", "reason"];
|
|
42
43
|
const PLAN_REVIEW_NOT_IN_PROOF_KEYS = ["decidedBy"];
|
|
43
44
|
const PROOF_SLOT_OF = {
|
|
44
45
|
answer: "answer",
|
|
45
46
|
boundCallId: "boundCallId",
|
|
46
47
|
boundInputHash: "boundInputHash",
|
|
48
|
+
checkpointId: "checkpointId",
|
|
47
49
|
decision: "decision",
|
|
48
50
|
editedPlan: "boundInputHash",
|
|
49
51
|
permissionModeAfter: "permissionModeAfter",
|
|
@@ -260,6 +262,26 @@ async function decideIdempotentReplay(cs, args) {
|
|
|
260
262
|
},
|
|
261
263
|
};
|
|
262
264
|
}
|
|
265
|
+
async function gateObjectivePreview(reader, runStore, agentStore, gate) {
|
|
266
|
+
const previewOf = async (taskId) => readOwnedRun(reader.config, runStore, reader.principal, taskId, "owner_or_operator");
|
|
267
|
+
const own = (await runStore.getActiveTaskId(gate.sessionId)) ?? (await runStore.latestRunForSession(gate.sessionId))?.taskId;
|
|
268
|
+
if (own !== undefined)
|
|
269
|
+
return (await previewOf(own))?.objectivePreview ?? null;
|
|
270
|
+
if (agentStore === undefined)
|
|
271
|
+
return null;
|
|
272
|
+
let row = (await findParkedAgentForCheckpoint(agentStore, gate))?.row;
|
|
273
|
+
for (let hop = 0; row !== undefined && hop < MAX_DELEGATION_HOPS; hop++) {
|
|
274
|
+
const spawner = row.parentTaskId;
|
|
275
|
+
if (spawner === undefined)
|
|
276
|
+
return null;
|
|
277
|
+
const run = await previewOf(spawner);
|
|
278
|
+
if (run !== undefined)
|
|
279
|
+
return run.objectivePreview ?? null;
|
|
280
|
+
row = (await agentStore.get(spawner, row.scope)) ?? undefined;
|
|
281
|
+
}
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
const MAX_DELEGATION_HOPS = 8;
|
|
263
285
|
export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
264
286
|
const { deps } = ctx;
|
|
265
287
|
const { preemptableRuns } = ctx.registry;
|
|
@@ -357,13 +379,18 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
357
379
|
sendApprovalsReadFailure(req, res, deps.logger, err);
|
|
358
380
|
return;
|
|
359
381
|
}
|
|
382
|
+
const ledger = facts.durableRuns(deps);
|
|
383
|
+
const agentStore = deps.backgroundAgentStore;
|
|
360
384
|
const inbox = (await Promise.all(summaries.map(async (s) => {
|
|
361
|
-
|
|
362
|
-
if (
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
385
|
+
let objective = null;
|
|
386
|
+
if (ledger.available) {
|
|
387
|
+
try {
|
|
388
|
+
objective = await gateObjectivePreview({ config: deps.config, principal }, ledger.runStore, agentStore, s);
|
|
389
|
+
}
|
|
390
|
+
catch (err) {
|
|
391
|
+
recordFailOpen("server.assistant-inbox.objective-preview-unreadable", `run row read for the inbox row of session ${JSON.stringify(s.sessionId)} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
367
394
|
const input = s.toolInput != null ? redactedPreview(s.toolInput) : null;
|
|
368
395
|
return {
|
|
369
396
|
sessionId: s.sessionId,
|
|
@@ -380,9 +407,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
380
407
|
...(s.mandate !== undefined ? { mandate: s.mandate } : {}),
|
|
381
408
|
...(s.denialLimitFallback !== undefined ? { denialLimitFallback: s.denialLimitFallback } : {}),
|
|
382
409
|
...(s.origin !== undefined ? { origin: s.origin } : {}),
|
|
383
|
-
...(s
|
|
384
|
-
...(s.boundGrants !== undefined ? { boundGrants: s.boundGrants } : {}),
|
|
385
|
-
...(s.classifierNoRuling !== undefined ? { classifierNoRuling: s.classifierNoRuling } : {}),
|
|
410
|
+
...skillCardFacts(s),
|
|
386
411
|
...(s.boundGrantAbsence !== undefined ? { boundGrantAbsence: s.boundGrantAbsence } : {}),
|
|
387
412
|
objective,
|
|
388
413
|
input,
|
|
@@ -601,6 +626,17 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
601
626
|
}
|
|
602
627
|
}
|
|
603
628
|
const planNonce = typeof body.boundCallId === "string" ? body.boundCallId : undefined;
|
|
629
|
+
if (body.checkpointId !== undefined) {
|
|
630
|
+
if (typeof body.checkpointId !== "string" || body.checkpointId.length === 0) {
|
|
631
|
+
sendError(res, 400, "request.field_invalid", "checkpointId must be a non-empty string (the identity of the plan gate you decided on: read terminal.checkpointId on the run result, or the checkpointId on its approvals / inbox entry)");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (body.checkpointId.length > MAX_CHECKPOINT_ID_CHARS) {
|
|
635
|
+
sendError(res, 400, "request.field_invalid", `checkpointId too large (max ${MAX_CHECKPOINT_ID_CHARS} chars)`);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const sighting = typeof body.checkpointId === "string" ? { face: "checkpointId", value: body.checkpointId } : undefined;
|
|
604
640
|
const run = await runStore.getRun(taskId);
|
|
605
641
|
if (!run) {
|
|
606
642
|
if (deps.config.directDoorActive) {
|
|
@@ -623,6 +659,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
623
659
|
const hdr = (n) => { const h = req.headers[n]; return Array.isArray(h) ? h[0] : h; };
|
|
624
660
|
const proof = verifyDirectDoorProof({ jwt: hdr(PRINCIPAL_TOKEN_HEADER), mac: hdr(APPROVAL_MAC_HEADER), kid: hdr(APPROVAL_MAC_KID_HEADER) }, buildApprovalEnvelope(run.sessionId, PLAN_REVIEW_PROOF_COVERED_KEYS, {
|
|
625
661
|
boundCallId: planNonce ?? null,
|
|
662
|
+
checkpointId: sighting?.value,
|
|
626
663
|
decision,
|
|
627
664
|
editedPlan: decision === "edit" ? editedPlanBinding(body.editedPlan) : null,
|
|
628
665
|
permissionModeAfter: isPlanApprovePermissionModeAfter(body.permissionModeAfter) ? body.permissionModeAfter : undefined,
|
|
@@ -641,7 +678,7 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
641
678
|
sendError(res, 404, "not_found.run", "task not found");
|
|
642
679
|
return;
|
|
643
680
|
}
|
|
644
|
-
const plan = await preparePlanReview(run.sessionId, { taskId, principalPresent: principal !== undefined }, { principal: deciderPrincipal, explicitOperator });
|
|
681
|
+
const plan = await preparePlanReview(run.sessionId, { taskId, principalPresent: principal !== undefined }, { principal: deciderPrincipal, explicitOperator }, sighting);
|
|
645
682
|
if (!plan.ok) {
|
|
646
683
|
sendResumeOutcome(res, plan.answer, deps.logger);
|
|
647
684
|
return;
|
|
@@ -871,7 +908,19 @@ export async function handleApprovalsAssistant(req, res, match, ctx) {
|
|
|
871
908
|
}
|
|
872
909
|
const APPROVALS_STREAM_POLL_MS = 3000;
|
|
873
910
|
function approvalsReadFailureOf(err) {
|
|
874
|
-
|
|
911
|
+
const fault = storeFaultClassOf(err);
|
|
912
|
+
switch (fault) {
|
|
913
|
+
case "corrupt":
|
|
914
|
+
return "approvals.read_corrupt";
|
|
915
|
+
case "connection":
|
|
916
|
+
case "deployment":
|
|
917
|
+
case "unclassified":
|
|
918
|
+
return "approvals.read_unavailable";
|
|
919
|
+
default: {
|
|
920
|
+
const unhandled = fault;
|
|
921
|
+
throw new Error(`store fault class without an approvals read answer: ${String(unhandled)}`);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
875
924
|
}
|
|
876
925
|
function sendApprovalsReadFailure(req, res, logger, err) {
|
|
877
926
|
logRequestError(logger, req, err);
|
|
@@ -23,6 +23,7 @@ import { projectWriteProtectionCapability } from "../../write-protection.js";
|
|
|
23
23
|
import { projectStoreCapability } from "../../store-posture.js";
|
|
24
24
|
import { projectWebSearchCapability } from "../../plugins/web-search.js";
|
|
25
25
|
import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
|
|
26
|
+
import { judgeSandboxGitPlane, sandboxGitPlanePremisesOf } from "../../sandbox-git-plane.js";
|
|
26
27
|
import { MCP_ASSERTION_HEADER_SENT } from "../../mcp-assertion-header.js";
|
|
27
28
|
import { gateCompositeMembers } from "../../capabilities/composite-members.js";
|
|
28
29
|
import { projectApprovalsStreamLiveCapability } from "../../capabilities/approvals-push.js";
|
|
@@ -88,7 +89,14 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
88
89
|
approvalReadRootGrant: facts.readRootGrant(deps).available,
|
|
89
90
|
sessions: facts.sessionAuditView(deps).available,
|
|
90
91
|
deviceExecutor: facts.deviceLane(deps).available
|
|
91
|
-
? {
|
|
92
|
+
? {
|
|
93
|
+
enabled: true,
|
|
94
|
+
protocolVersion: DEVICE_PROTOCOL_VERSION,
|
|
95
|
+
maxInflightPerDevice: deps.config.remoteExec?.provider === "device" ? (deps.config.remoteExec.maxInflightPerDevice ?? 4) : 4,
|
|
96
|
+
wsPath: DEVICE_WS_PATH,
|
|
97
|
+
management: facts.deviceManagement(deps).available,
|
|
98
|
+
...(deps.config.remoteExec?.provider === "device" && deps.config.remoteExec.relayUrl !== undefined ? { relayUrl: deps.config.remoteExec.relayUrl } : {}),
|
|
99
|
+
}
|
|
92
100
|
: false,
|
|
93
101
|
sessionEvents: facts.sessionEventStream(deps).available,
|
|
94
102
|
fleet: facts.fleetStream(deps).available
|
|
@@ -180,6 +188,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
180
188
|
approvalFrameFamilies: [...facts.approvalFrameFamilies(streamApprovalFact)],
|
|
181
189
|
approvalDecisionNote: true,
|
|
182
190
|
approvalDecidedBy: true,
|
|
191
|
+
planReviewCheckpointId: true,
|
|
183
192
|
permissionModes: [...PERMISSION_MODES],
|
|
184
193
|
sessionReadRedaction: true,
|
|
185
194
|
sideQuery: true,
|
|
@@ -224,6 +233,7 @@ export async function handleCapabilities(req, res, match, ctx) {
|
|
|
224
233
|
configCatalog: facts.configCatalog(deps).available,
|
|
225
234
|
sealedKey: projectSealedKeyCapability(deps.sealedKeyPublic),
|
|
226
235
|
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)),
|
|
236
|
+
credentialPlane: { sandboxGit: deps.sandboxGitPlane ?? judgeSandboxGitPlane(sandboxGitPlanePremisesOf(deps.config, {})) },
|
|
227
237
|
}));
|
|
228
238
|
return;
|
|
229
239
|
}
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
*
|
|
27
27
|
* ── 下一步动作按 `status`,不解析 `reason`(core [ref] 键表逐字)────────────────────────────────
|
|
28
28
|
* `status.errorCode` / `status.httpStatus` 是机器面(`http_status` 401 是一扇重新授权的门,`spawn_failed` 是
|
|
29
|
-
* 缺二进制);`reason`
|
|
29
|
+
* 缺二进制);`reason` 是**人读**的一句话(它可能嵌远端文本:引擎铸点抹过请求回显与同主体词表,本仓那一遍只读引擎没抹的段、定长截断后投出 —— `http/mcp-remote-text.ts`),
|
|
30
30
|
* 消费端**不许**解析它。名册**按 `tools` 的在场性**投成 `toolNames`(present-iff),**不按 `outcome`**。
|
|
31
31
|
*
|
|
32
32
|
* ── 查找与拒绝形(与 steer / capture-optout **同一只**,不新造)───────────────────────────────────
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { isParkedRunStatus, isTerminalRunStatus, runProvenElsewhere } from "../../plugins/store-contracts.js";
|
|
3
|
-
import { redactSecrets } from "../../trace/redact.js";
|
|
4
3
|
import { DeadlineExceeded, MCP_DIAL_DEADLINE_MS, raceDeadline } from "../mcp-dial-deadline.js";
|
|
5
4
|
import { sendJson, sendError } from "../send.js";
|
|
6
5
|
import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
|
|
@@ -8,9 +7,8 @@ import {} from "../route-ctx.js";
|
|
|
8
7
|
import { assertNever, SESSION_NOT_FOUND_MESSAGE } from "../route-ctx.js";
|
|
9
8
|
import * as facts from "../../capability-facts.js";
|
|
10
9
|
import { sessionOwnerCellOf } from "../../security.js";
|
|
10
|
+
import { redactMcpRemoteText } from "../mcp-remote-text.js";
|
|
11
11
|
const BodySchema = z.object({ server: z.string().min(1).max(190) }).strict();
|
|
12
|
-
const NEUTRALIZED_TEXT_MAX = 200;
|
|
13
|
-
const humanText = (s) => redactSecrets(s).slice(0, NEUTRALIZED_TEXT_MAX);
|
|
14
12
|
function outcomeOf(coreOutcome) {
|
|
15
13
|
switch (coreOutcome) {
|
|
16
14
|
case "reconnected": return "accepted";
|
|
@@ -27,7 +25,7 @@ function projectStatus(s) {
|
|
|
27
25
|
...(s.httpStatus !== undefined ? { httpStatus: s.httpStatus } : {}),
|
|
28
26
|
...(s.delivered !== undefined ? { delivered: s.delivered } : {}),
|
|
29
27
|
...(s.serverInfo !== undefined ? { serverInfo: s.serverInfo } : {}),
|
|
30
|
-
...(s.error !== undefined ? { error:
|
|
28
|
+
...(s.error !== undefined ? { error: redactMcpRemoteText(String(s.error)) } : {}),
|
|
31
29
|
...(s.transportClosed !== undefined ? { transportClosed: s.transportClosed } : {}),
|
|
32
30
|
};
|
|
33
31
|
}
|
|
@@ -36,7 +34,7 @@ function projectListingIncomplete(l) {
|
|
|
36
34
|
reason: l.reason,
|
|
37
35
|
pages: l.pages,
|
|
38
36
|
...(l.budgetMs !== undefined ? { budgetMs: l.budgetMs } : {}),
|
|
39
|
-
...(l.error !== undefined ? { error:
|
|
37
|
+
...(l.error !== undefined ? { error: redactMcpRemoteText(l.error) } : {}),
|
|
40
38
|
};
|
|
41
39
|
}
|
|
42
40
|
function projectResult(r) {
|
|
@@ -48,7 +46,7 @@ function projectResult(r) {
|
|
|
48
46
|
added: r.added,
|
|
49
47
|
removed: r.removed,
|
|
50
48
|
...(r.tools !== undefined ? { toolNames: r.tools.map((t) => t.name) } : {}),
|
|
51
|
-
...(r.reason !== undefined ? { reason:
|
|
49
|
+
...(r.reason !== undefined ? { reason: redactMcpRemoteText(r.reason) } : {}),
|
|
52
50
|
...(r.status !== undefined ? { status: projectStatus(r.status) } : {}),
|
|
53
51
|
...(r.listingIncomplete !== undefined ? { listingIncomplete: projectListingIncomplete(r.listingIncomplete) } : {}),
|
|
54
52
|
};
|
|
@@ -114,7 +112,7 @@ export async function handleMcpReconnect(req, res, match, ctx) {
|
|
|
114
112
|
const msg = e instanceof Error ? e.message : String(e);
|
|
115
113
|
switch (code) {
|
|
116
114
|
case "mcp.reconnect_unavailable":
|
|
117
|
-
sendJson(res, 200, { taskId: activeTaskId, sessionId, server, outcome: "unsupported", reason:
|
|
115
|
+
sendJson(res, 200, { taskId: activeTaskId, sessionId, server, outcome: "unsupported", reason: redactMcpRemoteText(msg) });
|
|
118
116
|
return;
|
|
119
117
|
case "steering.invalid_content":
|
|
120
118
|
sendError(res, 422, "steering.invalid_content", msg);
|