@sema-agent/server 7.141.0 → 7.142.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 (171) hide show
  1. package/MIGRATION.md +215 -0
  2. package/USAGE.md +67 -12
  3. package/dist/approval-ask-audit-store.d.ts +54 -38
  4. package/dist/approval-ask-audit-store.js +58 -39
  5. package/dist/approval-card.d.ts +8 -8
  6. package/dist/approval-crash-convergence.d.ts +17 -7
  7. package/dist/approval-crash-convergence.js +94 -22
  8. package/dist/approval-deny-reasons.d.ts +34 -30
  9. package/dist/approval-deny-reasons.js +21 -6
  10. package/dist/approval-policy-settlement.d.ts +1 -1
  11. package/dist/approval-reconciler.d.ts +33 -1
  12. package/dist/approval-reconciler.js +43 -40
  13. package/dist/auth-bridge.d.ts +9 -6
  14. package/dist/auth-bridge.js +9 -3
  15. package/dist/boot/adoption.js +2 -1
  16. package/dist/boot/budget-tracing.d.ts +16 -0
  17. package/dist/boot/budget-tracing.js +10 -6
  18. package/dist/boot/device-lane.d.ts +1 -1
  19. package/dist/boot/execution-env.d.ts +24 -4
  20. package/dist/boot/execution-env.js +26 -23
  21. package/dist/boot/execution-lane.js +2 -1
  22. package/dist/boot/exit-boundary.js +5 -4
  23. package/dist/boot/memory-consolidation.js +2 -1
  24. package/dist/boot/parked-revive-gate.js +2 -1
  25. package/dist/boot/permission-rules-audit.d.ts +0 -28
  26. package/dist/boot/permission-rules-audit.js +2 -1
  27. package/dist/boot/read-deny-anchor-audit.js +2 -1
  28. package/dist/boot/reapers.js +4 -3
  29. package/dist/boot/resolve-spec.js +11 -2
  30. package/dist/boot/retention-lane.js +5 -3
  31. package/dist/boot/runner-deps.js +16 -13
  32. package/dist/boot/runtime-caps.js +3 -2
  33. package/dist/boot/session-faces.js +4 -3
  34. package/dist/boot/shutdown.js +3 -2
  35. package/dist/boot/stage-04-stores.js +3 -2
  36. package/dist/boot/stage-05-execution-env.d.ts +1 -1
  37. package/dist/boot/stage-05-execution-env.js +8 -6
  38. package/dist/boot/stage-06-runners.d.ts +1 -1
  39. package/dist/boot/stage-06-runners.js +2 -1
  40. package/dist/boot/stage-07-capability-layer.d.ts +1 -1
  41. package/dist/boot/stage-07-capability-layer.js +2 -1
  42. package/dist/boot/stage-08-reapers.d.ts +1 -1
  43. package/dist/boot/stage-08-reapers.js +3 -2
  44. package/dist/boot/stage-09-leader.d.ts +1 -1
  45. package/dist/boot/stage-10-http-server.d.ts +1 -1
  46. package/dist/boot/stage-10-http-server.js +5 -4
  47. package/dist/boot/stores.js +3 -2
  48. package/dist/boot/web-admin.js +17 -17
  49. package/dist/boot/webfetch-summarize-lane.js +2 -1
  50. package/dist/boot/workflow-orchestration.js +3 -2
  51. package/dist/capabilities/builtin-tools.d.ts +2 -1
  52. package/dist/capabilities/builtin-tools.js +2 -4
  53. package/dist/capabilities/composite-members.d.ts +2 -1
  54. package/dist/capabilities/composite-members.js +2 -1
  55. package/dist/capability-facts.d.ts +24 -0
  56. package/dist/capability-facts.js +23 -0
  57. package/dist/config-catalog.js +1 -0
  58. package/dist/config-types.d.ts +15 -4
  59. package/dist/config.d.ts +6 -0
  60. package/dist/config.js +17 -3
  61. package/dist/execution-lane-caps.d.ts +2 -1
  62. package/dist/fleet/fleet-bus.d.ts +6 -3
  63. package/dist/hooks/hook-runner.d.ts +6 -0
  64. package/dist/hooks/hook-runner.js +30 -1
  65. package/dist/http/dispatch.js +4 -2
  66. package/dist/http/execution-context.d.ts +2 -2
  67. package/dist/http/idempotency.d.ts +1 -1
  68. package/dist/http/principal-gate.d.ts +3 -1
  69. package/dist/http/principal-gate.js +17 -8
  70. package/dist/http/resume-legs.js +5 -9
  71. package/dist/http/route-table.js +2 -0
  72. package/dist/http/routes/attachments.js +18 -0
  73. package/dist/http/routes/capabilities.js +5 -2
  74. package/dist/http/routes/memory-policy.js +26 -6
  75. package/dist/http/routes/runs.js +3 -3
  76. package/dist/http/routes/session-memory-sync.d.ts +63 -0
  77. package/dist/http/routes/session-memory-sync.js +157 -0
  78. package/dist/http/routes/sessions-list.d.ts +2 -1
  79. package/dist/http/routes/tasks.js +7 -5
  80. package/dist/http/run-read-face.d.ts +7 -0
  81. package/dist/http/run-read-face.js +9 -3
  82. package/dist/http/send.d.ts +5 -0
  83. package/dist/http/send.js +9 -2
  84. package/dist/http/server.d.ts +14 -8
  85. package/dist/http/server.js +2 -4
  86. package/dist/http/submit-replay.d.ts +4 -3
  87. package/dist/http/submit-replay.js +2 -2
  88. package/dist/http/sync-submit-body.d.ts +21 -4
  89. package/dist/http/sync-submit-body.js +13 -0
  90. package/dist/identity-source.d.ts +21 -2
  91. package/dist/identity-source.js +6 -0
  92. package/dist/memory-incident-sink.d.ts +19 -6
  93. package/dist/memory-incident-sink.js +8 -4
  94. package/dist/memory-sync-client.d.ts +21 -21
  95. package/dist/memory-sync-client.js +50 -42
  96. package/dist/memory-sync-identity.d.ts +24 -0
  97. package/dist/memory-sync-identity.js +57 -0
  98. package/dist/memory-sync.d.ts +7 -1
  99. package/dist/memory-sync.js +6 -3
  100. package/dist/observability/fail-open.d.ts +27 -10
  101. package/dist/observability/fail-open.js +20 -7
  102. package/dist/observability/run-terminal.d.ts +92 -34
  103. package/dist/observability/run-terminal.js +167 -162
  104. package/dist/plugins/approval-ask-store-file.d.ts +14 -4
  105. package/dist/plugins/approval-ask-store-file.js +48 -7
  106. package/dist/plugins/approval-ask-store-memory.d.ts +2 -1
  107. package/dist/plugins/approval-ask-store-memory.js +9 -8
  108. package/dist/plugins/approval-ask-store-sql.d.ts +42 -10
  109. package/dist/plugins/approval-ask-store-sql.js +49 -19
  110. package/dist/plugins/file-run-store.d.ts +6 -1
  111. package/dist/plugins/file-run-store.js +11 -1
  112. package/dist/plugins/local-session-store.d.ts +36 -7
  113. package/dist/plugins/local-session-store.js +76 -32
  114. package/dist/plugins/local-task-attachment-store.d.ts +12 -2
  115. package/dist/plugins/local-task-attachment-store.js +67 -43
  116. package/dist/plugins/memory-key-guards.d.ts +3 -3
  117. package/dist/plugins/memory-run-store.d.ts +6 -1
  118. package/dist/plugins/memory-run-store.js +11 -1
  119. package/dist/plugins/one-time-migrations.d.ts +27 -1
  120. package/dist/plugins/one-time-migrations.js +57 -3
  121. package/dist/plugins/remote-env-e2b.d.ts +23 -4
  122. package/dist/plugins/remote-env-e2b.js +111 -28
  123. package/dist/plugins/remote-env-k8s.d.ts +31 -5
  124. package/dist/plugins/remote-env-k8s.js +86 -19
  125. package/dist/plugins/remote-env-local-docker.d.ts +15 -3
  126. package/dist/plugins/remote-env-local-docker.js +40 -12
  127. package/dist/plugins/run-store-sql.d.ts +9 -1
  128. package/dist/plugins/run-store-sql.js +9 -1
  129. package/dist/plugins/store-backend.d.ts +10 -7
  130. package/dist/plugins/store-backend.js +1 -0
  131. package/dist/plugins/store-contracts.d.ts +26 -1
  132. package/dist/plugins/store-contracts.js +3 -0
  133. package/dist/plugins/task-attachment-store.d.ts +112 -14
  134. package/dist/plugins/task-attachment-store.js +221 -28
  135. package/dist/plugins/workspace-fill.d.ts +60 -0
  136. package/dist/plugins/workspace-fill.js +106 -0
  137. package/dist/project-bundle.d.ts +38 -0
  138. package/dist/project-bundle.js +609 -0
  139. package/dist/run-local.js +2 -8
  140. package/dist/runs.js +1 -2
  141. package/dist/sandbox-egress-policy.d.ts +4 -1
  142. package/dist/sandbox-egress-policy.js +3 -1
  143. package/dist/sandbox-git-plane.d.ts +31 -12
  144. package/dist/sandbox-git-plane.js +24 -4
  145. package/dist/security.d.ts +9 -0
  146. package/dist/security.js +8 -0
  147. package/dist/session-error-wire.d.ts +24 -0
  148. package/dist/session-error-wire.js +7 -0
  149. package/dist/task-settings.d.ts +10 -2
  150. package/dist/task-settings.js +15 -2
  151. package/dist/terminal.d.ts +39 -19
  152. package/dist/terminal.js +65 -22
  153. package/dist/tool-approval.d.ts +4 -3
  154. package/dist/tool-approval.js +12 -14
  155. package/dist/trace/core-keyset-guard.d.ts +123 -6
  156. package/dist/trace/engine-notice-wire.d.ts +8 -2
  157. package/dist/trace/engine-notice-wire.js +4 -2
  158. package/dist/trace/exit-seat.d.ts +2 -1
  159. package/dist/trace/ledger-sink.d.ts +8 -4
  160. package/dist/trace/ledger-sink.js +2 -2
  161. package/dist/trace/project.d.ts +18 -1
  162. package/dist/trace/project.js +16 -0
  163. package/dist/trace/self-minted-wire.d.ts +251 -2
  164. package/dist/trace/self-minted-wire.js +82 -0
  165. package/dist/trace/wire-projection-faces.d.ts +1 -1
  166. package/dist/trace/wire-projection-faces.js +1 -1
  167. package/dist/web-admin-client/apply-effective.js +21 -1
  168. package/dist/web-admin-client/effective-keys.js +2 -0
  169. package/dist/web-admin-client/types.d.ts +4 -0
  170. package/package.json +1 -1
  171. package/wire-changes/server@7.142.0.json +639 -0
@@ -6,19 +6,23 @@ import { centerLaneConfigured } from "../web-admin-client/center-request.js";
6
6
  import { CostQuota } from "../observability/cost-quota.js";
7
7
  import { buildQuotaBounds } from "./limit-sync.js";
8
8
  import { assembleFileHistory } from "../file-history-capture.js";
9
- export function createBudgetAndTracing(ctx) {
10
- const { config, logger, metrics, backend, breakerState } = ctx;
11
- const { brain, degradeRoute: degradeRouteWatch } = createBrainAssembly(config, { breakerState });
12
- const pricing = buildPricing(config.models);
13
- const counterDegradeHook = (info) => {
9
+ import { seatText } from "../trace/exit-seat.js";
10
+ export function createCounterDegradeHook({ logger, metrics }) {
11
+ return (info) => {
14
12
  metrics.setGauge("counter_flush_fail_streak", info.streak, { table: info.table, kind: info.kind });
15
13
  if (info.streak > 0)
16
14
  metrics.inc("counter_flush_failed_total", { table: info.table, kind: info.kind });
17
15
  if (info.streak === 3)
18
- logger.warn("counter_flush_degraded", { table: info.table, kind: info.kind, streak: info.streak, ...(info.error ? { error: info.error } : {}) });
16
+ logger.warn("counter_flush_degraded", { table: info.table, kind: info.kind, streak: info.streak, ...(info.error ? { error: seatText("operator-log-text", info.error) } : {}) });
19
17
  else if (info.streak === 0 && info.prevStreak >= 3)
20
18
  logger.info("counter_flush_recovered", { table: info.table, kind: info.kind, prevStreak: info.prevStreak });
21
19
  };
20
+ }
21
+ export function createBudgetAndTracing(ctx) {
22
+ const { config, logger, metrics, backend, breakerState } = ctx;
23
+ const { brain, degradeRoute: degradeRouteWatch } = createBrainAssembly(config, { breakerState });
24
+ const pricing = buildPricing(config.models);
25
+ const counterDegradeHook = createCounterDegradeHook({ logger, metrics });
22
26
  const bounds = buildQuotaBounds(config);
23
27
  const costQuota = backend?.costQuota
24
28
  ? backend.costQuota(bounds.limitMicroUsd, bounds.windowMs, counterDegradeHook).startRefresh()
@@ -77,7 +77,7 @@ export declare function createDeviceLane(ctx: DeviceLaneCtx): DeviceLane | undef
77
77
  */
78
78
  export declare function assertDeviceLaneWired(isDeviceLane: boolean, lane: DeviceLane | undefined, hasFactory: boolean): void;
79
79
  /**
80
- * 设备入口 × 已绑设备的起服一行(拒的那一半在配置期:device 车道上 `DEVICE_RELAY_URL` 必填,缺席拒起 —— `config.ts` `parseDeviceRelayUrlEnv`;
80
+ * 设备入口 × 已绑设备的起服一行(拒的那一半在配置期:device 车道上 `DEVICE_RELAY_URL` 必填 —— 缺席拒起是车道必需 env 门(`config.ts` `declaredExecutionLane` → `laneMissingEnv`),坏形 / 带凭据形拒启是 `parseDeviceRelayUrlEnv`;
81
81
  * 起不起服不随表里有几条绑定变)。
82
82
  *
83
83
  * 字面 `none` = 运维声明「设备拨本引擎自己的地址 + `wsPath`」。此刻根会话行上若有设备绑定(writer(device, D) 的格),这些会话的设备从此只能拨那个入口 ——
@@ -8,10 +8,13 @@ import type { DeviceEnrollment } from "../device-enrollment.js";
8
8
  import type { DeviceStore } from "../device-store.js";
9
9
  import type { ExecutionPlacementSeat } from "../execution-placement.js";
10
10
  import type { DeviceWsHub } from "../device-ws-hub.js";
11
+ import { type RemoteContainerEnvDeps } from "../plugins/remote-env-e2b.js";
12
+ import { type WorkspaceFillOf } from "../plugins/workspace-fill.js";
11
13
  import { k8sExecutionEnvFactory, type K8sEnvConfig, type K8sEnvDeps, type SandboxSecretSeat } from "../plugins/remote-env-k8s.js";
12
- import { type SandboxGitPlane, type SandboxGitRunRows } from "../sandbox-git-plane.js";
14
+ import { type SandboxGitPlaneSeat, type SandboxGitRunRows } from "../sandbox-git-plane.js";
13
15
  import { type SandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
14
16
  import { type SpaceCommitRows } from "../workspace-space-commit.js";
17
+ import { type LocalDockerEnvDeps } from "../plugins/remote-env-local-docker.js";
15
18
  import * as facts from "../capability-facts.js";
16
19
  import { type TaskAttachmentStore } from "../plugins/task-attachment-store.js";
17
20
  import type { TaskInheritEnv } from "../request-value-closure.js";
@@ -77,10 +80,19 @@ export declare function createSessionScopedRegistries(maxEntries?: number): Sess
77
80
  * · 运维行座(`logger`)随部署级配置走。
78
81
  * `deps` = 适配器自己的测试缝(假集群 / 假对象存储),生产装配不传。返回的是工厂闭包 ⇒ `create*`。
79
82
  */
83
+ /**
84
+ * 一条 run 是不是它工作区的属主:它的放置根就是它自己的会话(core `ExecutionEnvFactoryContext.placementRootSessionId`)。委派子代 / 后台代理与父 run 并发
85
+ * (一只工作区两个写者)、本服务为自己起的 run(放置根 = `INTERNAL_RUN_PLACEMENT_ROOT`)都不是。两个读者同一判:k8s 工作区空间(子代依律关)与
86
+ * 恢复点的播种(只有属主播种)。
87
+ */
88
+ export declare function ownsItsWorkspace(ctx: {
89
+ readonly sessionId: string;
90
+ readonly placementRootSessionId: string;
91
+ }): boolean;
80
92
  export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
81
93
  imageOf(sessionId: string): string | undefined;
82
94
  /** The member git-credential broker seat for the pod's run (`runId` = the one reading of the run that owns the pod, the same value its
83
- * `sema.io/run` label carries; `sandbox-git-plane.ts` `sandboxBrokerSeatOf` over the boot's plane reading and the run row's identity);
95
+ * `sema.io/run` label carries; `sandbox-git-plane.ts` `createSandboxBrokerSeatOf` over the plane seat's reading at that pod and the run row's identity);
84
96
  * absent seat / `undefined` answer ⇒ the pod carries no broker. */
85
97
  brokerSeatOf?(runId: string | undefined): Promise<K8sEnvConfig["sandboxBroker"]>;
86
98
  /** The session store's by-id head read (`SessionStore.headOf`, picked once by `headReadOf`); the occupant is its live-occupant projection
@@ -90,11 +102,13 @@ export declare function createK8sRunEnvFactory(k8sCfg: K8sEnvConfig, seats: {
90
102
  /** The run store's commit-fence rows (`workspace_space_commit`); answers `undefined` only before stage 7 binds the run store
91
103
  * or without a store backend — the latter `assertWorkspaceSpaceReadable` refuses together with `space`. */
92
104
  spaceCommits(): SpaceCommitRows | undefined;
105
+ /** The restore point's fill seat of the run (`workspace-fill.ts`; `createExecutionEnv` mints the one reading). Absent / `undefined` ⇒ the pod as it comes. */
106
+ workspaceFillOf?: WorkspaceFillOf;
93
107
  }, deps?: K8sEnvDeps): ReturnType<typeof k8sExecutionEnvFactory>;
94
108
  /**
95
109
  * k8s 车道沙箱秘密座的**起服前提**(装配后的真门,第 5 段 await)。法只有一条:**秘密座非空(`K8S_SANDBOX_ENV` 点到了本进程环境里有的键)⇒
96
110
  * 问一次 SelfSubjectAccessReview(env 座那一行动词 {@link SANDBOX_SECRET_SEAT_VERBS}`.env`,沙箱命名空间)** —— 只问这一座自己发的调用,
97
- * 不问租约座的 `patch`(那一座由凭据平面的判官在它的前提都成立时另问,见 `readSandboxGitPlane`):
111
+ * 不问租约座的 `patch`(那一座由凭据平面座在它的前提都成立时另问一次,见 `sandbox-git-plane.ts` 的 `createSandboxGitPlaneSeat` 的 `settle`):
98
112
  * · 答了「不许」(任一动词)⇒ 拒启,一行拒句点名缺的动词、命名空间、Role 规则原文与出路(`[sandbox-secret-rbac]`)—— 不等第一个 run 才失败;
99
113
  * · 问不到(请求失败 / 审查本身被拒 / 答复没有 `status.allowed`)⇒ 照常起服:F 类兜底口 `server.sandbox-secret.seat-unverified` 计数 + 一行
100
114
  * `sandbox_secret_seat_unverified`。放行的最坏后果:RBAC 真缺时第一个要建 Secret 的 run 连接即拒(`auth_failed`,pod 被删)—— 秘密值仍然
@@ -141,6 +155,12 @@ export interface ExecutionEnvCtx {
141
155
  /** run 店的按 id 读(成员凭据代理座读 run 行上受理时记下的身份元组 `RunRecord.identity`)。取值闭包:run 店第 7 段才绑(`late-binds.ts`),
142
156
  * 平面开着时每个 run 的 pod 建起前求值一次。不传 / 答 `undefined`(没有存储后端)⇒ 读不到身份 ⇒ 不挂代理。 */
143
157
  runRows?: () => SandboxGitRunRows | undefined;
158
+ /** 隔离车道三只适配器自己的测试缝(假 e2b SDK / 假集群 / 假 docker CLI),原样交给各自的工厂 —— 与 `K8sEnvDeps` 同一条律:生产装配不传。 */
159
+ laneDeps?: {
160
+ readonly e2b?: RemoteContainerEnvDeps;
161
+ readonly k8s?: K8sEnvDeps;
162
+ readonly localDocker?: LocalDockerEnvDeps;
163
+ };
144
164
  }
145
165
  export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
146
166
  perTaskImage: PerTaskImageRegistry;
@@ -158,6 +178,6 @@ export declare function createExecutionEnv(ctx: ExecutionEnvCtx): {
158
178
  spaceReclaimer: SpaceReclaimer | undefined;
159
179
  sandboxSecretSeatGate: (() => Promise<void>) | undefined;
160
180
  sandboxPodReaper: SandboxPodReaper | undefined;
161
- readSandboxGitPlane: () => Promise<SandboxGitPlane>;
181
+ sandboxGitPlane: SandboxGitPlaneSeat;
162
182
  };
163
183
  //# sourceMappingURL=execution-env.d.ts.map
@@ -11,10 +11,11 @@ import { PerTaskImageRegistry } from "../per-task-image.js";
11
11
  import { adbExecutionEnvFactory } from "../plugins/remote-env-adb.js";
12
12
  import { deviceExecutionEnvFactory } from "../plugins/remote-env-device.js";
13
13
  import { e2bExecutionEnvFactory } from "../plugins/remote-env-e2b.js";
14
+ import { createWorkspaceFill } from "../plugins/workspace-fill.js";
14
15
  import { hostExecutionEnvFactory, RemoteHostExecutionEnv } from "../plugins/remote-env-host.js";
15
16
  import { boundedStoreCall, checkSandboxEgressPolicy, createSandboxPodApi, createSpaceReclaimer, k8sExecutionEnvFactory, readSandboxSecretSeat, rpcTimeoutMsOf } from "../plugins/remote-env-k8s.js";
16
17
  import { podSpecPatchBrokerRefusal, SANDBOX_SECRET_SEAT_VERBS } from "../plugins/k8s-exec-protocol.js";
17
- import { judgeSandboxGitPlane, runIdentitySourceOf, sandboxBrokerSeatOf, sandboxGitPlaneAsksSecretSeat, sandboxGitPlanePremisesOf } from "../sandbox-git-plane.js";
18
+ import { createSandboxBrokerSeatOf, createSandboxGitPlaneSeat, sandboxGitPlanePremisesOf } from "../sandbox-git-plane.js";
18
19
  import { createSandboxPodReaper } from "../plugins/k8s-pod-reaper.js";
19
20
  import { recordFailOpen } from "../observability/fail-open.js";
20
21
  import { createRunWorkspaceSnapshotSeat } from "../fleet/workspace-snapshot-residual.js";
@@ -38,6 +39,7 @@ import { sandboxPathEnvSlots, withSandboxPathEnvSlot } from "./deferred-sandbox-
38
39
  import { createLaneHomeAbsenceReporter } from "../lane-home-probe.js";
39
40
  import { REMOTE_EXEC_PROVIDER_WORDS, buildLaneHomeBootNote, buildLaneHomeClaim, capabilitiesFor, executionLaneOf, laneBindsReads, laneIsolated, laneSandboxReadFloorState } from "../execution-lane-caps.js";
40
41
  import { warnReadDenyFloorUnkept } from "./execution-lane.js";
42
+ import { seatText } from "../trace/exit-seat.js";
41
43
  function boundedSessionSetter(map, maxEntries) {
42
44
  return (sid, value) => {
43
45
  map.delete(sid);
@@ -105,15 +107,19 @@ export function createSessionScopedRegistries(maxEntries = MAX_CWD_SESSIONS) {
105
107
  dropSession,
106
108
  };
107
109
  }
110
+ export function ownsItsWorkspace(ctx) {
111
+ return ctx.placementRootSessionId === ctx.sessionId;
112
+ }
108
113
  export function createK8sRunEnvFactory(k8sCfg, seats, deps) {
109
114
  const cacheMisses = createCacheMissMemory();
110
115
  return async (ctx) => {
111
116
  const onWorkspaceSnapshot = createRunWorkspaceSnapshotSeat(ctx.sessionId);
112
- const child = ctx.placementRootSessionId !== ctx.sessionId;
117
+ const child = !ownsItsWorkspace(ctx);
113
118
  const s3Snapshot = child && k8sCfg.s3Snapshot !== undefined ? spaceOff(k8sCfg.s3Snapshot) : k8sCfg.s3Snapshot;
114
119
  const spaceOccupant = child ? undefined : await occupantOf(seats, ctx.sessionId, ctx.taskId, rpcTimeoutMsOf(k8sCfg));
115
120
  const runId = !child && ctx.taskId !== undefined ? ctx.taskId : undefined;
116
121
  const sandboxBroker = await seats.brokerSeatOf?.(runId);
122
+ const workspaceFill = seats.workspaceFillOf?.(ctx);
117
123
  const { cache, ...deploymentCfg } = k8sCfg;
118
124
  return k8sExecutionEnvFactory({
119
125
  ...deploymentCfg,
@@ -123,6 +129,7 @@ export function createK8sRunEnvFactory(k8sCfg, seats, deps) {
123
129
  ...(spaceOccupant !== undefined ? { spaceOccupant } : {}),
124
130
  ...(runId !== undefined ? { runId } : {}),
125
131
  ...(sandboxBroker !== undefined ? { sandboxBroker } : {}),
132
+ ...(workspaceFill !== undefined ? { workspaceFill } : {}),
126
133
  onWorkspaceSnapshot,
127
134
  }, deps)(ctx);
128
135
  };
@@ -171,7 +178,7 @@ export async function ensureSandboxSecretSeat(k8sCfg, logger, deps, read = () =>
171
178
  recordFailOpen("server.sandbox-secret.seat-unverified", `namespace=${namespace} ${seat.detail}`);
172
179
  logger.warn("sandbox_secret_seat_unverified", {
173
180
  namespace,
174
- detail: seat.detail,
181
+ detail: seatText("operator-log-text", seat.detail),
175
182
  effect: `the boot could not confirm the sandbox Secret RBAC (secrets ${SANDBOX_SECRET_SEAT_VERBS.env.join(" / ")}) — the service starts; if the Role lacks them, the first run that forwards K8S_SANDBOX_ENV is refused at connect (auth_failed) and its pod deleted — the values never go into the pod spec`,
176
183
  });
177
184
  return;
@@ -239,9 +246,9 @@ export function createExecutionEnv(ctx) {
239
246
  let spaceReclaimer;
240
247
  let sandboxSecretSeatGate;
241
248
  let sandboxPodReaper;
242
- const planeCell = {};
243
- let readSandboxGitPlane = async () => (planeCell.current = judgeSandboxGitPlane(sandboxGitPlanePremisesOf(config, {})));
249
+ let sandboxGitPlane = createSandboxGitPlaneSeat((r) => sandboxGitPlanePremisesOf(config, r), {});
244
250
  let worktreeReap;
251
+ const workspaceFillOf = taskAttachmentStore === undefined ? undefined : (fctx) => createWorkspaceFill({ store: taskAttachmentStore, sessionId: fctx.sessionId, seeds: ownsItsWorkspace(fctx), logger });
245
252
  if (config.remoteExec?.provider === "e2b") {
246
253
  executionEnvFactory = e2bExecutionEnvFactory({
247
254
  apiKey: config.remoteExec.apiKey,
@@ -253,9 +260,10 @@ export function createExecutionEnv(ctx) {
253
260
  ...(config.remoteExec.mountPath ? { mountPath: config.remoteExec.mountPath } : {}),
254
261
  ...((env) => (env !== undefined ? { sandboxEnv: env } : {}))(sandboxLaneEnvMerged(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv))),
255
262
  sandboxReadFloor: config.sandboxReadFloor,
263
+ ...(workspaceFillOf !== undefined ? { workspaceFillOf } : {}),
256
264
  logger,
257
265
  metrics,
258
- });
266
+ }, ctx.laneDeps?.e2b);
259
267
  }
260
268
  else if (config.remoteExec?.provider === "k8s") {
261
269
  const k8sCfg = {
@@ -281,30 +289,24 @@ export function createExecutionEnv(ctx) {
281
289
  ...((lane) => ({ ...(lane.env !== undefined ? { podEnv: lane.env } : {}), ...(lane.secretEnv !== undefined ? { podSecretEnv: lane.secretEnv } : {}) }))(buildSandboxLaneEnv(config, config.remoteExec.sandboxEnv)),
282
290
  sandboxReadFloor: config.sandboxReadFloor,
283
291
  };
284
- const brokerImage = config.remoteExec.brokerImage;
292
+ sandboxGitPlane = createSandboxGitPlaneSeat((r) => sandboxGitPlanePremisesOf(config, r), {
293
+ patchRefusal: podSpecPatchBrokerRefusal(k8sCfg.podSpecPatch),
294
+ askSecretSeat: () => readSandboxSecretSeat(k8sCfg, "lease"),
295
+ });
285
296
  executionEnvFactory = createK8sRunEnvFactory(k8sCfg, {
286
297
  imageOf: (sid) => perTaskImage.get(sid),
287
298
  headOf: ctx.headOf,
288
299
  spaceCommits: ctx.spaceCommits ?? (() => undefined),
289
- brokerSeatOf: async (runId) => {
290
- const plane = planeCell.current;
291
- if (plane === undefined || plane.state !== "on")
292
- return undefined;
293
- return sandboxBrokerSeatOf(plane, brokerImage, await runIdentitySourceOf(runId, ctx.runRows?.(), logger));
294
- },
295
- });
300
+ brokerSeatOf: createSandboxBrokerSeatOf(sandboxGitPlane.now, config.remoteExec.brokerImage, ctx.runRows, logger),
301
+ ...(workspaceFillOf !== undefined ? { workspaceFillOf } : {}),
302
+ }, ctx.laneDeps?.k8s);
296
303
  if (k8sCfg.cache !== undefined || k8sCfg.s3Snapshot !== undefined)
297
304
  spaceReclaimer = createSpaceReclaimer(k8sCfg);
298
305
  sandboxSecretSeatGate = () => ensureSandboxSecretSeat(k8sCfg, logger, undefined, () => readSandboxSecretSeat(k8sCfg, "env"));
299
- readSandboxGitPlane = async () => {
300
- const premises = sandboxGitPlanePremisesOf(config, { ...((r) => (r !== undefined ? { patchRefusal: r } : {}))(podSpecPatchBrokerRefusal(k8sCfg.podSpecPatch)) });
301
- const secretSeat = sandboxGitPlaneAsksSecretSeat(premises) ? await readSandboxSecretSeat(k8sCfg, "lease") : undefined;
302
- return (planeCell.current = judgeSandboxGitPlane({ ...premises, secretSeat }));
303
- };
304
306
  sandboxPodReaper = createSandboxPodReaper({ api: createSandboxPodApi(k8sCfg), logger, metrics });
305
307
  void checkSandboxEgressPolicy(k8sCfg)
306
308
  .then((gaps) => sayEgressPolicyReading(gaps, logger, k8sCfg.namespace))
307
- .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" }));
309
+ .catch((e) => logger.error("sandbox_egress_policy_check_crashed", { error: seatText("operator-log-text", 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" }));
308
310
  }
309
311
  else if (config.remoteExec?.provider === "ssh") {
310
312
  executionEnvFactory = sshExecutionEnvFactory({
@@ -397,7 +399,8 @@ export function createExecutionEnv(ctx) {
397
399
  ...(config.remoteExec.commandTimeoutMs != null ? { commandTimeoutMs: config.remoteExec.commandTimeoutMs } : {}),
398
400
  ...(config.remoteExec.env ? { env: config.remoteExec.env } : {}),
399
401
  sandboxReadFloor: config.sandboxReadFloor,
400
- });
402
+ ...(workspaceFillOf !== undefined ? { workspaceFillOf } : {}),
403
+ }, ctx.laneDeps?.localDocker);
401
404
  }
402
405
  if (executionEnvFactory && DEPLOYMENT_SHAPE_PREDICATES.remoteScratchpad.holds(config)) {
403
406
  executionEnvFactory = withRemoteScratchpad(executionEnvFactory, logger);
@@ -451,7 +454,7 @@ export function createExecutionEnv(ctx) {
451
454
  executionEnvFactory = registry.wrapFactory(executionEnvFactory);
452
455
  return registry;
453
456
  })();
454
- if (taskAttachmentStore && executionEnvFactory) {
457
+ if (taskAttachmentStore && executionEnvFactory && !laneIsolated(executionLaneOf(config.remoteExec.provider))) {
455
458
  const inner = executionEnvFactory;
456
459
  const attStore = taskAttachmentStore;
457
460
  executionEnvFactory = async (ctx) => {
@@ -507,7 +510,7 @@ export function createExecutionEnv(ctx) {
507
510
  perTaskImage, sessionEnvSelection, perSessionCwd, setSessionCwd, setSessionShellEnv, setSessionInheritEnv,
508
511
  fenceSessionOwner: sessionScoped.fenceSessionOwner,
509
512
  dropSessionScoped: sessionScoped.dropSession,
510
- executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer, sandboxSecretSeatGate, sandboxPodReaper, readSandboxGitPlane,
513
+ executionEnvFactory, worktreeReap, sendUserFileTaskEnvs, lspManager, spaceReclaimer, sandboxSecretSeatGate, sandboxPodReaper, sandboxGitPlane,
511
514
  };
512
515
  }
513
516
  //# sourceMappingURL=execution-env.js.map
@@ -4,6 +4,7 @@ import { crossInvariant } from "../config-invariants.js";
4
4
  import { buildReadDenyFloorUnkeptNote, executionLaneOf, laneIsolated } from "../execution-lane-caps.js";
5
5
  import { buildReadFacePostureNotice, settleDerivedReadFace } from "../read-face-posture.js";
6
6
  import { settleHostShellAtBoot } from "../plugins/host-platform.js";
7
+ import { seatText } from "../trace/exit-seat.js";
7
8
  export const FILE_SOURCED_LANES = ["host", "local-docker"];
8
9
  export async function composeExecutionLane(config, ctx) {
9
10
  if (ctx.configSource === "local")
@@ -55,7 +56,7 @@ async function applyRemoteExecFile(config, ctx) {
55
56
  fileRx = await loadRemoteExec(localRoot);
56
57
  }
57
58
  catch (err) {
58
- logger.error("remote_exec_file_invalid", { root: localRoot, err: String(err) });
59
+ logger.error("remote_exec_file_invalid", { root: localRoot, err: seatText("operator-log-text", String(err)) });
59
60
  throw new Error(`config.d/remote-exec.json under ${localRoot} is invalid: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
60
61
  }
61
62
  if (!fileRx)
@@ -2,6 +2,7 @@ import { defaultTaskRegistry } from "@sema-agent/core";
2
2
  import { reportHardKillReceipts } from "../background-session-reap.js";
3
3
  import { liveHostForegroundChildCount, reapHostForegroundChildren } from "../plugins/host-platform.js";
4
4
  import { liveEphemeralHostWorkspaceCount, reapEphemeralHostWorkspaces } from "../plugins/host-workspace-registry.js";
5
+ import { seatText } from "../trace/exit-seat.js";
5
6
  const PROCESS_EXIT_INCLUDES_RETAINED = false;
6
7
  export function installProcessExitBoundary({ logger }) {
7
8
  const reapOwnChildProcesses = () => {
@@ -13,7 +14,7 @@ export function installProcessExitBoundary({ logger }) {
13
14
  logger.info("shutdown_background_reaped", { reaped });
14
15
  }
15
16
  catch (err) {
16
- logger.warn("shutdown_background_reap_failed", { err: String(err) });
17
+ logger.warn("shutdown_background_reap_failed", { err: seatText("operator-log-text", String(err)) });
17
18
  }
18
19
  try {
19
20
  const fg = reapHostForegroundChildren();
@@ -23,7 +24,7 @@ export function installProcessExitBoundary({ logger }) {
23
24
  logger.warn("shutdown_foreground_reap_incomplete", { groups: fg.failed });
24
25
  }
25
26
  catch (err) {
26
- logger.warn("shutdown_foreground_reap_failed", { err: String(err) });
27
+ logger.warn("shutdown_foreground_reap_failed", { err: seatText("operator-log-text", String(err)) });
27
28
  }
28
29
  return backgroundSoftStopped;
29
30
  };
@@ -33,7 +34,7 @@ export function installProcessExitBoundary({ logger }) {
33
34
  reportHardKillReceipts(logger, receipts, "process_exit");
34
35
  }
35
36
  catch (err) {
36
- logger.warn("shutdown_background_hard_kill_failed", { err: String(err) });
37
+ logger.warn("shutdown_background_hard_kill_failed", { err: seatText("operator-log-text", String(err)) });
37
38
  }
38
39
  };
39
40
  const reapOwnHostWorkspaces = () => {
@@ -45,7 +46,7 @@ export function installProcessExitBoundary({ logger }) {
45
46
  logger.warn("shutdown_host_workspace_reap_incomplete", { dirs: failed.map((f) => f.dir) });
46
47
  }
47
48
  catch (err) {
48
- logger.warn("shutdown_host_workspace_reap_failed", { err: String(err) });
49
+ logger.warn("shutdown_host_workspace_reap_failed", { err: seatText("operator-log-text", String(err)) });
49
50
  }
50
51
  };
51
52
  const onProcessExit = () => {
@@ -7,6 +7,7 @@ import { DEFAULT_MODEL_REF, resolveMainModel } from "../model-select.js";
7
7
  import { backendOwnsControlPlane, captureRecordStoreOf } from "../memory-operator-faces.js";
8
8
  import { memoryStoreForRequest } from "../memory-scope-stores.js";
9
9
  import { createConsolidationWriteJudge, deliverWriteRefusal, expandTiers, MemoryEngine, modelOriginOf, readConsolidationDriverRun, resolveMemoryConsolidationDriver, runMemoryConsolidationDriver, } from "@sema-agent/core";
10
+ import { seatText } from "../trace/exit-seat.js";
10
11
  export function resolveConsolidationDriverSeat(deps) {
11
12
  let options;
12
13
  try {
@@ -238,7 +239,7 @@ function decideConsolidationValve(input) {
238
239
  return { valve: { enabled: true, source: "derived", catalogDependent: true } };
239
240
  const seatFact = billing.kind === "main-model"
240
241
  ? `Here the seat resolves to the main model "${billing.mainModel}" (no MODEL_CHEAP_ID naming another model, and no catalog roles.consolidate / roles.summarize / "flash" tier binding to another model)`
241
- : `Here no consolidation seat resolves at all (${billing.fault})`;
242
+ : `Here no consolidation seat resolves at all (${seatText("operator-log-text", billing.fault)})`;
242
243
  return {
243
244
  valve: {
244
245
  enabled: false,
@@ -8,6 +8,7 @@ import { DeferredSandboxPathEnv, sandboxPathEnvSlots } from "./deferred-sandbox-
8
8
  import { DEPLOYMENT_SHAPE_PREDICATES } from "../deployment-shape.js";
9
9
  import { executionLaneHomeDir } from "../execution-lane-caps.js";
10
10
  import { createDeploymentListExemption } from "../approval.js";
11
+ import { seatText } from "../trace/exit-seat.js";
11
12
  function hostGateCwd(config, localRoot) {
12
13
  return join(config.localDataRoot ?? localRoot, "parked-revive-unrooted");
13
14
  }
@@ -63,7 +64,7 @@ async function resolveCapsFor(deps, identity) {
63
64
  return await deps.resolveRuntimeCaps(identity.principal);
64
65
  }
65
66
  catch (err) {
66
- deps.logger.warn("parked_revive_caps_unresolved", { principal: identity.principal ?? null, err: err instanceof Error ? err.message : String(err) });
67
+ deps.logger.warn("parked_revive_caps_unresolved", { principal: identity.principal ?? null, err: seatText("operator-log-text", err instanceof Error ? err.message : String(err)) });
67
68
  return undefined;
68
69
  }
69
70
  }
@@ -1,31 +1,3 @@
1
- /**
2
- * [ref] §3([ref] v2 §6 F4 的**残余采纳**)—— 默认 ON 的**休眠行审计**。
3
- *
4
- * 🔴 这条审计存在的唯一理由:`PERMISSION_RULES_ENABLED` 的默认值在本车从 OFF 翻成 ON,而**旋钮翻转
5
- * 与规则行的寿命是两条独立的时间线**。三张表自 [ref] 车二起就随中央 `ensureSchema` 建、并且从来没有被
6
- * 清过,所以下面这两类部署在升级的那一刻会突然带着**既有**规则行开始放行:
7
- * · 曾经把旋钮开过一段时间又关掉的部署(行留在库里,只是没人读);
8
- * · 从旧库恢复/克隆出来的部署(行跟着数据一起来的)。
9
- * 对运维来说,这是「我什么都没改,worker 却开始少问我了」——一次**放宽面**上的静默变化。审计做的事
10
- * 很小但是承重:在启动日志里把它说出来,让「少问」是一条能被读到的因果,而不是一次玄学。
11
- *
12
- * ── 三条判据,逐条有理由 ────────────────────────────────────────────────────────────────────────
13
- * ① **店缺席 ⇒ 整条不上场**(零 I/O、零行)。店缺席包括:显式关旋钮、`local` 车道没接 File 店的旧
14
- * 部署、以及压根没有 backend 的 env-only worker。没有店就没有行,谈不上唤醒。
15
- * ② **零桶 ⇒ 不打行**。一条「0 条既有规则随默认 ON 激活」的行是纯噪音:它对读日志的人不传递任何
16
- * 可行动的信息,却让真正有东西被唤醒的那一行变得不显眼。
17
- * ③ **运维**显式**表过态(`PERMISSION_RULES_ENABLED` 被设成 true 或 false)⇒ 不打行**。那些行本来
18
- * 就是活的(或者旋钮被显式关着、店根本没装配),默认值翻不翻转对它们零影响 —— 说「随默认 ON
19
- * 激活」是**假话**,而在启动日志里说假话比不说更坏。判据用 `permissionRulesEnabledExplicit`
20
- * (config.ts 与 `dbBackendExplicit` 同姿势),不是猜。
21
- *
22
- * ── 失败方向 ────────────────────────────────────────────────────────────────────────────────────
23
- * 数不出来(库抖动 / 文件读不了)⇒ **不拒启、也不编一个 0 出来**:返回 `undefined` + 一条 warn。
24
- * 这是一条纯诊断面(它不参与任何放行/拒绝判决),所以它的降级方向是「安静地不知道」而不是「谎报
25
- * 零」——`{ buckets: 0 }` 会被下游读成「确认没有休眠行」,那是把一次读失败伪装成一个结论。
26
- * 因为它零判决,这条 fail-open 不属于 `docs/FAIL-OPEN-CENSUS.md` 的安全轴 P 类,故不占 `recordFailOpen`
27
- * 的标签;它的可见性由那条 warn 承担。
28
- */
29
1
  /** 审计只需要一只桶计数口——**刻意**不吃整个 `PermissionRuleStoreBundle`:诊断面对店的其余能力
30
2
  * 一无所知也不该知道,窄口让测试能用四行 fixture 驱动真实现同一段代码。 */
31
3
  export interface DormantRuleCounter {
@@ -1,3 +1,4 @@
1
+ import { seatText } from "../trace/exit-seat.js";
1
2
  export async function auditDormantPermissionRules(ctx) {
2
3
  if (ctx.stores === undefined)
3
4
  return undefined;
@@ -7,7 +8,7 @@ export async function auditDormantPermissionRules(ctx) {
7
8
  }
8
9
  catch (err) {
9
10
  ctx.logger.warn?.("permission_rules_dormant_audit_failed", {
10
- error: err instanceof Error ? err.message : String(err),
11
+ error: seatText("operator-log-text", err instanceof Error ? err.message : String(err)),
11
12
  note: "the boot audit could not count pre-existing permission-rule buckets; the lane itself is unaffected",
12
13
  });
13
14
  return undefined;
@@ -1,5 +1,6 @@
1
1
  import { compileReadDeny, readDenyRowAnchor } from "@sema-agent/core";
2
2
  import { echoCallerText } from "../caller-text-echo.js";
3
+ import { seatText } from "../trace/exit-seat.js";
3
4
  export const READ_DENY_AUDIT_MAX_NAMED_ROWS = 20;
4
5
  const NAMED_ROW_ECHO_CHARS = 200;
5
6
  function isAnchoredCanonicalRow(canonical) {
@@ -14,7 +15,7 @@ export function auditReadDenyAnchoring(ctx) {
14
15
  }
15
16
  catch (err) {
16
17
  ctx.logger.warn("read_deny_anchor_audit_failed", {
17
- error: echoCallerText(err instanceof Error ? err.message : String(err), { maxChars: 400 }),
18
+ error: seatText("operator-log-text", err instanceof Error ? err.message : String(err)),
18
19
  note: "the boot audit could not read the deployment read-deny rows, so it names none and changes nothing; the engine itself refuses a row it cannot read when the Runner is built",
19
20
  });
20
21
  return [];
@@ -6,6 +6,7 @@ import { createApprovalReconciler } from "../approval-reconciler.js";
6
6
  import { createE2bOrphanSandboxProvider, reclaimOrphanE2bSandboxes, E2B_ORPHAN_GRACE_MS } from "../plugins/e2b-orphan-reclaim.js";
7
7
  import { SANDBOX_POD_REAP_PERIOD_MS } from "../plugins/k8s-pod-reaper.js";
8
8
  import { APPROVAL_ASK_RETENTION_PASS_MS, runApprovalAskRetentionPass } from "../approval-ask-retention.js";
9
+ import { seatText } from "../trace/exit-seat.js";
9
10
  export const REAPER_FAILURE_WARN_THRESHOLD = 5;
10
11
  export function createThrottledReaperCatch(name, logger, threshold = REAPER_FAILURE_WARN_THRESHOLD) {
11
12
  let streak = 0;
@@ -21,7 +22,7 @@ export function createThrottledReaperCatch(name, logger, threshold = REAPER_FAIL
21
22
  logger.warn("reaper_leg_repeated_failure", {
22
23
  reaper: name,
23
24
  consecutiveFailures: streak,
24
- lastError: err instanceof Error ? err.message : String(err),
25
+ lastError: seatText("operator-log-text", err instanceof Error ? err.message : String(err)),
25
26
  });
26
27
  }
27
28
  catch {
@@ -169,7 +170,7 @@ export function startReapers(ctx) {
169
170
  catch (err) {
170
171
  failedScopes++;
171
172
  try {
172
- logger.warn("bg_agent_reap_scope_failed", { scope, err: err instanceof Error ? err.message : String(err) });
173
+ logger.warn("bg_agent_reap_scope_failed", { scope, err: seatText("operator-log-text", err instanceof Error ? err.message : String(err)) });
173
174
  }
174
175
  catch {
175
176
  }
@@ -266,7 +267,7 @@ export function startReapers(ctx) {
266
267
  } })
267
268
  .catch((err) => {
268
269
  try {
269
- logger.warn("attachment_orphan_sweep_failed", { err: err instanceof Error ? err.message : String(err) });
270
+ logger.warn("attachment_orphan_sweep_failed", { err: seatText("operator-log-text", err instanceof Error ? err.message : String(err)) });
270
271
  }
271
272
  catch {
272
273
  }
@@ -21,7 +21,7 @@ import { isAtMentionable, matchCatalogModel, refusedCatalogEntry, resolveTaskMod
21
21
  import { echoModelRef, modelUnavailableMessage } from "../model-entry-refusal.js";
22
22
  import { PerTaskImageRegistry, resolveSandboxImageRef } from "../per-task-image.js";
23
23
  import { remoteScratchpadDirFor } from "../plugins/remote-scratchpad.js";
24
- import { bindAttachmentsForTask } from "../plugins/task-attachment-store.js";
24
+ import { attachmentBindRefusalAnswer, bindAttachmentsForTask } from "../plugins/task-attachment-store.js";
25
25
  import { resourceSuspendOptIn } from "../resource-suspend.js";
26
26
  import { routeServiceTask, supPostureOverrides } from "../router/route-orchestration.js";
27
27
  import { tightenGoverned } from "../governance-overrides.js";
@@ -50,7 +50,7 @@ import { inertToolListEntryOf } from "../rule-entry-list.js";
50
50
  import { TENANCY_WALL_CLOCK_BASE_SEC, enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
51
51
  import { redactSecrets } from "../trace/redact.js";
52
52
  import { DeferredSandboxPathEnv, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
53
- import { executionLaneHomeDir, executionLaneOf, laneWiresExecutionEnv } from "../execution-lane-caps.js";
53
+ import { executionLaneHomeDir, executionLaneOf, laneIsolated, laneWiresExecutionEnv } from "../execution-lane-caps.js";
54
54
  import { executionConfigRootOf } from "../execution-config-root.js";
55
55
  import { withUnattendedLeg } from "../unattended-permission-mode.js";
56
56
  import { fieldConflictFacts } from "../field-conflict.js";
@@ -217,11 +217,13 @@ export function createResolveSpec(ctx) {
217
217
  if (!auth?.sessionId)
218
218
  throw new HttpError(400, "`attachmentIds` requires a session-resolving deployment (no authorizer session)");
219
219
  const sid = auth.sessionId;
220
+ const lane = opts.execution.lane;
220
221
  const r = await bindAttachmentsForTask({
221
222
  store: taskAttachmentStore,
222
223
  owner: auth?.principal ?? "default",
223
224
  ids: reqAtt,
224
225
  sessionId: sid,
226
+ lane: { word: lane, isolated: laneIsolated(lane) },
225
227
  leg: opts?.leg === "fresh" ? "fresh" : "resume",
226
228
  nowMs: Date.now(),
227
229
  onMissing: (id) => {
@@ -229,6 +231,12 @@ export function createResolveSpec(ctx) {
229
231
  throw new HttpError(400, `attachment not found: ${id}`);
230
232
  logger.warn("attachment_missing_on_resume", { sessionId: sid, id });
231
233
  },
234
+ onRefused: (refusal) => {
235
+ const answer = attachmentBindRefusalAnswer(refusal);
236
+ if (opts?.leg === "fresh")
237
+ throw createCodedHttpError(answer.status, answer.errorCode, answer.message, answer.extra);
238
+ logger.warn("attachment_refused_on_resume", { sessionId: sid, errorCode: answer.errorCode, ...answer.extra });
239
+ },
232
240
  });
233
241
  attachmentNotice = r.notice;
234
242
  }
@@ -277,6 +285,7 @@ export function createResolveSpec(ctx) {
277
285
  sessionId: auth?.sessionId ?? "",
278
286
  cwd: (auth?.sessionId ? perSessionCwd.get(auth.sessionId) : undefined) ?? process.cwd(),
279
287
  ...(parsedSettings.settings?.shellEnv ? { shellEnv: parsedSettings.settings.shellEnv } : {}),
288
+ httpHookAllowlistLayers: parsedSettings.settings?.httpHookAllowlist !== undefined ? [parsedSettings.settings.httpHookAllowlist] : [],
280
289
  hookLlm,
281
290
  hookAgent,
282
291
  onHookNotice: (n) => fleetBus.publishHookNotice({
@@ -1,4 +1,5 @@
1
1
  import { recordFailOpen } from "../observability/fail-open.js";
2
+ import { seatText } from "../trace/exit-seat.js";
2
3
  export const RETENTION_SWEEP_STUCK_THRESHOLD = 5;
3
4
  export const RETENTION_LEASE_TTL_FACTOR = 2;
4
5
  const DAY_MS = 86_400_000;
@@ -137,15 +138,16 @@ async function recordDomainFailure(ctx, d) {
137
138
  }
138
139
  const streaks = ctx.streaks;
139
140
  const streak = (streaks?.get(d.domain) ?? 0) + 1;
141
+ const shown = seatText("operator-log-text", message);
140
142
  streaks?.set(d.domain, streak);
141
143
  try {
142
- ctx.logger.warn("retention_sweep_domain_failed", { domain: d.domain, fencingToken: d.fencingToken, consecutiveFailures: streak, auditRowWritten, err: message });
144
+ ctx.logger.warn("retention_sweep_domain_failed", { domain: d.domain, fencingToken: d.fencingToken, consecutiveFailures: streak, auditRowWritten, err: shown });
143
145
  if (streak >= RETENTION_SWEEP_STUCK_THRESHOLD) {
144
146
  ctx.metrics.inc("retention_sweep_stuck", {}, 1);
145
147
  ctx.logger.warn("retention_sweep_stuck", {
146
148
  domain: d.domain,
147
149
  consecutiveFailures: streak,
148
- lastError: message,
150
+ lastError: shown,
149
151
  note: "this retention domain has failed its sweep for RETENTION_SWEEP_STUCK_THRESHOLD consecutive rounds — its data is NOT being retained per policy. Check retention_audit rows with action='failed' for this domain.",
150
152
  });
151
153
  streaks?.set(d.domain, 0);
@@ -168,7 +170,7 @@ export function startRetentionLane(ctx) {
168
170
  .then(() => undefined)
169
171
  .catch((err) => {
170
172
  try {
171
- ctx.logger.warn("retention_sweep_round_failed", { err: err instanceof Error ? err.message : String(err) });
173
+ ctx.logger.warn("retention_sweep_round_failed", { err: seatText("operator-log-text", err instanceof Error ? err.message : String(err)) });
172
174
  }
173
175
  catch {
174
176
  recordFailOpen("server.retention.sweep-report-dropped", "round");