@sema-agent/server 7.107.0 → 7.109.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 (101) hide show
  1. package/MIGRATION.md +72 -1
  2. package/USAGE.md +51 -25
  3. package/dist/approval-ask-audit-store.d.ts +62 -4
  4. package/dist/approval-ask-audit-store.js +67 -11
  5. package/dist/approval-ask-machine.d.ts +9 -25
  6. package/dist/approval-ask-machine.js +5 -2
  7. package/dist/approval-card.d.ts +32 -3
  8. package/dist/approval-card.js +29 -2
  9. package/dist/approval.js +16 -13
  10. package/dist/bench/s1/live-deps.d.ts +4 -5
  11. package/dist/bench/s1/live-deps.js +9 -10
  12. package/dist/boot/coordinators.d.ts +4 -0
  13. package/dist/boot/coordinators.js +3 -2
  14. package/dist/boot/leader.js +0 -2
  15. package/dist/boot/resolve-spec.d.ts +1 -1
  16. package/dist/boot/resolve-spec.js +3 -8
  17. package/dist/boot/shutdown.d.ts +8 -0
  18. package/dist/boot/shutdown.js +31 -8
  19. package/dist/boot/stage-06-runners.d.ts +1 -0
  20. package/dist/boot/stage-06-runners.js +6 -3
  21. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  22. package/dist/boot/stage-08-reapers.d.ts +1 -0
  23. package/dist/boot/stage-09-leader.d.ts +1 -0
  24. package/dist/boot/stage-10-http-server.d.ts +1 -0
  25. package/dist/boot/stage-10-http-server.js +3 -1
  26. package/dist/brain.js +2 -1
  27. package/dist/capabilities/scenarios.d.ts +3 -2
  28. package/dist/config-catalog.js +1 -3
  29. package/dist/config-center/apply-effective.js +2 -0
  30. package/dist/config-center/types.d.ts +7 -0
  31. package/dist/config-provider.js +1 -0
  32. package/dist/config-types.d.ts +2 -9
  33. package/dist/config.js +2 -5
  34. package/dist/deployment-governance.js +2 -1
  35. package/dist/fleet-client.d.ts +10 -2
  36. package/dist/fleet-client.js +14 -8
  37. package/dist/http/admission.js +8 -2
  38. package/dist/http/memory-compliance-failure-reply.d.ts +41 -0
  39. package/dist/http/memory-compliance-failure-reply.js +73 -0
  40. package/dist/http/memory-store-refusal-reply.d.ts +2 -2
  41. package/dist/http/resume-legs.js +3 -2
  42. package/dist/http/route-ctx.d.ts +3 -3
  43. package/dist/http/routes/capabilities.js +1 -0
  44. package/dist/http/routes/diagnostics.js +2 -0
  45. package/dist/http/routes/memory-compliance.js +2 -34
  46. package/dist/http/routes/memory-origin.d.ts +1 -1
  47. package/dist/http/routes/sessions.js +3 -11
  48. package/dist/http/routes/side-query.js +7 -2
  49. package/dist/http/routes/tasks.js +2 -1
  50. package/dist/http/server.d.ts +2 -2
  51. package/dist/http/wire-types.d.ts +8 -0
  52. package/dist/leader/diffup.d.ts +53 -0
  53. package/dist/leader/diffup.js +95 -1
  54. package/dist/leader/leader.d.ts +0 -6
  55. package/dist/leader/leader.js +1 -1
  56. package/dist/leader/repair-wire.js +1 -1
  57. package/dist/leader/wire.d.ts +3 -10
  58. package/dist/leader/wire.js +39 -13
  59. package/dist/memory-store-refusal.d.ts +2 -1
  60. package/dist/model-entry-headers.d.ts +41 -0
  61. package/dist/model-entry-headers.js +61 -0
  62. package/dist/model-entry-refusal.d.ts +6 -0
  63. package/dist/model-entry-refusal.js +4 -0
  64. package/dist/model-reach-scope.d.ts +55 -0
  65. package/dist/model-reach-scope.js +16 -0
  66. package/dist/model-unreachable-notice.d.ts +38 -0
  67. package/dist/model-unreachable-notice.js +72 -0
  68. package/dist/observability/fail-open.d.ts +5 -5
  69. package/dist/observability/fail-open.js +5 -5
  70. package/dist/observability/run-terminal.d.ts +2 -1
  71. package/dist/outbound-dispatcher.d.ts +69 -0
  72. package/dist/outbound-dispatcher.js +123 -3
  73. package/dist/plugins/approval-ask-store-file.d.ts +2 -1
  74. package/dist/plugins/approval-ask-store-file.js +3 -0
  75. package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
  76. package/dist/plugins/approval-ask-store-memory.js +7 -1
  77. package/dist/plugins/approval-ask-store-sql.d.ts +18 -0
  78. package/dist/plugins/approval-ask-store-sql.js +7 -0
  79. package/dist/plugins/park-commit-watch.d.ts +48 -0
  80. package/dist/plugins/park-commit-watch.js +87 -0
  81. package/dist/request-key-closure.js +1 -1
  82. package/dist/router/route-orchestration.js +7 -2
  83. package/dist/run-local.js +2 -3
  84. package/dist/runs.js +3 -2
  85. package/dist/runtime-governance.d.ts +3 -3
  86. package/dist/runtime-governance.js +3 -2
  87. package/dist/task-settings.js +4 -2
  88. package/dist/tool-approval.d.ts +99 -15
  89. package/dist/tool-approval.js +75 -10
  90. package/dist/tool-rule-names.d.ts +25 -0
  91. package/dist/tool-rule-names.js +5 -0
  92. package/dist/trace/core-keyset-guard.d.ts +14 -2
  93. package/dist/trace/engine-notice-wire.d.ts +3 -1
  94. package/dist/trace/engine-notice-wire.js +2 -0
  95. package/dist/trace/project.d.ts +3 -1
  96. package/dist/trace/project.js +2 -3
  97. package/dist/trace/self-minted-wire.d.ts +71 -0
  98. package/dist/trace/self-minted-wire.js +28 -0
  99. package/dist/unattended-permission-mode.d.ts +54 -93
  100. package/dist/unattended-permission-mode.js +8 -61
  101. package/package.json +3 -3
@@ -40,6 +40,7 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
40
40
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
41
41
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
42
42
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
43
+ parkCommits: import("../plugins/park-commit-watch.js").ParkCommitWatch | undefined;
43
44
  durableEnabled: boolean;
44
45
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
45
46
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -76,6 +76,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
76
76
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
77
77
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
78
78
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
79
+ parkCommits: import("../plugins/park-commit-watch.js").ParkCommitWatch | undefined;
79
80
  durableEnabled: boolean;
80
81
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
81
82
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -76,6 +76,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
76
76
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
77
77
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
78
78
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
79
+ parkCommits: import("../plugins/park-commit-watch.js").ParkCommitWatch | undefined;
79
80
  durableEnabled: boolean;
80
81
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
81
82
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -26,7 +26,7 @@ import { bindServerLateSeats } from "./late-binds.js";
26
26
  import { serviceVersion } from "../service-version.js";
27
27
  import { buildDisplaySafeUrl } from "../trace/redact.js";
28
28
  export async function runStage10(ctx) {
29
- const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, config, configCenter, consolidationValve, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, noteSessionIncarnation, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveSurface, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionInheritEnv, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storePosture, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
29
+ const { approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, parkCommits, config, configCenter, consolidationValve, costQuota, deploymentHooks, deviceLane, drainState, durableEnabled, elicitation, fenceSessionOwner, noteSessionIncarnation, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, localRoot, logger, lspManager, mcpRevocations, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportBackend, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveSurface, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, registryJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekResolver, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionInheritEnv, setSessionShellEnv, sharedMemoryStore, sideQueryAccounting, sqlEngineFacts, sqlWorkflowRunStore, staticWiring, storePosture, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable } = ctx;
30
30
  const stores = {
31
31
  runStore,
32
32
  resumeAnchorStore: resumeAnchorStore ? resumeAnchorStore : undefined,
@@ -303,6 +303,8 @@ export async function runStage10(ctx) {
303
303
  version: serviceVersion(),
304
304
  runner, subRunner, lspManager, workflowNotifyJournal, fleetClient, backend, drainState,
305
305
  storeLiveProbe, configCenter,
306
+ toolApproval,
307
+ parkCommits,
306
308
  deviceHub: deviceLane?.hub,
307
309
  retentionLane: retentionLaneStarted,
308
310
  releaseRetentionLease: retentionLaneStore !== undefined ? () => retentionLaneStore.release(instanceId) : undefined,
package/dist/brain.js CHANGED
@@ -8,6 +8,7 @@ import { dispatchBrainLane } from "./model-provider.js";
8
8
  import { openaiRouteBaseUrl } from "./model-route-endpoint.js";
9
9
  import { catalogEntryFingerprint } from "./config-center/restart-signal.js";
10
10
  import { buildDisplaySafeRefusal, buildDisplaySafeUrl } from "./trace/redact.js";
11
+ import { createModelReachBrain } from "./model-unreachable-notice.js";
11
12
  function isOffDeploymentRoot(baseUrl, config) {
12
13
  if (!baseUrl)
13
14
  return false;
@@ -138,7 +139,7 @@ export function createBrain(config, deps = {}) {
138
139
  });
139
140
  }
140
141
  }
141
- return brain;
142
+ return createModelReachBrain(brain);
142
143
  }
143
144
  export function degradeTargetOf(config) {
144
145
  return config.degrade?.reactive ? config.models?.[config.degrade.to] : undefined;
@@ -246,8 +246,9 @@ export declare function centerScenarios(deps: ScenarioDeps, specs: CenterScenari
246
246
  };
247
247
  /**
248
248
  * Personalization seam: merge the caller's own skills AFTER the scenario's, with
249
- * SCENARIO-WINS on a name clash — the security baseline (scenario/center skills) can never be shadowed
250
- * by user content. Duplicate names within the user list keep the first. Clashes are logged for audit
249
+ * SCENARIO-WINS on a name clash — the scenario / center skills own the name (they are the deployment's baseline
250
+ * wording, not an enforcement face: permissions / write gates / hooks do not ride on skills, and a caller may
251
+ * unload the whole set with `disableSkills`, S-946); user content never shadows them. Duplicate names within the user list keep the first. Clashes are logged for audit
251
252
  * (silent drops would make "my skill didn't fire" undebuggable). Input is already shape/size-validated
252
253
  * by the HTTP layer (validateUserSkills); this stays pure merge policy.
253
254
  */
@@ -1,10 +1,9 @@
1
- import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES, PERMISSION_MODES } from "@sema-agent/core";
1
+ import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } from "@sema-agent/core";
2
2
  import { CENTER_CREDENTIAL_ENV, DOMAIN_SCHEMAS } from "@sema-agent/settings-schema";
3
3
  import { CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS, CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS, CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS } from "./center-credential-renewer.js";
4
4
  import { THINKING_FORMATS } from "./model-compat.js";
5
5
  import { AUTO_MODE_CLASSIFIER_TIMEOUT_BOUNDS, CONFIG_PROVIDER_WORDS, DB_BACKEND_WORDS, DRAIN_SECOND_SIGNAL_MIN_GAP_BOUNDS, DRAIN_SECOND_SIGNAL_MIN_GAP_DEFAULT_MS, FIRST_BYTE_TIMEOUT_BOUNDS, RESILIENCE_DEFAULTS, TASK_WRITE_FACE_ESCALATIONS } from "./config-types.js";
6
6
  import { webSearchProviderFromEnv, WEB_SEARCH_PROVIDERS } from "./plugins/web-search.js";
7
- import { UNATTENDED_PERMISSION_MODE_DEFAULT } from "./unattended-permission-mode.js";
8
7
  import { AUTONOMY_MODES } from "./runtime-governance.js";
9
8
  import { REMOTE_EXEC_PROVIDER_WORDS } from "./execution-lane-caps.js";
10
9
  import { MODEL_PROVIDER_WORDS } from "./model-provider.js";
@@ -454,7 +453,6 @@ export const CONFIG_CATALOG = [
454
453
  r("TRACE_THINKING", "observability", "boolean", "思考轨迹入 durable 事件日志(敏感部署可关;落盘前脱敏)", { staticDefault: "true", danger: DATA, configKey: "traceThinking" }),
455
454
  r("TRACE_TOKEN", "observability", "secret", "Task Trace API 只读令牌(fleet 拉轨迹,不授任务/配置权)", { danger: SEC }),
456
455
  r("UNATTENDED_APPROVAL_POLICY", "approval", "enum", "无人能答的治理 ask 终局(park=durable 等人;deny=当场拒 —— 自声明无人值守)", { enumValues: ["park", "deny"], staticDefault: "park", danger: SEC, configKey: "unattendedApprovalPolicy" }),
457
- r("UNATTENDED_PERMISSION_MODE", "approval", "enum", "无人值守腿(leader worker / leader 修复腿 / run-local:本仓自铸 spec、无调用方可表态、无审批席)的权限模式;写面恒 roots(不随 bypassPermissions 开放);词表外拒启", { enumValues: [...PERMISSION_MODES], staticDefault: UNATTENDED_PERMISSION_MODE_DEFAULT, danger: SEC, configKey: "unattendedPermissionMode" }),
458
456
  r("UNREACHED_ASK_TTL_MS", "approval", "number", "零活流铸造的 ask 悬挂窗(ms;[60s,24h] 拒启带界)", { staticDefault: "3600000" }),
459
457
  r("USAGE_WINDOWS", "orchestration", "opaque", "部署级治理窗(JSON 数组;滑动窗内跨任务 token/$ 预算,key=principal)", { danger: BILL }),
460
458
  r("WEB_SEARCH_API_KEY", "orchestration", "secret", "web search 后端 API 凭据(brave/tavily)", { danger: [...SEC, ...BILL] }),
@@ -2,6 +2,7 @@ import { resolveActiveTiers } from "@sema-agent/settings-schema";
2
2
  import { modelKeyPoison } from "../key-resolver.js";
3
3
  import { applyAutoCompactWindow, buildModelCostKey, findUnmatchableToolNames, formatUnmatchableToolNames, parseAutonomy } from "../config.js";
4
4
  import { buildModelCompat, readModelCompat } from "../model-compat.js";
5
+ import { buildModelHeadersKey } from "../model-entry-headers.js";
5
6
  import { validateCommandRules } from "../runtime-governance.js";
6
7
  import { projectCollabToWorkflows, registerCollabWorkflows } from "../capabilities/collab-workflows.js";
7
8
  import { stageLimits } from "./stage-limits.js";
@@ -50,6 +51,7 @@ function toModel(m, envDefaults = {}, logger) {
50
51
  ...(compat ? { compat } : {}),
51
52
  ...(m.extraBody ? { extraBody: m.extraBody } : {}),
52
53
  ...(Array.isArray(m.promptGuidance) && m.promptGuidance.length > 0 ? { promptGuidance: m.promptGuidance.map(String) } : {}),
54
+ ...buildModelHeadersKey(m.name, m.headers, m.api),
53
55
  };
54
56
  const explicitAct = m.autoCompactTokens;
55
57
  applyAutoCompactWindow(model, typeof explicitAct === "number" ? explicitAct : undefined);
@@ -82,6 +82,13 @@ export interface CenterModel {
82
82
  * configured sentences parse fine in registry-core but never reach the assembly face ([ref]① 排雷:
83
83
  * the missing leg made every promptGuidance pilot a false control). */
84
84
  promptGuidance?: string[];
85
+ /**
86
+ * S-931(7.108.0)—— 这条条目发给供应商的**附加请求头**(部署 ↔ 供应商的契约;今天只有 `anthropic-beta`),上 core 的 per-model
87
+ * 头座 `Model.headers`。类型是 `unknown` 而不是那个结构,理由与 {@link CenterModel.compat} 逐字同:本类型是承载形,判据(闭集白名单 +
88
+ * 线路 + 值形)与坏形处置的单一属主是 `model-entry-headers.ts`(执法点 = S-704 逐条判据,判不过 ⇒ 条目不可用)。
89
+ * ⚠️ 两条读腿今天**剥掉**它(settings-schema 6.3.0 `ModelEntry` 无此键;候裁加字段):今天到得了这里的只有绕过读腿的输入。
90
+ */
91
+ headers?: unknown;
85
92
  enabled?: boolean;
86
93
  }
87
94
  export type CenterRoleTarget = {
@@ -125,6 +125,7 @@ export function mapToServiceEffective(eff, version) {
125
125
  ...(m.cost !== undefined ? { cost: m.cost } : {}),
126
126
  ...(m.extraBody !== undefined ? { extraBody: m.extraBody } : {}),
127
127
  ...(m.compat !== undefined ? { compat: m.compat } : {}),
128
+ ...(m.headers !== undefined ? { headers: m.headers } : {}),
128
129
  ...(m.promptGuidance !== undefined ? { promptGuidance: m.promptGuidance } : {}),
129
130
  enabled: m.enabled,
130
131
  })),
@@ -9,7 +9,7 @@ import type { ReadFaceSource } from "./read-face-posture.js";
9
9
  import type { PostureSource } from "./posture-source.js";
10
10
  import type { SealedCustodyPoison } from "./key-resolver.js";
11
11
  import type { ModelEntryRefusal } from "./model-entry-refusal.js";
12
- import type { PermissionMode, A2aServerSpec, CompliancePosture, CrossSessionDialogExpiry, CrossSessionInboundSetting, FileHistoryRetentionPolicy, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy, ShellGateDoctrine, WriteProtectedEntry } from "@sema-agent/core";
12
+ import type { A2aServerSpec, CompliancePosture, CrossSessionDialogExpiry, CrossSessionInboundSetting, FileHistoryRetentionPolicy, LockedKey, McpServerSpec, Model, ModelRoles, RetentionPolicy, ShellGateDoctrine, WriteProtectedEntry } from "@sema-agent/core";
13
13
  import type { ApprovalHmacKey, PrincipalJwtKey } from "./auth-keys.js";
14
14
  import type { ElicitationThrottle } from "./elicitation.js";
15
15
  import type { QuestionThrottle } from "./question.js";
@@ -1920,13 +1920,6 @@ export interface ServiceConfigFlat {
1920
1920
  * 把批关掉(同腿后续的卡人批不动)、把兄弟连坐成 VOID(裸 `false` = 人拒)。
1921
1921
  */
1922
1922
  unattendedApprovalPolicy: UnattendedApprovalPolicy;
1923
- /**
1924
- * S-877 候裁 15 → B(`UNATTENDED_PERMISSION_MODE`)—— 本仓**自铸** spec、无调用方可表态、无审批席的腿(leader worker / leader 修复腿 /
1925
- * run-local)的 `TaskSpec.permissionMode`。闭集 = core `PERMISSION_MODES`,缺省 `bypassPermissions`(写门 / 形状层 = 7.105.0;写保护经 `createUnattendedWriteProtectionPolicy` 从问改拒;其余六类释放 = 宽化,候裁 18 D),坏值拒启。
1926
- * 规则 / 三问 / 写面 / 射程外**一处成文** = `src/unattended-permission-mode.ts` 模块头注;唯一读函数 `unattendedPermissionMode(config)`。
1927
- * 纯部署级:不读任何客户端表态、不参与托管姿态(这些腿不是请求)。
1928
- */
1929
- unattendedPermissionMode: PermissionMode;
1930
1923
  /** [ref]① (clay A 案 = workflow 权限全面 CC parity): workflow 子 agent 的默认权限基线与主 LLM 同权(base `{}`)。
1931
1924
  * 安全论证:主 LLM 与子 agent 同 root 同信任域,主 LLM 本就能写这棵树([ref] ask 门照管),单独钳子 agent 的
1932
1925
  * 安全增益≈0(只防绕路不防直路的门不是边界);实测产品代价=[ref]A 死锁。`WORKFLOW_AGENTS_READONLY=true` =
@@ -2113,7 +2106,7 @@ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "ses
2113
2106
  export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "envModel" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelRefusals" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
2114
2107
  /** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
2115
2108
  * 就是本组的门状态,故进组;`parseApprovalDomain` 的返回类型相应是 `Omit<…, "directDoorActive">`。 */
2116
- 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" | "unattendedPermissionMode" | "mcpElicitation" | "sensitiveWritePatterns" | "writeProtectedPaths" | "manualModeShellGate" | "permissionsDisableAutoMode" | "autoModeClassifierFallback" | "autoModeClassifierTimeoutMs" | "crossSessionInbound" | "crossSessionDialogExpiry">;
2109
+ 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">;
2117
2110
  /** 组:memory(记忆面 + TOC 同步腿)。 */
2118
2111
  export type ServiceMemoryConfig = Pick<ServiceConfigFlat, "memoryEngineEnabled" | "memoryEngineDir" | "memoryEngineRemoteLaneAllowed" | "memoryEngineBackend" | "memoryScope" | "memoryPersistenceCapable" | "memorySync" | "memoryEmbedder" | "memoryOrgAdmissionMode" | "memoryDelegationEvidence" | "memoryProvenance" | "memoryCapturePolicy" | "memoryConsolidationDriver" | "memoryOrgDirectoryJson" | "memoryOrgGrantTtlMs" | "memoryOrgUnavailableBackoffMs" | "projectMemoryEnabled" | "syncImportLeaseStaleSec">;
2119
2112
  /** 组:auth(鉴权 / 身份 / 治理棒)。`commandPolicy` 只有 sema-registry 腿(无 env 标量形),故 env 解析
package/dist/config.js CHANGED
@@ -27,7 +27,6 @@ import { modelRouteEndpointRefusal } from "./model-route-endpoint.js";
27
27
  import { buildDisplaySafeUrl, redactSecrets } from "./trace/redact.js";
28
28
  import { allowlistedEnv } from "./server-secret-env.js";
29
29
  import { ENV_NAME_ALLOWLIST_KNOBS, ENV_NAME_ALLOWLIST_KNOB_NAMES, describeAllowlistPostures } from "./env-name-allowlist-knobs.js";
30
- import { parseUnattendedPermissionMode } from "./unattended-permission-mode.js";
31
30
  export { RETENTION_MODES, MEMORY_CONSOLIDATION_MIN_INTERVAL_SEC } from "./config-types.js";
32
31
  function csv(name) {
33
32
  return (process.env[name] ?? "")
@@ -135,6 +134,7 @@ export const RETIRED_ENVS = [
135
134
  ["CONFIG_LKG_DISABLED", "CONFIG_LKG_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: CONFIG_LKG_DISABLED=true is CONFIG_LKG_ENABLED=false (the positive name defaults on)"],
136
135
  ["HOST_BG_DISABLED", "HOST_BG_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: HOST_BG_DISABLED=true is HOST_BG_ENABLED=false (the positive name defaults on)"],
137
136
  ["HOST_EXEC_SPOOL_DISABLED", "HOST_EXEC_SPOOL_ENABLED", "replaced", "negative-name alias retired in server 3.0.0 — unset it; the polarity is inverted: HOST_EXEC_SPOOL_DISABLED=true is HOST_EXEC_SPOOL_ENABLED=false (the positive name defaults on)"],
137
+ ["UNATTENDED_PERMISSION_MODE", "since server 7.109.0 the unattended legs (leader workers and their repair legs, run-local, and their durable resumes) always run permissionMode \"default\" with an approver that answers by ask class: a machine answers only \"ordinary\" asks, and every other class goes to the leg's approver seat (run-local's terminal prompt) or parks / is refused — there is no mode to choose", "removed"],
138
138
  ];
139
139
  for (const row of RETIRED_ENVS)
140
140
  Object.freeze(row);
@@ -1327,8 +1327,6 @@ function unattendedApprovalPolicyEnv() {
1327
1327
  function parseApprovalDomain(ctx) {
1328
1328
  const { postureOn, singleUserTurnkey, infraReady } = ctx;
1329
1329
  const unattendedPolicy = unattendedApprovalPolicyEnv();
1330
- const unattendedMode = parseUnattendedPermissionMode(process.env.UNATTENDED_PERMISSION_MODE);
1331
- CONFIG_NOTICES.push({ level: "info", event: "unattended_permission_mode", fields: { value: unattendedMode.value, source: unattendedMode.source } });
1332
1330
  const streamAskWindowMsSource = postureSourceOfNumEnv(process.env.STREAM_ASK_WINDOW_MS, singleUserTurnkey);
1333
1331
  const toolApprovalEnabled = postureOn("TOOL_APPROVAL_ENABLED");
1334
1332
  let streamAskWindowMsEffectiveSource = streamAskWindowMsSource;
@@ -1429,7 +1427,6 @@ function parseApprovalDomain(ctx) {
1429
1427
  streamAskWindowMsSource: streamAskWindowMsEffectiveSource,
1430
1428
  durableApprovalSource: postureSourceOfKnob("DURABLE_APPROVAL"),
1431
1429
  unattendedApprovalPolicy: unattendedPolicy,
1432
- unattendedPermissionMode: unattendedMode.value,
1433
1430
  approvalTimeoutSec: numEnv("APPROVAL_TIMEOUT_SEC", "0"),
1434
1431
  };
1435
1432
  }
@@ -2299,7 +2296,7 @@ const MODEL_PLANE_GROUP_KEYS = [
2299
2296
  const APPROVAL_GROUP_KEYS = [
2300
2297
  "approvalRequire", "approvalDeny", "approvalTimeoutSec", "approvalAutoBudget", "approvalNeverAuto",
2301
2298
  "approvalHmacKeys", "durableApproval", "durableApprovalSource", "streamAskWindowMsSource", "directApprovalDoor", "directDoorActive", "resourceSuspend",
2302
- "resourceSuspendTtlSec", "resourceSuspendTotalTokens", "resourceSuspendTotalBudgetUsd", "resourceSuspendMaxSlices", "askQuestionEnabled", "questionThrottle", "toolApprovalEnabled", "permissionRulesEnabled", "permissionRulesEnabledExplicit", "streamAskWindowMarginMs", "streamApproval", "unattendedApprovalPolicy", "unattendedPermissionMode", "mcpElicitation",
2299
+ "resourceSuspendTtlSec", "resourceSuspendTotalTokens", "resourceSuspendTotalBudgetUsd", "resourceSuspendMaxSlices", "askQuestionEnabled", "questionThrottle", "toolApprovalEnabled", "permissionRulesEnabled", "permissionRulesEnabledExplicit", "streamAskWindowMarginMs", "streamApproval", "unattendedApprovalPolicy", "mcpElicitation",
2303
2300
  "sensitiveWritePatterns", "writeProtectedPaths", "manualModeShellGate", "permissionsDisableAutoMode", "autoModeClassifierFallback", "autoModeClassifierTimeoutMs", "crossSessionInbound", "crossSessionDialogExpiry",
2304
2301
  ];
2305
2302
  const MEMORY_GROUP_KEYS = [
@@ -2,12 +2,13 @@ import { join, posix } from "node:path";
2
2
  import { FileError, StubExecutionEnv, combinePolicies, createAllowDenyPolicy, createDurableQuestionPolicy, createSensitivePathPolicy, createTranscriptIntegrityPolicy, err, isAbsoluteForFamily, isProtectedWrite, ok, pathTargetValue, protectivePathTargetOf, } from "@sema-agent/core";
3
3
  import { createDurableAskPolicy, hasApprovalListIntent } from "./approval.js";
4
4
  import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
5
+ import { heldRuleName } from "./tool-rule-names.js";
5
6
  export function createDurableQuestionGate(live) {
6
7
  if (live === undefined)
7
8
  return createDurableQuestionPolicy();
8
9
  return {
9
10
  check(req) {
10
- if (req.toolName !== ASK_USER_QUESTION_TOOL_NAME)
11
+ if (heldRuleName(req, (n) => n === ASK_USER_QUESTION_TOOL_NAME) === undefined)
11
12
  return { action: "allow" };
12
13
  return live.hasLiveContext()
13
14
  ? { action: "allow" }
@@ -102,8 +102,16 @@ export interface FleetClient {
102
102
  /** Immediate out-of-cycle announce — call when a self-reported input flips (draining: self-remove from routing FIRST;
103
103
  * roster landed: become routable without waiting a heartbeat; restart signal changed: S-741). */
104
104
  announceNow: () => Promise<void>;
105
- /** Stop timers + best-effort DELETE deregistration (bounded; never throws). */
106
- stop: () => Promise<void>;
105
+ /**
106
+ * **路由面**停:停心跳、等在飞公告排空、best-effort DELETE 注销(各请求有界;从不抛)。之后的 `announceNow` 是空操作。
107
+ * 收尾链在 hardShutdown **起点**调(与 7.107.0 同序 —— 退出中的副本第一时间离开中心的路由表)。
108
+ */
109
+ stopRouting: () => Promise<void>;
110
+ /**
111
+ * **记账面**停:停用量窗定时器、上报最后一个(可能不满的)用量窗(有界;从不抛;空窗不发)。与路由面无关(不公告的引擎也报用量)。
112
+ * 收尾链排在**排空等待之后**调(S-929 F12:宽限窗里跑完的腿记下的用量必须进这一窗)。
113
+ */
114
+ flushFinalUsage: () => Promise<void>;
107
115
  }
108
116
  /**
109
117
  * Start the worker-side fleet loops. Caller has already gated on the center lane (startFleetClientFromEnv below).
@@ -161,19 +161,16 @@ export function startFleetClient(opts) {
161
161
  hb?.unref?.();
162
162
  const uw = opts.usage ? setInterval(() => void flushUsage(), usageWindowMs) : undefined;
163
163
  uw?.unref?.();
164
- let stopping = false;
165
- const stop = async () => {
166
- if (stopping)
164
+ let routingStopping = false;
165
+ const stopRouting = async () => {
166
+ if (routingStopping)
167
167
  return;
168
- stopping = true;
168
+ routingStopping = true;
169
169
  if (hb)
170
170
  clearInterval(hb);
171
- if (uw)
172
- clearInterval(uw);
173
171
  while (inFlight !== undefined)
174
172
  await inFlight;
175
173
  stopped = true;
176
- await flushUsage();
177
174
  if (!announcing)
178
175
  return;
179
176
  try {
@@ -182,7 +179,16 @@ export function startFleetClient(opts) {
182
179
  catch {
183
180
  }
184
181
  };
185
- return { announceNow, stop };
182
+ let usageStopping = false;
183
+ const flushFinalUsage = async () => {
184
+ if (usageStopping)
185
+ return;
186
+ usageStopping = true;
187
+ if (uw)
188
+ clearInterval(uw);
189
+ await flushUsage();
190
+ };
191
+ return { announceNow, stopRouting, flushFinalUsage };
186
192
  }
187
193
  export function startFleetClientFromEnv(cfg, opts) {
188
194
  const cc = cfg.configCenter;
@@ -458,8 +458,14 @@ export function createAdmission(ctx) {
458
458
  sendError(res, 400, "request.field_invalid", "retainBackgroundProcesses must be a boolean");
459
459
  return null;
460
460
  }
461
- if (body.excludeAllTools !== undefined && typeof body.excludeAllTools !== "boolean") {
462
- sendError(res, 400, "request.field_invalid", "excludeAllTools must be a boolean");
461
+ for (const key of ["excludeAllTools", "disableSkills"]) {
462
+ if (body[key] !== undefined && typeof body[key] !== "boolean") {
463
+ sendError(res, 400, "request.field_invalid", `${key} must be a boolean`);
464
+ return null;
465
+ }
466
+ }
467
+ if (body.disableSkills === true && ((body.skills?.length ?? 0) > 0 || (body.agents ?? []).some((a) => (a.skills?.length ?? 0) > 0))) {
468
+ sendError(res, 400, "request.field_conflict", "disableSkills: true means this run installs no skill at all (not the deployment's, not the request's), so it cannot come with a non-empty skills or agents[].skills — send one or the other");
463
469
  return null;
464
470
  }
465
471
  for (const key of ["excludeTools", "deferTools"]) {
@@ -0,0 +1,41 @@
1
+ /**
2
+ * S-930 —— 记忆合规面(抹除 / 出处)上 core **已分类拒**的**唯一** HTTP 映射。两面同调:
3
+ * · operator 面 `routes/memory-compliance.ts`(`POST /v1/memory/erase` · `GET /v1/memory/entries/:entryId/provenance`);
4
+ * · 会话所有者自助抹除面 `routes/sessions.ts`(`POST /v1/sessions/:id/memory/erase`)。
5
+ *
6
+ * ## 病(sdk 车 SH 黑盒实证,S-930)
7
+ * 修前自助面在路由里自写了一份更窄的分类:只认 `config.memory_erasure_request`(400)与 `memory.target_outside_store`(409),
8
+ * core 的另外三只 409 与 `ControlPlaneCorruptError` 全落 500 `internal.error` —— 「同一个 requestId 拿去抹另一个会话」这种**调用方自己
9
+ * 能改**的错被说成服务器坏了。两份分类 = 两个判官,少的那份恒是静默的一份。现在**码与状态**只在这里判一次,两面同读。
10
+ *
11
+ * ## 规则(三层,与 operator 面既有层序逐字同序)
12
+ * ① core 的**本口专属**码(闭集穷举 `switch`,见 {@link sentCoreCode})—— 码与状态两面同;文案按受众(下一节,一条规则);
13
+ * ② `memory.target_outside_store`(core 7.31.1 #1094 跨面 store 状态码)—— 409;
14
+ * ③ `ControlPlaneCorruptError` —— typed 500 `internal.memory_control_plane_corrupt`(fail-closed 信号,一码两义见下);
15
+ * 其余 ⇒ `false`(未分类,调用方发它自己那句 500 且**不回显**内部文本)。
16
+ *
17
+ * ## 文案按受众分(码与状态不分)—— **一条规则,三层同施,不按码挑**(F9,7.108.0 合并复审 opus 腿 O-9)
18
+ * core 原句是写给运维的诊断材料:② ③ 点名**宿主路径**(store 根、链解析到哪 / 控制面账本文件),① 的 `selector_mismatch` 带**首个请求的
19
+ * 选择子哈希**(`pinned set <selectHash>`,无盐 sha256 —— operator 选择子可字典还原,首个请求未必是这位会话所有者发的):
20
+ * · operator 受众 ⇒ 原句上 wire(过了 operator 门,这些材料对这个受众不是泄露);
21
+ * · 会话所有者受众 ⇒ **一律**一句固定指路(每码一句,只说「怎么办」),原句整句交 `withheld` 落运维 warn —— 不逐码判断哪句安全
22
+ * (逐码挑 = core 改一句文案就可能静默漏出一样新东西)。② ③ 的固定句**两形都真**:
23
+ * ② 链先于请求在场 ⇒ 提交点之前拒、什么都没做;提交点之后才换上的链 ⇒ 已删的几条算数、journal 留盘(codex 一审 C1);
24
+ * ③ 一码两义 —— 操作前账本读不出 ⇒ 什么都没发生;落定后残留枚举失败 ⇒ 抹除已提交、重放收敛(core 原话)。两句都不许诺零删除,
25
+ * 都指路「运维修好后用同一个 requestId 重发」(两义下这句都对)。本层**不**嗅 core 文案分辨前后(拿上游自由文本当判别式是第二真源)。
26
+ */
27
+ import type { ServerResponse } from "node:http";
28
+ /**
29
+ * 已分类拒 → HTTP 状态的**唯一**码表 = 自铸 wire 值登记处的 `sessionMemoryEraseStatus` 集(F3:wire 变更清单从同一张表 diff,状态改一处、
30
+ * 清单跟着列)。两面同读(S-930:码与状态只判一次)。键写错 ⇒ tsc 红(索引的是 `as const` 表的键)。
31
+ */
32
+ /** 读这张映射的受众(闭集)。会话所有者受众必须交一只 `withheld` —— 被换成固定句的 core 原句落到那里(运维材料不许丢)。 */
33
+ export type MemoryComplianceAudience = {
34
+ readonly kind: "operator";
35
+ } | {
36
+ readonly kind: "session_owner";
37
+ readonly withheld: (coreMessage: string) => void;
38
+ };
39
+ /** 按上面三层应答;返回 `true` = 已应答,`false` = 未分类(一个字节都没写,调用方发它自己那句 500 并落它自己的运维 warn)。 */
40
+ export declare function sentMemoryComplianceFailure(res: ServerResponse, err: unknown, audience: MemoryComplianceAudience): boolean;
41
+ //# sourceMappingURL=memory-compliance-failure-reply.d.ts.map
@@ -0,0 +1,73 @@
1
+ import { ControlPlaneCorruptError } from "@sema-agent/core";
2
+ import { targetOutsideStoreOf } from "../memory-store-refusal.js";
3
+ import { sentMemoryStoreRefusal } from "./memory-store-refusal-reply.js";
4
+ import { sendError } from "./send.js";
5
+ import { assertNever } from "./route-ctx.js";
6
+ function sentCoreCode(res, coreCode, coreMessage, audience) {
7
+ switch (coreCode) {
8
+ case "config.memory_erasure_request":
9
+ sendError(res, 400, "config.memory_erasure_request", textFor(audience, coreMessage, "session memory erasure refused — the erasure request is not valid; correct it and resend"));
10
+ return true;
11
+ case "memory.erasure_evidence_unavailable":
12
+ sendError(res, 409, "memory.erasure_evidence_unavailable", textFor(audience, coreMessage, "session memory erasure refused — this deployment's memory store cannot produce erasure evidence; resend with allowUnevidenced: true to erase without evidence, or ask the operator"));
13
+ return true;
14
+ case "memory.erasure_selector_mismatch":
15
+ sendError(res, 409, "memory.erasure_selector_mismatch", textFor(audience, coreMessage, "session memory erasure refused — this requestId already names a different erasure; resend with a new requestId"));
16
+ return true;
17
+ case "memory.erasure_census_incomplete":
18
+ sendError(res, 409, "memory.erasure_census_incomplete", textFor(audience, coreMessage, "session memory erasure refused — the memory store could not be read completely, so nothing was erased and nothing was recorded; ask the operator to repair it, then resend with the same requestId"));
19
+ return true;
20
+ default:
21
+ return false;
22
+ }
23
+ }
24
+ function textFor(audience, coreMessage, ownerSentence) {
25
+ switch (audience.kind) {
26
+ case "operator":
27
+ return coreMessage;
28
+ case "session_owner":
29
+ audience.withheld(coreMessage);
30
+ return ownerSentence;
31
+ default:
32
+ return assertNever(audience);
33
+ }
34
+ }
35
+ function codeOf(err) {
36
+ if (!(err instanceof Error))
37
+ return undefined;
38
+ const { code } = err;
39
+ return typeof code === "string" ? code : undefined;
40
+ }
41
+ export function sentMemoryComplianceFailure(res, err, audience) {
42
+ const coreCode = codeOf(err);
43
+ if (coreCode !== undefined && sentCoreCode(res, coreCode, err instanceof Error ? err.message : String(err), audience))
44
+ return true;
45
+ const outside = targetOutsideStoreOf(err);
46
+ if (outside !== undefined) {
47
+ switch (audience.kind) {
48
+ case "operator":
49
+ return sentMemoryStoreRefusal(res, err);
50
+ case "session_owner":
51
+ audience.withheld(outside.message);
52
+ sendError(res, 409, "memory.target_outside_store", "session memory erasure refused — a directory in this deployment's memory store leads outside it (a link), so this erasure did not complete (a refusal before its commit point changed nothing; one after it keeps the deletions already made and a journal that finishes the rest once the link is removed); ask the operator to remove the link, then resend with the same requestId");
53
+ return true;
54
+ default:
55
+ return assertNever(audience);
56
+ }
57
+ }
58
+ if (err instanceof ControlPlaneCorruptError) {
59
+ switch (audience.kind) {
60
+ case "operator":
61
+ sendError(res, 500, "internal.memory_control_plane_corrupt", err.message);
62
+ return true;
63
+ case "session_owner":
64
+ audience.withheld(err.message);
65
+ sendError(res, 500, "internal.memory_control_plane_corrupt", "session memory erasure could not be completed — this deployment's memory control plane could not be read cleanly (depending on where it stopped, either nothing was changed or the deletions were already committed and a replay finishes them); ask the operator to repair it, then resend with the same requestId");
66
+ return true;
67
+ default:
68
+ return assertNever(audience);
69
+ }
70
+ }
71
+ return false;
72
+ }
73
+ //# sourceMappingURL=memory-compliance-failure-reply.js.map
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * core 7.31.1 #1094 `memory.target_outside_store` 的 **operator 面 HTTP 映射** —— 唯一一处字面量(判据在
3
3
  * `src/memory-store-refusal.ts`)。挂在每一个触 store 树的 operator 记忆口的已分类收尾上:
4
- * `routes/memory-compliance.ts`(出处 / 抹除)· `routes/memory-origin.ts`(外源列表 / 清标;**不含**只读控制面的清标账列表 ——
4
+ * `routes/memory-compliance.ts`(出处 / 抹除;经 `http/memory-compliance-failure-reply.ts` 的 operator 受众臂)· `routes/memory-origin.ts`(外源列表 / 清标;**不含**只读控制面的清标账列表 ——
5
5
  * core 的 wire 表逐字:只碰控制面的面不判,给它挂一条永远收不到的码路径正是附录 A 活触发门要防的形)·
6
- * `routes/memory-bundle.ts`(导出 / 导入)。会话所有者的自助抹除口**不走这里**(`routes/sessions.ts`:同码、固定文案,见那里的注)。
6
+ * `routes/memory-bundle.ts`(导出 / 导入)。会话所有者的自助抹除口**不走这里**(S-930 起经 `http/memory-compliance-failure-reply.ts` 的会话所有者受众臂:同码、固定文案)。
7
7
  * `POST /v1/admin/memory/consolidation/run` 也不走这里:那口把 core 的**任何**有码拒一律收成 409 `memory.consolidation_refused`
8
8
  * (码进文案,附录 A 该行),本码已在那条臂上,不再给它第二个码。
9
9
  *
@@ -19,6 +19,7 @@ import { APPROVAL_GATE_KINDS, createApprovalCardEmitter, isApprovalGateKind, par
19
19
  import { buildCheckpointAnchorHash } from "../approval-ask-audit-store.js";
20
20
  import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
21
21
  import { withPrincipal } from "../observability/principal-context.js";
22
+ import { withModelReachScope } from "../model-reach-scope.js";
22
23
  import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
23
24
  import { redactSecrets } from "../trace/redact.js";
24
25
  import { createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
@@ -502,7 +503,7 @@ export function createResumeLegs(ctx) {
502
503
  await args.onResumeCommitted();
503
504
  let unregisterEngineNotice;
504
505
  try {
505
- result = await withPrincipal(principal, async () => {
506
+ result = await withModelReachScope(sessionId, () => withPrincipal(principal, async () => {
506
507
  const resumeNotifiedKeys = new NotifiedKeys();
507
508
  if (deps.runStore && taskId) {
508
509
  const rs0 = deps.runStore;
@@ -871,7 +872,7 @@ export function createResumeLegs(ctx) {
871
872
  return await (deps.sendUserFile && taskId
872
873
  ? deps.sendUserFile.runWithContext({ taskId, emit: async (frame) => { const { type, ...rest } = frame; await append(type, rest); } }, withElicitation)
873
874
  : withElicitation());
874
- });
875
+ }));
875
876
  }
876
877
  catch (e) {
877
878
  if (e instanceof CheckpointError) {
@@ -150,9 +150,9 @@ export interface CheckpointCtx {
150
150
  /**
151
151
  * 🔴 S-877 候裁 18(codex r3 F1)—— 这条会话的 run 是**无人值守腿**铸的(今天唯一的写者 = leader worker 的 durable 块,
152
152
  * `leader/wire.ts`),**服务端写、不来自请求体**(`body` 是提交体;本位是它的兄弟键,客户端够不着)。
153
- * 续跑家族据此请 `resolveSpec` 带 `unattendedLeg: true`,由它按续跑 Runner 的同名座经 `createUnattendedLeg` / `withUnattendedLeg`
154
- * 重挂整条腿(模式 = 旋钮、写面 roots、模式释放写保护时的写保护拒层)—— 拒层是活闭包、进不了 checkpoint,而 core 在 spec 不说
155
- * 模式时取 checkpoint 记下的那一个(`seat ?? recorded`),不重挂 = 续跑落 bypass 而拒层缺席。写回口(`DriveResumeArgs.resumeCtx`)
153
+ * 续跑家族据此请 `resolveSpec` 带 `unattendedLeg: true`,由它经首跑同一只合并口 `withUnattendedLeg` 重挂整条腿(S-934 ①:`default` +
154
+ * 按类作答的 onAsk)—— onAsk 是活闭包、进不了 checkpoint,而 core 在 spec 不说模式时取 checkpoint 记下的那一个(`seat ?? recorded`),
155
+ * 不重挂 = 续跑落 checkpoint 记下的旧模式(7.106.0–7.108.0 的行记 `bypassPermissions`)而按类作答缺席。写回口(`DriveResumeArgs.resumeCtx`)
156
156
  * 传原 ctx / 其展开 ⇒ 标随行。缺席 = 不是无人值守腿(HTTP 提交腿;7.106.0 前落盘的行 —— 那时这些腿不铸模式,checkpoint 里也没有)。
157
157
  */
158
158
  unattendedLeg?: true;
@@ -138,6 +138,7 @@ export async function handleCapabilities(req, res, match, ctx) {
138
138
  taskAgents: deps.config.requirePrincipal !== true,
139
139
  retainBackgroundProcesses: deps.config.requirePrincipal !== true,
140
140
  interactiveTools: true,
141
+ disableSkills: true,
141
142
  projectContext: cwdHonored(deps.config),
142
143
  callerCwd: cwdHonored(deps.config) || deviceCwdHonored(deps.config),
143
144
  taskWriteFaceOpen: taskWriteFaceRefusal(deps.config) === undefined && autonomyOverrides(deps.config.autonomy).writeFace !== "roots",
@@ -1,6 +1,7 @@
1
1
  import { buildPostureKnobReading } from "../../posture-source.js";
2
2
  import { buildReadFaceWiring } from "../../read-face-posture.js";
3
3
  import { projectExecutionLaneCapability } from "../../capabilities/execution-lane.js";
4
+ import { readOutboundWiring } from "../../outbound-dispatcher.js";
4
5
  import { resolveStreamApprovalGate } from "../../tool-approval.js";
5
6
  import { sendJson, sendError } from "../send.js";
6
7
  import { gatedPrincipal, explicitOperatorOk, isLoopbackAddress, verifiedIdentitySource } from "../principal-gate.js";
@@ -106,6 +107,7 @@ export async function handleDiagnostics(req, res, match, ctx) {
106
107
  writeProtection: deps.writeProtection ?? null,
107
108
  readFace: buildReadFaceWiring(deps.config),
108
109
  executionLane: projectExecutionLaneCapability(deps.config),
110
+ outbound: readOutboundWiring(),
109
111
  configApply: deps.configApplyState?.() ?? null,
110
112
  mcpRevocations: deps.mcpRevocationState?.() ?? null,
111
113
  serverGates: buildServerWiringGates({
@@ -1,7 +1,6 @@
1
1
  import { z } from "zod";
2
- import { ControlPlaneCorruptError } from "@sema-agent/core";
3
2
  import { erasureInputSmugglesPrototypeKey } from "../../memory-operator-faces.js";
4
- import { sentMemoryStoreRefusal } from "../memory-store-refusal-reply.js";
3
+ import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.js";
5
4
  import { sendJson, sendError } from "../send.js";
6
5
  import { gatedPrincipal, explicitOperatorOk, sendPrincipalRefusal } from "../principal-gate.js";
7
6
  import { assertNever } from "../route-ctx.js";
@@ -11,40 +10,9 @@ const EraseRequestSchema = z.object({
11
10
  select: z.record(z.string(), z.unknown()),
12
11
  allowUnevidenced: z.boolean().optional(),
13
12
  });
14
- function sentCoreCode(res, coreCode, message) {
15
- switch (coreCode) {
16
- case "config.memory_erasure_request":
17
- sendError(res, 400, "config.memory_erasure_request", message);
18
- return true;
19
- case "memory.erasure_evidence_unavailable":
20
- sendError(res, 409, "memory.erasure_evidence_unavailable", message);
21
- return true;
22
- case "memory.erasure_selector_mismatch":
23
- sendError(res, 409, "memory.erasure_selector_mismatch", message);
24
- return true;
25
- case "memory.erasure_census_incomplete":
26
- sendError(res, 409, "memory.erasure_census_incomplete", message);
27
- return true;
28
- default:
29
- return false;
30
- }
31
- }
32
- function codeOf(err) {
33
- if (!(err instanceof Error))
34
- return undefined;
35
- const { code } = err;
36
- return typeof code === "string" ? code : undefined;
37
- }
38
13
  function sentClassifiedFailure(res, ctx, face, err) {
39
- const coreCode = codeOf(err);
40
- if (coreCode !== undefined && sentCoreCode(res, coreCode, err instanceof Error ? err.message : String(err)))
14
+ if (sentMemoryComplianceFailure(res, err, { kind: "operator" }))
41
15
  return true;
42
- if (sentMemoryStoreRefusal(res, err))
43
- return true;
44
- if (err instanceof ControlPlaneCorruptError) {
45
- sendError(res, 500, "internal.memory_control_plane_corrupt", err.message);
46
- return true;
47
- }
48
16
  ctx.deps.logger?.warn?.(`memory_compliance_${face}_failed`, { err: String(err) });
49
17
  return false;
50
18
  }
@@ -114,7 +114,7 @@ export declare function buildClearanceRowWire(r: OriginClearanceRow): {
114
114
  baseRev: string;
115
115
  origin: {
116
116
  at: number;
117
- cause?: "static" | "unattributed" | "observed" | "derived" | undefined;
117
+ cause?: "static" | "observed" | "unattributed" | "derived" | undefined;
118
118
  taint: "external";
119
119
  };
120
120
  };