@sema-agent/server 7.96.0 → 7.97.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 (65) hide show
  1. package/USAGE.md +4 -1
  2. package/dist/approval.d.ts +28 -0
  3. package/dist/approval.js +24 -0
  4. package/dist/boot/budget-tracing.d.ts +1 -0
  5. package/dist/boot/budget-tracing.js +3 -2
  6. package/dist/boot/config-center.js +2 -5
  7. package/dist/boot/coordinators.js +2 -2
  8. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -2
  9. package/dist/boot/deferred-sandbox-path-env.js +3 -1
  10. package/dist/boot/memory-boundary.d.ts +17 -2
  11. package/dist/boot/memory-boundary.js +11 -0
  12. package/dist/boot/reapers.d.ts +3 -1
  13. package/dist/boot/reapers.js +2 -2
  14. package/dist/boot/resolve-spec.js +6 -6
  15. package/dist/boot/runner-deps.d.ts +2 -1
  16. package/dist/boot/session-faces.d.ts +4 -1
  17. package/dist/boot/session-faces.js +5 -5
  18. package/dist/boot/stage-04-stores.d.ts +1 -0
  19. package/dist/boot/stage-04-stores.js +2 -2
  20. package/dist/boot/stage-05-execution-env.d.ts +1 -0
  21. package/dist/boot/stage-06-runners.d.ts +1 -0
  22. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  23. package/dist/boot/stage-07-capability-layer.js +10 -14
  24. package/dist/boot/stage-08-reapers.d.ts +2 -3
  25. package/dist/boot/stage-08-reapers.js +2 -2
  26. package/dist/boot/stage-09-leader.d.ts +1 -0
  27. package/dist/boot/stage-09-leader.js +2 -2
  28. package/dist/boot/stage-10-http-server.d.ts +1 -0
  29. package/dist/boot/stores.js +5 -11
  30. package/dist/capabilities/execution-lane.d.ts +5 -7
  31. package/dist/capabilities/execution-lane.js +2 -1
  32. package/dist/capabilities/memory-notice.d.ts +30 -12
  33. package/dist/capabilities/memory-notice.js +8 -1
  34. package/dist/capabilities/skills.d.ts +14 -0
  35. package/dist/capabilities/skills.js +4 -0
  36. package/dist/config-catalog.js +1 -0
  37. package/dist/config-types.d.ts +8 -1
  38. package/dist/config.js +4 -3
  39. package/dist/env-facts.js +2 -4
  40. package/dist/execution-lane-caps.d.ts +36 -6
  41. package/dist/execution-lane-caps.js +4 -0
  42. package/dist/file-history-capture.d.ts +65 -0
  43. package/dist/file-history-capture.js +21 -0
  44. package/dist/hosted-posture.d.ts +1 -1
  45. package/dist/http/resume-legs.d.ts +14 -4
  46. package/dist/http/resume-legs.js +26 -17
  47. package/dist/http/route-ctx.d.ts +35 -9
  48. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  49. package/dist/http/routes/approvals-assistant.js +16 -6
  50. package/dist/http/routes/capabilities.js +2 -0
  51. package/dist/http/routes/runs.js +2 -2
  52. package/dist/http/routes/session-sync.js +6 -1
  53. package/dist/http/server.d.ts +4 -1
  54. package/dist/http/server.js +1 -1
  55. package/dist/http/verify-rounds.d.ts +1 -1
  56. package/dist/memory-scope.d.ts +6 -2
  57. package/dist/memory-scope.js +3 -2
  58. package/dist/observability/fail-open.d.ts +4 -0
  59. package/dist/observability/fail-open.js +4 -0
  60. package/dist/run-local.js +4 -5
  61. package/dist/session-sync.d.ts +4 -1
  62. package/dist/session-sync.js +2 -2
  63. package/dist/task-cwd.d.ts +9 -3
  64. package/dist/task-cwd.js +3 -1
  65. package/package.json +1 -1
package/USAGE.md CHANGED
@@ -441,7 +441,9 @@ MODEL_CASCADE_LADDER=deepseek-flash,deepseek-pro # 目录里的模型名,cheap
441
441
  —— 没有任何东西把 B 的路径映到 A。要么把服务(或 `run-local`)跑在放着文件的那台机器上,要么走容器车道
442
442
  (`e2b`/`k8s`/`local-docker`),那里的工作区是沙箱自己的,`cwd` 被忽略。
443
443
  - **读面(S-426 起)**:`GET /v1/capabilities` 的 `executionLane` 把本部署的车道广告成
444
- `{provider:<REMOTE_EXEC 闭集词>, toolsOnThisHost:boolean}`(`REMOTE_EXEC` 未设 ⇒ `provider:"host"`)。
444
+ `{provider:<执行车道八词>, toolsOnThisHost:boolean}`。S-602 起 `REMOTE_EXEC` 未设 ⇒
445
+ `provider:"in-process"`、`toolsOnThisHost:false`(本部署没有执行车道 = 无手;改前折报 `"host"`/`true`)。
446
+ 要本机有手,显式设 `REMOTE_EXEC=host`(sema-up 产物已显式写车道)。
445
447
  壳/客户端判「工具跑不跑在本机」**读这一位**,不要再从自己这一侧的 `REMOTE_EXEC` 猜 —— 连远端引擎时
446
448
  那个 env 说的是**壳自己这台**的事。⚠️ 它是**文件系统同一性**位,**不是**权限位:`cwd` 兑不兑现仍由
447
449
  `projectContext` / `callerCwd` 回答(那两位的判据多一条单用户约束,多租户 host 部署上两者会分叉)。
@@ -473,6 +475,7 @@ SEND_USER_FILE_SANDBOX_PUT_ENDPOINT=… # 可选:沙箱直传 PUT 的端
473
475
  - **词表优先级**:内网端点 `MINIO_ENDPOINT` 优先,`S3_ENDPOINT` 是外接 S3 兼容位(内网/公网同址场景可只给它,同时充当公网面);公网端点 `S3_PUBLIC_ENDPOINT` 优先,缺席回落 `S3_ENDPOINT`。空串一律按未设处理。
474
476
  - **🔴 云形(`DB_BACKEND=mysql|pg`)快照 blob 强制对象存储(1.295+,clay 拍)**:缺 MinIO 三键 ⇒ **boot 拒启**——单行 SQL blob 写会撞 TiDB `txn-entry-size-limit`(默认 6MiB,真库实测矮墙)/ mysql 协议 `max_allowed_packet`,大字节归对象存储(与 D-1 附件面同裁定)。单机/测试台显式逃生:`SNAPSHOT_BLOB_ALLOW_SQL_BYTES=true`(SQL 店此时带 per-blob 帽,tidb 默认 6MiB,超限 PUT 413 `blob_too_large_for_sql`;帽可用 `SNAPSHOT_BLOB_SQL_MAX_BYTES` 按部署真实限值覆写,两方言生效;pg 默认无帽)。
475
477
  - **per-file rewind 历史的边界上限**(与 workspace 浏览面同一个整树快照纪元退役批留下的保留条款):`FILE_HISTORY_RETENTION_KEEP` —— 每个 scope(会话)存活的**最新** boundary 数,**每次 boundary 提交后自剪**(最旧者先亡,刚提交者恒存活;失引用的版本行级联删,blob 字节走异步 grace 窗 GC;v1 基线永存)。未设=core 缺省 **100**(由 core 的 `resolveFileHistoryRetention` 兑现,server 不复制那个数);`unbounded` 是**唯一**的「不剪」拼法(整店自剪关闭,GC 归部署自排 `reap`);`0`/负数/小数/非数 ⇒ **boot 拒启**(码 `config.retention_policy_invalid`,与 core 内置后端同码同判——**省略这个键不等于不剪**,它选的是缺省 100)。三条车道(TiDB / PG / local 文件店)同旋钮同判。⚠️ 升级即生效:存量超 100 boundary 的 scope 在下一次提交时被剪到 100;要保全量先写 `unbounded`。被剪 entry 的键**已花掉**(再次 snapshot 同 entry 典型拒,不复铸)。排序=DB 分配的 per-scope 发布序号 `file_history_boundary.publish_seq`(不是墙钟):**自 7.50.x 有存量 `file_history_boundary` 的部署升级本版会在 boot 期被 `assertFileHistorySchema` 拒启并指明 `DROP TABLE file_history_boundary`**(本仓无 ALTER;丢的是 per-file rewind 便利态,会话与其他持久面不动)。
478
+ - **文件历史捕获总开关**(S-589):`FILE_HISTORY_CAPTURE`(缺省 `true`;布尔词表 true/false/1/0/yes/no/on/off,未知词 ⇒ boot 拒启;TiDB / PG / local 三条车道同义)。`false` ⇒ 不把历史店交给引擎:改动文件的首次快照**一个都不捕获**;交换是同时没有 rewind(`resumeAt`+`restoreFiles` / `rewindFilesTo` 的 run 以 `rewind.store_unconfigured` 失败,文件不动)与 2c session-sync(整族 `501 capability.snapshot_store_required`)。`GET /v1/capabilities` 的 `fileHistoryCapture` 报 `"off"`,`rewindFiles` / `restoreFiles` / `rewindFilesTo` / `sessionSync` 同源翻 `false`(缺省部署报 `"on-always"` = 捕获中且无法按会话关闭)。⚠️ **关掉 ≠ 删掉**:翻旋钮不批量删除存量历史,存量也不再被读用;它照旧随 `DELETE /v1/sessions/:id` 与留存车道清理(擦除面不受本旋钮管;留存车道只在 SQL 后端,local 车道上删会话走 `reap` —— 边界删光、v1 基线按 core 契约留存,既有残余)—— 要清存量走会话删除或留存旋钮。关着时 `FILE_HISTORY_RETENTION_KEEP` 没有效果(不再有边界提交,自剪不会触发),值仍照常校验。翻旋钮后每个进程首个带手的 run 会有一条引擎的 `runner_config_advisory` warn(「file history is not live for this run」),是预期。
476
479
  - **🔴 公桶策略必须 GetObject-only**:`mc anonymous set download` 会**连带打开 ListBucket**——匿名 `GET /<bucket>/?list-type=2` 能枚举全部不可猜 key,能力链接设计即告失效。正确姿势=`mc anonymous set-json`,policy 只含 `Action:["s3:GetObject"]` on `arn:aws:s3:::<bucket>/*`(验证:对象 GET 200、桶 LIST 403)。
477
480
  - **两条轨**:ttl=0(默认)→ 匿名 GET 公桶下 `uuidv7/<name>` 不可猜 key,链接永久、可回收(删对象);0<ttl≤7 天 → 私桶 + SigV4 限时签名链接(7 天是 SigV4 物理上限,更久用 ttl=0)。
478
481
  - **执行 lane 与租户门**:e2b/k8s 沙箱 lane 任意租户可用(沙箱文件系统=租户边界,沙箱内 `curl -T` 直传、字节不中转、凭据不进沙箱);host/ssh lane 仅单用户部署(`REQUIRE_PRINCIPAL` 未开)时挂载。
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { type ToolPolicy } from "@sema-agent/core";
7
7
  import { type PostureSource } from "./posture-source.js";
8
+ import { type ExecutionLane } from "./execution-lane-caps.js";
8
9
  /**
9
10
  * 运维**是否表达了门意图** —— boot 的单用户 allow-all 基线只在"零门意图"时才允许铺开,
10
11
  * 所以这个谓词漏一格 = 那一格的意图被 allow-all 静默吞掉。
@@ -39,6 +40,33 @@ export declare function assertGateIntentServiceable(config: {
39
40
  durableApproval: boolean;
40
41
  durableApprovalSource: PostureSource;
41
42
  }, checkpointStorePresent: boolean): void;
43
+ /**
44
+ * S-602 C3 —— 「**无应答面**」两条 boot warn 的判据与文案(纯函数;stage-07 只负责打日志)。
45
+ *
46
+ * 两条 warn 警的是同一类失败:一个 `ask` 发生了,而这台部署既没有活体协调器(`TOOL_APPROVAL_ENABLED`)也没有
47
+ * durable 停车(`DURABLE_APPROVAL`)⇒ core `resolveAsk` 无 onAsk ⇒ headless 自动拒(fail-closed,写进转录)。
48
+ * 运维要在 **boot** 就知道,否则会把随之而来的拒绝读成故障。两条 warn 按「ask 从哪来」**划分车道**,互补不重叠:
49
+ * · `fs_write_gate_no_responder` —— 写门的 Write/Edit ask。它只在**手写本机盘**时才会发生(写门挂在 host
50
+ * 语义上,而 ask 要有手去触发)⇒ 判据 = {@link handsOnWorkerFilePlane}(今天 = `host`)。改前写的是
51
+ * `remoteExec === undefined || provider === "host"`(门 D R3 同义折):把没有手的 in-process 也算进来,
52
+ * 那一形上一次 Write ask 都不会发生,warn 是假话;
53
+ * · `workflow_gate_no_responder` —— `run_workflow` 的 ask,**每条车道都挂**(不以挂手为前提,见
54
+ * `createWorkflowAskPolicy`)⇒ 判据 = 自编排开着 ∧ **不在上一条的射程里**(上一条的文案已覆盖本机手的车道)。
55
+ * 改前写的是 `remoteExec !== undefined && provider !== "host"` —— 依赖上一条对 in-process 的兜底;
56
+ * 上一条收窄之后,in-process ∧ 自编排 ∧ 无应答面必须由这一条接住(否则整形零告警)。
57
+ */
58
+ export declare function buildNoResponderBootWarns(input: {
59
+ /** 本部署的执行车道(归一读口 `executionLaneOf` 的答案)。 */
60
+ lane: ExecutionLane;
61
+ selfOrchestrationEnabled: boolean;
62
+ /** 活体协调器 ∨ durable 停车 —— 任何一面在场,ask 都有人答。 */
63
+ approvalFaceWired: boolean;
64
+ }): Array<{
65
+ event: "fs_write_gate_no_responder" | "workflow_gate_no_responder";
66
+ fields: {
67
+ hint: string;
68
+ };
69
+ }>;
42
70
  /**
43
71
  * [ref] 件⑩ —— **center 的 `forceDurableGate` 在无 checkpoint 店的部署上会静默 inert**,补一条 boot 披露。
44
72
  *
package/dist/approval.js CHANGED
@@ -2,6 +2,7 @@ import { isNamespacedCoveringRuleName, namespacedRuleNameCovers } from "@sema-ag
2
2
  import { operatorAsk } from "./operator-ask.js";
3
3
  import { ASK_USER_QUESTION_TOOL_NAME } from "./approval-content-kind.js";
4
4
  import { isExplicitPosture } from "./posture-source.js";
5
+ import { handsOnWorkerFilePlane } from "./execution-lane-caps.js";
5
6
  export function hasOperatorGateIntent(config) {
6
7
  return (config.approvalRequire.length > 0 ||
7
8
  config.approvalDeny.length > 0 ||
@@ -16,6 +17,29 @@ export function assertGateIntentServiceable(config, checkpointStorePresent) {
16
17
  }
17
18
  throw new Error("DURABLE_APPROVAL=true but no checkpoint store is available — the configured DB backend has no checkpoint face (or no backend is configured). Wire a MySQL-protocol/PostgreSQL/file backend, or unset DURABLE_APPROVAL.");
18
19
  }
20
+ export function buildNoResponderBootWarns(input) {
21
+ if (input.approvalFaceWired)
22
+ return [];
23
+ const handsOnThisDisk = handsOnWorkerFilePlane(input.lane);
24
+ const out = [];
25
+ if (handsOnThisDisk) {
26
+ out.push({
27
+ event: "fs_write_gate_no_responder",
28
+ fields: {
29
+ hint: "host-semantics lane with no approval face: permissionMode default/auto Write/Edit asks will auto-deny (fail-closed); wire TOOL_APPROVAL_ENABLED=true or DURABLE_APPROVAL=true to answer them",
30
+ },
31
+ });
32
+ }
33
+ if (input.selfOrchestrationEnabled && !handsOnThisDisk) {
34
+ out.push({
35
+ event: "workflow_gate_no_responder",
36
+ fields: {
37
+ hint: "self-orchestration with no approval face: permissionMode default/auto/acceptEdits run_workflow asks will auto-deny (fail-closed); wire TOOL_APPROVAL_ENABLED=true or DURABLE_APPROVAL=true to answer them",
38
+ },
39
+ });
40
+ }
41
+ return out;
42
+ }
19
43
  export function buildForceDurableGateInertNotice(input) {
20
44
  if (!input.entitlementSourceWired || input.checkpointStorePresent)
21
45
  return undefined;
@@ -43,5 +43,6 @@ export declare function createBudgetAndTracing(ctx: BudgetTracingCtx): {
43
43
  toolResultStore: import("../plugins/store-backend.js").ToolResultStoreFull | undefined;
44
44
  sessionPolicyStore: import("@sema-agent/core").SessionPolicyStore | undefined;
45
45
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
46
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
46
47
  };
47
48
  //# sourceMappingURL=budget-tracing.d.ts.map
@@ -4,6 +4,7 @@ import { FleetUsageAccumulator } from "../fleet-client.js";
4
4
  import { createFleetLeaseFromEnv } from "../fleet-lease.js";
5
5
  import { CostQuota } from "../observability/cost-quota.js";
6
6
  import { buildQuotaBounds } from "./limit-sync.js";
7
+ import { assembleFileHistory } from "../file-history-capture.js";
7
8
  export function createBudgetAndTracing(ctx) {
8
9
  const { config, logger, metrics, backend, breakerState } = ctx;
9
10
  const brain = createBrain(config, { breakerState });
@@ -29,10 +30,10 @@ export function createBudgetAndTracing(ctx) {
29
30
  const sideQueryAccounting = createSideQueryAccountant(metrics, costQuota, fleetUsage, fleetLease, (m) => config.modelQuotaWeights[m] ?? 1);
30
31
  const toolResultStore = backend?.toolResult ? backend.toolResult() : undefined;
31
32
  const sessionPolicyStore = backend ? backend.sessionPolicy() : undefined;
32
- const fileHistoryStore = backend ? backend.fileHistory() : undefined;
33
+ const { store: fileHistoryStore, erasure: fileHistoryErasure } = assembleFileHistory(backend, config.fileHistoryCapture);
33
34
  return {
34
35
  brain, pricing, counterDegradeHook, costQuota, modelUsageTracker, promptManifestTracker,
35
- fleetUsage, fleetLease, tracer, sideQueryAccounting, toolResultStore, sessionPolicyStore, fileHistoryStore,
36
+ fleetUsage, fleetLease, tracer, sideQueryAccounting, toolResultStore, sessionPolicyStore, fileHistoryStore, fileHistoryErasure,
36
37
  };
37
38
  }
38
39
  //# sourceMappingURL=budget-tracing.js.map
@@ -388,11 +388,8 @@ export async function createConfigCenterRuntime(ctx) {
388
388
  try {
389
389
  const fileRx = await loadRemoteExec(localRoot);
390
390
  if (fileRx) {
391
- const preemptedLane = config.remoteExec?.provider ?? "in-process";
392
- const isolatedLane = (lane) => {
393
- const word = executionLaneOf(lane === "in-process" ? undefined : lane);
394
- return word !== undefined && executionLaneCaps(word).isolation === "supported";
395
- };
391
+ const preemptedLane = executionLaneOf(config.remoteExec?.provider);
392
+ const isolatedLane = (lane) => executionLaneCaps(lane).isolation === "supported";
396
393
  const disclosePreemption = (winner) => {
397
394
  if (winner === preemptedLane)
398
395
  return;
@@ -7,6 +7,7 @@ import { createSandboxFileSend, TaskEnvRegistry } from "../capabilities/sandbox-
7
7
  import { createSendUserFileIssuer } from "../plugins/send-user-file.js";
8
8
  import { withLedgerRecording } from "../plugins/send-file-ledger.js";
9
9
  import { basename, resolve } from "node:path";
10
+ import { toolsRunOnThisHost } from "../task-cwd.js";
10
11
  import { stat as fsStat, readFile as fsReadFile } from "node:fs/promises";
11
12
  export function createLiveCoordinators(ctx) {
12
13
  const { config, logger, backend, sendUserFileTaskEnvs, ruleConsent, ruleScopeRootFor, getParkedRedeem } = ctx;
@@ -75,8 +76,7 @@ export function createLiveCoordinators(ctx) {
75
76
  return undefined;
76
77
  if (sendUserFileTaskEnvs)
77
78
  return new SendUserFileEmitter();
78
- const provider = config.remoteExec?.provider ?? "host";
79
- if (provider === "host" && config.requirePrincipal !== true)
79
+ if (toolsRunOnThisHost(config) && config.requirePrincipal !== true)
80
80
  return new SendUserFileEmitter();
81
81
  return undefined;
82
82
  })();
@@ -46,8 +46,11 @@
46
46
  * 该面由 test/task-settings.test.ts 的「真身胜过名字」特征化钉机器可见。
47
47
  */
48
48
  import { FileError, StubExecutionEnv, type ExecutionEnv, type ExecutionEnvFactory, type FileInfo, type Result } from "@sema-agent/core";
49
- /** 哪些 lane 用本模块的代理裁决写门 —— `REMOTE_EXEC` 未设(进程内 host)与显式 `host` 之外的全部。
50
- * 与 resolve-spec 的 `hostSemanticsLane` 是同一判别式的两面,取值处**只此一个**(两处各写一份正是漂移的成因)。 */
49
+ /** 哪些 lane 用本模块的代理裁决写门 —— 文件平面**不在** worker 本机的全部(= 能力表 `hostFilePlane` 答否)。
50
+ * 与 resolve-spec 的 `hostSemanticsLane` 是同一判别式的两面,取值处**只此一个**(两处各写一份正是漂移的成因)。
51
+ * 🔴 S-602:改前写的是 `provider !== undefined && provider !== "host"` —— 把缺席并进 host 的同义折(门 D R3),
52
+ * 今天改读表那一位(`in-process` / `host` 答 YES,其余答 NO;读数逐格不变)。认不出的词 ⇒ 走代理裁决:
53
+ * 代理 slot 空 ⇒ 写门 fail-closed(本模块头注裁定 2),与改前同向。 */
51
54
  export declare function isSandboxPathAdjudicationLane(provider: string | undefined): boolean;
52
55
  /** slot 取值的三态。`ok:false` 的两支都是 fail-closed,分开只为让留痕说得出**哪一种**缺席。 */
53
56
  export type SandboxPathEnvLookup = {
@@ -1,7 +1,9 @@
1
1
  import { FileError, StubExecutionEnv, err, ok } from "@sema-agent/core";
2
2
  import { normalizeAbsolutePathLexically } from "../task-cwd.js";
3
+ import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
3
4
  export function isSandboxPathAdjudicationLane(provider) {
4
- return provider !== undefined && provider !== "host";
5
+ const lane = executionLaneOf(provider);
6
+ return lane === undefined || executionLaneCaps(lane).hostFilePlane !== "supported";
5
7
  }
6
8
  const MAX_BOUND_SESSIONS = 4096;
7
9
  export class SandboxPathEnvSlots {
@@ -54,6 +54,7 @@
54
54
  * `TaskSpec.memoryPersistenceCapable` 一条路进去(`boot/resolve-spec.ts` 直通),我方显式**不铸**受限会话、
55
55
  * 也**不投**这个读面(硬造一个消费点 = 造给自己看的 wire 键)。哪天真要在诊断面露它,先有消费方需求。
56
56
  */
57
+ import { type ExecutionLane, type RemoteExecProvider } from "../execution-lane-caps.js";
57
58
  /**
58
59
  * [ref] A3 源头修(黑板 [ref]/[ref]/[ref],[ref])—— `memoryPersistenceCapable` 的**装配层有效值**:
59
60
  * operator 显式 env 恒赢;env 缺席时,`REMOTE_EXEC=host` 腿自动补 `true`;其余一律缺席(现行为)。
@@ -93,8 +94,8 @@ export interface MemoryWriteBoundaryFacts {
93
94
  * core 7.16.0([ref])之后写家还要再深一层(`<平面>/<label>`),但 label 归引擎注册簿,boot 期说不出口;
94
95
  * 平面是**包含关系上等价**的那一只(家恒为平面的直接子目录),所以判据停在平面,不猜 label。 */
95
96
  memoryRoot: string | undefined;
96
- /** 执行车道名,只进文案(`config.remoteExec?.provider ?? "in-process"`)。 */
97
- lane: string;
97
+ /** 执行车道名,只进文案(归一读口 `executionLaneOf` 的答案;S-602 起不在调用点手写 `?? "in-process"`)。 */
98
+ lane: ExecutionLane;
98
99
  /** 该车道的文件工具是否跑在**本机** fs 上(in-process / `REMOTE_EXEC=host`)。false = 沙箱盘,与记忆根分平面。
99
100
  * ⚠️ 这一位管的是**文件平面**,与下面的 `coreRemoteExecutionEnv` 是**两根轴**,别合并(codex round2
100
101
  * [medium] 就是把它们当成一根轴的后果):`host` 车道的文件平面就是本机(这一位 true),但 core 眼里它
@@ -116,6 +117,20 @@ export interface MemoryWriteBoundaryFacts {
116
117
  /** boot 期算得出的文件工具围栏根。空数组 = 算不出(每任务临时目录 / 未配 workspace)⇒ 不判。 */
117
118
  containmentRoots: readonly string[];
118
119
  }
120
+ /**
121
+ * S-602 —— 审计的**车道那四位**由本函数从能力表派生(boot/stores.ts 的装配点只调它,不再就地手写):
122
+ * 改前 stores 里是 `lane === undefined || lane === "host"`(同义折,门 D R3)+ `config.remoteExec !== undefined`
123
+ * (座位上的裸缺席判,R4)+ `lane ?? "in-process"` ×3(R2)。两根轴各读表里各自那一位,不合并:
124
+ * · `hostSemanticsLane` ⇐ `hostFilePlane`(文件平面在不在本机);
125
+ * · `coreRemoteExecutionEnv` ⇐ 同名位(core 拿没拿到执行环境)。
126
+ * 读数逐车道不变(对账:`test/execution-lane-caps.test.ts` 的真装配点联合覆盖格)。`processCwd` 由调用点传
127
+ * (stores 传 `process.cwd()`),本函数保持纯。
128
+ */
129
+ export declare function buildMemoryBoundaryLaneFacts(config: {
130
+ readonly remoteExec?: {
131
+ readonly provider: RemoteExecProvider;
132
+ } | undefined;
133
+ }, processCwd: string): Pick<MemoryWriteBoundaryFacts, "lane" | "hostSemanticsLane" | "coreRemoteExecutionEnv" | "containmentRoots">;
119
134
  /** 一条启动告警:`tag` 进日志事件名(可被运维按名过滤/告警),`detail` 是给人读的整句(含恢复路径)。 */
120
135
  export interface MemoryWriteBoundaryWarning {
121
136
  tag: "memory_root_outside_write_boundary" | "memory_remote_lane_write_instruction_dropped";
@@ -1,9 +1,20 @@
1
1
  import { resolve } from "node:path";
2
+ import { executionLaneCaps, executionLaneOf } from "../execution-lane-caps.js";
2
3
  export function effectiveMemoryPersistenceCapable(config) {
3
4
  if (config.memoryPersistenceCapable !== undefined)
4
5
  return config.memoryPersistenceCapable;
5
6
  return config.remoteExec?.provider === "host" ? true : undefined;
6
7
  }
8
+ export function buildMemoryBoundaryLaneFacts(config, processCwd) {
9
+ const lane = executionLaneOf(config.remoteExec?.provider);
10
+ const caps = executionLaneCaps(lane);
11
+ return {
12
+ lane,
13
+ hostSemanticsLane: caps.hostFilePlane === "supported",
14
+ coreRemoteExecutionEnv: caps.coreRemoteExecutionEnv === "supported",
15
+ containmentRoots: lane === "in-process" ? [processCwd] : [],
16
+ };
17
+ }
7
18
  function isInside(child, root) {
8
19
  const c = resolve(child);
9
20
  const r = resolve(root);
@@ -20,6 +20,7 @@ import type { Logger } from "../observability/logger.js";
20
20
  import type { Metrics } from "../observability/metrics.js";
21
21
  import type { WorkflowNotifyGate, WorkflowNotifyJournalStore } from "../orchestration/workflow-notify-journal.js";
22
22
  import type { TaskAttachmentStore } from "../plugins/task-attachment-store.js";
23
+ import type { FileHistoryErasureFace } from "../file-history-capture.js";
23
24
  import type { ApprovalNonceStore, CheckpointStoreFull, CostQuotaStore, ImageBake, RateLimiterStore, ServiceWorkflowJournalStore, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
24
25
  import type { ToolApprovalCoordinator } from "../tool-approval.js";
25
26
  /** [ref] / [ref]§五(sema-comms `audits/failopen-governance-176.md` §7 "Z 类 1 条"):default consecutive-failure
@@ -69,7 +70,8 @@ export interface ReapersCtx {
69
70
  rateLimiter: RateLimiter | RateLimiterStore | undefined;
70
71
  costQuota: CostQuota | CostQuotaStore | undefined;
71
72
  toolResultStore: ToolResultStoreFull | undefined;
72
- fileHistoryStore: ReturnType<StoreBackend["fileHistory"]> | undefined;
73
+ /** S-589:文件历史的**擦除脸**(后端在即在,与 `FILE_HISTORY_CAPTURE` 无关)—— 本段只用它的孤儿 blob GC。 */
74
+ fileHistoryErasure: FileHistoryErasureFace | undefined;
73
75
  taskAttachmentStore: TaskAttachmentStore | undefined;
74
76
  imageBakes: ImageBake | undefined;
75
77
  worktreeReap: (() => Promise<void>) | undefined;
@@ -56,7 +56,7 @@ export function createPlacementAwareReapSessions(deps) {
56
56
  };
57
57
  }
58
58
  export function startReapers(ctx) {
59
- const { config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore, rateLimiter, costQuota, toolResultStore, fileHistoryStore, taskAttachmentStore, imageBakes, worktreeReap, workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore, backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore, getRunDenySweep, } = ctx;
59
+ const { config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore, rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap, workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore, backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore, getRunDenySweep, } = ctx;
60
60
  const leg = (p) => {
61
61
  if (p !== undefined)
62
62
  trackMaintenanceLeg(p);
@@ -227,7 +227,7 @@ export function startReapers(ctx) {
227
227
  leg(toolResultStore
228
228
  ?.reapOlderThan?.(Date.now() - config.toolResultTtlSec * 1000)
229
229
  ?.then(() => toolResultReapOlderThanGuard.onSuccess(), toolResultReapOlderThanGuard.onError));
230
- leg(fileHistoryStore
230
+ leg(fileHistoryErasure
231
231
  ?.sweepOrphanBlobs?.()
232
232
  .then(() => fileHistorySweepOrphanBlobsGuard.onSuccess(), fileHistorySweepOrphanBlobsGuard.onError));
233
233
  leg(backend?.legacySnapshotBlobSweep?.()
@@ -7,7 +7,7 @@ import { SessionEnvironmentSelection, selectEnvironmentTool } from "../capabilit
7
7
  import { sendUserFileTool } from "../capabilities/send-user-file-tool.js";
8
8
  import { gateScenarioRequest, mergeUserSkills, selectScenario } from "../capabilities/scenarios.js";
9
9
  import { stampHandsLane, toolPolicyForHands } from "../capabilities/hands-lane.js";
10
- import { shouldDiscloseNoPersistentMemory, withNoPersistentMemoryNotice } from "../capabilities/memory-notice.js";
10
+ import { buildNoticeLaneFacts, shouldDiscloseNoPersistentMemory, withNoPersistentMemoryNotice } from "../capabilities/memory-notice.js";
11
11
  import { applyLongtailDefer } from "../capabilities/tool-defer.js";
12
12
  import { assertGuardPatternsUsable, buildOnlySensitiveBaselineWarning, createApprovalBaselinePolicy, createDeploymentGovernanceInputs } from "../deployment-governance.js";
13
13
  import { acceptShellScratchpadDir, buildEnvFacts, egressForRemoteExec, ensureScratchpadDir, resumeFactsForLane, scratchpadCoveringTaskRoot } from "../env-facts.js";
@@ -36,7 +36,7 @@ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSet
36
36
  import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
37
37
  import { redactSecrets } from "../trace/redact.js";
38
38
  import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
39
- import { executionLaneHomeDir } from "../execution-lane-caps.js";
39
+ import { executionLaneCaps, executionLaneHomeDir, executionLaneOf } from "../execution-lane-caps.js";
40
40
  import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
41
41
  import { memoryFaceMounted } from "../memory-scope.js";
42
42
  export function createResolveSpec(ctx) {
@@ -88,7 +88,7 @@ export function createResolveSpec(ctx) {
88
88
  };
89
89
  const gateScenarioAndAppend = async (body, auth, opts) => {
90
90
  const requested = gateScenarioRequest(await principalCaps?.scenarioRuling(auth?.principal), body.scenario, config.defaultScenario, Object.keys(scenarios), logger);
91
- gateExecutionLane(await principalCaps?.executionRuling(auth?.principal), config.remoteExec?.provider ?? "in-process", logger);
91
+ gateExecutionLane(await principalCaps?.executionRuling(auth?.principal), executionLaneOf(config.remoteExec?.provider), logger);
92
92
  const scenarioName = scenarios[requested] ? requested : "default";
93
93
  const cap = selectScenario(scenarios, scenarioName)(body, auth?.principal, auth?.sessionId !== undefined ? { sessionId: auth.sessionId } : {});
94
94
  const centerDecls = getCenterPrompts()?.declarations;
@@ -144,7 +144,7 @@ export function createResolveSpec(ctx) {
144
144
  const bindSettingsCwdEnvAndModel = (body, auth, gated, effMode, opts) => {
145
145
  const { objective, parsedSettings } = gated;
146
146
  const cwdWriteGate = cwdHonored(config) || deviceCwdHonored(config);
147
- const execLane = config.remoteExec?.provider ?? "in-process";
147
+ const execLane = executionLaneOf(config.remoteExec?.provider);
148
148
  if (auth?.sessionId)
149
149
  fenceSessionOwner(auth.sessionId, auth.principal ?? null);
150
150
  if (typeof body.cwd === "string" && body.cwd.length > 0 && auth?.sessionId) {
@@ -558,7 +558,7 @@ export function createResolveSpec(ctx) {
558
558
  handsReadOnly: governed.handsReadOnly === true,
559
559
  excludeAllTools: governed.excludeAllTools === true,
560
560
  excludeTools: governed.excludeTools,
561
- remoteExecutionEnv: config.remoteExec !== undefined,
561
+ ...buildNoticeLaneFacts(executionLaneOf(config.remoteExec?.provider)),
562
562
  })) {
563
563
  governed = { ...governed, promptProvider: withNoPersistentMemoryNotice(governed.promptProvider) };
564
564
  }
@@ -619,7 +619,7 @@ export function createResolveSpec(ctx) {
619
619
  const isolatedExecEnv = isIsolatedExecEnv(config.remoteExec?.provider, config.remoteExec?.provider === "k8s" ? config.remoteExec.runtimeClass : undefined);
620
620
  const decision = routeServiceTask({
621
621
  isolatedExecEnv,
622
- hasExecutionEnv: config.remoteExec != null,
622
+ hasExecutionEnv: executionLaneCaps(executionLaneOf(config.remoteExec?.provider)).coreRemoteExecutionEnv === "supported",
623
623
  ...(config.autonomy ? { autonomy: config.autonomy } : {}),
624
624
  explicitTeam: body.council === true || body.debate === true || scenarioName === "discuss",
625
625
  });
@@ -191,7 +191,8 @@ export interface RunnerDepsCtx {
191
191
  sessionPolicyStore: ReturnType<StoreBackend["sessionPolicy"]> | undefined;
192
192
  runtimeCapsResolver: RunnerDeps["runtimeCapsResolver"];
193
193
  /** [ref] per-edited-file rewind history (core `RunnerDeps.fileHistoryStore`; replaces the retired E19
194
- * whole-tree snapshot seat — core 94632197 renamed the seat, not a coexistence). WIRING IS THE OPT-IN. */
194
+ * whole-tree snapshot seat — core 94632197 renamed the seat, not a coexistence). WIRING IS THE OPT-IN.
195
+ * S-589:值 = `assembleFileHistory` 的捕获脸(后端 ∧ `FILE_HISTORY_CAPTURE`)。 */
195
196
  fileHistoryStore: ReturnType<StoreBackend["fileHistory"]> | undefined;
196
197
  /** [ref] 车二:持久化权限规则店 provider(core `RunnerDeps.permissionRuleStore`)。缺席 ⇒ 引擎的
197
198
  * `permissionRules.storeWired` 如实报 false、`AskRequest.ruleOffers` 不铸(诚实缺席)。 */
@@ -5,6 +5,7 @@ import type { WorkflowCompletionInbox } from "../orchestration/workflow-completi
5
5
  import { PlanCacheProbe } from "../plan-cache-probe.js";
6
6
  import type { CheckpointStoreFull, StoreBackend, ToolResultStoreFull } from "../plugins/store-backend.js";
7
7
  import type { TaskAttachmentStore } from "../plugins/task-attachment-store.js";
8
+ import type { FileHistoryErasureFace } from "../file-history-capture.js";
8
9
  import type { OwnerAwareSessionStore } from "../security.js";
9
10
  import type { DeviceStore } from "../device-store.js";
10
11
  import { SessionWatchRegistry } from "../session-watch.js";
@@ -25,7 +26,9 @@ export interface SessionFacesCtx {
25
26
  approvalExemptionStore: ReturnType<StoreBackend["approvalExemption"]> | undefined;
26
27
  sessionPolicyStore: ReturnType<StoreBackend["sessionPolicy"]> | undefined;
27
28
  taskAttachmentStore: TaskAttachmentStore | undefined;
28
- fileHistoryStore: ReturnType<StoreBackend["fileHistory"]> | undefined;
29
+ /** S-589:文件历史的**擦除脸**(后端在即在,与 `FILE_HISTORY_CAPTURE` 无关;只有删除类动词)。E21 删除读它,
30
+ * 不读捕获脸 —— 关掉捕获的部署照样要删得掉已捕获的文件字节(旋钮只管写入,从不管擦除)。 */
31
+ fileHistoryErasure: FileHistoryErasureFace | undefined;
29
32
  workflowCompletionInbox: WorkflowCompletionInbox | undefined;
30
33
  /** [ref]:会话级任务清单车道(E21 级联的一条腿)。缺席 = 装配未接本车道 ⇒ 无清单可删。 */
31
34
  taskListLane: TaskListLane | undefined;
@@ -6,7 +6,7 @@ import { setLeafAdvanceListener } from "../session-leaf-bus.js";
6
6
  import { SessionWatchRegistry, posIntEnv } from "../session-watch.js";
7
7
  import { defaultTaskRegistry, peerSessionBoxHandle } from "@sema-agent/core";
8
8
  export function createSessionFaces(ctx) {
9
- const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryStore, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, } = ctx;
9
+ const { config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore, resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure, workflowCompletionInbox, taskListLane, deviceStore, dropSessionScoped, mailboxStore, mailboxRecipientLifecycle, } = ctx;
10
10
  const mysqlPool = backend?.mysqlPool();
11
11
  const auditPgPool = backend?.pgPool();
12
12
  const auditLocalRoot = backend?.kind === "local" ? (config.localDataRoot ?? localRoot) : undefined;
@@ -66,11 +66,11 @@ export function createSessionFaces(ctx) {
66
66
  if (deviceStore)
67
67
  await deviceStore.deleteByRootSession(sessionId);
68
68
  dropSessionScoped?.(sessionId);
69
- if (fileHistoryStore) {
70
- if (typeof fileHistoryStore.deleteBySession === "function")
71
- await fileHistoryStore.deleteBySession(sessionId);
69
+ if (fileHistoryErasure) {
70
+ if (typeof fileHistoryErasure.deleteBySession === "function")
71
+ await fileHistoryErasure.deleteBySession(sessionId);
72
72
  else {
73
- await fileHistoryStore.reap(sessionId, []);
73
+ await fileHistoryErasure.reap(sessionId, []);
74
74
  logger.warn("session_file_history_baselines_retained", {
75
75
  sessionId,
76
76
  note: "the local file-history store has no whole-scope purge verb; boundaries were reaped but v1 baselines (original file bytes) remain under the data root until the upstream purge seam lands — recorded as an open deletion-rights gap, not silent",
@@ -9,6 +9,7 @@ export declare function runStage04(ctx: Awaited<ReturnType<typeof runStage03>>):
9
9
  prevStreak: number;
10
10
  error?: string;
11
11
  }) => void;
12
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
12
13
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
13
14
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
14
15
  fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;
@@ -27,7 +27,7 @@ export async function runStage04(ctx) {
27
27
  if (adoptionBoot.scanned > 0 || adoptionBoot.error !== undefined) {
28
28
  logger.info("adoption_boot_scan", { ...adoptionBoot });
29
29
  }
30
- const { brain, pricing, counterDegradeHook, costQuota, modelUsageTracker, promptManifestTracker, fleetUsage, fleetLease, tracer, sideQueryAccounting, toolResultStore, sessionPolicyStore, fileHistoryStore, } = createBudgetAndTracing({ config, logger, metrics, backend, breakerState });
30
+ const { brain, pricing, counterDegradeHook, costQuota, modelUsageTracker, promptManifestTracker, fleetUsage, fleetLease, tracer, sideQueryAccounting, toolResultStore, sessionPolicyStore, fileHistoryStore, fileHistoryErasure, } = createBudgetAndTracing({ config, logger, metrics, backend, breakerState });
31
31
  const permissionRuleStores = config.permissionRulesEnabled && backend ? backend.permissionRule() : undefined;
32
32
  const permissionRuleProvider = permissionRuleStores
33
33
  ? createPermissionRuleStoreProvider({ durable: permissionRuleStores.durable })
@@ -44,6 +44,6 @@ export async function runStage04(ctx) {
44
44
  });
45
45
  }
46
46
  await auditDormantPermissionRules({ stores: permissionRuleStores, logger, explicit: config.permissionRulesEnabledExplicit });
47
- return { ...ctx, brain, costQuota, counterDegradeHook, fileHistoryStore, fleetLease, fleetUsage, memoryBundleFaces, memoryComplianceFaces, memoryOriginFaces, modelUsageTracker, permissionRuleProvider, permissionRuleStores, pricing, promptManifestTracker, ruleConsent, sessionMemoryStatusFace, sessionPolicyStore, sideQueryAccounting, toolResultStore, tracer };
47
+ return { ...ctx, brain, costQuota, counterDegradeHook, fileHistoryErasure, fileHistoryStore, fleetLease, fleetUsage, memoryBundleFaces, memoryComplianceFaces, memoryOriginFaces, modelUsageTracker, permissionRuleProvider, permissionRuleStores, pricing, promptManifestTracker, ruleConsent, sessionMemoryStatusFace, sessionPolicyStore, sideQueryAccounting, toolResultStore, tracer };
48
48
  }
49
49
  //# sourceMappingURL=stage-04-stores.js.map
@@ -22,6 +22,7 @@ export declare function runStage05(ctx: Awaited<ReturnType<typeof runStage04>>):
22
22
  prevStreak: number;
23
23
  error?: string;
24
24
  }) => void;
25
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
25
26
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
26
27
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
27
28
  fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;
@@ -83,6 +83,7 @@ export declare function runStage06(ctx: Awaited<ReturnType<typeof runStage05>>):
83
83
  prevStreak: number;
84
84
  error?: string;
85
85
  }) => void;
86
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
86
87
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
87
88
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
88
89
  fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;
@@ -98,6 +98,7 @@ export declare function runStage07(ctx: Awaited<ReturnType<typeof runStage06>>):
98
98
  prevStreak: number;
99
99
  error?: string;
100
100
  }) => void;
101
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
101
102
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
102
103
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
103
104
  fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;
@@ -6,9 +6,9 @@ import { parseCapEnv, parseNumOrFailNonNegative } from "../config.js";
6
6
  import { ensureChildSessionDurableWithPromotion } from "../plugins/session-store.js";
7
7
  import { webSearchConfigFromEnv, createWebSearchBackend, shouldProbeWebSearchOnBoot } from "../plugins/web-search.js";
8
8
  import { createAuthorizer } from "../security.js";
9
- import { assertGateIntentServiceable, buildForceDurableGateInertNotice, hasOperatorGateIntent } from "../approval.js";
10
- import { loadSkills } from "../capabilities/skills.js";
11
- import { toolsRunOnThisHost } from "../task-cwd.js";
9
+ import { assertGateIntentServiceable, buildForceDurableGateInertNotice, buildNoResponderBootWarns, hasOperatorGateIntent } from "../approval.js";
10
+ import { executionLaneOf } from "../execution-lane-caps.js";
11
+ import { loadDeploymentSkills } from "../capabilities/skills.js";
12
12
  import { createRepoClient } from "../capabilities/repo-tools.js";
13
13
  import { buildScenarios, builtinScenarioDetails } from "../capabilities/scenarios.js";
14
14
  import { RateLimiter } from "../observability/rate-limit.js";
@@ -73,16 +73,12 @@ export async function runStage07(ctx) {
73
73
  tasksUsable: runStore !== undefined,
74
74
  });
75
75
  }
76
- if ((config.remoteExec === undefined || config.remoteExec.provider === "host") && !toolApproval && !durableEnabled) {
77
- logger.warn("fs_write_gate_no_responder", {
78
- hint: "host-semantics lane with no approval face: permissionMode default/auto Write/Edit asks will auto-deny (fail-closed); wire TOOL_APPROVAL_ENABLED=true or DURABLE_APPROVAL=true to answer them",
79
- });
80
- }
81
- if (config.selfOrchestrationEnabled && config.remoteExec !== undefined && config.remoteExec.provider !== "host" && !toolApproval && !durableEnabled) {
82
- logger.warn("workflow_gate_no_responder", {
83
- hint: "self-orchestration with no approval face: permissionMode default/auto/acceptEdits run_workflow asks will auto-deny (fail-closed); wire TOOL_APPROVAL_ENABLED=true or DURABLE_APPROVAL=true to answer them",
84
- });
85
- }
76
+ for (const w of buildNoResponderBootWarns({
77
+ lane: executionLaneOf(config.remoteExec?.provider),
78
+ selfOrchestrationEnabled: config.selfOrchestrationEnabled,
79
+ approvalFaceWired: toolApproval !== undefined || durableEnabled,
80
+ }))
81
+ logger.warn(w.event, w.fields);
86
82
  if (singleUserAutoAcceptBaseline) {
87
83
  logger.info("tool_policy_single_user_default", {
88
84
  posture: "adjudicated auto-accept — ALL write/hand/MCP tool surfaces auto-allowed (core per-surface UNGATED enumeration suppressed for single-user turnkey)",
@@ -110,7 +106,7 @@ export async function runStage07(ctx) {
110
106
  ? backend.rateLimiter(config.rateLimitPerMin, counterDegradeHook).startRefresh()
111
107
  : new RateLimiter(config.rateLimitPerMin);
112
108
  bindRateLimiter(rateLimiter);
113
- let skills = loadSkills(config.skillsDir, { toolsShareThisFilesystem: toolsRunOnThisHost(config) });
109
+ let skills = loadDeploymentSkills(config);
114
110
  skills = await configCenter.applyCenterCapabilities(skills);
115
111
  const repoClient = config.gitApiBaseUrl ? createRepoClient(config.gitApiKind, config.gitApiBaseUrl, config.gitApiToken) : undefined;
116
112
  const webSearchCfg = webSearchConfigFromEnv();
@@ -109,10 +109,9 @@ export declare function runStage08(ctx: Awaited<ReturnType<typeof runStage07>>):
109
109
  kind: string;
110
110
  streak: number;
111
111
  prevStreak: number;
112
- error
113
- /** 租约 TTL = 2× sweep 间隔(§3)。抢租与每 domain 的续租共用同一个值 —— 两处手抄迟早只改一处。 */
114
- ?: string;
112
+ error?: string;
115
113
  }) => void;
114
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
116
115
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
117
116
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
118
117
  fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;
@@ -5,10 +5,10 @@ import { startRetentionLane, RETENTION_LEASE_TTL_FACTOR } from "./retention-lane
5
5
  import { createLimitSync } from "./limit-sync.js";
6
6
  import { runDenySweep } from "./late-binds.js";
7
7
  export function runStage08(ctx) {
8
- const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, costQuota, fileHistoryStore, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, runnerTierFrozen, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
8
+ const { approvalNonceStore, backend, backgroundAgentStore, ceilingVerdict, checkpointStore, config, configCenter, costQuota, fileHistoryErasure, handslessRunner, handslessSubRunner, hookAgentRunner, imageBakes, instanceId, localRoot, logger, mailboxStore, metrics, permissionRuleStores, pricing, rateLimiter, retentionExecutor, retentionLaneStore, rosterStore, runStore, runner, runnerTierFrozen, sqlWorkflowRunStore, subRunner, taskAttachmentStore, toolApproval, toolResultStore, workflowJournalStore, workflowNotifyGate, workflowNotifyJournal, workflowRecoverOpts, worktreeReap } = ctx;
9
9
  const reaper = startReapers({
10
10
  config, logger, metrics, localRoot, backend, subRunner, runStore, checkpointStore,
11
- rateLimiter, costQuota, toolResultStore, fileHistoryStore, taskAttachmentStore, imageBakes, worktreeReap,
11
+ rateLimiter, costQuota, toolResultStore, fileHistoryErasure, taskAttachmentStore, imageBakes, worktreeReap,
12
12
  workflowNotifyGate, workflowRecoverOpts, workflowJournalStore, sqlWorkflowRunStore, workflowNotifyJournal, rosterStore,
13
13
  backgroundAgentStore, mailboxStore, toolApproval, permissionRuleStores, approvalNonceStore,
14
14
  getRunDenySweep: () => runDenySweep,
@@ -145,6 +145,7 @@ export declare function runStage09(ctx: Awaited<ReturnType<typeof runStage08>>):
145
145
  prevStreak: number;
146
146
  error?: string;
147
147
  }) => void;
148
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
148
149
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
149
150
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
150
151
  fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;
@@ -6,12 +6,12 @@ import { createLeaderFace } from "./leader.js";
6
6
  import { assertStaticWiringConsistent } from "../http/routes/diagnostics.js";
7
7
  import { buildDisplaySafeUrl } from "../trace/redact.js";
8
8
  export function runStage09(ctx) {
9
- const { approvalExemptionStore, backend, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryStore, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, workflowCompletionInbox } = ctx;
9
+ const { approvalExemptionStore, backend, brain, checkpointStore, config, configCenter, deviceLane, dropSessionScoped, durableEnabled, executionEnvFactory, fileHistoryErasure, governanceSeams, localRoot, logger, mailboxRecipientLifecycle, mailboxStore, metrics, pricing, resumeAnchorStore, runStore, runnerDeps, sessionPolicyStore, sessionStore, streamApprovalGate, taskAttachmentStore, taskListLane, toolResultStore, tracer, usageWindowStore, workflowCompletionInbox } = ctx;
10
10
  const { ownerAware, sessionAudit, sessionWatchRegistry, purgeSession, instrumentDegenerate, planCacheProbe } = createSessionFaces({
11
11
  deviceStore: deviceLane?.store,
12
12
  dropSessionScoped,
13
13
  config, logger, metrics, localRoot, backend, sessionStore, runStore, checkpointStore, toolResultStore,
14
- resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryStore,
14
+ resumeAnchorStore, approvalExemptionStore, sessionPolicyStore, taskAttachmentStore, fileHistoryErasure,
15
15
  workflowCompletionInbox, taskListLane, mailboxStore, mailboxRecipientLifecycle,
16
16
  });
17
17
  const leaderEndpoint = createLeaderFace({ config, logger, brain, pricing, executionEnvFactory, toolResultStore, sessionStore, checkpointStore, governanceSeams, backend, tracer, usageWindowStore, promptSource: configCenter.promptSource, onError: runnerDeps.onError });
@@ -145,6 +145,7 @@ export declare function runStage10(ctx: Awaited<ReturnType<typeof runStage09>>):
145
145
  prevStreak: number;
146
146
  error?: string;
147
147
  }) => void;
148
+ fileHistoryErasure: import("../file-history-capture.js").FileHistoryErasureFace | undefined;
148
149
  fileHistoryStore: import("../plugins/store-backend.js").ServiceFileHistoryStore | undefined;
149
150
  fleetLease: import("../fleet-lease.js").FleetLeaseManager | undefined;
150
151
  fleetUsage: import("../fleet-client.js").FleetUsageAccumulator | undefined;