@sema-agent/server 7.107.0 → 7.108.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 (63) hide show
  1. package/MIGRATION.md +29 -0
  2. package/USAGE.md +36 -0
  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.js +16 -13
  6. package/dist/boot/coordinators.d.ts +4 -0
  7. package/dist/boot/coordinators.js +3 -2
  8. package/dist/boot/shutdown.d.ts +8 -0
  9. package/dist/boot/shutdown.js +31 -8
  10. package/dist/boot/stage-06-runners.d.ts +1 -0
  11. package/dist/boot/stage-06-runners.js +6 -3
  12. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  13. package/dist/boot/stage-08-reapers.d.ts +1 -0
  14. package/dist/boot/stage-09-leader.d.ts +1 -0
  15. package/dist/boot/stage-10-http-server.d.ts +1 -0
  16. package/dist/boot/stage-10-http-server.js +3 -1
  17. package/dist/brain.js +2 -1
  18. package/dist/config-center/apply-effective.js +2 -0
  19. package/dist/config-center/types.d.ts +7 -0
  20. package/dist/deployment-governance.js +2 -1
  21. package/dist/fleet-client.d.ts +10 -2
  22. package/dist/fleet-client.js +14 -8
  23. package/dist/http/memory-compliance-failure-reply.d.ts +41 -0
  24. package/dist/http/memory-compliance-failure-reply.js +73 -0
  25. package/dist/http/memory-store-refusal-reply.d.ts +2 -2
  26. package/dist/http/resume-legs.js +3 -2
  27. package/dist/http/routes/diagnostics.js +2 -0
  28. package/dist/http/routes/memory-compliance.js +2 -34
  29. package/dist/http/routes/memory-origin.d.ts +1 -1
  30. package/dist/http/routes/sessions.js +3 -11
  31. package/dist/http/routes/side-query.js +7 -2
  32. package/dist/http/routes/tasks.js +2 -1
  33. package/dist/memory-store-refusal.d.ts +2 -1
  34. package/dist/model-entry-headers.d.ts +41 -0
  35. package/dist/model-entry-headers.js +61 -0
  36. package/dist/model-entry-refusal.d.ts +6 -0
  37. package/dist/model-entry-refusal.js +4 -0
  38. package/dist/model-reach-scope.d.ts +55 -0
  39. package/dist/model-reach-scope.js +16 -0
  40. package/dist/model-unreachable-notice.d.ts +38 -0
  41. package/dist/model-unreachable-notice.js +72 -0
  42. package/dist/observability/fail-open.d.ts +4 -0
  43. package/dist/observability/fail-open.js +4 -0
  44. package/dist/observability/run-terminal.d.ts +2 -1
  45. package/dist/outbound-dispatcher.d.ts +69 -0
  46. package/dist/outbound-dispatcher.js +123 -3
  47. package/dist/plugins/park-commit-watch.d.ts +48 -0
  48. package/dist/plugins/park-commit-watch.js +87 -0
  49. package/dist/router/route-orchestration.js +7 -2
  50. package/dist/runs.js +3 -2
  51. package/dist/runtime-governance.js +3 -2
  52. package/dist/task-settings.js +4 -2
  53. package/dist/tool-approval.d.ts +62 -0
  54. package/dist/tool-approval.js +67 -3
  55. package/dist/tool-rule-names.d.ts +25 -0
  56. package/dist/tool-rule-names.js +5 -0
  57. package/dist/trace/core-keyset-guard.d.ts +13 -1
  58. package/dist/trace/engine-notice-wire.d.ts +3 -1
  59. package/dist/trace/engine-notice-wire.js +2 -0
  60. package/dist/trace/project.d.ts +3 -1
  61. package/dist/trace/self-minted-wire.d.ts +71 -0
  62. package/dist/trace/self-minted-wire.js +28 -0
  63. package/package.json +1 -1
@@ -14,6 +14,7 @@ export declare function runStage06(ctx: Awaited<ReturnType<typeof runStage05>>):
14
14
  approvalExemptionStore: import("../plugins/approval-exemption-store.js").ApprovalExemptionStore | undefined;
15
15
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
16
16
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
17
+ parkCommits: import("../plugins/park-commit-watch.js").ParkCommitWatch | undefined;
17
18
  durableEnabled: boolean;
18
19
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
19
20
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -13,6 +13,7 @@ import { buildConsolidationValveAudit, createMemoryConsolidationFaces, resolveCo
13
13
  import { createPerModelAuthSeat } from "./side-query-lane.js";
14
14
  import { createWorkflowOrchestration } from "./workflow-orchestration.js";
15
15
  import { createLiveCoordinators } from "./coordinators.js";
16
+ import { createParkCommitWatch } from "../plugins/park-commit-watch.js";
16
17
  import { createRuntimeCaps } from "./runtime-caps.js";
17
18
  import { createRunnerDeps, createSharedRunnerDeps } from "./runner-deps.js";
18
19
  import { createOrgMemoryAdmissionWiring } from "./org-memory.js";
@@ -22,8 +23,11 @@ import { parkedAskRedeem, runStore } from "./late-binds.js";
22
23
  export function runStage06(ctx) {
23
24
  const { backend, backgroundAgentStore, brain, sessionIncarnationOf, config, configCenter, consolidationValve, deploymentHooks, executionEnvFactory, fileHistoryStore, localRoot, logger, lspManager, mailboxRecipientLifecycle, mailboxStore, memoryEngine, memorySyncRunner, metrics, perSessionCwd, permissionRuleProvider, pricing, rosterStore, ruleConsent, sendUserFileTaskEnvs, sessionCaptureRecordStore, sessionCaptureRecords, sessionPolicyStore, sessionStore, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore } = ctx;
24
25
  const { sqlWorkflowRunStore, workflowNotifyJournal, workflowCompletionInbox, deliverWorkflowCompletion, workflowNotifyGate, fleetBus, workflowRunStore, workflowJournalStore, outcomeSink, workflowRecoverOpts, workflowAgentRegistry, subagentSteerRegistry, workflowAgentSessionIndex, } = createWorkflowOrchestration({ config, logger, metrics, localRoot, backend, getRunStore: () => runStore });
26
+ const parkCommitted = backend?.checkpoint && config.durableApproval ? createParkCommitWatch(backend.checkpoint(logger)) : undefined;
27
+ const checkpointStore = parkCommitted?.store;
28
+ const parkCommits = parkCommitted?.watch;
25
29
  const { elicitation, question, toolApproval, durableEnabled, streamApprovalGate, approvalAskAudit, approvalExemptionStore, sendUserFileEmitter, sendFileLedger, sendUserFileToolSpec } = createLiveCoordinators({
26
- config, logger, backend, sendUserFileTaskEnvs, ruleConsent,
30
+ config, logger, backend, sendUserFileTaskEnvs, ruleConsent, parkCommits,
27
31
  getParkedRedeem: () => parkedAskRedeem,
28
32
  ruleScopeRootFor: (sessionId) => cardRuleScopeRoot(sessionId !== undefined ? perSessionCwd.get(sessionId) : undefined, config),
29
33
  });
@@ -40,7 +44,6 @@ export function runStage06(ctx) {
40
44
  logger.info("shared_memory_stores_enabled", { backend: backend?.kind });
41
45
  const runnerOffloadStore = toolResultStore ?? new InMemoryToolResultStore({ maxTotalChars: 64_000_000 });
42
46
  const commitHands = config.configProvider === "local" ? { commitCoAuthor: "Sema <noreply@vivi-ai.com>" } : undefined;
43
- const checkpointStore = backend?.checkpoint && config.durableApproval ? backend.checkpoint(logger) : undefined;
44
47
  const retentionExecutor = backend?.retention?.({ taskAttachmentTable: taskAttachmentStore !== undefined, taskListTables: taskListLane.backend !== "memory", logger });
45
48
  const retentionLaneStore = backend?.retentionLane?.();
46
49
  assertRetentionLaneWirable({
@@ -202,6 +205,6 @@ export function runStage06(ctx) {
202
205
  };
203
206
  const subRunner = new Runner(subRunnerDeps);
204
207
  const handslessSubRunner = new Runner(withoutExecutionEnv(subRunnerDeps));
205
- return { ...ctx, approvalAskAudit, approvalExemptionStore, centerRuntimeCapsResolver, checkpointStore, durableEnabled, elicitation, fleetBus, governanceSeams, handslessRunner, handslessSubRunner, hookAgent, hookAgentRunner, hookLlm, mcpRevocations, memoryConsolidationFaces, memoryOptOutGrantStore, orgMemoryAdmission, outcomeSink, principalCaps, question, retentionExecutor, retentionLaneStore, runner, runnerDeps, runtimeCapsPeekResolver, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionTitler, sharedMemoryStore, sqlWorkflowRunStore, streamApprovalGate, subRunner, subRunnerSessions, subagentSteerRegistry, toolApproval, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable };
208
+ return { ...ctx, approvalAskAudit, approvalExemptionStore, centerRuntimeCapsResolver, checkpointStore, parkCommits, durableEnabled, elicitation, fleetBus, governanceSeams, handslessRunner, handslessSubRunner, hookAgent, hookAgentRunner, hookLlm, mcpRevocations, memoryConsolidationFaces, memoryOptOutGrantStore, orgMemoryAdmission, outcomeSink, principalCaps, question, retentionExecutor, retentionLaneStore, runner, runnerDeps, runtimeCapsPeekResolver, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionTitler, sharedMemoryStore, sqlWorkflowRunStore, streamApprovalGate, subRunner, subRunnerSessions, subagentSteerRegistry, toolApproval, workflowAgentRegistry, workflowAgentSessionIndex, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable };
206
209
  }
207
210
  //# sourceMappingURL=stage-06-runners.js.map
@@ -27,6 +27,7 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
27
27
  approvalAskAudit: import("../approval-ask-audit-store.js").FileApprovalAskAuditStore | undefined;
28
28
  centerRuntimeCapsResolver: ((principal: string | undefined) => Promise<import("@sema-agent/core").RuntimeCaps | undefined>) | undefined;
29
29
  checkpointStore: import("../plugins/store-backend.js").CheckpointStoreFull | undefined;
30
+ parkCommits: import("../plugins/park-commit-watch.js").ParkCommitWatch | undefined;
30
31
  durableEnabled: boolean;
31
32
  elicitation: import("../elicitation.js").ElicitationCoordinator | undefined;
32
33
  fleetBus: import("../fleet/fleet-bus.js").FleetEventBus;
@@ -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;
@@ -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 = {
@@ -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;
@@ -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) {
@@ -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
  };
@@ -4,7 +4,7 @@ import { isUuidV7, isUuidShape, legacyUnownedSessionOk } from "../../security.js
4
4
  import { windowMessages, truncateMessageBlobs } from "../../audit.js";
5
5
  import { redactSecrets } from "../../trace/redact.js";
6
6
  import { recordFailOpen } from "../../observability/fail-open.js";
7
- import { targetOutsideStoreOf } from "../../memory-store-refusal.js";
7
+ import { sentMemoryComplianceFailure } from "../memory-compliance-failure-reply.js";
8
8
  import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, MAX_SETTINGS_PERMISSION_RULES, MAX_SETTINGS_ENV_VARS, MAX_SETTINGS_ENV_KEY_CHARS, MAX_SETTINGS_ENV_VALUE_CHARS } from "../../task-settings.js";
9
9
  import { readWritableSessionPermissionRules, describeSessionPermissionRulesProblem } from "../../session-policy-wire.js";
10
10
  import { sendJson, sendError, sendSessionError, sendSessionPolicyError, sseHeaders } from "../send.js";
@@ -526,17 +526,9 @@ export async function handleSessions(req, res, match, ctx) {
526
526
  sendJson(res, 200, await faces.erase({ requestId: raw.requestId, select: { sessionId }, ...(raw.allowUnevidenced !== undefined ? { allowUnevidenced: raw.allowUnevidenced } : {}) }));
527
527
  }
528
528
  catch (err) {
529
- const code = err.code;
530
- if (code === "config.memory_erasure_request") {
531
- sendError(res, 400, "config.memory_erasure_request", err instanceof Error ? err.message : "invalid memory erasure request");
529
+ const withheld = (coreMessage) => deps.logger?.warn?.("session_memory_erase_refused", { sessionId, err: coreMessage });
530
+ if (sentMemoryComplianceFailure(res, err, { kind: "session_owner", withheld }))
532
531
  return;
533
- }
534
- const outside = targetOutsideStoreOf(err);
535
- if (outside !== undefined) {
536
- deps.logger?.warn?.("session_memory_erase_refused", { sessionId, err: outside.message });
537
- 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");
538
- return;
539
- }
540
532
  deps.logger?.warn?.("session_memory_erase_failed", { sessionId, err: String(err) });
541
533
  sendError(res, 500, "internal.error", "session memory erasure failed");
542
534
  }
@@ -3,7 +3,7 @@ import { HttpError } from "../../security.js";
3
3
  import { isModelAllowlisted, matchCatalogModel, refusedCatalogEntry, selectableCatalog } from "../../model-select.js";
4
4
  import { modelUnavailableMessage } from "../../model-entry-refusal.js";
5
5
  import { redactSecrets } from "../../trace/redact.js";
6
- import { admitProviderEcho } from "../../observability/run-terminal.js";
6
+ import { admitProviderEcho, redactErrorMessage } from "../../observability/run-terminal.js";
7
7
  import { sendJson, sendError, httpErrorCode, msg } from "../send.js";
8
8
  import { gatedPrincipal, sendPrincipalRefusal } from "../principal-gate.js";
9
9
  import { assertNever } from "../route-ctx.js";
@@ -35,6 +35,11 @@ function shapeSideQueryEcho(result) {
35
35
  const { responseModel: _outOfShape, ...admitted } = result;
36
36
  return admitted;
37
37
  }
38
+ function projectSideQueryResult(result) {
39
+ const shaped = shapeSideQueryEcho(result);
40
+ const redacted = redactErrorMessage(shaped.errorMessage);
41
+ return redacted === undefined || redacted === shaped.errorMessage ? shaped : { ...shaped, errorMessage: redacted };
42
+ }
38
43
  export async function handleSideQuery(req, res, match, ctx) {
39
44
  const { deps } = ctx;
40
45
  const { readJson, rateLimited, quotaExceeded, leaseDenied } = ctx.helpers;
@@ -107,7 +112,7 @@ export async function handleSideQuery(req, res, match, ctx) {
107
112
  deps.sideQueryAccounting?.(principal, { model: modelId, usage: rr.usage });
108
113
  deps.metrics?.inc?.("side_query_total", { result: "ok" });
109
114
  if (!res.writableEnded)
110
- sendJson(res, 200, shapeSideQueryEcho(result));
115
+ sendJson(res, 200, projectSideQueryResult(result));
111
116
  }
112
117
  catch (e) {
113
118
  deps.metrics?.inc?.("side_query_total", { result: "error" });
@@ -27,6 +27,7 @@ import { headerStr, gatedPrincipal, explicitOperatorOk } from "../principal-gate
27
27
  import { failOpenTagForDroppedFrame, recordFailOpen } from "../../observability/fail-open.js";
28
28
  import { buildSyncSubmitBody } from "../sync-submit-body.js";
29
29
  import { assertNever } from "../route-ctx.js";
30
+ import { withModelReachScope } from "../../model-reach-scope.js";
30
31
  const DISCONNECT_PARK_GRACE_MS = 10_000;
31
32
  function sseData(res, payload) {
32
33
  const anchor = payload.eventId;
@@ -644,7 +645,7 @@ export async function handleTasks(req, res, match, ctx) {
644
645
  });
645
646
  }
646
647
  const withApproval = () => approvalCtx && deps.toolApproval ? deps.toolApproval.runWithContext(approvalCtx, withQuestion) : withQuestion();
647
- await (deps.sendUserFile
648
+ await withModelReachScope(prepared.spec.sessionId, () => deps.sendUserFile
648
649
  ? deps.sendUserFile.runWithContext({
649
650
  taskId: askTaskId,
650
651
  emit: async (f) => {
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * ## 两条到达车道,两个读者(本模块各给一只,别处不许再写第二份判据)
10
10
  * ① **抛出面**(抹除 / 出处 / 外源列表 / 清标 / bundle 导出导入 —— 凡触 store 树的 backend 面):`.code` 就是码
11
- * ⇒ {@link targetOutsideStoreOf}。HTTP 映射在 `http/memory-store-refusal-reply.ts`(409,一处字面量)。
11
+ * ⇒ {@link targetOutsideStoreOf}。HTTP 映射在 `http/memory-store-refusal-reply.ts`(409,operator 面一处字面量)与
12
+ * `http/memory-compliance-failure-reply.ts`(合规面两受众的那一只;会话所有者受众换固定句,S-930)。
12
13
  * ② **报告面**(harvest `incident`):core 契约写明 report 车道上码**只在文本里** —— `sidecar_corrupt` 的 detail 恰两种形
13
14
  * `memory commit refused: memory.target_outside_store: …` / `memory control plane refused: memory.target_outside_store: …`
14
15
  * (core `docs/wire-consumption/7.31.1.md` 的 harvest 行逐字)⇒ {@link targetOutsideStoreInIncident} 只认**这两个 lead 紧跟码**
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 白名单 = 名字 → 读它的线路(闭集;**加一个名字 = 加一行并写明哪条线路读它、为什么是部署的事**)。
3
+ * `anthropic-beta`:Anthropic Messages 的 beta 开关(逗号分隔的 beta 名;core anthropic 腿会把自己的 beta 并进同一个头)。
4
+ */
5
+ export declare const CATALOG_ENTRY_HEADER_LANES: {
6
+ readonly "anthropic-beta": readonly ["anthropic-messages"];
7
+ };
8
+ export type CatalogEntryHeaderName = keyof typeof CATALOG_ENTRY_HEADER_LANES;
9
+ export declare const CATALOG_ENTRY_HEADER_NAMES: readonly CatalogEntryHeaderName[];
10
+ /** 判过的袋(只含白名单里的名字,值逐字)。 */
11
+ export type CatalogEntryHeaders = Readonly<Partial<Record<CatalogEntryHeaderName, string>>>;
12
+ /** 值的长度上界(字符)。`anthropic-beta` 的真实值是几枚逗号分隔的 beta 名,远在此下;上界只挡「把别的东西塞进来」。 */
13
+ export declare const MAX_HEADER_VALUE_CHARS = 1024;
14
+ /**
15
+ * 拒因 / warn 里名字的回显尺:token 形、≤64、且脱敏器不改它 ⇒ 照写;否则固定占位(名字位也可能被塞进秘密,值更是从不回显)。
16
+ */
17
+ export declare function displayHeaderName(name: string): string;
18
+ /** 判决:`ok` ⇒ 判过的袋(`undefined` = 条目没声明任何头:缺席与 `{}` 同一个读法,都不铸键);否则 ⇒ 坏字段坐标 + 一句不带值的理由。 */
19
+ export type CatalogEntryHeadersVerdict = {
20
+ readonly ok: true;
21
+ readonly headers: CatalogEntryHeaders | undefined;
22
+ } | {
23
+ readonly ok: false;
24
+ readonly fields: readonly string[];
25
+ readonly issue: string;
26
+ };
27
+ /**
28
+ * **唯一判据**:条目的 `headers` 原值(`unknown`:承载形,不信类型)+ 条目的线路词原值(`api`;两条读腿恒补齐它,
29
+ * 绕过读腿的输入缺它 ⇒ 按「没有线路读它」拒 —— 判据与铸点读同一个原值,不另补一份 schema 缺省)。
30
+ * 整只判:任一名字 / 值不合 ⇒ 整只不收(留下好的那几个 = 让运维以为整袋生效了)。坏字段逐个列出(`headers.<名>`)。
31
+ */
32
+ export declare function readCatalogEntryHeaders(raw: unknown, api: unknown): CatalogEntryHeadersVerdict;
33
+ /**
34
+ * **唯一铸点**(`toModel` 调):判过的袋 → `Model.headers` 的键。缺席 ⇒ 不铸键(一个 `undefined` 值的键也不铸:
35
+ * 缺席只有一种拼法)。判不过 ⇒ **抛**:到这里的条目已经过 S-704 逐条判据(判不过的进 `modelRefusals`、到不了 `toModel`),
36
+ * 抛 = 两处判据分叉了,stage 整候选拒、响亮,不静默丢头。
37
+ */
38
+ export declare function buildModelHeadersKey(entryName: string, raw: unknown, api: unknown): {
39
+ headers?: Record<string, string>;
40
+ };
41
+ //# sourceMappingURL=model-entry-headers.d.ts.map