@sema-agent/server 7.126.0 → 7.127.1

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 (79) hide show
  1. package/MIGRATION.md +38 -0
  2. package/USAGE.md +86 -8
  3. package/deploy/sema-up/chart/templates/networkpolicy-sandbox.yaml +15 -5
  4. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +28 -1
  5. package/deploy/sema-up/chart/templates/server-v4pro.yaml +1 -0
  6. package/deploy/sema-up/chart/templates/server.yaml +1 -0
  7. package/deploy/sema-up/chart/values.yaml +16 -6
  8. package/deploy/sema-up/kube-up.sh +10 -4
  9. package/deploy/sema-up/lib/node-addrs.sh +42 -0
  10. package/deploy/sema-up/stack/sema-stack-cluster.yaml +20 -1
  11. package/dist/audit.js +1 -0
  12. package/dist/boot/execution-env.js +17 -4
  13. package/dist/boot/leader.d.ts +2 -1
  14. package/dist/boot/memory-consolidation.d.ts +37 -3
  15. package/dist/boot/memory-consolidation.js +18 -2
  16. package/dist/boot/runner-deps.d.ts +2 -1
  17. package/dist/boot/session-faces.d.ts +2 -1
  18. package/dist/boot/stage-06-runners.js +5 -2
  19. package/dist/config-catalog.js +3 -1
  20. package/dist/config-types.d.ts +5 -1
  21. package/dist/config.js +2 -0
  22. package/dist/execution-lane-caps.d.ts +12 -0
  23. package/dist/execution-lane-caps.js +16 -2
  24. package/dist/fleet/subagent-tail-bus.js +15 -5
  25. package/dist/http/admission.js +5 -1
  26. package/dist/http/durable-run-leg.js +1 -3
  27. package/dist/http/resume-legs.js +15 -6
  28. package/dist/http/route-ctx.d.ts +7 -1
  29. package/dist/http/routes/runs.js +23 -1
  30. package/dist/http/routes/tasks.js +17 -8
  31. package/dist/http/routes/workflows.js +8 -0
  32. package/dist/http/server.js +1 -3
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +1 -0
  35. package/dist/lsp/manager.js +1 -1
  36. package/dist/observability/fail-open.d.ts +16 -0
  37. package/dist/observability/fail-open.js +16 -0
  38. package/dist/observability/metrics.js +1 -1
  39. package/dist/observability/run-terminal.js +3 -0
  40. package/dist/plugins/caching-session-store.d.ts +39 -22
  41. package/dist/plugins/caching-session-store.js +16 -9
  42. package/dist/plugins/host-platform.d.ts +18 -3
  43. package/dist/plugins/host-platform.js +23 -10
  44. package/dist/plugins/k8s-exec-protocol.js +3 -0
  45. package/dist/plugins/one-time-migrations.d.ts +28 -4
  46. package/dist/plugins/one-time-migrations.js +22 -4
  47. package/dist/plugins/remote-env-host.d.ts +23 -6
  48. package/dist/plugins/remote-env-host.js +21 -12
  49. package/dist/plugins/remote-env-k8s.d.ts +40 -5
  50. package/dist/plugins/remote-env-k8s.js +119 -25
  51. package/dist/plugins/session-store.d.ts +4 -3
  52. package/dist/plugins/session-store.js +1 -1
  53. package/dist/plugins/store-backend.d.ts +12 -5
  54. package/dist/plugins/store-backend.js +12 -3
  55. package/dist/plugins/store-contracts.d.ts +3 -0
  56. package/dist/plugins/store-open-wait.js +3 -0
  57. package/dist/request-key-closure.d.ts +20 -7
  58. package/dist/request-key-closure.js +22 -6
  59. package/dist/run-acceptance.d.ts +51 -0
  60. package/dist/run-acceptance.js +27 -0
  61. package/dist/runs.d.ts +7 -11
  62. package/dist/runs.js +5 -16
  63. package/dist/sandbox-egress-policy.d.ts +61 -0
  64. package/dist/sandbox-egress-policy.js +235 -0
  65. package/dist/session-error-wire.d.ts +2 -2
  66. package/dist/session-titler.d.ts +5 -3
  67. package/dist/trace/core-keyset-guard.d.ts +3 -2
  68. package/dist/trace/ledger-events.d.ts +9 -3
  69. package/dist/trace/ledger-sink.d.ts +7 -0
  70. package/dist/trace/ledger-sink.js +14 -4
  71. package/dist/trace/project.d.ts +49 -8
  72. package/dist/trace/project.js +39 -7
  73. package/dist/trace/self-minted-wire.d.ts +19 -29
  74. package/dist/trace/self-minted-wire.js +2 -9
  75. package/dist/trace/tool-output-media.d.ts +26 -0
  76. package/dist/trace/tool-output-media.js +57 -0
  77. package/dist/trace/wire-projection-faces.d.ts +7 -1
  78. package/dist/trace/wire-projection-faces.js +2 -1
  79. package/package.json +3 -3
@@ -10,7 +10,7 @@ import { adbExecutionEnvFactory } from "../plugins/remote-env-adb.js";
10
10
  import { deviceExecutionEnvFactory } from "../plugins/remote-env-device.js";
11
11
  import { e2bExecutionEnvFactory } from "../plugins/remote-env-e2b.js";
12
12
  import { hostExecutionEnvFactory, RemoteHostExecutionEnv } from "../plugins/remote-env-host.js";
13
- import { createSpaceReclaimer, k8sExecutionEnvFactory } from "../plugins/remote-env-k8s.js";
13
+ import { checkSandboxEgressPolicy, createSpaceReclaimer, K8S_SANDBOX_NAMESPACE_DEFAULT, k8sExecutionEnvFactory } from "../plugins/remote-env-k8s.js";
14
14
  import { createRunWorkspaceSnapshotSeat } from "../fleet/workspace-snapshot-residual.js";
15
15
  import { createSpaceCommitFence } from "../workspace-space-commit.js";
16
16
  import { localDockerExecutionEnvFactory } from "../plugins/remote-env-local-docker.js";
@@ -24,8 +24,9 @@ import { customPkgSourceFromEnv, derivePkgSourceEnv } from "../sandbox-pkg-sourc
24
24
  import { effectiveHostWorkspace } from "../task-cwd.js";
25
25
  import { taskInheritEnvRefusal } from "../hosted-posture.js";
26
26
  import { workspaceSnapshotTimeoutOf } from "../workspace-space.js";
27
+ import { sayEgressPolicyReading } from "../sandbox-egress-policy.js";
27
28
  import { isSandboxPathAdjudicationLane, sandboxPathEnvSlots, withSandboxPathEnvSlot } from "./deferred-sandbox-path-env.js";
28
- import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneHomeDir, executionLaneOf, isSingleMachineDefaultLane, laneIsolated } from "../execution-lane-caps.js";
29
+ import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneHomeDir, executionLaneOf, isSingleMachineDefaultLane, laneBindsReads, laneIsolated } from "../execution-lane-caps.js";
29
30
  function boundedSessionSetter(map, maxEntries) {
30
31
  return (sid, value) => {
31
32
  map.delete(sid);
@@ -224,6 +225,7 @@ export function createExecutionEnv(ctx) {
224
225
  dataTimeoutMs: workspaceSnapshotTimeoutOf(config.remoteExec),
225
226
  ...(config.remoteExec.memory ? { memory: config.remoteExec.memory } : {}),
226
227
  ...(config.remoteExec.cpu ? { cpu: config.remoteExec.cpu } : {}),
228
+ ...(config.remoteExec.sandboxIpv6 !== undefined ? { ipv6: config.remoteExec.sandboxIpv6 } : {}),
227
229
  ...(config.remoteExec.s3Snapshot ? { s3Snapshot: { ...config.remoteExec.s3Snapshot, ...((f) => (f.available ? { space: { persist: f.policy.persist, keep: f.policy.keep } } : {}))(facts.workspaceSpace(config)) } } : {}),
228
230
  ...((f) => (f.available ? { cache: f.cache } : {}))(facts.workspaceCache(config)),
229
231
  ...(config.remoteExec.workspaceSnapshotExclude ? { snapshotExclude: config.remoteExec.workspaceSnapshotExclude } : {}),
@@ -233,6 +235,9 @@ export function createExecutionEnv(ctx) {
233
235
  executionEnvFactory = createK8sRunEnvFactory(k8sCfg, { imageOf: (sid) => perTaskImage.get(sid), incarnationOf: ctx.incarnationOf, spaceCommits: ctx.spaceCommits ?? (() => undefined) });
234
236
  if (k8sCfg.cache !== undefined || k8sCfg.s3Snapshot !== undefined)
235
237
  spaceReclaimer = createSpaceReclaimer(k8sCfg);
238
+ void checkSandboxEgressPolicy(k8sCfg)
239
+ .then((gaps) => sayEgressPolicyReading(gaps, logger, k8sCfg.namespace ?? K8S_SANDBOX_NAMESPACE_DEFAULT))
240
+ .catch((e) => logger.error("sandbox_egress_policy_check_crashed", { error: e instanceof Error ? e.message : String(e), effect: "the sandbox egress self-check did not complete this boot — the lane runs; the egress policy was not read" }));
236
241
  }
237
242
  else if (config.remoteExec?.provider === "ssh") {
238
243
  executionEnvFactory = sshExecutionEnvFactory({
@@ -402,7 +407,15 @@ export function createExecutionEnv(ctx) {
402
407
  logger.info("task_inherit_env_honored", { honored: taskInheritEnvRefusal(config) === undefined });
403
408
  }
404
409
  const lspProvider = config.remoteExec?.provider;
405
- const lspManager = (lspProvider === "e2b" || lspProvider === "k8s") && config.lspEnabled
410
+ const lspLane = executionLaneOf(lspProvider);
411
+ const lspLaneBindsReads = lspLane !== undefined && laneBindsReads(lspLane);
412
+ if ((lspProvider === "e2b" || lspProvider === "k8s") && config.lspEnabled && !lspLaneBindsReads) {
413
+ logger.warn("lsp_not_mounted", {
414
+ provider: lspProvider,
415
+ reason: "LSP_ENABLED is set, but this lane's execution environment cannot bind a read to the file that was checked (openBound), so every language-server file sync would be refused; the LSP tool is not mounted on this lane in this version",
416
+ });
417
+ }
418
+ const lspManager = (lspProvider === "e2b" || lspProvider === "k8s") && config.lspEnabled && lspLaneBindsReads
406
419
  ? createE2bLspManager({
407
420
  log: (event, fields) => logger.info(event, fields),
408
421
  scheme: lspProvider === "k8s" ? "ws" : "wss",
@@ -410,7 +423,7 @@ export function createExecutionEnv(ctx) {
410
423
  metrics,
411
424
  })
412
425
  :
413
- lspProvider === "host" && config.lspHostEnabled && config.requirePrincipal !== true
426
+ lspProvider === "host" && config.lspHostEnabled && config.requirePrincipal !== true && lspLaneBindsReads
414
427
  ? createHostLspManager(config, (event, fields) => logger.info(event, fields))
415
428
  : undefined;
416
429
  if (lspManager)
@@ -12,6 +12,7 @@ import type { buildPricing } from "../budget.js";
12
12
  import { createLeaderEndpoint } from "../leader/endpoint.js";
13
13
  import type { Logger } from "../observability/logger.js";
14
14
  import type { CheckpointStoreFull, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
15
+ import type { createSessionStore } from "../plugins/session-store.js";
15
16
  import type { RunnerDeps } from "@sema-agent/core";
16
17
  export interface LeaderCtx {
17
18
  config: ServiceConfig;
@@ -20,7 +21,7 @@ export interface LeaderCtx {
20
21
  pricing: ReturnType<typeof buildPricing>;
21
22
  executionEnvFactory: RunnerDeps["executionEnvFactory"];
22
23
  toolResultStore: ToolResultStoreFull | undefined;
23
- sessionStore: ReturnType<StoreBackend["session"]>;
24
+ sessionStore: ReturnType<typeof createSessionStore>;
24
25
  checkpointStore: CheckpointStoreFull | undefined;
25
26
  /** [ref] 件B/C/D([ref],codex R2-F1):部署治理三座席 —— leader 车道的每一只 Runner 都要带上它,
26
27
  * 否则开了 leader 端点的部署就有一条在部署治理**之外**的执行面(合规否决/锁都够不着)。 */
@@ -1,6 +1,11 @@
1
1
  import type { RestartSliceCtx } from "../web-admin-client/restart-signal.js";
2
2
  import type { MemoryConsolidationDriverConfig, ServiceConfig } from "../config-types.js";
3
- import { type ConsolidationDriverRunRow, type ConsolidationRunReceipt, type MemoryBackend, type MemoryConsolidationDriverDeps, type RunMemoryConsolidationOptions, type SessionCaptureRecordStore } from "@sema-agent/core";
3
+ import { resolveMemoryConsolidationDriver, type ConsolidationWriteJudge, type EngineNotice, type ConsolidationDriverRunRow, type ConsolidationRunReceipt, type MemoryBackend, type MemoryConsolidationDriverDeps, type SessionCaptureRecordStore } from "@sema-agent/core";
4
+ /**
5
+ * 座解析的 options 形 = core 解析器交回的那只(core 7.50.0 起它**不含** `writeJudge`:判官不是模型席的一部分,由调用方在动词上带)。
6
+ * 取解析器的返回型而不是自己写 `Omit<…>`:形的属主是 core 的解析器,本仓不持第二份。
7
+ */
8
+ type ConsolidationSeatOptions = ReturnType<typeof resolveMemoryConsolidationDriver>;
4
9
  /** D3 投影的座标签闭集。`role:<name>` 里的 name 只可能是 core 的 role 链两跳之一。 */
5
10
  export type ConsolidationSeatLabel = "explicit" | "role:consolidate" | "role:summarize";
6
11
  /** {@link buildConsolidationValveAudit} 的告警名闭集(事件名进日志 ⇒ 运维可按名过滤/告警)。 */
@@ -48,8 +53,8 @@ export type ConsolidationSeatBilling = {
48
53
  };
49
54
  /** 座解析的产物:core 的 options 原样 + 给投影用的两位自证。 */
50
55
  export interface ConsolidationDriverSeat {
51
- /** `runMemoryConsolidationDriver` 的第三参,core 的 `resolveMemoryConsolidationDriver` 原样产出。 */
52
- readonly options: RunMemoryConsolidationOptions;
56
+ /** `runMemoryConsolidationDriver` 第三参的**模型席那一半**,core 的 `resolveMemoryConsolidationDriver` 原样产出(写判官另由操作面的座给)。 */
57
+ readonly options: ConsolidationSeatOptions;
53
58
  /** D3 的 `seat` 位。 */
54
59
  readonly seat: ConsolidationSeatLabel;
55
60
  /** 真正会被记进 run 行与 mint 归档的模型 id(= `options.model`,单独拎出来是投影面的便利)。 */
@@ -390,6 +395,27 @@ export declare function assertAutoConsolidationWirable(input: {
390
395
  /** 铸好的 `RunnerDeps` 上**真的**有整理席吗(`deps.memoryConsolidation !== undefined`)。 */
391
396
  memoryConsolidationWired: boolean;
392
397
  }): void;
398
+ /**
399
+ * 运维阀门腿的写判官座(core 7.50.0 起驱动动词必带)。
400
+ *
401
+ * ## 判官只有一只,这条腿拿不到它
402
+ *
403
+ * 「这条路径能不能写」在引擎里只有**一只**判官(合成写判官:技能拒行 / 收窄折叠 / 调用方与继承策略 / 人的持久规则车道 /
404
+ * 组织车道,一条走法一个优先序);引擎的自动整理腿把**腿自己的**那只交给驱动。本腿(运维手动阀门)没有腿:没有名册上的
405
+ * `Write` 脸、没有引擎盖的豁免根与工作目录、没有持久规则车道 —— 而引擎包今天不导出那只判官的构造器。在本仓手搭一只
406
+ * 「对部署策略问一次合成 `Write`」的判官 = 把引擎判官的层走法、脸、豁免根、已中止信号的约定与判词读法**再写一份**
407
+ * (第二个读者,与引擎那只迟早分歧,而且分歧是静默的)。所以本腿不造第二只。
408
+ *
409
+ * ## 于是它是一只**响亮的放行座**,登记为保护型欠账
410
+ *
411
+ * 答 `no_objection` —— 与 v7.49.x 本腿的行为逐字同(那时驱动根本不问),不回退任何现网能力;每被问一次记一次
412
+ * `server.memory-consolidation.operator-valve-unjudged`(P-DEBT:计数 + 探针行 + 一次性告警),债在遥测里显形。
413
+ * 自动整理腿不受影响:它走引擎腿自己的判官,人的持久规则与部署策略照判。
414
+ * 退役条件:引擎导出宿主可用的写判官构造器(收部署策略 + 一只规则店主体)那一版,本函数换成它、本 tag 删。
415
+ * scope 与人:本仓的文件后端不按请求主体铸 scope(单用户形是部署声明的全局层 `MEMORY_SCOPE` 与按任务根派生的项目层),
416
+ * 所以从 scope 读不出「这是谁的库」;匿名 run 读不读本地属主那只规则桶另候裁定 —— 两件都不在本座里猜。
417
+ */
418
+ export declare function createOperatorValveWriteJudge(): ConsolidationWriteJudge;
393
419
  /** 两个 admin 口消费的窄能力面(`ServiceDeps.memoryConsolidation` 的实参来源)。 */
394
420
  export interface MemoryConsolidationFaces {
395
421
  /** D3 的座位自证(投影用;boot 期解析一次,restart-to-apply)。 */
@@ -446,5 +472,13 @@ export declare function createMemoryConsolidationFaces(store: {
446
472
  * 机器钉 capture-optout-lane ②)。S-215 起 local 车道在引擎接线时也供(core 文件三腿,同一只控制面);
447
473
  * 缺席 ⇒ 记忆面暗(引擎未接线),与 Runner 同本。 */
448
474
  captureRecordStore?: SessionCaptureRecordStore;
475
+ /** core 7.50.0:整理跑的**写判官座**(必填 —— 缺席不是「没有规则」,core 也拒)。操作面不判任何东西,只把它原样递给驱动动词:
476
+ * 早问库目录一次、应用时问每个产物文件,答 `no_objection` 以外一律拒写。装配层给哪一只见 {@link createOperatorValveWriteJudge}。 */
477
+ writeJudge: ConsolidationWriteJudge;
478
+ /** 引擎通告的**运维行**座(与 Runner 的 `onNotice` 同一只转发器)。缺席 ⇒ 通告只在回执上。判官拒的两种时机都经它走一次:
479
+ * 应用时拒 = 引擎在驱动里铸的 `memory.consolidation_incomplete`;早问拒 = 驱动抛出的拒绝经引擎导出的构造器转成的
480
+ * `memory.consolidation_refused`(本仓不读通告名、不自铸通告形)。 */
481
+ onNotice?: (notice: EngineNotice) => void;
449
482
  }): MemoryConsolidationFaces;
483
+ export {};
450
484
  //# sourceMappingURL=memory-consolidation.d.ts.map
@@ -1,8 +1,9 @@
1
1
  import { redactSecrets } from "../trace/redact.js";
2
+ import { recordFailOpen } from "../observability/fail-open.js";
2
3
  import { memoryMountRootOf } from "../memory-scope.js";
3
4
  import { DEFAULT_MODEL_REF, resolveMainModel } from "../model-select.js";
4
5
  import { backendOwnsControlPlane } from "../memory-operator-faces.js";
5
- import { expandTiers, MemoryEngine, readConsolidationDriverRun, resolveMemoryConsolidationDriver, runMemoryConsolidationDriver, } from "@sema-agent/core";
6
+ import { CONSOLIDATION_WRITE_REFUSED_CODE, ConsolidationRefusedError, expandTiers, MemoryEngine, memoryConsolidationRefusedNotice, readConsolidationDriverRun, resolveMemoryConsolidationDriver, runMemoryConsolidationDriver, } from "@sema-agent/core";
6
7
  export function resolveConsolidationDriverSeat(deps) {
7
8
  let options;
8
9
  try {
@@ -316,6 +317,12 @@ export function assertAutoConsolidationWirable(input) {
316
317
  `unset, or set MEMORY_AUTO_CONSOLIDATION=off (unsetting it is NOT the same: the engine's named default is ON as of ` +
317
318
  `core 7.21.1).`);
318
319
  }
320
+ export function createOperatorValveWriteJudge() {
321
+ return () => {
322
+ recordFailOpen("server.memory-consolidation.operator-valve-unjudged");
323
+ return { verdict: "no_objection" };
324
+ };
325
+ }
319
326
  export function createMemoryConsolidationFaces(store, input) {
320
327
  const engine = new MemoryEngine({
321
328
  backend: store.backend,
@@ -331,7 +338,16 @@ export function createMemoryConsolidationFaces(store, input) {
331
338
  model: input.seat.model,
332
339
  scopes: input.scopes,
333
340
  run: (scope) => flight(scope, async () => {
334
- const receipt = await runMemoryConsolidationDriver(engine, scope, input.seat.options);
341
+ const receipt = await runMemoryConsolidationDriver(engine, scope, {
342
+ ...input.seat.options,
343
+ writeJudge: input.writeJudge,
344
+ ...(input.onNotice !== undefined ? { onNotice: input.onNotice } : {}),
345
+ }).catch((err) => {
346
+ if (input.onNotice !== undefined && err instanceof ConsolidationRefusedError && err.code === CONSOLIDATION_WRITE_REFUSED_CODE) {
347
+ input.onNotice(memoryConsolidationRefusedNotice({ code: err.code, scope, reason: err.message, ...(err.occurrenceId !== undefined ? { occurrenceId: err.occurrenceId } : {}) }));
348
+ }
349
+ throw err;
350
+ });
335
351
  if (receipt.stopDetail !== undefined) {
336
352
  input.onStopDetail?.(buildStopDetailAudit({ scope, runId: receipt.runId, outcome: receipt.outcome, stopDetail: receipt.stopDetail }));
337
353
  }
@@ -23,6 +23,7 @@ import { type WorkflowCompletionInbox } from "../orchestration/workflow-completi
23
23
  import { type EngineNoticeRouter } from "../trace/engine-notice-wire.js";
24
24
  import { WorkflowNotifyGate, type WorkflowCompletionPayload } from "../orchestration/workflow-notify-journal.js";
25
25
  import type { ServiceWorkflowJournalStore, SessionCaptureRecordSource, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
26
+ import type { createSessionStore } from "../plugins/session-store.js";
26
27
  import type { QuestionCoordinator } from "../question.js";
27
28
  import type { ToolApprovalCoordinator } from "../tool-approval.js";
28
29
  import type { MemoryBackend } from "@sema-agent/core";
@@ -102,7 +103,7 @@ export interface RunnerDepsCtx {
102
103
  elicitation: ElicitationCoordinator | undefined;
103
104
  question: QuestionCoordinator | undefined;
104
105
  toolApproval: ToolApprovalCoordinator | undefined;
105
- sessionStore: ReturnType<StoreBackend["session"]>;
106
+ sessionStore: ReturnType<typeof createSessionStore>;
106
107
  /**
107
108
  * B-060(test [ref] 核心发现①)—— **一条部署腿上的每一只 Runner 看同一只检查点店**。
108
109
  *
@@ -17,6 +17,7 @@ import type { Metrics } from "../observability/metrics.js";
17
17
  import type { WorkflowCompletionInbox } from "../orchestration/workflow-completion-inbox.js";
18
18
  import { PlanCacheProbe } from "../plan-cache-probe.js";
19
19
  import type { CheckpointStoreFull, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
20
+ import type { createSessionStore } from "../plugins/session-store.js";
20
21
  import type { TaskAttachmentStore } from "../plugins/task-attachment-store.js";
21
22
  import type { FileHistoryErasureFace } from "../file-history-capture.js";
22
23
  import type { OwnerAwareSessionStore } from "../security.js";
@@ -33,7 +34,7 @@ export interface SessionFacesCtx {
33
34
  metrics: Metrics;
34
35
  localRoot: string;
35
36
  backend: StoreBackend | undefined;
36
- sessionStore: ReturnType<StoreBackend["session"]>;
37
+ sessionStore: ReturnType<typeof createSessionStore>;
37
38
  runStore: ReturnType<StoreBackend["run"]> | undefined;
38
39
  checkpointStore: CheckpointStoreFull | undefined;
39
40
  toolResultStore: ToolResultStoreFull | undefined;
@@ -9,13 +9,14 @@ import { hasServiceAuth } from "../http/server.js";
9
9
  import { workflowModelAllowlistFor } from "../task-workflow.js";
10
10
  import { createHookLlm, buildHookFailureText } from "../hooks/hook-llm.js";
11
11
  import { assertRetentionLaneWirable } from "./retention-lane.js";
12
- import { buildConsolidationValveAudit, createMemoryConsolidationFaces, resolveConsolidationDriverSeat } from "./memory-consolidation.js";
12
+ import { buildConsolidationValveAudit, createMemoryConsolidationFaces, createOperatorValveWriteJudge, resolveConsolidationDriverSeat } from "./memory-consolidation.js";
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
16
  import { createParkCommitWatch } from "../plugins/park-commit-watch.js";
17
17
  import { createRuntimeCaps } from "./runtime-caps.js";
18
- import { createRunnerDeps, createSharedRunnerDeps } from "./runner-deps.js";
18
+ import { createEngineNoticeForwarder, createRunnerDeps, createSharedRunnerDeps } from "./runner-deps.js";
19
+ import { defaultEngineNoticeRouter } from "../trace/engine-notice-wire.js";
19
20
  import { createOrgMemoryAdmissionWiring } from "./org-memory.js";
20
21
  import { createGovernanceSeams } from "./governance-seams.js";
21
22
  import { createSharedMemoryScopeAuthorizer } from "../shared-memory-scope-authorizer.js";
@@ -75,6 +76,8 @@ export function runStage06(ctx) {
75
76
  onIncident: (err) => logger.warn("memory_consolidation_incident", { code: err.code ?? null, detail: err.message }),
76
77
  onStopDetail: (audit) => logger.warn("memory_consolidation_stop_detail", audit),
77
78
  ...(sessionCaptureRecordStore !== undefined ? { captureRecordStore: sessionCaptureRecordStore } : {}),
79
+ writeJudge: createOperatorValveWriteJudge(),
80
+ onNotice: createEngineNoticeForwarder(logger, defaultEngineNoticeRouter, metrics),
78
81
  })
79
82
  : undefined;
80
83
  for (const w of buildConsolidationValveAudit({
@@ -2,6 +2,7 @@ import { CROSS_SESSION_INBOUND_SETTINGS, CROSS_SESSION_DIALOG_EXPIRY_VALUES } fr
2
2
  import { GLOBAL_SCOPE } from "@sema-agent/settings-schema/api/scopes";
3
3
  import { WEB_ADMIN_ENV, DOMAIN_SCHEMAS, buildRetiredEnvWire } from "@sema-agent/settings-schema";
4
4
  import { CENTER_CREDENTIAL_RENEW_LEAD_DEFAULT_MS, CENTER_CREDENTIAL_RENEW_LEAD_MAX_MS, CENTER_CREDENTIAL_RENEW_LEAD_MIN_MS } from "./center-credential-renewer.js";
5
+ import { SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS } from "./sandbox-egress-policy.js";
5
6
  import { WORKSPACE_CACHE_DEFAULT, WORKSPACE_CACHE_MOUNT_DEFAULT, WORKSPACE_CACHE_SIZE_DEFAULT, WORKSPACE_CACHE_WORDS } from "./workspace-cache.js";
6
7
  import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE } from "./workspace-space.js";
7
8
  import { THINKING_FORMATS } from "./model-compat.js";
@@ -196,6 +197,7 @@ export const CONFIG_CATALOG = [
196
197
  r("K8S_RUNTIME_CLASS", "orchestration", "string", "沙箱 pod runtimeClass(Kata 等)"),
197
198
  r("K8S_SANDBOX_ENV", "orchestration", "secret", "转发进 k8s 沙箱 pod 容器 env 的本进程 env 点名表(NAME,NAME;点到的键从 worker env 原样取,值只进 pod env —— 不进 spec 的其它位置 / 工作区 / 快照 / center / 事件日志;与 LSP_ENV_ALLOW / E2B_SANDBOX_ENV / DOCKER_SANDBOX_ENV 同一只解析器与「点名放行」谓词:形错 / 点名名单型旋钮拒启且不回显值;本进程环境里没有的名按序号 warn、不转发;与包源派生 env 撞名时显式名单胜;只在本车道被选中时求值;原文按凭据类对待:只回在场位、不进任何子进程)", { danger: SEC }),
198
199
  r("K8S_SANDBOX_IMAGE", "orchestration", "string", "k8s 车道沙箱镜像(REMOTE_EXEC=k8s 必需件)"),
200
+ r("K8S_SANDBOX_IPV6", "orchestration", "enum", "k8s 沙箱 pod 的 IPv6:disabled = pod 级 sysctl 关 IPv6(net.ipv6.conf.all / default.disable_ipv6=1;两只都不在 k8s 的 safe sysctl 表里 ⇒ 每个节点 kubelet 要 --allowed-unsafe-sysctls 点名,否则 pod 起不来;沙箱 ns 不能 enforce PodSecurity baseline / restricted);enabled = 不动 pod(pod 保留平台给的 IPv6,只执行 IPv4 的出网策略管不到它,起服自检每次一行 sandbox_egress_policy_gap{kind: ipv6_enabled});词表外拒启", { enumValues: SANDBOX_IPV6_WORDS, staticDefault: SANDBOX_IPV6_DEFAULT, danger: SEC }),
199
201
  r("K8S_TIMEOUT_MS", "orchestration", "number", "k8s 沙箱 pod 寿命(activeDeadlineSeconds,ms;整只 pod 的泄漏护栏,后台任务超时也被它钳;各条命令的超时见工具超时)"),
200
202
  r("K8S_TOKEN", "orchestration", "secret", "k8s API token(缺席走 in-cluster SA)", { danger: SEC }),
201
203
  r("KUBERNETES_SERVICE_HOST", "orchestration", "string", "平台注入的 in-cluster k8s API host(K8S_API_URL 缺席时用)"),
@@ -218,7 +220,7 @@ export const CONFIG_CATALOG = [
218
220
  r("LOCAL_DATA_ROOT", "store", "path", "file 店数据根的最高优先覆写(设了则与配置根分家,boot 出声)", { danger: DATA }),
219
221
  r("LOCKED_CONFIG_KEYS", "auth", "csv", "管理员锁定层声明(词表=core 闭集注册表,未知键拒启)", { danger: SEC, configKey: "lockedConfigKeys" }),
220
222
  r("LOG_LEVEL", "observability", "string", "日志档位", { staticDefault: "info", configKey: "logLevel" }),
221
- r("LSP_ENABLED", "orchestration", "boolean", "LSP 沙箱腿 opt-in(需烤好的 LSP 模板;host 腿另有 LSP_HOST_ENABLED)", { staticDefault: "false", configKey: "lspEnabled" }),
223
+ r("LSP_ENABLED", "orchestration", "boolean", "LSP 沙箱腿 opt-in —— 本版 e2b / k8s 执行环境不实现绑定读,开了也不挂(起服一行 lsp_not_mounted);host 腿另有 LSP_HOST_ENABLED", { staticDefault: "false", configKey: "lspEnabled" }),
222
224
  r("LSP_ENV_ALLOW", "orchestration", "secret", `host 腿语言服务器子进程 env 的点名放行名单(点到的键从服务 env 原样放回、部署方显式承担;没点到的照剥;与 host shell inheritEnv:[names] 同一只谓词;形错 / 点名任一只名单型旋钮(含自己)拒启且不回显值;本进程环境里没有的名按序号 warn、不放行、不回显;多租户姿态(忽略时起服 warn 一次):${describeAllowlistPostures()};原文按凭据类对待:只回在场位、不进任何子进程,生效名单见起服披露行 allowed)`, { danger: SEC, derivedDefaultNote: "缺席 / 空串 ⇒ 空名单 = 全剥(7.100.0 行为)" }),
223
225
  r("LSP_HOST_ENABLED", "orchestration", "boolean", "LSP host 腿开关(本机 language server,优雅降级)", { staticDefault: "true", configKey: "lspHostEnabled" }),
224
226
  r("MANUAL_MODE_SHELL_GATE", "approval", "enum", "手动模式 shell 门收紧腿(off=no-op 非放松 —— 本键只有收紧半场;拼错拒启;S-877 起 classify 退役 = 拒启指路)", { danger: SEC, enumValues: ["always", "off"], echoWords: ["always"], resolve: (c) => c.manualModeShellGate ?? null, derivedDefaultNote: "off/未设 ⇒ 本键不贡献任何东西(归一成键缺席)⇒ 回显 null;真姿态只有 always 一个(core 7.34.0 起形状档在每一种模式下都装,classify 不再是运维选的档)" }),
@@ -631,7 +631,8 @@ export interface ServiceConfigFlat {
631
631
  * engine DB_BACKEND picks), a historical label kept to avoid churning every comparison site.
632
632
  */
633
633
  sessionBackend: "memory" | "tidb" | "auto";
634
- /** Warm-cache idle TTL (seconds) for woken TiDB sessions; 0 disables. Requires session affinity. */
634
+ /** Warm-cache idle TTL (seconds) for woken durable sessions; 0 disables. Correct under any routing: a hit is served only
635
+ * while the store's head still names the cached leaf (`plugins/caching-session-store.ts`). */
635
636
  sessionCacheTtlSec: number;
636
637
  /** B1 /health 活体 store 探测:后台缓存探针间隔 ms(STORE_PROBE_INTERVAL_MS;0=显式关,负/非数 boot
637
638
  * 响亮拒)。只对 SQL 后端接线;探针原语=backend.dbNowMs()(S10 时钟探针的真 DB 往返,零新 SQL 面)。 */
@@ -899,6 +900,9 @@ export interface ServiceConfigFlat {
899
900
  /** Resource-profile knobs: sandbox pod limits (adapter defaults: 2Gi / 1 cpu). */
900
901
  memory?: string;
901
902
  cpu?: string;
903
+ /** `K8S_SANDBOX_IPV6`(闭集与缺省的属主 `sandbox-egress-policy.ts`):`disabled` ⇒ 沙箱 pod 级 sysctl 关 IPv6;`enabled` ⇒ pod spec 不带 sysctl。
904
+ * loader 恒铸;可选只为手搓配置。 */
905
+ sandboxIpv6?: import("./sandbox-egress-policy.js").SandboxIpv6;
902
906
  /** `K8S_SANDBOX_ENV=NAME,NAME` 点到、且本进程环境里真有的键 ⇒ 原值(与 e2b 的 `sandboxEnv` 同名同形、同一只解析器、同一只谓词 `allowlistedEnv`,
903
907
  * 空串值照带)。只进 pod 容器 env;装配点与包源派生 env 合并一处,显式名单胜。键缺席 = 旋钮未设 / 空串(零转发)。 */
904
908
  sandboxEnv?: Record<string, string>;
package/dist/config.js CHANGED
@@ -27,6 +27,7 @@ import { bakeOperatorPrincipalsRefusal, bakeRunnerCredentialRefusal, centerIdent
27
27
  import { declaredSemaConfigDir } from "./sema-config-dir.js";
28
28
  import { lkgPathOverrideOf } from "./config-lkg.js";
29
29
  import { K8S_WORKSPACE_MOUNT_DEFAULT, WORKSPACE_PERSIST_DEFAULT, WORKSPACE_PERSIST_WORDS, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_RANGE, WORKSPACE_SNAPSHOT_TIMEOUT_MS_DEFAULT, WORKSPACE_SNAPSHOT_TIMEOUT_MS_RANGE, parseWorkspaceSnapshotExclude } from "./workspace-space.js";
30
+ import { SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS } from "./sandbox-egress-policy.js";
30
31
  import { WORKSPACE_CACHE_DEFAULT, WORKSPACE_CACHE_MOUNT_DEFAULT, WORKSPACE_CACHE_SIZE_DEFAULT, WORKSPACE_CACHE_WORDS, isK8sQuantity, mountsOverlap } from "./workspace-cache.js";
31
32
  import { REMOTE_EXEC_PROVIDER_WORDS, executionLaneOf, executionLaneRequiredRefusal, isSingleMachineDefaultLane, laneRequiredEnv } from "./execution-lane-caps.js";
32
33
  import { MEMORY_ENGINE_BACKENDS, SESSION_BACKENDS, STORE_BACKENDS } from "@sema-agent/settings-schema/execution-lanes";
@@ -1856,6 +1857,7 @@ function parseOrchestrationDomain(ctx) {
1856
1857
  ...((n) => (n !== undefined ? { timeoutMs: n } : {}))(optNumEnvStrict("K8S_TIMEOUT_MS")),
1857
1858
  ...(process.env.K8S_MEMORY ? { memory: process.env.K8S_MEMORY } : {}),
1858
1859
  ...(process.env.K8S_CPU ? { cpu: process.env.K8S_CPU } : {}),
1860
+ sandboxIpv6: enumEnv("K8S_SANDBOX_IPV6", SANDBOX_IPV6_DEFAULT, SANDBOX_IPV6_WORDS),
1859
1861
  ...((names) => (names !== undefined ? { sandboxEnv: allowlistedEnv(names, process.env) } : {}))(parseEnvNameAllowlist("K8S_SANDBOX_ENV", singleUserTurnkey)),
1860
1862
  ...k8sWorkspaceStore(),
1861
1863
  ...((c) => (c !== undefined ? { workspaceCache: c } : {}))(k8sWorkspaceCache()),
@@ -190,6 +190,18 @@ export declare function executionLaneOf(provider: string | undefined): Execution
190
190
  * ③ `approval.ts` 的 `buildNoResponderBootWarns` —— 写门的 ask 只在「有手写本机盘」时才会发生。
191
191
  */
192
192
  export declare function handsOnWorkerFilePlane(lane: ExecutionLane): boolean;
193
+ /**
194
+ * core 7.50.0 —— 「这条车道的执行环境能不能把一次读**绑到判过的那只对象**上」(`FileSystem.openBound`:开着并持有,叶链接不跟,
195
+ * 开后身份对拍)。LSP 会话的读口与路径附件只经它读;环境不实现 ⇒ 那两个读口在这条车道上一律拒。
196
+ *
197
+ * **一条律**:LSP 只在本谓词为真的车道挂(`boot/execution-env.ts` 的 LSP 管理器两臂都合取它;表的 `lsp` 位与之同向,
198
+ * 对拍钉 `test/host-env-open-bound.test.ts`:本谓词 ⟺ 适配器类原型上有 `openBound`)。
199
+ * · `host` —— `RemoteHostExecutionEnv.openBound` 委托 core `NodeExecutionEnv` 的实现(唯一属主),真;
200
+ * · `in-process` —— 没有手(core 落桩环境),无物可绑;
201
+ * · 其余车道(沙箱 / 远端 shell / 设备)—— 读走 exec / sftp / 设备协议,本版不实现绑定读,假。实现之日翻这一格,LSP 沙箱桥随之回来。
202
+ * 穷举 switch、无 default 臂:车道加词而此处不表态 = 编译红。
203
+ */
204
+ export declare function laneBindsReads(lane: ExecutionLane): boolean;
193
205
  /**
194
206
  * S-281 —— 「这条车道的手在不在 OS 级隔离边界里」。**全仓唯一**的布尔判别式(读表 `isolation` 位,见该位头注的读者清单)。
195
207
  * 穷举 switch、无 default 臂:`isolation` 的型只有两档,加档 = 编译红。
@@ -46,7 +46,7 @@ export function executionLaneCaps(lane) {
46
46
  callerCwd: NO,
47
47
  backgroundShell: YES,
48
48
  scheduler: NO,
49
- lsp: YES,
49
+ lsp: NO,
50
50
  sandboxSendUserFile: YES,
51
51
  isolation: YES,
52
52
  suspendable: YES,
@@ -64,7 +64,7 @@ export function executionLaneCaps(lane) {
64
64
  callerCwd: NO,
65
65
  backgroundShell: YES,
66
66
  scheduler: NO,
67
- lsp: YES,
67
+ lsp: NO,
68
68
  sandboxSendUserFile: YES,
69
69
  isolation: YES,
70
70
  suspendable: YES,
@@ -202,6 +202,20 @@ export function handsOnWorkerFilePlane(lane) {
202
202
  const caps = executionLaneCaps(lane);
203
203
  return caps.hostFilePlane === "supported" && caps.coreRemoteExecutionEnv === "supported";
204
204
  }
205
+ export function laneBindsReads(lane) {
206
+ switch (lane) {
207
+ case "host":
208
+ return true;
209
+ case "in-process":
210
+ case "e2b":
211
+ case "k8s":
212
+ case "ssh":
213
+ case "adb":
214
+ case "device":
215
+ case "local-docker":
216
+ return false;
217
+ }
218
+ }
205
219
  export function laneIsolated(lane) {
206
220
  const isolation = executionLaneCaps(lane).isolation;
207
221
  switch (isolation) {
@@ -1,5 +1,6 @@
1
1
  import { redactSecrets } from "../trace/redact.js";
2
- import { taskProgressEventData, toolStartEventData, toolEndEventData, textEndEventData } from "../trace/project.js";
2
+ import { taskProgressEventData, taskStartedEventData, toolStartEventData, toolEndEventData, textEndEventData } from "../trace/project.js";
3
+ import { liveMediaBytesOf, liveToolEndForBacklog } from "../trace/tool-output-media.js";
3
4
  import { createReasoningSegments } from "../trace/model-segment.js";
4
5
  const TAIL_QUEUE_CAP = 512;
5
6
  export function projectTailFrame(e) {
@@ -7,6 +8,9 @@ export function projectTailFrame(e) {
7
8
  if (t === "task_progress") {
8
9
  return { type: "task_progress", ...taskProgressEventData(e) };
9
10
  }
11
+ if (t === "task_started") {
12
+ return { type: "task_started", ...taskStartedEventData(e) };
13
+ }
10
14
  if (t === "tool_start") {
11
15
  const tsv = e;
12
16
  return { type: "tool_start", ...toolStartEventData(tsv) };
@@ -80,10 +84,14 @@ export class SubagentTailBus {
80
84
  if (sub.closed)
81
85
  continue;
82
86
  if (sub.queue.length >= TAIL_QUEUE_CAP) {
83
- sub.queue.shift();
87
+ const oldest = sub.queue.shift();
88
+ if (oldest !== undefined)
89
+ sub.mediaBytes -= liveMediaBytesOf(oldest);
84
90
  sub.dropped++;
85
91
  }
86
- sub.queue.push(frame);
92
+ const queued = frame.type === "tool_end" ? liveToolEndForBacklog(frame, sub.mediaBytes) : frame;
93
+ sub.mediaBytes += liveMediaBytesOf(queued);
94
+ sub.queue.push(queued);
87
95
  sub.wake?.();
88
96
  }
89
97
  }
@@ -91,7 +99,7 @@ export class SubagentTailBus {
91
99
  return (this.subs.get(bgAgentId)?.size ?? 0) > 0;
92
100
  }
93
101
  subscribe(bgAgentId) {
94
- const sub = { queue: [], dropped: 0, closed: false };
102
+ const sub = { queue: [], dropped: 0, mediaBytes: 0, closed: false };
95
103
  let set = this.subs.get(bgAgentId);
96
104
  if (!set) {
97
105
  set = new Set();
@@ -121,8 +129,10 @@ export class SubagentTailBus {
121
129
  return { value: { type: "lagged", dropped }, done: false };
122
130
  }
123
131
  const head = sub.queue.shift();
124
- if (head !== undefined)
132
+ if (head !== undefined) {
133
+ sub.mediaBytes -= liveMediaBytesOf(head);
125
134
  return { value: head, done: false };
135
+ }
126
136
  if (sub.closed)
127
137
  return { value: undefined, done: true };
128
138
  await new Promise((resolve) => {
@@ -14,6 +14,7 @@ import { MAX_ADDITIONAL_DIRS } from "../task-cwd.js";
14
14
  import { verifyRoundsFromBody } from "./verify-rounds.js";
15
15
  import { skillCarriesRetiredManifest } from "./retired-skill-manifest.js";
16
16
  import { resolveDevicePlacementIntent } from "./device-placement.js";
17
+ import { createRunAcceptance } from "../run-acceptance.js";
17
18
  import { sendError, httpErrorCode } from "./send.js";
18
19
  import { ABSOLUTE_HOST_PATH_MAX, isAbsoluteHostPath, WEB_ADMIN_ENV } from "@sema-agent/settings-schema";
19
20
  const APPROVAL_REACH_WORDS = APPROVAL_REACH.map((word) => JSON.stringify(word)).join(" / ");
@@ -62,6 +63,7 @@ function refuseUnavailableModel(res, config, field, ref) {
62
63
  sendError(res, 400, "request.model_unavailable", message);
63
64
  return true;
64
65
  }
66
+ const modelIdOf = (model) => (typeof model === "string" ? model : model?.id);
65
67
  export function createAdmission(ctx) {
66
68
  const { deps, readJson, runnerFor } = ctx;
67
69
  async function prepareSpec(req, res, bodyIn, onTypedFailure, legFacts) {
@@ -640,7 +642,9 @@ export function createAdmission(ctx) {
640
642
  store: deps.deviceStore,
641
643
  enrollment: deps.deviceEnrollment,
642
644
  });
643
- return { spec, runner: runnerFor(spec), auth, verify, cascade, jobId: admitted.jobId, body: admitted, ...(bindingIntent === undefined ? {} : { bindingIntent }), ...(engineNotices.length > 0 ? { engineNotices } : {}) };
645
+ const model = modelIdOf(spec.model);
646
+ const acceptance = createRunAcceptance({ sessionTitler: deps.sessionTitler }, { ...(spec.sessionId ? { sessionId: spec.sessionId } : {}), objective: spec.objective, ...(model !== undefined ? { model } : {}) });
647
+ return { spec, runner: runnerFor(spec), auth, verify, cascade, jobId: admitted.jobId, body: admitted, acceptance, ...(bindingIntent === undefined ? {} : { bindingIntent }), ...(engineNotices.length > 0 ? { engineNotices } : {}) };
644
648
  }
645
649
  catch (err) {
646
650
  if (err instanceof HttpError) {
@@ -20,8 +20,6 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
20
20
  clearTurnActivity(taskId);
21
21
  if (created.ok)
22
22
  recordResourceWindow(taskId, args.prepared.spec.resourceSuspend);
23
- if (created.ok)
24
- deps.sessionTitler?.maybeTitle(sessionId, args.prepared.spec.objective, typeof args.prepared.spec.model === "string" ? args.prepared.spec.model : args.prepared.spec.model?.id);
25
23
  if (created.existing)
26
24
  return { existing: created.existing };
27
25
  if (!created.ok) {
@@ -65,7 +63,7 @@ export async function mintAndDriveDurableRun(ctx, runStore, args) {
65
63
  windowMs: deps.config.streamApproval?.windowMs,
66
64
  windowMarginMs: deps.config.streamAskWindowMarginMs,
67
65
  }
68
- : undefined, deps.checkpointStore, args.prepared.engineNotices, args.trustedInternals, deps.userInputLedger);
66
+ : undefined, deps.checkpointStore, args.prepared.engineNotices, args.trustedInternals, deps.userInputLedger, args.prepared.acceptance);
69
67
  return { status: 202, body: { taskId, sessionId, status: "running" } };
70
68
  }
71
69
  //# sourceMappingURL=durable-run-leg.js.map
@@ -6,7 +6,7 @@ const sessionFaceUnavailable = async (sessionId) => {
6
6
  throw new Error(`parked decide cannot verify which occupant session ${sessionId} belongs to: this deployment's session store has no sessionFaceOf read (every built-in store has one) — refusing rather than resuming unverified`);
7
7
  };
8
8
  import { HttpError, decodeCheckpointScope, CHECKPOINT_PUBLIC_SCOPE } from "../security.js";
9
- import { evictIfConflict, parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
9
+ import { parkToolCallId, TurnAnchorCapture, HEARTBEAT_MS, markChildrenStoppedByUserOnAbort } from "../runs.js";
10
10
  import { failureCodeOf, nestedPauseOf, sealResultForExit, outwardPlane } from "../terminal.js";
11
11
  import { buildCancelledLedgerRow, createRunAbort, mintCancelledResult } from "../run-cancel-context.js";
12
12
  import { readTurnActivityMs, recordTurnActivity } from "../turn-activity.js";
@@ -22,10 +22,10 @@ import { TERMINAL_PARK_GATE_KINDS } from "../plugins/checkpoint-store-sql.js";
22
22
  import { withPrincipal } from "../observability/principal-context.js";
23
23
  import { withModelReachScope } from "../model-reach-scope.js";
24
24
  import { createInstructionSelectionLeg } from "../instruction-selection.js";
25
- 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";
25
+ import { modelTextEventData, turnEndEventData, contextUsageEventData, toolStartEventData, toolEndEventData, taskProgressEventData, taskStartedEventData, taskNotificationEventData, compactedEventData, diagnosticsEventData, brainStatusEventData, steeringInjectedEventData, compactionOutcomeEventData, workspaceChangedEventData, wiringManifestEventData, toolRosterDeltaEventData, toolDisclosureEventData, humanInputEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "../trace/project.js";
26
26
  import { redactSecrets } from "../trace/redact.js";
27
27
  import { buildRunObjectivePreview } from "../user-authored-text.js";
28
- import { createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
28
+ import { createForwardAppendFailedNote, createLedgerSink, createSerialLedgerAppend } from "../trace/ledger-sink.js";
29
29
  import { registerEngineNoticeLeg } from "../trace/engine-notice-wire.js";
30
30
  import { reasoningDeltaBytes, reasoningSegmentEventId } from "../trace/model-segment.js";
31
31
  import { errorText } from "../observability/err-text.js";
@@ -48,6 +48,7 @@ import { readResumeCtx, resumeCtxCorruptAnswer } from "./resume-ctx-read.js";
48
48
  import { buildCheckpointVersionKeys, resumeBlockedByPolicyAnswer, storedBodyReplayAnswer, storedBodyReplayLogFields, withPermissionModeRewritten } from "./checkpoint-ctx.js";
49
49
  import { storedBodyReplayRefusal } from "./stored-body-replay.js";
50
50
  import { httpErrorCode } from "./send.js";
51
+ import { createRunAcceptance } from "../run-acceptance.js";
51
52
  function isCheckpointReopenedFailure(r) {
52
53
  const code = failureCodeOf(r.terminal);
53
54
  return code === "resume.env_failed" || code === "resume.tool_unavailable" || code === "resume.tool_contract_mismatch";
@@ -769,10 +770,16 @@ export function createResumeLegs(ctx) {
769
770
  if (!rs || !taskId)
770
771
  return;
771
772
  const t = e.type;
772
- const warnAppend = (kind) => (err) => deps.logger?.warn?.("forward_event_append_failed", { type: kind, taskId, err: err instanceof Error ? err.message : String(err) });
773
+ const warnAppend = (kind) => (err) => {
774
+ createForwardAppendFailedNote(kind)(err);
775
+ deps.logger?.warn?.("forward_event_append_failed", { type: kind, taskId, err: err instanceof Error ? err.message : String(err) });
776
+ };
773
777
  if (t === "task_progress") {
774
778
  void append("task_progress", taskProgressEventData(e)).catch(warnAppend(t));
775
779
  }
780
+ else if (t === "task_started") {
781
+ void append("task_started", taskStartedEventData(e)).catch(warnAppend(t));
782
+ }
776
783
  else if (t === "tool_start") {
777
784
  void append("tool_start", toolStartEventData(e)).catch(warnAppend(t));
778
785
  }
@@ -901,6 +908,9 @@ export function createResumeLegs(ctx) {
901
908
  case "message_committed":
902
909
  await anchor.onMessageCommitted(ev.role, ev.entryId);
903
910
  break;
911
+ case "task_started":
912
+ await append("task_started", taskStartedEventData(ev));
913
+ break;
904
914
  case "task_progress":
905
915
  await append("task_progress", taskProgressEventData(ev));
906
916
  break;
@@ -1085,7 +1095,6 @@ export function createResumeLegs(ctx) {
1085
1095
  clearInterval(resumeHeartbeat);
1086
1096
  resumeLegLive = false;
1087
1097
  }
1088
- evictIfConflict(deps.runner, sessionId, result);
1089
1098
  const checkpointReopened = reopenConfirmedPark;
1090
1099
  const sealed = sealResultForExit(result);
1091
1100
  const resumePlane = outwardPlane(sealed.terminal);
@@ -1214,7 +1223,7 @@ export function createResumeLegs(ctx) {
1214
1223
  const { spec, engineNotices } = resolved;
1215
1224
  const hostSpec = { ...spec, objective, sessionId };
1216
1225
  const out = await mintAndDriveDurableRun(routeCtxBase, deps.runStore, {
1217
- prepared: { spec: hostSpec, runner: runnerFor(hostSpec), auth, body: ctx.body, ...(engineNotices.length > 0 ? { engineNotices } : {}) },
1226
+ prepared: { spec: hostSpec, runner: runnerFor(hostSpec), auth, body: ctx.body, acceptance: createRunAcceptance({}, { objective }), ...(engineNotices.length > 0 ? { engineNotices } : {}) },
1218
1227
  fleetScope: resumeFleetScope(req, auth),
1219
1228
  elicitOwner: principal ?? null,
1220
1229
  source: "workflow_parked_decide",
@@ -140,6 +140,12 @@ export interface PreparedTaskSubmission {
140
140
  * 成功点**必须**把它交给 `commitPlacement`(唯一写入属主;类级机器门钉住每个调用点都接了)。
141
141
  */
142
142
  bindingIntent?: import("./device-placement.js").DevicePlacementIntent;
143
+ /**
144
+ * 这次提交的**受理座**(`run-acceptance.ts`):标题器只在引擎受理之后起。提交门(`prepareSpec`)随 spec 一起铸;
145
+ * 三条提交腿只喂它(持流的形逐帧 `observe`,终局落行之后 `settle` 一次),不再各自判「什么时候起标题」。
146
+ * 不经提交门的内部车道(`/decide` 的宿主新 turn)铸一只不带标题器的座:不起标题。
147
+ */
148
+ acceptance: import("../run-acceptance.js").RunAcceptance;
143
149
  }
144
150
  /**
145
151
  * 一条会话的**续跑重建输入**(`checkpoint_ctx` 的行体)—— 提交腿写一次,续跑腿读它重建 spec。
@@ -384,7 +390,7 @@ export interface RouteLegs {
384
390
  liveStream?: boolean;
385
391
  }): Promise<PreparedTaskSubmission | null>;
386
392
  /** 同步腿的终局记账(计费/配额/指标),tasks 域用。 */
387
- finalizeTaskResult(result: TaskResult, principal: string | undefined, objective: string, sessionId: string | undefined): void;
393
+ finalizeTaskResult(result: TaskResult, principal: string | undefined, objective: string): void;
388
394
  /** approvals 决策腿:session → pending checkpoint → markResuming CAS → 驱动续跑。
389
395
  * `hostDecision` = **这次结算的事实**(谁结束了这次等待 + 通道自报的归属),必填:本腿有两个调用方
390
396
  * (HTTP `/decide` = 人;内部 D-D SLA deny-sweep = 窗到期),而函数内部分辨不出谁在叫它 —— 必填参数把