@sema-agent/server 7.149.0 → 7.150.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 (194) hide show
  1. package/MIGRATION.md +122 -9
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/USAGE.md +39 -15
  5. package/dist/approval-card.d.ts +1 -1
  6. package/dist/approval-gate-currency.d.ts +1 -1
  7. package/dist/audit.d.ts +1 -1
  8. package/dist/audit.js +2 -2
  9. package/dist/background-session-reap.d.ts +1 -1
  10. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -3
  11. package/dist/boot/deferred-sandbox-path-env.js +1 -2
  12. package/dist/boot/parked-revive-gate.d.ts +5 -5
  13. package/dist/boot/parked-revive-gate.js +2 -3
  14. package/dist/boot/resolve-spec.d.ts +4 -5
  15. package/dist/boot/resolve-spec.js +61 -25
  16. package/dist/boot/retention-lane.d.ts +1 -1
  17. package/dist/boot/session-faces.js +21 -21
  18. package/dist/boot/stage-03-store-backend.js +2 -2
  19. package/dist/boot/stage-06-runners.js +1 -2
  20. package/dist/boot/stage-07-capability-layer.js +1 -1
  21. package/dist/boot/stage-10-http-server.d.ts +33 -1
  22. package/dist/boot/stage-10-http-server.js +9 -4
  23. package/dist/boot/web-admin.d.ts +1 -1
  24. package/dist/capability-facts.d.ts +65 -56
  25. package/dist/capability-facts.js +42 -35
  26. package/dist/config-catalog.js +1 -1
  27. package/dist/config-types.d.ts +12 -11
  28. package/dist/deployment-governance.d.ts +7 -28
  29. package/dist/deployment-governance.js +1 -14
  30. package/dist/device-ws-hub.d.ts +1 -0
  31. package/dist/device-ws-hub.js +64 -28
  32. package/dist/device-ws-protocol.d.ts +20 -6
  33. package/dist/device-ws-protocol.js +9 -2
  34. package/dist/engine-identity.d.ts +7 -1
  35. package/dist/engine-identity.js +5 -0
  36. package/dist/field-conflict.d.ts +2 -2
  37. package/dist/fleet/engine-signal.d.ts +14 -1
  38. package/dist/fleet/engine-signal.js +5 -1
  39. package/dist/fleet/fleet-reconciler.d.ts +1 -1
  40. package/dist/fleet/serving-lease.d.ts +18 -3
  41. package/dist/fleet/serving-lease.js +81 -35
  42. package/dist/fleet-client.d.ts +13 -11
  43. package/dist/fleet-client.js +53 -33
  44. package/dist/history-anchor.js +1 -1
  45. package/dist/hitl-delivery.d.ts +1 -3
  46. package/dist/hitl-delivery.js +1 -1
  47. package/dist/http/active-run-conflict.d.ts +3 -7
  48. package/dist/http/active-run-conflict.js +2 -2
  49. package/dist/http/admission.js +2 -2
  50. package/dist/http/approval-sweeps.js +2 -1
  51. package/dist/http/caller-trust.d.ts +2 -2
  52. package/dist/http/caller-trust.js +3 -3
  53. package/dist/http/checkpoint-ctx.d.ts +18 -63
  54. package/dist/http/checkpoint-ctx.js +1 -24
  55. package/dist/http/claim-heartbeat.d.ts +4 -4
  56. package/dist/http/claim-heartbeat.js +3 -3
  57. package/dist/http/dispatch.js +2 -2
  58. package/dist/http/durable-run-leg.js +4 -4
  59. package/dist/http/owner-access.d.ts +243 -19
  60. package/dist/http/owner-access.js +184 -5
  61. package/dist/http/readiness.d.ts +4 -2
  62. package/dist/http/resume-ctx-read.js +16 -1
  63. package/dist/http/resume-legs.js +18 -14
  64. package/dist/http/route-ctx.d.ts +23 -11
  65. package/dist/http/routes/a2a-serve.js +15 -14
  66. package/dist/http/routes/approvals-assistant.d.ts +3 -2
  67. package/dist/http/routes/approvals-assistant.js +8 -9
  68. package/dist/http/routes/capabilities.js +4 -3
  69. package/dist/http/routes/config-catalog.d.ts +1 -1
  70. package/dist/http/routes/mcp-reconnect.js +2 -2
  71. package/dist/http/routes/memory-origin.d.ts +1 -1
  72. package/dist/http/routes/notify-wake.js +2 -3
  73. package/dist/http/routes/projects.d.ts +1 -0
  74. package/dist/http/routes/projects.js +5 -3
  75. package/dist/http/routes/retention-ops.d.ts +1 -1
  76. package/dist/http/routes/run-permission-mode.d.ts +1 -1
  77. package/dist/http/routes/run-permission-mode.js +2 -2
  78. package/dist/http/routes/runs.d.ts +1 -1
  79. package/dist/http/routes/runs.js +23 -30
  80. package/dist/http/routes/session-memory-sync.js +4 -5
  81. package/dist/http/routes/session-sync.js +3 -7
  82. package/dist/http/routes/sessions.d.ts +1 -1
  83. package/dist/http/routes/sessions.js +18 -44
  84. package/dist/http/routes/tasks.js +9 -8
  85. package/dist/http/routes/trace-usage.js +14 -14
  86. package/dist/http/routes/workflows.d.ts +1 -0
  87. package/dist/http/routes/workflows.js +20 -38
  88. package/dist/http/server.d.ts +8 -4
  89. package/dist/http/server.js +5 -1
  90. package/dist/http/stored-body-replay.d.ts +3 -24
  91. package/dist/http/stored-body-replay.js +2 -12
  92. package/dist/http/submit-ctx.d.ts +1 -2
  93. package/dist/http/submit-ctx.js +3 -2
  94. package/dist/http/submit-replay.d.ts +15 -2
  95. package/dist/http/submit-replay.js +10 -4
  96. package/dist/http/wire-types.d.ts +6 -3
  97. package/dist/leader/endpoint.js +9 -8
  98. package/dist/leg-facts.d.ts +8 -2
  99. package/dist/leg-facts.js +1 -0
  100. package/dist/mcp-authorization-face.d.ts +18 -14
  101. package/dist/mcp-authorization-face.js +3 -2
  102. package/dist/memory-store-refusal.d.ts +1 -1
  103. package/dist/observability/corrupt-read-seat.d.ts +1 -1
  104. package/dist/observability/fail-open.d.ts +5 -5
  105. package/dist/observability/fail-open.js +5 -5
  106. package/dist/observability/logger.d.ts +7 -0
  107. package/dist/observability/logger.js +3 -1
  108. package/dist/observability/run-terminal.d.ts +1 -1
  109. package/dist/owner-faces.d.ts +27 -3
  110. package/dist/owner-faces.js +2 -0
  111. package/dist/peer-seat.d.ts +1 -1
  112. package/dist/peer-seat.js +52 -59
  113. package/dist/plugins/exec-path-law.d.ts +34 -0
  114. package/dist/plugins/exec-path-law.js +13 -0
  115. package/dist/plugins/file-run-store.js +1 -1
  116. package/dist/plugins/local-checkpoint-store.d.ts +4 -1
  117. package/dist/plugins/local-checkpoint-store.js +3 -0
  118. package/dist/plugins/memory-run-store.js +1 -1
  119. package/dist/plugins/one-time-migrations.d.ts +74 -49
  120. package/dist/plugins/one-time-migrations.js +134 -69
  121. package/dist/plugins/pg-pool.js +4 -0
  122. package/dist/plugins/posix-shell-fs.js +4 -2
  123. package/dist/plugins/remote-env-adb.js +3 -2
  124. package/dist/plugins/remote-env-device.js +8 -6
  125. package/dist/plugins/remote-env-e2b.d.ts +3 -1
  126. package/dist/plugins/remote-env-e2b.js +11 -8
  127. package/dist/plugins/remote-env-host.d.ts +1 -1
  128. package/dist/plugins/remote-env-host.js +20 -11
  129. package/dist/plugins/remote-env-k8s.js +3 -2
  130. package/dist/plugins/remote-env-local-docker.js +3 -2
  131. package/dist/plugins/remote-env-ssh.js +4 -3
  132. package/dist/plugins/remote-shell.d.ts +3 -1
  133. package/dist/plugins/remote-shell.js +3 -3
  134. package/dist/plugins/replica-registration.d.ts +35 -0
  135. package/dist/plugins/replica-registration.js +48 -0
  136. package/dist/plugins/retention-lane-store-sql.d.ts +1 -1
  137. package/dist/plugins/run-store-sql.d.ts +1 -1
  138. package/dist/plugins/run-store-sql.js +2 -2
  139. package/dist/plugins/scenario-prefix-switch-m42.d.ts +6 -12
  140. package/dist/plugins/scenario-prefix-switch-m42.js +7 -5
  141. package/dist/plugins/schema-consistency.d.ts +7 -5
  142. package/dist/plugins/schema-consistency.js +29 -16
  143. package/dist/plugins/schema-drift-sentence.js +1 -1
  144. package/dist/plugins/serving-requirement.d.ts +49 -0
  145. package/dist/plugins/serving-requirement.js +53 -0
  146. package/dist/plugins/stop-then-start-sentence.d.ts +42 -3
  147. package/dist/plugins/stop-then-start-sentence.js +44 -0
  148. package/dist/plugins/store-backend.d.ts +1 -1
  149. package/dist/plugins/store-contracts.d.ts +12 -2
  150. package/dist/plugins/store-contracts.js +3 -0
  151. package/dist/plugins/tidb-pool.js +4 -0
  152. package/dist/process-instance.d.ts +3 -0
  153. package/dist/process-instance.js +7 -0
  154. package/dist/project-context.d.ts +66 -0
  155. package/dist/project-context.js +70 -0
  156. package/dist/question.d.ts +9 -21
  157. package/dist/question.js +28 -19
  158. package/dist/run-cancel-card.d.ts +1 -1
  159. package/dist/run-cancel-card.js +2 -1
  160. package/dist/security.d.ts +1 -1
  161. package/dist/session-agent.d.ts +1 -10
  162. package/dist/session-agent.js +0 -6
  163. package/dist/session-incarnation.d.ts +18 -4
  164. package/dist/session-incarnation.js +4 -1
  165. package/dist/session-notify-door.d.ts +4 -4
  166. package/dist/session-notify-door.js +5 -6
  167. package/dist/session-rules-seat.d.ts +4 -3
  168. package/dist/session-rules-seat.js +3 -11
  169. package/dist/session-sync.js +3 -3
  170. package/dist/stores-sql/checkpoint-store-sql.d.ts +21 -2
  171. package/dist/stores-sql/checkpoint-store-sql.js +106 -18
  172. package/dist/stores-sql/retention-store-sql.d.ts +1 -1
  173. package/dist/task-cwd.d.ts +4 -3
  174. package/dist/task-settings.d.ts +13 -0
  175. package/dist/task-settings.js +13 -7
  176. package/dist/tool-approval.d.ts +7 -6
  177. package/dist/tool-approval.js +41 -37
  178. package/dist/trace/engine-notice-wire.d.ts +14 -0
  179. package/dist/trace/engine-notice-wire.js +18 -1
  180. package/dist/trace/project.d.ts +4 -3
  181. package/dist/trace/project.js +3 -2
  182. package/dist/trace/projection-table.d.ts +33 -2
  183. package/dist/trace/self-minted-wire.d.ts +150 -17
  184. package/dist/trace/self-minted-wire.js +54 -16
  185. package/dist/trace/tool-card.d.ts +3 -0
  186. package/dist/trace/tool-card.js +24 -0
  187. package/dist/trace/tool-output-media.d.ts +2 -1
  188. package/dist/unattended-permission-mode.d.ts +1 -1
  189. package/dist/web-admin-client/apply-effective.d.ts +1 -1
  190. package/dist/web-admin-client/effective-keys.d.ts +1 -1
  191. package/dist/web-admin-client/skills-mcp.d.ts +1 -1
  192. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +57 -29
  193. package/package.json +2 -2
  194. package/wire-changes/server@7.150.0.json +335 -0
@@ -13,7 +13,7 @@ function hostGateCwd(config, localRoot) {
13
13
  return join(config.localDataRoot ?? localRoot, "parked-revive-unrooted");
14
14
  }
15
15
  export function createParkedReviveInheritedGate(deps) {
16
- const { config, question, approvalExemptionStore, logger, localRoot, approverSeat } = deps;
16
+ const { config, approvalExemptionStore, logger, localRoot, approverSeat } = deps;
17
17
  const slots = deps.slots ?? sandboxPathEnvSlots;
18
18
  return async (row, identity) => {
19
19
  const metadata = await chainMetadataOf(deps, row, identity);
@@ -32,7 +32,6 @@ export function createParkedReviveInheritedGate(deps) {
32
32
  const rootSessionId = row.rootSessionId;
33
33
  const baseline = createApprovalBaselinePolicy(config, {
34
34
  kind: "durable",
35
- question,
36
35
  ...(approvalExemptionStore !== undefined && rootSessionId ? { exemption: createSessionExemptionRead(approvalExemptionStore, rootSessionId, logger, "deployment_list", "parked_revive") } : {}),
37
36
  });
38
37
  const governed = applyRuntimeGovernance({ objective: "", toolPolicy: baseline }, createDeploymentGovernanceInputs(config, pathAdjudication));
@@ -53,7 +52,7 @@ async function chainMetadataOf(deps, row, identity) {
53
52
  const forced = caps?.forceDurableGate === true;
54
53
  return {
55
54
  ...(forced || deps.approverSeat === undefined ? { durableMandate: true } : {}),
56
- ...(forced || deps.question === undefined ? { contentMandate: true } : {}),
55
+ ...(forced || !deps.questionSeat ? { contentMandate: true } : {}),
57
56
  ...autoModePostureOf(deps, row, caps),
58
57
  };
59
58
  }
@@ -3,7 +3,6 @@ import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
3
3
  import { selectScenario } from "../capabilities/scenarios.js";
4
4
  import type { LoadedSkill } from "../capabilities/skills.js";
5
5
  import type { ServiceConfig } from "../config.js";
6
- import { type LiveQuestionFace } from "../deployment-governance.js";
7
6
  import { FleetEventBus } from "../fleet/fleet-bus.js";
8
7
  import { type HookLlmCall } from "../hooks/hook-runner.js";
9
8
  import type { createHookLlm } from "../hooks/hook-llm.js";
@@ -64,10 +63,10 @@ export interface ResolveSpecCtx {
64
63
  /** [ref]:记忆同步腿(开 `MEMORY_SYNC_*` 且记忆引擎接线时在场)—— 项目层准备一步把本任务的项目层交给它跟踪(`memory-project-layer.ts`)。 */
65
64
  memorySyncRunner: MemorySyncRunner | undefined;
66
65
  durableEnabled: boolean;
67
- /** [ref] ([ref] 案二):活体 AskUserQuestion 面(QuestionCoordinator 的判决探针切面)。在场(=
68
- * ASK_QUESTION_ENABLED)时 durable question 门按活流上下文分腿、spec 不再 stamp QUESTION_AWAITS_RESUME
69
- * (否则 spec.onQuestion 恒遮蔽 RunnerDeps.onQuestion 的活人腿);缺席时行为与旧形逐字一致。 */
70
- liveQuestionFace: LiveQuestionFace | undefined;
66
+ /** 问题席在场(= `ASK_QUESTION_ENABLED` 装了 `QuestionCoordinator`,`RunnerDeps.onQuestion` 是它):本仓「问题席在不在」这一事实的**一位**,
67
+ * 三个读者同读 —— 本文件的 posture 判据与 sentinel 铸不铸、赎回链的 `contentMandate`(`parked-revive-gate.ts`)。在场 ⇒ spec 不铸
68
+ * `QUESTION_AWAITS_RESUME`(否则 `spec.onQuestion` 恒遮蔽 `RunnerDeps.onQuestion` 的问题席);落点归 core 的同步优先。 */
69
+ questionSeat: boolean;
71
70
  /**
72
71
  * [ref] 件C —— 部署级**活体审批席**(`RunnerDeps.onAsk` 的同源铸法,main.ts 递
73
72
  * `createRunnerDepsOnAsk(toolApproval)`;单一属主同 `boot/runner-deps.ts`,与 `parked-revive-gate.ts`
@@ -15,6 +15,9 @@ import { acceptShellScratchpadDir, buildEnvFacts, egressForRemoteExec, ensureScr
15
15
  import { FleetEventBus } from "../fleet/fleet-bus.js";
16
16
  import { createSettingsHooksRegistrations, settingsHooksBudgetMs } from "../hooks/hook-runner.js";
17
17
  import { headerStr } from "../http/principal-gate.js";
18
+ import { routeListFilter } from "../http/owner-access.js";
19
+ import { projectRegistry } from "../capability-facts.js";
20
+ import { projectContextOf } from "../project-context.js";
18
21
  import { explicitOperatorOk } from "../http/server.js";
19
22
  import { isAtMentionable, matchCatalogModel, pickUserModel, refusedCatalogEntry, terminalCatalogName } from "../model-select.js";
20
23
  import { echoModelRef, modelNotAllowedMessage, modelUnavailableMessage, refusedNameLogField } from "../model-entry-refusal.js";
@@ -48,7 +51,7 @@ import { storedToolAxesRereadM19 } from "../stored-request-tool-axes-m19.js";
48
51
  import { storedHooksRereadM20 } from "../stored-request-hooks-m20.js";
49
52
  import { storedEnvCredentialsDropM38 } from "../stored-request-env-credentials-m38.js";
50
53
  import { serverCredentialValueSources } from "../server-secret-env.js";
51
- import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, REQUEST_SETTINGS_REFUSAL_PREFIX, SETTINGS_RULE_REFUSAL_CODE, SettingsRuleRefusalError, acceptAppendSystemPrompt, applyTaskSettings, createWorkflowAskPolicy, effectivePermissionMode, effectiveThinking, parseTaskSettings, readSettingsEnv, taskHooksHonored, settingsEnvCredentialIssue, settingsPermissionBucketIssue, withoutKeys } from "../task-settings.js";
54
+ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, REQUEST_SETTINGS_REFUSAL_PREFIX, SETTINGS_RULE_REFUSAL_CODE, SettingsRuleRefusalError, acceptAppendSystemPrompt, acceptOutputStyle, appendFold, applyTaskSettings, createWorkflowAskPolicy, effectivePermissionMode, effectiveThinking, parseTaskSettings, readSettingsEnv, taskHooksHonored, settingsEnvCredentialIssue, settingsPermissionBucketIssue, withoutKeys } from "../task-settings.js";
52
55
  import { tightenOnlySeatIssue } from "../tighten-only-seats.js";
53
56
  import { buildNeverAutoRetiredNotice } from "../retired-name-host-notice.js";
54
57
  import { entryProblemsFacts } from "../entry-problems.js";
@@ -71,7 +74,7 @@ function unrooted(spec) {
71
74
  return resolved;
72
75
  }
73
76
  export function createResolveSpec(ctx) {
74
- const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, belowTaskChainPatterns, fleetBus, hookWakeBus, historyAnchors, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, memorySyncRunner, durableEnabled, approvalExemptionStore, checkpointStore, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, approverSeat, lockedKeys, loadedSkills, peerDesk, } = ctx;
77
+ const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, fenceSessionOwner, hookLlm, hookAgent, belowTaskChainPatterns, fleetBus, hookWakeBus, historyAnchors, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, memorySyncRunner, durableEnabled, approvalExemptionStore, checkpointStore, imageIndex, perTaskImage, sessionEnvSelection, questionSeat, approverSeat, lockedKeys, loadedSkills, peerDesk, } = ctx;
75
78
  const deploymentSkillSources = new Map(loadedSkills.map((k) => [k.spec.name, k.source]));
76
79
  const scenarioGate = createScenarioGate({ config, scenarios, principalCaps, logger }, "enforce");
77
80
  const sessionHeadOf = headReadOf(ownerAware);
@@ -109,9 +112,40 @@ export function createResolveSpec(ctx) {
109
112
  }
110
113
  if (opts?.leg === "fresh")
111
114
  assertRequestMcpEntries(body.mcpServers);
115
+ const submittedProjectId = auth?.resolvedProjectId ?? (typeof body.projectId === "string" && body.projectId ? body.projectId.toLowerCase() : undefined);
116
+ const legProjectId = await legProjectIdOf(submittedProjectId, sessionWorkspace, root);
117
+ let projectContext;
118
+ if (opts?.leg === "fresh") {
119
+ const registry = projectRegistry({ config });
120
+ const decision = projectContextOf({
121
+ projectId: legProjectId,
122
+ registry: registry.available ? registry.registry : undefined,
123
+ viewer: req === undefined ? undefined : routeListFilter(req, { config }, "project-get"),
124
+ pathsHonored: DEPLOYMENT_SHAPE_PREDICATES.callerOwnsWorkerDisk.holds(config),
125
+ requestCwd: typeof body.cwd === "string" && body.cwd.length > 0,
126
+ projectMemoryOff: body.projectMemory === "disabled",
127
+ });
128
+ projectContext = decision.context;
129
+ if (decision.withheldPathItems > 0)
130
+ logger.warn("task_project_paths_ignored", { lane: config.remoteExec.provider, pathItems: decision.withheldPathItems, sessionId: root });
131
+ if (projectContext?.brief !== undefined) {
132
+ const append = acceptAppendSystemPrompt(body.appendSystemPrompt);
133
+ const style = acceptOutputStyle(body.settings?.outputStyle);
134
+ const fold = appendFold([append, projectContext.brief, style]);
135
+ if (fold.chars > MAX_SETTINGS_OUTPUT_STYLE_CHARS) {
136
+ const members = append !== undefined ? ["appendSystemPrompt", "projectId"] : ["projectId"];
137
+ if (style !== undefined)
138
+ members.push("settings.outputStyle");
139
+ throw new HttpError(400, `the project brief this run inherits from project ${projectContext.projectId} (its goal, notes and context items) folds into one system-prompt block with appendSystemPrompt and settings.outputStyle — combined they must be at most ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} characters (including the 2-char joiners), and they are ${fold.chars}. Shorten what you send, send projectMemory: "disabled" to run without the project's context, or have the project's goal / notes shortened in the registry.`, { code: "request.field_conflict", extra: { ...fieldConflictFacts("system_prompt_fold", members, { count: fold.chars, cap: MAX_SETTINGS_OUTPUT_STYLE_CHARS }) } });
140
+ }
141
+ }
142
+ }
143
+ else
144
+ projectContext = opts?.projectContext;
145
+ const legCwd = projectContext?.cwd ?? body.cwd;
112
146
  if (opts?.leg === "fresh") {
113
147
  const conflict = scratchpadCoveringTaskRoot({
114
- cwd: body.cwd,
148
+ cwd: legCwd,
115
149
  shellScratchpadDir: body.scratchpadDir,
116
150
  localDataRoot: config.localDataRoot ?? localRoot,
117
151
  sessionId: root,
@@ -119,7 +153,11 @@ export function createResolveSpec(ctx) {
119
153
  callerOwnsWorkerDisk: DEPLOYMENT_SHAPE_PREDICATES.callerOwnsWorkerDisk.holds(config),
120
154
  });
121
155
  if (conflict) {
122
- throw new HttpError(400, `cwd "${conflict.cwd.asSpelled}" is inside this session's scratchpad "${conflict.scratchpadDir.asSpelled}" — the scratchpad is the fs-write gate's EXEMPT directory, so a task rooted there would auto-approve every write under its own root and the engine refuses to build the gate at all (config.write_exempt_dir_covers_root). Point cwd at the project directory instead (or send no cwd and let the session keep the one it has): this session registers the cwd you send, so a task that happens not to mount the write gate would still leave every LATER task on this session rooted in the exemption. A workflow that really is meant to write without prompts says so with a knob that means it (a permission mode, an open write face) and still keeps its root outside the scratchpad — those knobs do not lift this refusal, because the conflict is between two fields of this request, not a permission level.`, { code: "request.field_conflict", extra: { ...fieldConflictFacts("scratchpad_root", body.scratchpadDir === conflict.scratchpadDir.asSpelled ? ["cwd", "scratchpadDir"] : ["cwd"]), cwd: conflict.cwd, scratchpadDir: conflict.scratchpadDir } });
156
+ const cwdCoordinate = projectContext?.cwd !== undefined ? "projectId" : "cwd";
157
+ const fields = body.scratchpadDir === conflict.scratchpadDir.asSpelled ? [cwdCoordinate, "scratchpadDir"] : [cwdCoordinate];
158
+ throw new HttpError(400, cwdCoordinate === "projectId"
159
+ ? `the working directory "${conflict.cwd.asSpelled}" this run inherits from project ${projectContext?.projectId} (its first folder) is inside this session's scratchpad "${conflict.scratchpadDir.asSpelled}" — the scratchpad is the fs-write gate's EXEMPT directory, so a task rooted there would auto-approve every write under its own root and the engine refuses to build the gate at all (config.write_exempt_dir_covers_root). Send a cwd outside the scratchpad with this request, or have the project's first folder moved out of it in the registry.`
160
+ : `cwd "${conflict.cwd.asSpelled}" is inside this session's scratchpad "${conflict.scratchpadDir.asSpelled}" — the scratchpad is the fs-write gate's EXEMPT directory, so a task rooted there would auto-approve every write under its own root and the engine refuses to build the gate at all (config.write_exempt_dir_covers_root). Point cwd at the project directory instead (or send no cwd and let the session keep the one it has): this session registers the cwd you send, so a task that happens not to mount the write gate would still leave every LATER task on this session rooted in the exemption. A workflow that really is meant to write without prompts says so with a knob that means it (a permission mode, an open write face) and still keeps its root outside the scratchpad — those knobs do not lift this refusal, because the conflict is between two fields of this request, not a permission level.`, { code: "request.field_conflict", extra: { ...fieldConflictFacts("scratchpad_root", fields), cwd: conflict.cwd, scratchpadDir: conflict.scratchpadDir } });
123
161
  }
124
162
  }
125
163
  const offFieldIssue = singleMemberOffFieldIssue(body);
@@ -191,12 +229,12 @@ export function createResolveSpec(ctx) {
191
229
  const gated = await gateScenarioAndAppend(body, auth, root, opts, settingsEnv);
192
230
  const effMode = effectivePermissionMode(body, gated.parsedSettings.settings);
193
231
  assertWriteFaceAdmitted(effMode, config);
194
- const lane = bindSettingsCwdEnvAndModel(body, auth, root, gated, opts);
195
- const anchors = await resolveHistoryAnchors(body, req, auth, root);
232
+ const lane = bindSettingsCwdEnvAndModel(body, auth, root, gated, opts, legCwd);
233
+ const anchors = await resolveHistoryAnchors(body, req, auth, root, submittedProjectId, legProjectId);
196
234
  const projectKey = memoryEngine ? await memoryProjectLayers.prepare(config, auth?.memoryScope, realTaskRootOf(root)) : undefined;
197
235
  const runSkills = buildRunSkills({ scenario: gated.cap.skills, request: body.skills, disable: body.disableSkills === true, deploymentSources: deploymentSkillSources }, logger);
198
- const spec = assembleSpecLiteral(body, auth, root, req, opts, { ...gated, ...lane, ...anchors, projectKey, a2aPeers, storedToolAxes, runSkills }, effMode);
199
- const folded = await foldGovernanceAndSettings(body, auth, root, spec, gated);
236
+ const spec = assembleSpecLiteral(body, auth, root, req, opts, { ...gated, ...lane, ...anchors, projectKey, a2aPeers, storedToolAxes, runSkills, projectBrief: projectContext?.brief }, effMode);
237
+ const folded = await foldGovernanceAndSettings(body, auth, root, spec, gated, projectContext?.brief);
200
238
  const retiredNotice = opts.onNotice !== undefined ? buildNeverAutoRetiredNotice(gated.parsedSettings.settings?.tightenOnly?.neverAuto, root) : undefined;
201
239
  if (retiredNotice !== undefined)
202
240
  opts.onNotice?.(retiredNotice);
@@ -206,7 +244,7 @@ export function createResolveSpec(ctx) {
206
244
  hostSemanticsLane: folded.hostSemanticsLane,
207
245
  });
208
246
  const registeredCwd = perSessionCwd.get(root);
209
- return { spec: final, facts: { hands: gated.hands, skills: runSkills.roster, sessionWitness: gated.sessionWitness, ...(registeredCwd !== undefined ? { registeredCwd } : {}) } };
247
+ return { spec: final, facts: { hands: gated.hands, skills: runSkills.roster, sessionWitness: gated.sessionWitness, ...(registeredCwd !== undefined ? { registeredCwd } : {}), ...(projectContext !== undefined ? { projectContext } : {}) } };
210
248
  };
211
249
  const gateScenarioAndAppend = async (body, auth, root, opts, settingsEnv) => {
212
250
  const { parts: subjectParts, person } = identityAxesOf(auth);
@@ -264,15 +302,15 @@ export function createResolveSpec(ctx) {
264
302
  }
265
303
  return { scenarioName, cap, hands: cap.hands, acceptedAppend, objective, parsedSettings, attachmentNotice, sessionWitness };
266
304
  };
267
- const bindSettingsCwdEnvAndModel = (body, auth, root, gated, opts) => {
305
+ const bindSettingsCwdEnvAndModel = (body, auth, root, gated, opts, cwd) => {
268
306
  const { objective, parsedSettings } = gated;
269
307
  const cwdWriteGate = DEPLOYMENT_SHAPE_PREDICATES.callerCwd.holds(config);
270
308
  const execLane = config.remoteExec.provider;
271
309
  fenceSessionOwner(root, auth?.subject ?? null, opts.execution.occupant?.incarnation);
272
- if (typeof body.cwd === "string" && body.cwd.length > 0) {
273
- if (cwdWriteGate && isAbsoluteHostPath(body.cwd))
274
- setSessionCwd(root, body.cwd);
275
- else if (DEPLOYMENT_SHAPE_PREDICATES.noHandsSingleUser.holds(config) && isAbsoluteHostPath(body.cwd) && satisfiedByProcessCwd(body.cwd, realpathSync))
310
+ if (typeof cwd === "string" && cwd.length > 0) {
311
+ if (cwdWriteGate && isAbsoluteHostPath(cwd))
312
+ setSessionCwd(root, cwd);
313
+ else if (DEPLOYMENT_SHAPE_PREDICATES.noHandsSingleUser.holds(config) && isAbsoluteHostPath(cwd) && satisfiedByProcessCwd(cwd, realpathSync))
276
314
  logger.debug("task_cwd_inherited", { lane: execLane, sessionId: root });
277
315
  else
278
316
  logger.warn("task_cwd_ignored", { honored: cwdWriteGate, lane: execLane, sessionId: root });
@@ -354,7 +392,7 @@ export function createResolveSpec(ctx) {
354
392
  }
355
393
  return { taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked };
356
394
  };
357
- const resolveHistoryAnchors = async (body, req, auth, root) => {
395
+ const resolveHistoryAnchors = async (body, req, auth, root, submittedProjectId, legProjectId) => {
358
396
  const anchorReader = req === undefined ? undefined : anchorReaderOf(req, { config, logger }, claimCallerOf(identityAxesOf(auth).subject, config));
359
397
  let resumeAtEntryId;
360
398
  if (typeof body.resumeAt === "string" && body.resumeAt.length > 0) {
@@ -368,9 +406,7 @@ export function createResolveSpec(ctx) {
368
406
  throw new HttpError(501, "rewind-files-to is not available on this worker (no session-store backend for the anchor map)");
369
407
  rewindFilesToEntryId = await resolveHistoryAnchor("rewindFilesTo", anchorsResolvable, anchorReader, root, body.rewindFilesTo, auth?.subject ?? null, "rewindFilesTo");
370
408
  }
371
- const submittedProjectId = auth?.resolvedProjectId ?? (typeof body.projectId === "string" && body.projectId ? body.projectId.toLowerCase() : undefined);
372
409
  const s4DefaultScopes = submittedProjectId ? config.projects[submittedProjectId]?.defaultScopes : undefined;
373
- const legProjectId = await legProjectIdOf(submittedProjectId, sessionWorkspace, root);
374
410
  return { resumeAtEntryId, rewindFilesToEntryId, s4DefaultScopes, legProjectId };
375
411
  };
376
412
  const declaresInteractivePosture = (body, req, opts) => {
@@ -378,7 +414,7 @@ export function createResolveSpec(ctx) {
378
414
  return false;
379
415
  if (body.verify === true || body.cascade === true)
380
416
  return false;
381
- if (liveQuestionFace === undefined)
417
+ if (!questionSeat)
382
418
  return false;
383
419
  if (body.interactiveTools === false)
384
420
  return false;
@@ -389,11 +425,11 @@ export function createResolveSpec(ctx) {
389
425
  };
390
426
  const assembleSpecLiteral = (body, auth, root, req, opts, parts, effMode) => {
391
427
  const { parts: subjectParts, person } = identityAxesOf(auth);
392
- const { scenarioName, cap, hands, acceptedAppend, parsedSettings, attachmentNotice, taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked, resumeAtEntryId, rewindFilesToEntryId, s4DefaultScopes, legProjectId, projectKey, a2aPeers, storedToolAxes, runSkills, } = parts;
428
+ const { scenarioName, cap, hands, acceptedAppend, parsedSettings, attachmentNotice, taskHooks, additionalDirectories, additionalReadDirectories, wireCatalog, picked, resumeAtEntryId, rewindFilesToEntryId, s4DefaultScopes, legProjectId, projectKey, a2aPeers, storedToolAxes, runSkills, projectBrief, } = parts;
393
429
  const spec = {
394
430
  objective: attachmentNotice ? `${picked.cleanedObjective}\n\n${attachmentNotice}` : picked.cleanedObjective,
395
431
  ...buildPromptSeats(scenarioName, cap.prompt, typeof body.systemPrompt === "string" ? body.systemPrompt : undefined),
396
- appendSystemPrompt: acceptedAppend,
432
+ appendSystemPrompt: appendFold([acceptedAppend, projectBrief]).text,
397
433
  ...(body.outputStyle !== undefined ? { outputStyle: body.outputStyle } : {}),
398
434
  ...(body.projectMemory !== undefined ? { projectMemory: body.projectMemory } : {}),
399
435
  ...(legProjectId !== undefined ? { projectId: legProjectId } : {}),
@@ -522,7 +558,6 @@ export function createResolveSpec(ctx) {
522
558
  toolPolicy: toolPolicyForHands(hands, durableEnabled
523
559
  ? createApprovalBaselinePolicy(config, {
524
560
  kind: "durable",
525
- question: liveQuestionFace,
526
561
  ...(approvalExemptionStore !== undefined ? { exemption: createSessionExemptionRead(approvalExemptionStore, root, logger, "deployment_list") } : {}),
527
562
  })
528
563
  : singleUserAutoAcceptBaselineApplies(config)
@@ -543,13 +578,13 @@ export function createResolveSpec(ctx) {
543
578
  totalBudgetUsd: config.resourceSuspendTotalBudgetUsd,
544
579
  maxSlices: config.resourceSuspendMaxSlices,
545
580
  })),
546
- ...(liveQuestionFace === undefined ? { onQuestion: QUESTION_AWAITS_RESUME } : {}),
581
+ ...(questionSeat ? {} : { onQuestion: QUESTION_AWAITS_RESUME }),
547
582
  }
548
583
  : {}),
549
584
  };
550
585
  return spec;
551
586
  };
552
- const foldGovernanceAndSettings = async (body, auth, root, spec, gated) => {
587
+ const foldGovernanceAndSettings = async (body, auth, root, spec, gated, projectBrief) => {
553
588
  const { acceptedAppend, parsedSettings } = gated;
554
589
  const hostSemanticsLane = !DEPLOYMENT_SHAPE_PREDICATES.sandboxPathAdjudication.holds(config);
555
590
  const laneHomeBase = () => {
@@ -578,8 +613,9 @@ export function createResolveSpec(ctx) {
578
613
  const governedBase = applyRuntimeGovernance(spec, createDeploymentGovernanceInputs(config, pathAdjudication));
579
614
  let governed = governedBase;
580
615
  let effectiveSettings = parsedSettings.settings;
581
- if (effectiveSettings?.outputStyle && acceptedAppend && acceptedAppend.length + 2 + effectiveSettings.outputStyle.length > MAX_SETTINGS_OUTPUT_STYLE_CHARS) {
582
- logger.warn("output_style_dropped", { detail: `combined append carriers exceed the ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} cap (rider ${acceptedAppend.length} + style ${effectiveSettings.outputStyle.length}) — style dropped (pre-cap stored body on a resume leg?)`, sessionId: root });
616
+ const foldedChars = effectiveSettings?.outputStyle ? appendFold([acceptedAppend, projectBrief, effectiveSettings.outputStyle]).chars : 0;
617
+ if (effectiveSettings?.outputStyle && foldedChars > MAX_SETTINGS_OUTPUT_STYLE_CHARS) {
618
+ logger.warn("output_style_dropped", { detail: `combined append carriers exceed the ${MAX_SETTINGS_OUTPUT_STYLE_CHARS} cap (rider ${acceptedAppend?.length ?? 0} + project brief ${projectBrief?.length ?? 0} + style ${effectiveSettings.outputStyle.length}, ${foldedChars} with joiners) — style dropped (pre-cap stored body on a resume leg?)`, sessionId: root });
583
619
  const { outputStyle: _dropped2, ...rest2 } = effectiveSettings;
584
620
  effectiveSettings = Object.keys(rest2).length > 0 ? rest2 : undefined;
585
621
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * [ref] 车2 —— 托管留存的 **sweep lane**(设计稿 `docs/DESIGN-270-retention-lane.md` v1.3 §3/§4/§7)。
2
+ * [ref] 车2 —— 托管留存的 **sweep lane**(设计稿 `docs/[ref]-retention-lane.md` v1.3 §3/§4/§7)。
3
3
  *
4
4
  * ── 为什么是独立文件、独立定时器,而不是折进 `startReapers()`(设计稿 §3 首条)───────────────────
5
5
  * `boot/reapers.ts` 里那二十条腿全都是**容忍多副本**的幂等清理:每台副本各扫各的,重复扫一遍只是浪费。
@@ -1,5 +1,5 @@
1
1
  import { buildRewindHandles, buildSessionAudit, buildSessionAuditLocal, buildSessionAuditPg } from "../audit.js";
2
- import { readSessionAgent } from "../session-agent.js";
2
+ import { readSessionAgent } from "../http/owner-access.js";
3
3
  import { readSessionProjectId, sessionWorkspaceStoreOf } from "../session-workspace.js";
4
4
  import { makeDegenerateInstrument } from "../degenerate-instrument.js";
5
5
  import { PlanCacheProbe } from "../plan-cache-probe.js";
@@ -24,27 +24,27 @@ export function createSessionFaces(ctx) {
24
24
  const ownerAware = sessionStore;
25
25
  const headOf = headReadOf(sessionStore);
26
26
  const workspaceStore = sessionWorkspaceStoreOf(sessionStore);
27
- const sessionAudit = mysqlPool || auditPgPool || auditLocalRows
28
- ? async (sessionId, opts) => {
29
- const handles = opts !== undefined && resumeAnchorStore !== undefined ? buildRewindHandles(await resumeAnchorStore.listBySession(sessionId), opts.reader) : undefined;
30
- const audit = mysqlPool
31
- ? await buildSessionAudit(mysqlPool, sessionId, handles)
32
- : auditPgPool
33
- ? await buildSessionAuditPg(auditPgPool, sessionId, handles)
34
- : await buildSessionAuditLocal(auditLocalRows, sessionId, handles);
35
- if (!audit)
36
- return undefined;
37
- const [head, agent, projectId] = await Promise.all([
38
- headOf?.(sessionId),
39
- opts !== undefined ? readSessionAgent(ownerAware, sessionId) : undefined,
40
- opts !== undefined ? readSessionProjectId(workspaceStore, sessionId) : undefined,
41
- ]);
42
- const owner = head?.owner;
43
- return { ...audit, owner: owner ?? null, ...(agent !== undefined ? { agent } : {}), ...(projectId !== undefined ? { projectId } : {}) };
44
- }
45
- : undefined;
27
+ const readSessionView = async (sessionId, opts) => {
28
+ const handles = opts !== undefined && resumeAnchorStore !== undefined ? buildRewindHandles(await resumeAnchorStore.listBySession(sessionId), opts.reader) : undefined;
29
+ const audit = mysqlPool
30
+ ? await buildSessionAudit(mysqlPool, sessionId, handles)
31
+ : auditPgPool
32
+ ? await buildSessionAuditPg(auditPgPool, sessionId, handles)
33
+ : await buildSessionAuditLocal(auditLocalRows, sessionId, handles);
34
+ if (!audit)
35
+ return undefined;
36
+ const [head, agent, projectId] = await Promise.all([
37
+ headOf?.(sessionId),
38
+ opts !== undefined ? readSessionAgent(ownerAware, sessionId) : undefined,
39
+ opts !== undefined ? readSessionProjectId(workspaceStore, sessionId) : undefined,
40
+ ]);
41
+ const owner = head?.owner;
42
+ return { ...audit, owner: owner ?? null, ...(agent !== undefined ? { agent } : {}), ...(projectId !== undefined ? { projectId } : {}) };
43
+ };
44
+ const sessionAudit = mysqlPool || auditPgPool || auditLocalRows ? readSessionView : undefined;
45
+ const probeWatchedHead = async (sid) => (headOf === undefined ? { owner: undefined, leafId: null } : ((await headOf(sid)) ?? { owner: undefined, leafId: null }));
46
46
  const sessionWatchRegistry = headOf
47
- ? new SessionWatchRegistry(async (sid) => (await headOf(sid)) ?? { owner: undefined, leafId: null }, {
47
+ ? new SessionWatchRegistry(probeWatchedHead, {
48
48
  hotMs: posIntEnv(process.env.SESSION_EVENTS_HOT_MS, 200),
49
49
  warmMs: posIntEnv(process.env.SESSION_EVENTS_WARM_MS, 2000),
50
50
  maxWatchedSessions: posIntEnv(process.env.SESSION_EVENTS_MAX_PROBES, 512, 100_000),
@@ -1,10 +1,10 @@
1
- import { uuidv7 } from "@sema-agent/core";
1
+ import { processInstanceId } from "../process-instance.js";
2
2
  import { openStores } from "./stores.js";
3
3
  export async function runStage03(ctx) {
4
4
  const { config, localAdmission, localRoot, logger, metrics, storeEvidence } = ctx;
5
5
  const { backend, storePosture, memoryEngine, engineDataRoot, memorySyncCursors, rosterStore, backgroundAgentStore, sessionCaptureRecords, taskAttachmentStore, mailboxStore, mailboxRecipientLifecycle, memoryExportStores, memorySyncRunner, sessionStore, breakerState, usageWindowStore, memoryPosture, taskListLane, sqlEngine, storeSweep, } = await openStores({ config, logger, metrics, localRoot, storeEvidence, localAdmission });
6
6
  const adoptionWiring = { memoryEngine: memoryExportStores !== undefined, taskAttachment: taskAttachmentStore !== undefined };
7
- const instanceId = uuidv7();
7
+ const instanceId = processInstanceId();
8
8
  return { ...ctx, adoptionWiring, backend, backgroundAgentStore, breakerState, engineDataRoot, instanceId, mailboxRecipientLifecycle, mailboxStore, memoryEngine, memoryExportStores, memoryPosture, memorySyncCursors, memorySyncRunner, rosterStore, sessionCaptureRecords, sessionStore, sqlEngine, storePosture, storeSweep, taskAttachmentStore, taskListLane, usageWindowStore };
9
9
  }
10
10
  //# sourceMappingURL=stage-03-store-backend.js.map
@@ -1,7 +1,6 @@
1
1
  import { createSessionEraseSettler } from "../session-erase.js";
2
2
  import { Runner, InMemoryToolResultStore, TtlSessionStore, uuidv7, createAllowDenyPolicy, workflowsCapability } from "@sema-agent/core";
3
3
  import { createEngineSessionRulesSeat } from "../session-rules-seat.js";
4
- import { headReadOf } from "../session-incarnation.js";
5
4
  import { outwardPlane } from "../terminal.js";
6
5
  import { createSessionTitler } from "../session-titler.js";
7
6
  import { cardRuleScopeRoot } from "../task-cwd.js";
@@ -133,7 +132,7 @@ export async function runStage06(ctx) {
133
132
  }
134
133
  const mcpRevocations = webAdmin.takeMcpRevocations();
135
134
  const workflowModelAllowlistSnapshot = workflowModelAllowlistFor(config) ?? [];
136
- const engineSessionRules = sessionPolicyStore ? createEngineSessionRulesSeat(sessionPolicyStore, headReadOf(sessionStore)) : undefined;
135
+ const engineSessionRules = sessionPolicyStore ? createEngineSessionRulesSeat(sessionPolicyStore, sessionStore) : undefined;
137
136
  const runnerDeps = createRunnerDeps({
138
137
  consolidationSeat,
139
138
  commitCoAuthor,
@@ -193,7 +193,7 @@ export async function runStage07(ctx) {
193
193
  const parkedReviveSurface = checkpointStore && backgroundAgentStore && builtinDefaultScenario ? createParkedReviveSurface(builtinDefaultScenario, { onBackgroundChildEvent: runnerDeps.onBackgroundChildEvent }) : undefined;
194
194
  const parkedReviveInheritedGate = parkedReviveSurface && config.durableApproval
195
195
  ? createParkedReviveInheritedGate({
196
- config, question, logger, localRoot,
196
+ config, questionSeat: question !== undefined, logger, localRoot,
197
197
  approverSeat: createRunnerDepsOnAsk(toolApproval),
198
198
  approvalExemptionStore,
199
199
  ...(runnerDeps.runtimeCapsResolver ? { resolveRuntimeCaps: runnerDeps.runtimeCapsResolver } : {}),
@@ -1,4 +1,36 @@
1
+ import { brainSummary } from "../brain.js";
2
+ import { type ServiceConfig } from "../config.js";
1
3
  import type { runStage09 } from "./stage-09-leader.js";
4
+ /**
5
+ * 启动协议行 `listening` 的字段(行本身是 `observability/logger.ts` 的 {@link import("../observability/logger.js").STARTUP_PROTOCOL_EVENTS} 一员:不随 `LOG_LEVEL`)。
6
+ * 铸点 = 本文件 listen 之后那一行(全仓唯一,`test/source-hygiene-gate.test.ts` M7),字面量过 {@link exactListeningLine}(键集恰等于本型,可选键与条件展开同算);
7
+ * 键集登记在自铸登记表 `listeningLineKey`(`satisfies Record<keyof ListeningLine, …>`:键加一个而登记表没跟 / 多一个 ⇒ tsc 红)。只登顶层键;`brain` 段里的键随 `brainSummary` 走,不在本型。
8
+ *
9
+ * 绑址两格,一格一个事实:`bindHost` = 本进程**请求**绑的地址(`resolveBindHost` 的答案 —— `BIND_HOST` 经无鉴权自收窄之后的那一只;答案缺席 = 全接口时是说明串);`boundAddress` = 内核**实际**绑上的地址
10
+ * (`server.address().address` 原样:`::` / `0.0.0.0` / `127.0.0.1` / `::1` …;配置写 `localhost` 时它是解析出的那一只)。口同理:`port` 读的也是绑定者自己的答案。
11
+ */
12
+ export interface ListeningLine {
13
+ readonly port: number;
14
+ readonly bindHost: string;
15
+ readonly bindHostNote?: string;
16
+ readonly boundAddress: string;
17
+ readonly model: ServiceConfig["model"]["id"];
18
+ readonly brain: ReturnType<typeof brainSummary>;
19
+ readonly breakerState: string;
20
+ readonly session: string;
21
+ readonly memory: string;
22
+ readonly instanceId: string;
23
+ readonly rateLimitPerMin: number;
24
+ readonly rateLimit: string;
25
+ readonly maxPrincipalCostUsd: number | null;
26
+ readonly costQuota: string;
27
+ readonly toolResultStore: string;
28
+ readonly otel: string | null;
29
+ readonly perModelKeys: number;
30
+ readonly sealedModelKeys: number;
31
+ readonly sealedPoisonedModels: number;
32
+ readonly workflowRunStore: string;
33
+ }
2
34
  export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>): Promise<{
3
35
  drainState: import("../fleet/drain-entry.js").DrainState;
4
36
  hookWakeBus: {
@@ -186,7 +218,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
186
218
  taskAttachmentStore: import("../plugins/task-attachment-store.js").TaskAttachmentStore | undefined;
187
219
  taskListLane: import("./task-list-lane.js").TaskListLane;
188
220
  usageWindowStore: import("@sema-agent/core").UsageWindowStore | undefined;
189
- config: import("../config-types.js").ServiceConfig;
221
+ config: ServiceConfig;
190
222
  ceilingVerdict: import("./unpriced-ceiling-verdict.js").UnpricedCeilingVerdict;
191
223
  storeEvidence: import("../store-posture.js").StoreEvidence;
192
224
  webAdmin: import("./web-admin.js").WebAdminRuntime;
@@ -31,6 +31,9 @@ import { bindServerLateSeats } from "./late-binds.js";
31
31
  import { serviceVersion } from "../service-version.js";
32
32
  import { buildDisplaySafeUrl } from "../trace/redact.js";
33
33
  import { seatText } from "../trace/exit-seat.js";
34
+ function exactListeningLine(line) {
35
+ return line;
36
+ }
34
37
  export async function runStage10(ctx) {
35
38
  const { peerDesk, approvalAskAudit, approvalExemptionStore, approvalNonceStore, authorize, backend, backgroundAgentStore, brain, breakerState, centerRuntimeCapsResolver, checkpointStore, checkpointCtxSealing, parkCommits, config, webAdmin, consolidationValve, costQuota, belowTaskChainPatterns, deploymentMods, deploymentModsLoaded, deviceLane, drainState, dropSessionScopedOf, durableEnabled, elicitation, fenceSessionOwner, fileHistoryStore, fleetBus, fleetLease, fleetUsage, governanceSeams, handslessRunner, hookAgent, hookLlm, hookWakeBus, imageBakes, imageIndex, instanceId, instrumentDegenerate, leaderEndpoint, loadedSkills, localRoot, logger, lspManager, mcpRevocations, sandboxCapacity, memoryBundleFaces, memoryComplianceFaces, memoryConsolidationFaces, memoryEngine, memoryExportStores, memoryOptOutGrantStore, memoryOriginFaces, memoryPosture, memorySyncCursors, memorySyncRunner, metrics, modelUsageTracker, orgMemoryAdmission, otelExporter, outcomeSink, ownerAware, parkedReviveInheritedGate, parkedReviveSurface, perSessionCwd, perTaskImage, planCacheProbe, pricing, principalCaps, promptManifestTracker, purgeSession, question, rateLimiter, reaper, webAdminJwtVerifier, resumeAnchorStore, retentionLaneStarted, retentionLaneStore, ruleConsent, runStore, runner, runnerDeps, runtimeCapsPeekReading, sandboxGitPlane, scenarioDetails, scenarios, selectEnvTool, sendFileLedger, sendUserFileEmitter, sendUserFileToolSpec, sessionAudit, sessionEnvSelection, sessionMemoryStatusFace, sessionPolicyStore, sessionTitler, sessionWatchRegistry, setSessionCwd, setSessionInheritEnv, setSessionShellEnv, sessionResidentBackground, sharedMemoryStore, sideQueryAccounting, sqlEngine, sqlWorkflowRunStore, staticWiring, storePosture, subRunner, subagentSteerRegistry, taskAttachmentStore, taskLimitCaps, taskTimeoutSec, toolApproval, toolResultStore, usageWindowStore, userInputLedger, webSearchProvider, workflowAgentRegistry, workflowAgentSessionIndex, workflowParkDecisions, workflowCompletionInbox, workflowJournalStore, workflowModelAllowlistSnapshot, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, workflowRunStore, workflowsCapable, orgDeliveryGate } = ctx;
36
39
  const stores = {
@@ -218,7 +221,7 @@ export async function runStage10(ctx) {
218
221
  checkpointStore, imageIndex, perTaskImage,
219
222
  sessionEnvSelection,
220
223
  lockedKeys: governanceSeams.lockedKeys,
221
- liveQuestionFace: question,
224
+ questionSeat: question !== undefined,
222
225
  approverSeat: createRunnerDepsOnAsk(toolApproval),
223
226
  });
224
227
  const runnerFor = createHandsRunnerSelector({ full: runner, handsless: handslessRunner });
@@ -284,11 +287,12 @@ export async function runStage10(ctx) {
284
287
  version: serviceVersion(),
285
288
  signal: () => mintEngineSignal({ ...deployment, config }),
286
289
  modelRefusals: () => deployment.modelRefusalsState?.(),
287
- ...(servingLease ? { onCenterCapabilities: (caps) => servingLease.noteCenterCapabilities(caps) } : {}),
290
+ ...(servingLease ? { onHeartbeat: (hearing) => servingLease.noteHeartbeat(hearing) } : {}),
288
291
  ...(fleetUsage ? { usage: fleetUsage } : {}),
289
292
  logger: { info: (m, d) => logger.info(m, d ?? {}), warn: (m, d) => logger.warn(m, d ?? {}) },
290
293
  });
291
294
  webAdmin.onSelfReportChange(() => void fleetClient?.announceNow());
295
+ servingLease?.onViewChange(() => void fleetClient?.announceNow());
292
296
  if (fleetClient) {
293
297
  const advertised = config.fleetAdvertiseAddress;
294
298
  logger.info("fleet_client_started", { worker: config.webAdmin?.worker, usageReport: fleetUsage !== undefined, announce: advertised !== undefined, ...(advertised !== undefined ? { address: buildDisplaySafeUrl(advertised) } : {}) });
@@ -299,12 +303,13 @@ export async function runStage10(ctx) {
299
303
  const bound = server.address();
300
304
  if (bound === null || typeof bound === "string")
301
305
  throw new Error(`http server listening without a TCP address (server.address() = ${JSON.stringify(bound)})`);
302
- logger.info("listening", {
306
+ logger.info("listening", exactListeningLine({
303
307
  port: bound.port,
304
308
  bindHost: bindHost ?? "0.0.0.0/::(all interfaces)",
305
309
  ...(bindHost === "127.0.0.1" && !config.bindHost
306
310
  ? { bindHostNote: "auto-narrowed to loopback: write face is unauthenticated (ALLOW_UNAUTHED_WRITES with no service token). Set BIND_HOST explicitly to override." }
307
311
  : {}),
312
+ boundAddress: bound.address,
308
313
  model: config.model.id,
309
314
  brain: brainSummary(config),
310
315
  breakerState: config.resilience.circuitBreaker ? (breakerState ? `shared(${backend.kind})` : "in-process") : "off",
@@ -327,7 +332,7 @@ export async function runStage10(ctx) {
327
332
  ? `db(${backend.kind})`
328
333
  : "file"
329
334
  : "off",
330
- });
335
+ }));
331
336
  if (workflowNotifyGate) {
332
337
  void workflowNotifyGate
333
338
  .recover(workflowRecoverOpts)
@@ -144,7 +144,7 @@ export interface WebAdminRuntime {
144
144
  /** [ref] ②:caps `centerWiring.effectivePull` / `effectiveRevision` 的取值口 —— `lastPull`(新状态,只此一枚:boot 实拉 / 迟到 boot 到货 /
145
145
  * refresh 拍收尾写)+ 与 `configApplyState()` **同一只** ledger 视图的已应用修订号。形与词表见 `web-admin-client/pull-state.ts`。 */
146
146
  centerPullState(): CenterPullView;
147
- /** DESIGN-278 §5 S2([ref]):center **当前在管**的 ServiceConfig 键集 —— `GET /v1/config/catalog` 判
147
+ /** [ref] §5 S2([ref]):center **当前在管**的 ServiceConfig 键集 —— `GET /v1/config/catalog` 判
148
148
  * `effectiveLane` 的 center 半场。投影自最近一次成功应用的候选(`latestEffective`,LKG/refresh 同一
149
149
  * 份推进点;判据单源 = apply-effective.ts `centerManagedConfigKeys`)。`undefined` = 没有配置管道。
150
150
  * ⚠️ 部分应用窗(codex R2-F2 登记):某组整拒时 live config 已吃进其它组而 latestEffective 不推进,