@sema-agent/server 7.114.0 → 7.116.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.
Files changed (159) hide show
  1. package/MIGRATION.md +237 -2
  2. package/USAGE.md +39 -3
  3. package/deploy/sema-up/cluster-up.sh +13 -1
  4. package/deploy/sema-up/kube-up.sh +13 -1
  5. package/deploy/sema-up/preflight.sh +13 -1
  6. package/deploy/sema-up/sema-up.sh +13 -1
  7. package/deploy/sema-up/smoke.sh +13 -1
  8. package/dist/adoption/wire.d.ts +0 -3
  9. package/dist/approval-ask-audit-store.d.ts +25 -6
  10. package/dist/approval-ask-audit-store.js +6 -1
  11. package/dist/approval-card.d.ts +25 -24
  12. package/dist/approval-card.js +14 -0
  13. package/dist/approval-crash-convergence.d.ts +60 -0
  14. package/dist/approval-crash-convergence.js +75 -0
  15. package/dist/approval-deny-reasons.d.ts +13 -0
  16. package/dist/approval-deny-reasons.js +2 -0
  17. package/dist/approval.js +7 -3
  18. package/dist/boot/coordinators.js +0 -12
  19. package/dist/boot/execution-env.js +2 -1
  20. package/dist/boot/exit-boundary.d.ts +3 -2
  21. package/dist/boot/exit-boundary.js +3 -0
  22. package/dist/boot/resolve-spec.js +5 -1
  23. package/dist/boot/shutdown.d.ts +19 -0
  24. package/dist/boot/shutdown.js +12 -1
  25. package/dist/boot/stage-07-capability-layer.js +21 -15
  26. package/dist/boot/stores.js +4 -4
  27. package/dist/capabilities/composite-members.d.ts +1 -0
  28. package/dist/capabilities/composite-members.js +1 -0
  29. package/dist/capabilities/hands-lane.d.ts +0 -23
  30. package/dist/capabilities/hands-lane.js +3 -2
  31. package/dist/capabilities/send-user-file-tool.d.ts +10 -22
  32. package/dist/capabilities/send-user-file-tool.js +1 -8
  33. package/dist/capability-facts.d.ts +296 -0
  34. package/dist/capability-facts.js +354 -0
  35. package/dist/config-catalog.js +2 -1
  36. package/dist/config-center/apply-effective.js +11 -11
  37. package/dist/config-provider.d.ts +16 -0
  38. package/dist/config-provider.js +11 -2
  39. package/dist/config-types.d.ts +20 -1
  40. package/dist/config.d.ts +55 -29
  41. package/dist/config.js +48 -25
  42. package/dist/deployment-governance.d.ts +15 -4
  43. package/dist/deployment-governance.js +20 -2
  44. package/dist/fleet/fleet-bus.d.ts +1 -22
  45. package/dist/hooks/hook-runner.d.ts +29 -3
  46. package/dist/hooks/hook-runner.js +30 -17
  47. package/dist/http/admission.d.ts +6 -0
  48. package/dist/http/admission.js +29 -7
  49. package/dist/http/approval-sweeps.js +20 -0
  50. package/dist/http/checkpoint-ctx.d.ts +68 -0
  51. package/dist/http/checkpoint-ctx.js +36 -0
  52. package/dist/http/durable-run-leg.js +2 -1
  53. package/dist/http/principal-gate.d.ts +3 -0
  54. package/dist/http/principal-gate.js +3 -0
  55. package/dist/http/resume-legs.js +19 -1
  56. package/dist/http/retired-skill-manifest.d.ts +23 -0
  57. package/dist/http/retired-skill-manifest.js +22 -0
  58. package/dist/http/route-ctx.d.ts +7 -0
  59. package/dist/http/route-table.d.ts +3 -2
  60. package/dist/http/route-table.js +5 -5
  61. package/dist/http/routes/a2a-serve.d.ts +1 -3
  62. package/dist/http/routes/a2a-serve.js +14 -23
  63. package/dist/http/routes/admin-config-refresh.js +7 -2
  64. package/dist/http/routes/admin-drain.js +7 -2
  65. package/dist/http/routes/admin-memory-optout.d.ts +0 -2
  66. package/dist/http/routes/admin-memory-optout.js +20 -7
  67. package/dist/http/routes/adoption.js +19 -5
  68. package/dist/http/routes/agents-roster.js +4 -2
  69. package/dist/http/routes/approvals-assistant.js +41 -18
  70. package/dist/http/routes/background.d.ts +0 -2
  71. package/dist/http/routes/background.js +4 -5
  72. package/dist/http/routes/capabilities.d.ts +2 -1
  73. package/dist/http/routes/capabilities.js +63 -65
  74. package/dist/http/routes/config-catalog.d.ts +4 -5
  75. package/dist/http/routes/config-catalog.js +16 -2
  76. package/dist/http/routes/devices.js +17 -11
  77. package/dist/http/routes/diagnostics.d.ts +5 -0
  78. package/dist/http/routes/diagnostics.js +8 -1
  79. package/dist/http/routes/fleet.js +4 -2
  80. package/dist/http/routes/images.js +30 -3
  81. package/dist/http/routes/leader.js +7 -1
  82. package/dist/http/routes/mcp-reconnect.js +17 -10
  83. package/dist/http/routes/memory-bundle.js +20 -6
  84. package/dist/http/routes/memory-compliance.js +19 -5
  85. package/dist/http/routes/memory-consolidation.d.ts +9 -12
  86. package/dist/http/routes/memory-consolidation.js +17 -3
  87. package/dist/http/routes/memory-origin.js +19 -5
  88. package/dist/http/routes/notify-wake.js +5 -3
  89. package/dist/http/routes/observability.js +19 -5
  90. package/dist/http/routes/retention-ops.js +6 -1
  91. package/dist/http/routes/rules.js +49 -21
  92. package/dist/http/routes/run-memory-capture.js +5 -2
  93. package/dist/http/routes/runs.js +90 -60
  94. package/dist/http/routes/session-sync.js +64 -53
  95. package/dist/http/routes/sessions-list.js +8 -5
  96. package/dist/http/routes/sessions.js +40 -25
  97. package/dist/http/routes/shared-memory.js +5 -3
  98. package/dist/http/routes/tasks.js +5 -3
  99. package/dist/http/routes/trace-usage.js +18 -11
  100. package/dist/http/routes/workflows.js +36 -13
  101. package/dist/http/server.js +4 -1
  102. package/dist/http/wire-types.d.ts +6 -0
  103. package/dist/index.d.ts +1 -1
  104. package/dist/leader/wire.js +2 -1
  105. package/dist/local-data-root.d.ts +1 -1
  106. package/dist/mcp-content-origin.d.ts +0 -8
  107. package/dist/model-entry-refusal.d.ts +10 -2
  108. package/dist/model-entry-refusal.js +19 -3
  109. package/dist/namespaced-rule-name.d.ts +54 -0
  110. package/dist/namespaced-rule-name.js +33 -0
  111. package/dist/observability/corrupt-read-seat.d.ts +4 -3
  112. package/dist/observability/corrupt-read-seat.js +4 -3
  113. package/dist/observability/fail-open.d.ts +10 -9
  114. package/dist/observability/fail-open.js +5 -1
  115. package/dist/observability/permission-rule-events.d.ts +3 -0
  116. package/dist/observability/permission-rule-events.js +4 -0
  117. package/dist/permission-rule-list.d.ts +81 -0
  118. package/dist/permission-rule-list.js +17 -0
  119. package/dist/plugins/file-run-store.d.ts +7 -1
  120. package/dist/plugins/file-run-store.js +20 -7
  121. package/dist/plugins/memory-run-store.js +2 -1
  122. package/dist/plugins/one-time-migrations.d.ts +4 -2
  123. package/dist/plugins/one-time-migrations.js +26 -3
  124. package/dist/plugins/pg-session-storage.js +1 -1
  125. package/dist/plugins/run-store-sql.js +4 -4
  126. package/dist/plugins/session-policy-store-sql.js +4 -2
  127. package/dist/plugins/store-backend.d.ts +6 -1
  128. package/dist/plugins/store-backend.js +2 -1
  129. package/dist/plugins/store-contracts.d.ts +27 -8
  130. package/dist/plugins/store-contracts.js +12 -3
  131. package/dist/plugins/tidb-session-store.js +1 -1
  132. package/dist/request-key-closure.js +1 -1
  133. package/dist/run-local.d.ts +37 -3
  134. package/dist/run-local.js +52 -6
  135. package/dist/runs.d.ts +0 -58
  136. package/dist/runs.js +15 -20
  137. package/dist/runtime-governance.d.ts +21 -1
  138. package/dist/runtime-governance.js +6 -1
  139. package/dist/security.js +2 -2
  140. package/dist/session-policy-wire.d.ts +40 -59
  141. package/dist/session-policy-wire.js +60 -32
  142. package/dist/spec-fields.d.ts +32 -0
  143. package/dist/spec-fields.js +31 -16
  144. package/dist/standing-answer-grant.d.ts +8 -0
  145. package/dist/standing-answer-grant.js +5 -0
  146. package/dist/task-settings.d.ts +17 -6
  147. package/dist/task-settings.js +16 -30
  148. package/dist/tool-approval.d.ts +32 -4
  149. package/dist/tool-approval.js +31 -8
  150. package/dist/trace/core-keyset-guard.d.ts +13 -8
  151. package/dist/trace/project.js +9 -0
  152. package/dist/trace/self-minted-wire.d.ts +42 -0
  153. package/dist/trace/self-minted-wire.js +13 -0
  154. package/dist/user-input-ledger.d.ts +8 -5
  155. package/package.json +6 -4
  156. package/scripts/drain-check-local.mjs +528 -0
  157. package/scripts/drain-check.mysql.sql +67 -0
  158. package/scripts/drain-check.pg.sql +71 -0
  159. package/scripts/retired-mode-seed-drain-local.mjs +0 -317
@@ -0,0 +1,354 @@
1
+ import { mcpInjectionHonored } from "./task-mcp.js";
2
+ import { resolveStreamApprovalGate } from "./tool-approval.js";
3
+ import { selfOrchestrationDenial } from "./task-workflow.js";
4
+ import { centerEntitlementSourceWired } from "./runtime-caps-resolver.js";
5
+ import { overwriteWipeIncapable } from "./session-sync.js";
6
+ export const CAPABILITY_FACT_WHY = {
7
+ run_store_absent: "no durable run store is wired (DB_BACKEND=mysql|pg|local)",
8
+ subagent_registry_absent: "the sub-agent steer registry is not wired",
9
+ subagent_output_face_absent: "the background sub-agent output face is not wired",
10
+ task_handle_faces_absent: "the background task-handle output/stop faces are not wired",
11
+ session_owner_face_absent: "the session store has no ownership face (sessionStorage.ownerOf)",
12
+ checkpoint_store_absent: "no durable checkpoint store is wired (DURABLE_APPROVAL)",
13
+ session_audit_absent: "no session audit view is wired (needs a durable session store)",
14
+ session_watch_absent: "the session watch registry is not wired",
15
+ session_leaf_face_absent: "the session store has no leaf-read face (sessionStorage.getLeafId)",
16
+ session_lister_absent: "no session enumerator is wired (session store or run store listSessions)",
17
+ session_fork_face_absent: "the session store has no fork face",
18
+ session_purge_absent: "no session purge coordinator is wired",
19
+ session_policy_store_absent: "no session policy store is wired",
20
+ store_backend_absent: "no store backend is wired (DB_BACKEND=memory, multi-tenant bare boot, or half-configured SQL)",
21
+ file_history_store_absent: "no file-history store is wired",
22
+ session_export_absent: "the session store has no entry export faces (exportEntries / exportEntriesStream)",
23
+ session_import_staging_absent: "the session store has no staged-import face (beginImportStaging)",
24
+ policy_purge_absent: "the session policy store has no deleteBySession purge verb",
25
+ sync_blob_faces_absent: "the file-history store has no blob transfer faces (SQL backends only)",
26
+ device_hub_absent: "the device lane hub is not wired",
27
+ device_store_absent: "the device store is not wired",
28
+ device_enrollment_absent: "device enrollment is not wired",
29
+ fleet_bus_absent: "the fleet event bus is not wired",
30
+ workspace_retired: "the workspace browse face was retired with the whole-tree snapshot epoch",
31
+ leader_endpoint_absent: "the leader endpoint is not wired",
32
+ image_index_absent: "no image index is wired (SQL backends only)",
33
+ shared_memory_store_absent: "no shared memory store provider is wired",
34
+ org_memory_directory_absent: "no org memory directory (membership fold) is wired",
35
+ operator_principals_empty: "OPERATOR_PRINCIPALS is empty, so no caller can ever be an operator",
36
+ adoption_store_absent: "no adoption log store is wired (SQL backends only)",
37
+ memory_bundle_faces_absent: "the memory engine's governance bundle faces are not wired",
38
+ memory_compliance_faces_absent: "the memory engine's provenance/erasure faces are not wired",
39
+ memory_origin_faces_absent: "the memory engine's origin marking faces are not wired",
40
+ consolidation_valve_closed: "the memory consolidation valve is closed (MEMORY_CONSOLIDATION_DRIVER)",
41
+ memory_optout_grant_absent: "no memory-capture opt-out grant store is wired (SQL backends only)",
42
+ outcome_ledger_unqueryable: "the outcome sink has no query face (File sink)",
43
+ config_catalog_disabled: "the config catalog is switched off (CONFIG_CATALOG_ENABLED=false)",
44
+ rule_store_absent: "no permission-rule store is wired",
45
+ caller_principal_absent: "this request carries no verified principal",
46
+ caller_mcp_injection_refused: "this deployment does not honor caller-supplied MCP servers",
47
+ a2a_serve_off: "A2A_SERVE_ENABLED is off",
48
+ elicitation_disabled: "inbound MCP elicitation is off (MCP_ELICITATION_ENABLED)",
49
+ ask_question_disabled: "AskUserQuestion live HITL is off (ASK_QUESTION_ENABLED)",
50
+ tool_approval_disabled: "live tool approval is off (TOOL_APPROVAL_ENABLED)",
51
+ stream_approval_disabled: "the stream approval protocol is off (STREAM_APPROVAL_ENABLED)",
52
+ background_agent_store_absent: "no background-agent store is wired",
53
+ sendfile_ledger_absent: "no SendUserFile ledger is wired",
54
+ workflow_engine_absent: "the workflow engine is not available on this worker",
55
+ entitlement_resolver_absent: "self-orchestration is on for a multi-tenant worker with no per-principal entitlement source",
56
+ workflow_run_store_absent: "no workflow run store is wired (SELF_ORCHESTRATION_ENABLED)",
57
+ workflow_journal_store_absent: "no workflow journal store is wired (needs a store backend)",
58
+ scheduler_disabled: "the self-wake scheduler is off (SCHEDULER_ENABLED)",
59
+ multi_tenant_deployment: "this is a multi-tenant deployment (REQUIRE_PRINCIPAL=true)",
60
+ not_host_lane: "the execution lane is not the single-user host lane (REMOTE_EXEC=host)",
61
+ };
62
+ const no = (why) => ({ available: false, why });
63
+ const yes = (w) => ({ available: true, ...w });
64
+ const YES = yes({});
65
+ export function durableRuns(d) {
66
+ return d.runStore !== undefined ? yes({ runStore: d.runStore }) : no("run_store_absent");
67
+ }
68
+ export function subagentSteer(d) {
69
+ const runs = durableRuns(d);
70
+ if (!runs.available)
71
+ return runs;
72
+ return d.subagentSteerRegistry !== undefined ? yes({ runStore: runs.runStore, registry: d.subagentSteerRegistry }) : no("subagent_registry_absent");
73
+ }
74
+ export function subagentOutput(d) {
75
+ const runs = durableRuns(d);
76
+ if (!runs.available)
77
+ return runs;
78
+ return d.subagentTaskOutput !== undefined ? yes({ runStore: runs.runStore, output: d.subagentTaskOutput }) : no("subagent_output_face_absent");
79
+ }
80
+ export function taskHandles(d) {
81
+ const runs = durableRuns(d);
82
+ if (!runs.available)
83
+ return runs;
84
+ return d.taskHandleOutput !== undefined && d.taskHandleStop !== undefined
85
+ ? yes({ runStore: runs.runStore, output: d.taskHandleOutput, stop: d.taskHandleStop })
86
+ : no("task_handle_faces_absent");
87
+ }
88
+ export function sessionOwnership(d) {
89
+ const s = d.sessionStorage;
90
+ return s?.ownerOf !== undefined ? yes({ sessionStorage: s }) : no("session_owner_face_absent");
91
+ }
92
+ export function mcpReconnect(d) {
93
+ const runs = durableRuns(d);
94
+ if (!runs.available)
95
+ return runs;
96
+ const owned = sessionOwnership(d);
97
+ if (!owned.available)
98
+ return owned;
99
+ return yes({ runStore: runs.runStore, sessionStorage: owned.sessionStorage });
100
+ }
101
+ export function sessionAuditView(d) {
102
+ return d.sessionAudit !== undefined ? yes({ sessionAudit: d.sessionAudit }) : no("session_audit_absent");
103
+ }
104
+ export function sessionEventStream(d) {
105
+ if (d.sessionWatch === undefined)
106
+ return no("session_watch_absent");
107
+ const s = d.sessionStorage;
108
+ if (s?.getLeafId === undefined)
109
+ return no("session_leaf_face_absent");
110
+ if (s.ownerOf === undefined)
111
+ return no("session_owner_face_absent");
112
+ return yes({ watch: d.sessionWatch, sessionStorage: s });
113
+ }
114
+ export function sessionList(d) {
115
+ const lister = d.sessionStorage?.listSessions?.bind(d.sessionStorage) ?? d.runStore?.listSessions?.bind(d.runStore);
116
+ return lister !== undefined ? yes({ lister }) : no("session_lister_absent");
117
+ }
118
+ export function sessionFork(d) {
119
+ const s = d.sessionStorage;
120
+ if (s?.fork === undefined)
121
+ return no("session_fork_face_absent");
122
+ if (s.ownerOf === undefined)
123
+ return no("session_owner_face_absent");
124
+ return yes({ sessionStorage: s });
125
+ }
126
+ export function sessionDelete(d) {
127
+ if (d.purgeSession === undefined)
128
+ return no("session_purge_absent");
129
+ const owned = sessionOwnership(d);
130
+ if (!owned.available)
131
+ return owned;
132
+ return yes({ purge: d.purgeSession, sessionStorage: owned.sessionStorage });
133
+ }
134
+ export function sessionPolicy(d) {
135
+ if (d.sessionPolicyStore === undefined)
136
+ return no("session_policy_store_absent");
137
+ const owned = sessionOwnership(d);
138
+ if (!owned.available)
139
+ return owned;
140
+ return yes({ policyStore: d.sessionPolicyStore, sessionStorage: owned.sessionStorage });
141
+ }
142
+ export function sessionSync(d) {
143
+ if (d.backend === undefined)
144
+ return no("store_backend_absent");
145
+ const s = d.sessionStorage;
146
+ if (s?.ownerOf === undefined)
147
+ return no("session_owner_face_absent");
148
+ if (d.fileHistoryStore === undefined)
149
+ return no("file_history_store_absent");
150
+ if (s.exportEntries === undefined || s.exportEntriesStream === undefined)
151
+ return no("session_export_absent");
152
+ if (s.beginImportStaging === undefined)
153
+ return no("session_import_staging_absent");
154
+ if (overwriteWipeIncapable(d.sessionPolicyStore))
155
+ return no("policy_purge_absent");
156
+ return yes({ backend: d.backend, sessionStorage: s, historyStore: d.fileHistoryStore });
157
+ }
158
+ export function sessionSyncFileHistory(d) {
159
+ const sync = sessionSync(d);
160
+ if (!sync.available)
161
+ return sync;
162
+ const fs = sync.historyStore;
163
+ return fs.syncBlobFaces === true && typeof fs.putBlob === "function" && typeof fs.getBlob === "function"
164
+ ? yes({ historyStore: fs })
165
+ : no("sync_blob_faces_absent");
166
+ }
167
+ export function checkpointPark(d) {
168
+ return d.checkpointStore !== undefined ? yes({ checkpointStore: d.checkpointStore }) : no("checkpoint_store_absent");
169
+ }
170
+ export function elicitation(d) {
171
+ return d.elicitation !== undefined ? yes({ coordinator: d.elicitation }) : no("elicitation_disabled");
172
+ }
173
+ export function askUserQuestion(d) {
174
+ return d.question !== undefined ? yes({ coordinator: d.question }) : no("ask_question_disabled");
175
+ }
176
+ export function toolApproval(d) {
177
+ return d.toolApproval !== undefined ? yes({ coordinator: d.toolApproval }) : no("tool_approval_disabled");
178
+ }
179
+ export function streamApproval(d) {
180
+ const gate = resolveStreamApprovalGate({
181
+ toolApprovalEnabled: d.toolApproval !== undefined,
182
+ streamApprovalEnabled: d.config.streamApproval?.enabled === true,
183
+ backend: d.backend,
184
+ parkFacility: d.checkpointStore !== undefined,
185
+ });
186
+ if (gate.active)
187
+ return yes({ askStore: gate.askStore });
188
+ switch (gate.reason) {
189
+ case "no_tool_approval":
190
+ return no("tool_approval_disabled");
191
+ case "protocol_disabled":
192
+ return no("stream_approval_disabled");
193
+ case "no_backend":
194
+ return no("store_backend_absent");
195
+ case "no_park_facility":
196
+ return no("checkpoint_store_absent");
197
+ default: {
198
+ const unhandled = gate.reason;
199
+ throw new Error(`unhandled stream-approval gate reason: ${JSON.stringify(unhandled)}`);
200
+ }
201
+ }
202
+ }
203
+ export function deviceLane(d) {
204
+ return d.deviceHub !== undefined ? yes({ hub: d.deviceHub }) : no("device_hub_absent");
205
+ }
206
+ export function deviceManagement(d) {
207
+ const lane = deviceLane(d);
208
+ if (!lane.available)
209
+ return lane;
210
+ if (d.deviceStore === undefined)
211
+ return no("device_store_absent");
212
+ if (d.deviceEnrollment === undefined)
213
+ return no("device_enrollment_absent");
214
+ const runs = durableRuns(d);
215
+ if (!runs.available)
216
+ return runs;
217
+ return yes({ hub: lane.hub, store: d.deviceStore, enrollment: d.deviceEnrollment, runStore: runs.runStore });
218
+ }
219
+ export function fleetStream(d) {
220
+ return d.fleetBus !== undefined ? yes({ fleetBus: d.fleetBus }) : no("fleet_bus_absent");
221
+ }
222
+ export function workspaceBrowse() {
223
+ return no("workspace_retired");
224
+ }
225
+ export function leaderLane(d) {
226
+ return d.leaderEndpoint !== undefined ? yes({ endpoint: d.leaderEndpoint }) : no("leader_endpoint_absent");
227
+ }
228
+ export function imagePool(d) {
229
+ return d.imageIndex !== undefined ? yes({ imageIndex: d.imageIndex }) : no("image_index_absent");
230
+ }
231
+ export function agentRoster(d) {
232
+ return d.backgroundAgentStore !== undefined ? yes({ store: d.backgroundAgentStore }) : no("background_agent_store_absent");
233
+ }
234
+ export function sendFileLedger(d) {
235
+ return d.sendFileLedger !== undefined ? yes({ ledger: d.sendFileLedger }) : no("sendfile_ledger_absent");
236
+ }
237
+ export function sharedMemoryRead(d) {
238
+ if (d.sharedMemoryStore === undefined)
239
+ return no("shared_memory_store_absent");
240
+ if (d.orgMemoryDirectory === undefined)
241
+ return no("org_memory_directory_absent");
242
+ return yes({ provider: d.sharedMemoryStore, directory: d.orgMemoryDirectory });
243
+ }
244
+ export function operatorLane(d) {
245
+ return d.config.operatorPrincipals.length > 0 ? YES : no("operator_principals_empty");
246
+ }
247
+ export function adoption(d) {
248
+ const lane = operatorLane(d);
249
+ if (!lane.available)
250
+ return lane;
251
+ const b = d.backend;
252
+ return b !== undefined && typeof b.adoptionLog === "function" ? yes({ backend: b }) : no("adoption_store_absent");
253
+ }
254
+ export function memoryBundle(d) {
255
+ const lane = operatorLane(d);
256
+ if (!lane.available)
257
+ return lane;
258
+ return d.memoryBundleExport !== undefined && d.memoryBundleImport !== undefined
259
+ ? yes({ exportScopes: d.memoryBundleExport, importBundle: d.memoryBundleImport })
260
+ : no("memory_bundle_faces_absent");
261
+ }
262
+ export function memoryCompliance(d) {
263
+ const lane = operatorLane(d);
264
+ if (!lane.available)
265
+ return lane;
266
+ return d.memoryCompliance !== undefined ? yes({ faces: d.memoryCompliance }) : no("memory_compliance_faces_absent");
267
+ }
268
+ export function memoryOrigin(d) {
269
+ const lane = operatorLane(d);
270
+ if (!lane.available)
271
+ return lane;
272
+ return d.memoryOriginFace !== undefined ? yes({ faces: d.memoryOriginFace }) : no("memory_origin_faces_absent");
273
+ }
274
+ export function memoryConsolidation(d) {
275
+ if (d.memoryConsolidation === undefined)
276
+ return no("consolidation_valve_closed");
277
+ const lane = operatorLane(d);
278
+ return lane.available ? yes({ valve: d.memoryConsolidation }) : lane;
279
+ }
280
+ export function memoryOptOutGrant(d) {
281
+ const lane = operatorLane(d);
282
+ if (!lane.available)
283
+ return lane;
284
+ return d.memoryOptOutGrant !== undefined ? yes({ store: d.memoryOptOutGrant }) : no("memory_optout_grant_absent");
285
+ }
286
+ export function outcomeLedger(d) {
287
+ if (d.config.requirePrincipal === true) {
288
+ const lane = operatorLane(d);
289
+ if (!lane.available)
290
+ return lane;
291
+ }
292
+ const sink = d.outcomeSink;
293
+ return sink?.summary !== undefined ? yes({ sink: sink }) : no("outcome_ledger_unqueryable");
294
+ }
295
+ export function configCatalog(d) {
296
+ if (d.config.configCatalogEnabled !== true)
297
+ return no("config_catalog_disabled");
298
+ return operatorLane(d);
299
+ }
300
+ export function ruleLane(d) {
301
+ return d.ruleConsent !== undefined ? yes({ lane: d.ruleConsent }) : no("rule_store_absent");
302
+ }
303
+ export function ruleLaneForCaller(d, principal) {
304
+ const lane = ruleLane(d);
305
+ if (!lane.available)
306
+ return lane;
307
+ return principal !== undefined ? YES : no("caller_principal_absent");
308
+ }
309
+ export function mcpInjection(d) {
310
+ return mcpInjectionHonored(d.config) ? YES : no("caller_mcp_injection_refused");
311
+ }
312
+ export function a2aServe(d) {
313
+ const serve = d.config.a2aServe;
314
+ if (serve === undefined)
315
+ return no("a2a_serve_off");
316
+ const runs = durableRuns(d);
317
+ return runs.available ? yes({ serve, runStore: runs.runStore }) : runs;
318
+ }
319
+ export function selfOrchestrationEngine(d) {
320
+ return (d.workflowsCapable ?? d.workflowRunStore !== undefined) ? YES : no("workflow_engine_absent");
321
+ }
322
+ export function selfOrchestrationAdmission(d) {
323
+ const denial = selfOrchestrationDenial({ selfOrchestrationEnabled: d.config.selfOrchestrationEnabled, requirePrincipal: d.config.requirePrincipal }, centerEntitlementSourceWired(d.config));
324
+ return denial === null ? YES : no(denial);
325
+ }
326
+ export function selfOrchestration(d) {
327
+ const engine = selfOrchestrationEngine(d);
328
+ if (!engine.available)
329
+ return engine;
330
+ return selfOrchestrationAdmission(d);
331
+ }
332
+ export function workflowRuns(d) {
333
+ return d.workflowRunStore !== undefined ? yes({ store: d.workflowRunStore }) : no("workflow_run_store_absent");
334
+ }
335
+ export function workflowJournal(d) {
336
+ return d.workflowJournalStore !== undefined ? yes({ journal: d.workflowJournalStore }) : no("workflow_journal_store_absent");
337
+ }
338
+ export function workflowsJournal(d) {
339
+ const runs = workflowRuns(d);
340
+ if (!runs.available)
341
+ return runs;
342
+ const journal = workflowJournal(d);
343
+ return journal.available ? yes({ store: runs.store, journal: journal.journal }) : journal;
344
+ }
345
+ export function hostScheduler(c) {
346
+ if (c.schedulerEnabled !== true)
347
+ return no("scheduler_disabled");
348
+ if (c.requirePrincipal === true)
349
+ return no("multi_tenant_deployment");
350
+ if (c.remoteExec?.provider !== "host")
351
+ return no("not_host_lane");
352
+ return YES;
353
+ }
354
+ //# sourceMappingURL=capability-facts.js.map
@@ -58,6 +58,7 @@ export const CONFIG_CATALOG = [
58
58
  r("ANTHROPIC_VERSION", "modelPlane", "string", "Anthropic API version 头(缺省由 SDK 定)"),
59
59
  r("APPROVAL_AUTO_BUDGET", "approval", "number", "每 run-leg 自动放行的普通门控工具调用数上限(0=关;防审批循环的熔断,不是安全 ask 的帽)", { staticDefault: "0", danger: SEC, configKey: "approvalAutoBudget" }),
60
60
  r("APPROVAL_DENY", "approval", "csv", "始终拒绝的工具名单(装配期拒不可匹配名)", { danger: SEC, configKey: "approvalDeny" }),
61
+ r("APPROVAL_GRANT", "approval", "csv", "运维的 allow 规则(工具名规则:精确名 / mcp__<server>__* / a2a__<peer>__*;命中 = 带理由放行,不再为这些调用出「没人表态」卡(不放掉任何一层自己的问);HTTP 提交 / durable 续跑 / 停驻后台 agent 赎回 / run-local 四腿同装(leader worker 首跑与修复腿到不了);不越过任何一层的 deny / ask;不是白名单;坏值拒启)", { danger: SEC, configKey: "approvalGrant", derivedDefaultNote: "缺席 = 空表(本层不表态,折叠与未设逐字同)" }),
61
62
  r("APPROVAL_HMAC_KEYS", "approval", "secret", "直连门(D-G)HMAC 完整性密钥集(空=直连门不激活)", { danger: SEC }),
62
63
  r("APPROVAL_NEVER_AUTO", "approval", "csv", "永不被 AUTO_BUDGET 自动放行的工具集(不可逆动作恒要人)", { danger: SEC, configKey: "approvalNeverAuto" }),
63
64
  r("APPROVAL_REQUIRE", "approval", "csv", "必须人审的工具名单(F4 门;center 腿热:refresh 拍整表换代,无 checkpoint 店的部署发非空名单 ⇒ 审批组整组拒)", { danger: SEC, configKey: "approvalRequire", center: cGov("approvalRequire") }),
@@ -156,7 +157,7 @@ export const CONFIG_CATALOG = [
156
157
  r("HOST_BG_DEFAULT_TIMEOUT_SEC", "orchestration", "number", "host 后台 shell 缺省超时(秒)", { staticDefault: "300" }),
157
158
  r("HOST_BG_ENABLED", "orchestration", "boolean", "host 车道后台 shell 开关", { staticDefault: "true" }),
158
159
  r("HOST_BG_FILE_CAP", "orchestration", "number", "host 后台 shell 磁盘护栏(字节;失控输出被 reap)", { staticDefault: String(64 * 1024 * 1024) }),
159
- r("HOST_BG_KILL_GRACE_MS", "orchestration", "number", "host 后台 shell SIGTERM→SIGKILL 宽限(ms);会话删除 / 会话停止口在软停与硬收之间等同一只", { staticDefault: "1000" }),
160
+ r("HOST_BG_KILL_GRACE_MS", "orchestration", "number", "host 后台 shell SIGTERM→SIGKILL 宽限(ms);会话删除 / 会话停止口在软停与硬收之间等同一只,受控退出(SIGTERM / SIGINT)在软停之后、退出之前也等一次(封顶在停机宽限窗内)", { staticDefault: "1000" }),
160
161
  r("HOST_BG_MAX_CONCURRENT", "orchestration", "number", "host 后台 shell 并发帽", { staticDefault: "8" }),
161
162
  r("HOST_BG_MAX_TIMEOUT_SEC", "orchestration", "number", "host 后台 shell 超时上限(秒)", { staticDefault: "1800" }),
162
163
  r("HOST_BG_MEM_CAP", "orchestration", "number", "host 后台 shell 内存流护栏(字节)", { staticDefault: String(8 * 1024 * 1024) }),
@@ -1,6 +1,6 @@
1
1
  import { resolveActiveTiers } from "@sema-agent/settings-schema";
2
2
  import { modelKeyPoison } from "../key-resolver.js";
3
- import { applyAutoCompactWindow, buildModelCostKey, findUnmatchableToolNames, formatUnmatchableToolNames, parseAutonomy } from "../config.js";
3
+ import { applyAutoCompactWindow, buildModelCostKey, parseAutonomy, unmatchableToolNamesRefusal } from "../config.js";
4
4
  import { buildModelCompat, readModelCompat } from "../model-compat.js";
5
5
  import { buildModelHeadersKey } from "../model-entry-headers.js";
6
6
  import { validateCommandRules } from "../runtime-governance.js";
@@ -9,7 +9,7 @@ import { stageLimits } from "./stage-limits.js";
9
9
  import { APPROVAL_GROUP_KEYS, BATCH1_LIMIT_KEYS } from "./hot-keys-registry.js";
10
10
  import { buildDisplaySafeUrl } from "../trace/redact.js";
11
11
  import { crossInvariant } from "../config-invariants.js";
12
- import { DEFAULT_MODEL_MISSING_CODE, REFUSED_NAME_MINTED_CODE, judgeCatalogEntries, mintedRefusedNames, refusalEventOf, refusalWarnFields, malformedDroppedCarrierRefusal } from "../model-entry-refusal.js";
12
+ import { DEFAULT_MODEL_MISSING_CODE, REFUSED_NAME_MINTED_CODE, judgeCatalogEntries, mintedRefusedNames, refusalEventOf, refusalWarnFields, refusedNameLogField, refusedNamesLogFields, malformedDroppedCarrierRefusal } from "../model-entry-refusal.js";
13
13
  import { judgeModelRouteEndpoint } from "../model-route-endpoint.js";
14
14
  import { gateIntentUnserviceableReason } from "../approval.js";
15
15
  import { refusedGroups } from "./apply-ledger.js";
@@ -314,7 +314,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
314
314
  if (usable.length > 0) {
315
315
  picked = resolveDefaultModelName(eff, (n) => models[n] !== undefined, usable[0].name, (source, name) => logger?.warn("sema_registry_default_dangling", {
316
316
  source,
317
- name,
317
+ ...(Object.hasOwn(verdict.refused, name) ? refusedNameLogField("name", name) : { name }),
318
318
  ...(Object.hasOwn(verdict.refused, name) ? { unavailable: verdict.refused[name].code } : {}),
319
319
  hint: "explicit default names a model that is not in the usable catalog (not enabled, or unavailable on this host — see `unavailable`) — falling to the next source",
320
320
  }));
@@ -327,7 +327,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
327
327
  continue;
328
328
  }
329
329
  const refusal = Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
330
- logger?.warn("sema_registry_tier_binding_unavailable", { tier, model: target, ...(refusal !== undefined ? { code: refusal.code } : {}), hint: "this tier binding names a model that is not in this generation's usable catalog — the binding is dropped (the tier word falls to the next lower bound tier, core's ladder); fix the entry or rebind the tier" });
330
+ logger?.warn("sema_registry_tier_binding_unavailable", { tier, ...(refusal !== undefined ? refusedNameLogField("model", target) : { model: target }), ...(refusal !== undefined ? { code: refusal.code } : {}), hint: "this tier binding names a model that is not in this generation's usable catalog — the binding is dropped (the tier word falls to the next lower bound tier, core's ladder); fix the entry or rebind the tier" });
331
331
  }
332
332
  const nextView = { ...config, models, tiers: activeTiers, modelRefusals: verdict.refused };
333
333
  const noDefault = crossInvariant("default-model-resolvable").check({ live: config, next: nextView });
@@ -343,7 +343,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
343
343
  const target = typeof spec === "string" ? spec : spec !== null && typeof spec === "object" && "model" in spec && typeof spec.model === "string" ? spec.model : undefined;
344
344
  const refusal = target !== undefined && Object.hasOwn(verdict.refused, target) ? verdict.refused[target] : undefined;
345
345
  if (refusal !== undefined) {
346
- logger?.warn("sema_registry_role_binding_unavailable", { role, model: target, code: refusal.code, hint: ROLE_BINDING_UNAVAILABLE_HINT });
346
+ logger?.warn("sema_registry_role_binding_unavailable", { role, ...refusedNameLogField("model", target), code: refusal.code, hint: ROLE_BINDING_UNAVAILABLE_HINT });
347
347
  }
348
348
  }
349
349
  const atModelAllowlist = stageAtModelAllowlist(eff.models?.atModelAllowlist, models, logger);
@@ -357,7 +357,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
357
357
  roles,
358
358
  activeTiers,
359
359
  atModelAllowlist,
360
- infoLine: { count: usable.length, ...(Object.keys(verdict.refused).length > 0 ? { unavailable: Object.keys(verdict.refused) } : {}), default: defaultModel.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers), atModelAllowlist: atModelAllowlist.length, sealedKeys: Object.values(modelApiKeys).filter((v) => typeof v === "string").length, sealedPoisoned: Object.values(modelApiKeys).filter((v) => typeof v !== "string").length },
360
+ infoLine: { count: usable.length, ...(Object.keys(verdict.refused).length > 0 ? refusedNamesLogFields("unavailable", Object.keys(verdict.refused)) : {}), default: defaultModel.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers), atModelAllowlist: atModelAllowlist.length, sealedKeys: Object.values(modelApiKeys).filter((v) => typeof v === "string").length, sealedPoisoned: Object.values(modelApiKeys).filter((v) => typeof v !== "string").length },
361
361
  };
362
362
  return { stages: { models: plane, collab: stageCollabDomain(eff) }, defaultName: picked.name, refused: verdict.refused };
363
363
  }
@@ -476,13 +476,13 @@ function stageApprovalRequire(raw) {
476
476
  return { ok: false, detail, reason: detail, names: nonString.map((e) => `[${e.i}]`), kept: KEPT_WOULD_REMOVE_GATE };
477
477
  }
478
478
  const names = raw.filter((n) => typeof n === "string").map((n) => n.trim()).filter((n) => n.length > 0);
479
- const bad = findUnmatchableToolNames(names, { consumer: "tool-name-table", behavior: "deny" });
480
- if (bad.length > 0) {
479
+ const unmatchable = unmatchableToolNamesRefusal("published governance.approvalRequire", names, { consumer: "tool-name-table", behavior: "deny" });
480
+ if (unmatchable !== undefined) {
481
481
  return {
482
482
  ok: false,
483
- detail: formatUnmatchableToolNames("published governance.approvalRequire", bad),
484
- reason: `published governance.approvalRequire has ${bad.length} entr${bad.length === 1 ? "y" : "ies"} that match no live tool (retired name / incomplete MCP name / CC-form rule) — the offending entries are named in the sema_registry_approval_require_invalid log line`,
485
- names: bad.map((b) => b.name),
483
+ detail: unmatchable.message,
484
+ reason: `published governance.approvalRequire has ${unmatchable.names.length} entr${unmatchable.names.length === 1 ? "y" : "ies"} that match no live tool (retired name / incomplete protocol name / server segment the engine never mints / CC-form rule) — the offending entries are named in the sema_registry_approval_require_invalid log line`,
485
+ names: unmatchable.names,
486
486
  kept: KEPT_WOULD_MATCH_NOTHING,
487
487
  };
488
488
  }
@@ -35,6 +35,22 @@ export type FetchEffectiveResult = {
35
35
  readWarnings: readonly EffectiveReadWarning[];
36
36
  credentialIdentity?: string;
37
37
  } | null;
38
+ /**
39
+ * 引号段 = **一条规则**:先过 `redactSecrets`;文本里没有引号字符(`"` / `'`)⇒ 原样返回;有 ⇒ 从**第一个**引号到**最后一个**引号(含两端,
40
+ * 两种引号不分种类)整段换成**一个**指纹;`"` 的个数是奇数、或 `'` 的个数是奇数(落单 / 句子被截断)⇒ 从第一个引号遮到**文末**。
41
+ * 本函数不辨认任何一个引号是「开头」「收尾」「转义」还是「撇号」—— 生产方原文插值的值自己带引号时,句子里没有信号能把「值里的引号」与「值的边界」
42
+ * 分开,逐个辨认边界的读法每补一条豁免就多一种漏形。
43
+ *
44
+ * 为什么是超集:
45
+ * · 配对读法(`/"[^"]*"|'[^']*'/g`)遮住的每一段都夹在两个引号之间,因此落在「第一个引号 … 最后一个引号」之内;
46
+ * · 生产方放进引号里的值(不论值里带几个引号、怎么转义、撇号在哪)也都落在这一段之内 —— 值左边至少有一个引号、右边至少有一个引号;
47
+ * · 引号个数不成对时遮到文末,只会更宽。
48
+ * 代价:两个操作数之间的固定词与第一个操作数之后的句子不再可读(例:`roster "#…" (not in the models catalog)`,原句里 `references unknown model`
49
+ * 一并进了指纹);首个引号之前的部分(引用完整性腿拼在前面的坐标 `rosters.0.models.0: `、固定句头)与末个引号之后的句尾仍是明文。
50
+ * 仍不覆盖:生产方**不加引号**直接插值的值(本函数看不见值在哪);句子被截断、而两种引号的个数恰好都是偶数的形。真收法在消息生产方给出结构化操作数、
51
+ * 不回声值(上游 settings-schema)。
52
+ */
53
+ export declare function redactConfigError(text: string): string;
38
54
  /** Sentinel returned by {@link raceBootFetch} when the boot pull outlived its budget — the caller
39
55
  * starts serving on the env fallback and the SAME promise settles in the background (never cancelled). */
40
56
  export declare const BOOT_FETCH_DEFERRED: unique symbol;
@@ -8,8 +8,17 @@ import { fetchEffective as remoteFetchEffective, fetchSkillContent as remoteFetc
8
8
  import { homedir } from "node:os";
9
9
  import { join } from "node:path";
10
10
  const OPERAND_FINGERPRINT_KEY = randomBytes(16);
11
- function redactConfigError(text) {
12
- return redactSecrets(text).replace(/"[^"]*"|'[^']*'/g, (m) => `"#${createHmac("sha256", OPERAND_FINGERPRINT_KEY).update(m.slice(1, -1)).digest("hex").slice(0, 8)}"`);
11
+ export function redactConfigError(text) {
12
+ const s = redactSecrets(text);
13
+ const first = s.search(/["']/);
14
+ if (first === -1)
15
+ return s;
16
+ const count = (q) => s.split(q).length - 1;
17
+ const fingerprint = (operand) => `"#${createHmac("sha256", OPERAND_FINGERPRINT_KEY).update(operand).digest("hex").slice(0, 8)}"`;
18
+ if (count('"') % 2 === 1 || count("'") % 2 === 1)
19
+ return `${s.slice(0, first)}${fingerprint(s.slice(first + 1))}`;
20
+ const last = Math.max(s.lastIndexOf('"'), s.lastIndexOf("'"));
21
+ return `${s.slice(0, first)}${fingerprint(s.slice(first + 1, last))}${s.slice(last + 1)}`;
13
22
  }
14
23
  export const BOOT_FETCH_DEFERRED = Symbol("boot-fetch-deferred");
15
24
  export function raceBootFetch(fetch, budgetMs) {
@@ -1324,6 +1324,25 @@ export interface ServiceConfigFlat {
1324
1324
  * 「只配 APPROVAL_NEVER_AUTO」这一形被判成"零门意图" ⇒ 落进单用户 allow-all 基线 ⇒ 那张表
1325
1325
  * 从未进入任何 policy。判定现在单源在 `hasOperatorGateIntent()`(与 inv#2 同文件)。 */
1326
1326
  approvalNeverAuto: string[];
1327
+ /**
1328
+ * 7.115.0 —— **运维的 allow 规则**(`APPROVAL_GRANT`,逗号分隔的工具名规则:精确名 / `mcp__<server>__*` / `a2a__<peer>__*`)。命中 = 带理由的
1329
+ * 放行(core `decisionReason: "rule"`,运维替这些调用表了态 —— 回答没人表态的服务器工具 / A2A 技能调用;内置工具上引擎的写门、shell 检查照问);未命中 = 不表态。与请求腿的 `settings.permissions.allow` 是**同一种东西**:
1330
+ * 同一只编译口(`permission-rule-list.ts` `createRuleListPolicy`)、同一只校验(`config.ts` `approvalGrantRefusal` → `unmatchableToolNamesRefusal`,
1331
+ * 坏值拒启)。施加点 = 部署治理链的单一构造口(`deployment-governance.ts` → `runtime-governance.ts` `applyRuntimeGovernance` 的一片叶),
1332
+ * 所以 HTTP 提交 / durable 续跑(含 leader worker 停驻后经 `/decide` 续跑的那一段)/ 停驻后台 agent 的跨副本赎回 / run-local 四条腿同装(leader worker 首跑与修复腿到不了),每次现取(不 memoize)。**不越过**任何一层的 deny / ask(折叠 deny > ask > allow)。
1333
+ *
1334
+ * ⚠️ 不是白名单:名字不叫 `…_ALLOW`,与会话规则的 `toolAllow`(「只许这些」)刻意分开 —— 本键只给出表态,不收窄任何东西。
1335
+ *
1336
+ * 三问:谁需要 = 在没有请求体的腿上接了 MCP 服务器的运维 —— `run-local`,以及 durable 续跑(每一次从持久化体重建,含 leader worker 停驻后经 `/decide`
1337
+ * 续跑的那一段;A2A 不在射程:运维声明的对端恒带外发标记,那一问本键不放) —— core 7.40.0 起没人表态的服务器工具调用要问,无席 ⇒ 拒、有 durable 席 ⇒ 每次停驻等人,
1338
+ * 这里是运维为自己接的服务器表态的唯一位置。**到不了**:leader worker 的首跑与修复腿(工具策略在 leader 编排里自己折出来 —— `leader/wire.ts` 的
1339
+ * `combinePolicies(createDurableQuestionPolicy(), createDurableAskPolicy(…))` —— 不经上面那只构造口);这两条腿今天不挂 MCP / A2A,本键在那里没有对象。谁受伤 = 缺省空表,无人受伤 —— 设了宽规则
1340
+ * (`mcp__<server>__*`,含那台服务器将来新增的工具)= 对**每一个租户、每一条腿**的那些调用都不再因「没人表态」而问人,风险随规则的宽度走
1341
+ * (射程只到这一问:任何一层自己的问照旧压过它 —— 内置工具的精确名写进来,引擎的写门 / 形状层照问);补偿 = 本键就是
1342
+ * core 7.40.0 那条收紧的补偿,deny / ask(`APPROVAL_DENY` / `APPROVAL_REQUIRE` / 请求 settings / 会话规则)照旧压过它。
1343
+ * v1 只有 env 一个写入通道(无 config-center 座位,不进 settings-schema)。空表 = 本层不铸叶,折叠逐字与修前同。
1344
+ */
1345
+ approvalGrant: string[];
1327
1346
  /** [ref] D-G (HMAC = integrity, not identity; ✅ WIRED — the "SCAFFOLD, not yet wired" note here was STALE,
1328
1347
  * corrected 2026-07-25: consumed by `verifyDirectDoorProof` → `verifyApprovalHmac`, reached from the live decide
1329
1348
  * routes — the two `verifyDirectDoorProof(` call sites in `http/routes/approvals-assistant.ts`): the
@@ -2101,7 +2120,7 @@ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "ses
2101
2120
  export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "envModel" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelRefusals" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
2102
2121
  /** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
2103
2122
  * 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
2104
- export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalHmacKeys" | "durableApproval" | "durableApprovalSource" | "streamAskWindowMsSource" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "writeProtectedPaths" | "manualModeShellGate" | "permissionsDisableAutoMode" | "autoModeClassifierFallback" | "autoModeClassifierTimeoutMs" | "crossSessionInbound" | "crossSessionDialogExpiry">;
2123
+ export type ServiceApprovalConfig = Pick<ServiceConfigFlat, "approvalRequire" | "approvalDeny" | "approvalTimeoutSec" | "approvalAutoBudget" | "approvalNeverAuto" | "approvalGrant" | "approvalHmacKeys" | "durableApproval" | "durableApprovalSource" | "streamAskWindowMsSource" | "directApprovalDoor" | "directDoorActive" | "resourceSuspend" | "resourceSuspendTtlSec" | "resourceSuspendTotalTokens" | "resourceSuspendTotalBudgetUsd" | "resourceSuspendMaxSlices" | "askQuestionEnabled" | "questionThrottle" | "toolApprovalEnabled" | "permissionRulesEnabled" | "permissionRulesEnabledExplicit" | "streamAskWindowMarginMs" | "streamApproval" | "unattendedApprovalPolicy" | "mcpElicitation" | "sensitiveWritePatterns" | "writeProtectedPaths" | "manualModeShellGate" | "permissionsDisableAutoMode" | "autoModeClassifierFallback" | "autoModeClassifierTimeoutMs" | "crossSessionInbound" | "crossSessionDialogExpiry">;
2105
2124
  /** 组:memory(记忆面 + TOC 同步腿)。 */
2106
2125
  export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "memoryConsolidationDriver" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
2107
2126
  /** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析