@sema-agent/server 7.95.1 → 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 (82) hide show
  1. package/USAGE.md +10 -5
  2. package/deploy/sema-up/README.md +8 -1
  3. package/deploy/sema-up/chart/templates/server-v4pro.yaml +3 -0
  4. package/deploy/sema-up/chart/templates/server.yaml +3 -0
  5. package/deploy/sema-up/lib/lanes.json +56 -0
  6. package/deploy/sema-up/lib/lanes.py +179 -0
  7. package/deploy/sema-up/lib/parse-machines.py +7 -1
  8. package/deploy/sema-up/machines.example.yaml +5 -2
  9. package/deploy/sema-up/preflight.sh +7 -4
  10. package/deploy/sema-up/sema-up.sh +201 -24
  11. package/deploy/sema-up/stack/compose.yaml +4 -0
  12. package/deploy/sema-up/stack/sema-stack-cluster.yaml +2 -0
  13. package/dist/approval.d.ts +28 -0
  14. package/dist/approval.js +24 -0
  15. package/dist/boot/budget-tracing.d.ts +1 -0
  16. package/dist/boot/budget-tracing.js +3 -2
  17. package/dist/boot/config-center.js +2 -5
  18. package/dist/boot/coordinators.js +2 -2
  19. package/dist/boot/deferred-sandbox-path-env.d.ts +5 -2
  20. package/dist/boot/deferred-sandbox-path-env.js +3 -1
  21. package/dist/boot/memory-boundary.d.ts +17 -2
  22. package/dist/boot/memory-boundary.js +11 -0
  23. package/dist/boot/reapers.d.ts +3 -1
  24. package/dist/boot/reapers.js +2 -2
  25. package/dist/boot/resolve-spec.js +6 -6
  26. package/dist/boot/runner-deps.d.ts +2 -1
  27. package/dist/boot/session-faces.d.ts +4 -1
  28. package/dist/boot/session-faces.js +5 -5
  29. package/dist/boot/stage-04-stores.d.ts +1 -0
  30. package/dist/boot/stage-04-stores.js +2 -2
  31. package/dist/boot/stage-05-execution-env.d.ts +1 -0
  32. package/dist/boot/stage-06-runners.d.ts +1 -0
  33. package/dist/boot/stage-07-capability-layer.d.ts +1 -0
  34. package/dist/boot/stage-07-capability-layer.js +10 -14
  35. package/dist/boot/stage-08-reapers.d.ts +2 -3
  36. package/dist/boot/stage-08-reapers.js +2 -2
  37. package/dist/boot/stage-09-leader.d.ts +1 -0
  38. package/dist/boot/stage-09-leader.js +2 -2
  39. package/dist/boot/stage-10-http-server.d.ts +1 -0
  40. package/dist/boot/stores.js +5 -11
  41. package/dist/capabilities/execution-lane.d.ts +5 -7
  42. package/dist/capabilities/execution-lane.js +2 -1
  43. package/dist/capabilities/memory-notice.d.ts +30 -12
  44. package/dist/capabilities/memory-notice.js +8 -1
  45. package/dist/capabilities/skills.d.ts +14 -0
  46. package/dist/capabilities/skills.js +4 -0
  47. package/dist/config-catalog.js +1 -0
  48. package/dist/config-types.d.ts +8 -1
  49. package/dist/config.d.ts +16 -0
  50. package/dist/config.js +13 -12
  51. package/dist/env-facts.js +2 -4
  52. package/dist/execution-lane-caps.d.ts +36 -6
  53. package/dist/execution-lane-caps.js +4 -0
  54. package/dist/file-history-capture.d.ts +65 -0
  55. package/dist/file-history-capture.js +21 -0
  56. package/dist/hosted-posture.d.ts +1 -1
  57. package/dist/http/resume-legs.d.ts +14 -4
  58. package/dist/http/resume-legs.js +26 -17
  59. package/dist/http/route-ctx.d.ts +35 -9
  60. package/dist/http/routes/approvals-assistant.d.ts +1 -1
  61. package/dist/http/routes/approvals-assistant.js +16 -6
  62. package/dist/http/routes/capabilities.js +4 -1
  63. package/dist/http/routes/runs.js +2 -2
  64. package/dist/http/routes/session-sync.js +6 -1
  65. package/dist/http/routes/tasks.js +2 -2
  66. package/dist/http/server.d.ts +4 -1
  67. package/dist/http/server.js +1 -1
  68. package/dist/http/verify-rounds.d.ts +1 -1
  69. package/dist/memory-scope.d.ts +6 -2
  70. package/dist/memory-scope.js +3 -2
  71. package/dist/observability/fail-open.d.ts +4 -0
  72. package/dist/observability/fail-open.js +4 -0
  73. package/dist/observability/run-terminal.js +1 -0
  74. package/dist/run-local.js +4 -5
  75. package/dist/session-sync.d.ts +4 -1
  76. package/dist/session-sync.js +2 -2
  77. package/dist/task-cwd.d.ts +9 -3
  78. package/dist/task-cwd.js +3 -1
  79. package/dist/trace/core-keyset-guard.d.ts +1 -1
  80. package/dist/trace/project.d.ts +11 -0
  81. package/dist/trace/project.js +1 -0
  82. package/package.json +4 -3
@@ -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;
@@ -32,7 +32,7 @@ import { assertSchemaMatchesCode } from "../plugins/schema-consistency.js";
32
32
  import { assertCheckpointColumnSchema } from "../plugins/checkpoint-store-sql.js";
33
33
  import { assertFileHistorySchema } from "../plugins/file-history-store-sql.js";
34
34
  import { resolveStreamApprovalGate } from "../tool-approval.js";
35
- import { buildMemoryWriteBoundaryAudit, effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
35
+ import { buildMemoryBoundaryLaneFacts, buildMemoryWriteBoundaryAudit, effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
36
36
  import { buildMemoryPosture } from "../memory-posture.js";
37
37
  import { establishSqlEnginePosture } from "../sql-engine-posture.js";
38
38
  import { buildDisplaySafeUrl } from "../trace/redact.js";
@@ -194,25 +194,19 @@ export async function openStores(ctx) {
194
194
  }
195
195
  }
196
196
  {
197
- const lane = config.remoteExec?.provider;
198
- const hostSemanticsLane = lane === undefined || lane === "host";
199
- const coreRemoteExecutionEnv = config.remoteExec !== undefined;
200
- const containmentRoots = lane === undefined ? [process.cwd()] : [];
197
+ const laneFacts = buildMemoryBoundaryLaneFacts(config, process.cwd());
201
198
  const memoryMountRoot = memoryEngine ? memoryMountRootOf(memoryEngine.backend, memoryEngine.root) : undefined;
202
199
  const audit = buildMemoryWriteBoundaryAudit({
203
200
  memoryRoot: memoryMountRoot,
204
- lane: lane ?? "in-process",
205
- hostSemanticsLane,
206
- coreRemoteExecutionEnv,
201
+ ...laneFacts,
207
202
  declaredCapable: effectiveMemoryPersistenceCapable(config),
208
- containmentRoots,
209
203
  engineBackend: config.memoryEngineBackend,
210
204
  });
211
205
  for (const w of audit.warnings)
212
- logger.warn(w.tag, { detail: w.detail, lane: lane ?? "in-process", root: memoryMountRoot ?? null });
206
+ logger.warn(w.tag, { detail: w.detail, lane: laneFacts.lane, root: memoryMountRoot ?? null });
213
207
  if (config.memoryPersistenceCapable === undefined && effectiveMemoryPersistenceCapable(config) === true) {
214
208
  logger.info?.("memory_persistence_capable_auto_declared", {
215
- lane: lane ?? "in-process",
209
+ lane: laneFacts.lane,
216
210
  detail: "REMOTE_EXEC=host: file tools run on this machine's filesystem, so the assembler declares memoryPersistenceCapable=true (the core-side remote-env withhold would otherwise silently drop the memory WRITE instruction on this lane). Override with MEMORY_PERSISTENCE_CAPABLE=true|false.",
217
211
  });
218
212
  }
@@ -1,18 +1,16 @@
1
- import type { RemoteExecProvider } from "../execution-lane-caps.js";
1
+ import { type ExecutionLane, type RemoteExecProvider } from "../execution-lane-caps.js";
2
2
  export interface ExecutionLaneCapability {
3
- /** 本部署的执行车道词(闭集 = {@link RemoteExecProvider};`REMOTE_EXEC` 未设 ⇒ `"host"`,见头注)。 */
4
- readonly provider: RemoteExecProvider;
3
+ /** 本部署的执行车道词(闭集 = {@link ExecutionLane} 八词;`REMOTE_EXEC` 未设 ⇒ `"in-process"`,见头注 S-602 段)。 */
4
+ readonly provider: ExecutionLane;
5
5
  /** 工具跑的那台机器就是本进程这台吗(= server 盘上的绝对路径对模型的工具有没有意义)。
6
6
  * 属主 = `task-cwd.ts` 的 {@link toolsRunOnThisHost},与 `skills[].baseDir` 的铸点同一只函数。 */
7
7
  readonly toolsOnThisHost: boolean;
8
8
  }
9
9
  /**
10
10
  * 部署席 → 消费端窄投影。形照 `projectWebSearchCapability` / `projectSqlEngineCapability`:
11
- * **只 import 型**,不碰 config.ts,于是本模块是叶。
11
+ * 不碰 config.ts(只从车道表模块取词与归一读口,那是叶),于是本模块是叶。
12
12
  *
13
- * 🔴 **唯一**写缺席折词的地方。`"host"` 这个字面量不入 `api-notice-text-freeze`:它是
14
- * {@link RemoteExecProvider} 的成员,配置联合里那条臂改名 ⇒ 本行当场 tsc 红(S-382 对三个 provider 词
15
- * 免入表的同一条理由 —— 会静默漂的只有**本仓自铸**的词,这里一个都没有)。
13
+ * 🔴 本处**不写**任何缺席折词(S-602 门 D):缺席归一只在 {@link executionLaneOf} 一处。
16
14
  */
17
15
  export declare function projectExecutionLaneCapability(config: {
18
16
  readonly remoteExec?: {
@@ -1,7 +1,8 @@
1
1
  import { toolsRunOnThisHost } from "../task-cwd.js";
2
+ import { executionLaneOf } from "../execution-lane-caps.js";
2
3
  export function projectExecutionLaneCapability(config) {
3
4
  return {
4
- provider: config.remoteExec?.provider ?? "host",
5
+ provider: executionLaneOf(config.remoteExec?.provider),
5
6
  toolsOnThisHost: toolsRunOnThisHost(config),
6
7
  };
7
8
  }
@@ -20,6 +20,7 @@
20
20
  */
21
21
  import { type PromptProvider, type ToolCatalogEntry } from "@sema-agent/core";
22
22
  import type { ScenarioHands } from "./hands-lane.js";
23
+ import { type ExecutionLane } from "../execution-lane-caps.js";
23
24
  /**
24
25
  * 「已知落盘路」的判据,作用在 core **静态工具目录**的一行上(S-503 / [ref])—— 与 core 自己的
25
26
  * `rosterCanPersist` 推断逐条同形,取的是同一批目录属性,不是它结论的抄件:
@@ -77,24 +78,41 @@ export interface MemoryFaceFacts {
77
78
  /** 折完之后的最终 `TaskSpec.excludeTools`(roster 真卸载,不是延迟披露)。**全部**已知落盘路都被排掉
78
79
  * ⇒ 手带在场也等于没有写通道 —— core 的 `rosterCanPersist` 也是按排除后的 roster 算的,这里跟上。 */
79
80
  excludeTools: readonly string[] | undefined;
80
- /** 本部署的执行环境在 core 眼里算不算 **remote**(`isRemoteExecutionEnv`)。判别位 = `REMOTE_EXEC` 有没有
81
- * 设 —— 注意 `host` 也算:core 的判别是**鸭子类型**(`workspaceHandle`/`execStream`/`suspendVM`/…),
82
- * 而我方的 `remote-env-host.ts` 这几件全实现,亲验坐实。
83
- * 为什么这一位必须进判别式(codex 复审 round2 [high],已核真):core 的 `rosterCanPersist` 在
84
- * `handsEnabled && isRemoteExecutionEnv` 时把 Write/Edit/NotebookEdit/Bash **全部不算**落盘路(沙箱盘
85
- * 每任务即焚,够不着 host 记忆根)。而 server 在任何 remote 车道上默认让记忆引擎 dark —— 两件事合起来
86
- * 正是**最常见的云部署形**:引擎不在、手带在,却一份持久面都没有。漏了这一位,那台机器就继续给
87
- * 「已经记住了」的幻觉式回执 —— 恰恰是 [ref] 立案要消灭的形态。 */
88
- remoteExecutionEnv: boolean;
81
+ /**
82
+ * 本部署的手带**挂没挂**(= core 拿没拿到执行环境;能力表 `coreRemoteExecutionEnv` 位,经
83
+ * {@link buildNoticeLaneFacts} 取)。`false` = `in-process`:core 落 `StubExecutionEnv`,手带整族不 mount
84
+ * ⇒ **一条落盘路都没有**,不管名册上写着什么名字。
85
+ * 🔴 S-602(clay 裁 [ref] A):改前这里只有一位 `remoteExecutionEnv`(判别位 = `REMOTE_EXEC` 设没设),
86
+ * 于是 in-process 读成「不是 remote ⇒ 手就在本机 fs 上 ⇒ 已知落盘路真的在」—— 而那条 lane 根本没有手,
87
+ * 用户说「记住 X」拿到的是无处落盘的幻觉回执([ref] 要消灭的原形)。一位拆成两位,各答各的问题。
88
+ */
89
+ handsWired: boolean;
90
+ /**
91
+ * 手带的文件平面**不在**本 worker(能力表 `hostFilePlane` 答否;经 {@link buildNoticeLaneFacts} 取)——
92
+ * 沙箱 / 远端 / 设备车道。为什么这一位必须进判别式(codex 复审 round2 [high],已核真):core 的
93
+ * `rosterCanPersist` 在沙箱盘上把 Write/Edit/NotebookEdit/Bash **全部不算**落盘路(每任务即焚,够不着
94
+ * host 记忆根)。而 server 在任何 remote 车道上默认让记忆引擎 dark —— 两件事合起来正是**最常见的云部署形**:
95
+ * 引擎不在、手带在,却一份持久面都没有。
96
+ * ⚠️ `host` 车道这一位是 `false`(文件平面就是本机);core 自己仍把 host 判 remote,那一面由装配层的
97
+ * `effectiveMemoryPersistenceCapable`(host 腿自动声明 capable)在臂② 接住,不归本位。
98
+ */
99
+ filePlaneRemote: boolean;
89
100
  }
101
+ /**
102
+ * S-602 —— 判别式的两位**车道事实**,由能力表派生([ref]:不手抄投影)。resolve-spec 的装配点只调它,
103
+ * 不在调用点手写 `config.remoteExec !== undefined`(门 D R4)。
104
+ */
105
+ export declare function buildNoticeLaneFacts(lane: ExecutionLane): Pick<MemoryFaceFacts, "handsWired" | "filePlaneRemote">;
90
106
  /**
91
107
  * 该不该把 `NO_PERSISTENT_MEMORY_NOTICE` 组进本次任务的 system prompt。
92
108
  *
93
109
  * 优先序(每一条都有独立理由,不是同一条判据的三种写法):
94
110
  * ① engine 在场 ⇒ **否**(引擎的座位,见文件头注);
95
- * ② 部署显式声明 ⇒ 声明说了算(`true` 撤回 / `false` 强制)—— operator 的部署事实压过我们的推断,
96
- * 与 core 对 `memoryPersistenceCapable` 的处置同向;
97
- * ③ 否则推断:有已知落盘路(手带在场、不是只读、不在 remote 车道、且没被卸载决定卸光)⇒ 否;没有 ⇒ 是。
111
+ * ② 部署显式声明(或 host 腿装配层自动声明)⇒ 声明说了算(`true` 撤回 / `false` 强制)—— operator 的部署
112
+ * 事实压过我们的推断,与 core 对 `memoryPersistenceCapable` 的处置同向;
113
+ * ③ 否则推断:有已知落盘路(手带挂上、场景不摘手、不是只读、文件平面在本机、且没被卸载决定卸光)⇒ 否;没有 ⇒ 是。
114
+ * 🔴 S-602 只改臂③ 里「车道」那一段(`handsWired` / `filePlaneRemote`),①② 的优先序一字不动 —— 「in-process ⇒
115
+ * 披露」只在这两臂**之后**成立(挂了记忆面、或声明了 capable 的 in-process 部署照旧不披露)。
98
116
  */
99
117
  export declare function shouldDiscloseNoPersistentMemory(facts: MemoryFaceFacts): boolean;
100
118
  /**
@@ -1,5 +1,6 @@
1
1
  import { describeToolCatalog, HANDS_BAND_TAGS, NO_PERSISTENT_MEMORY_NOTICE } from "@sema-agent/core";
2
2
  import { hasConstitutionAnchors } from "../task-settings.js";
3
+ import { executionLaneCaps } from "../execution-lane-caps.js";
3
4
  export function persistencePathToolNamesOf(entries) {
4
5
  const names = new Set();
5
6
  for (const e of entries) {
@@ -21,6 +22,10 @@ export function persistencePathToolNamesOf(entries) {
21
22
  }
22
23
  export const KNOWN_PERSISTENCE_TOOL_NAMES = persistencePathToolNamesOf(describeToolCatalog());
23
24
  export const NO_PERSISTENT_MEMORY_SECTION_ID = "sema/memory.no-persistent";
25
+ export function buildNoticeLaneFacts(lane) {
26
+ const caps = executionLaneCaps(lane);
27
+ return { handsWired: caps.coreRemoteExecutionEnv === "supported", filePlaneRemote: caps.hostFilePlane !== "supported" };
28
+ }
24
29
  function isUnmounted(facts, name) {
25
30
  return facts.excludeAllTools || (facts.excludeTools?.includes(name) ?? false);
26
31
  }
@@ -34,7 +39,9 @@ export function shouldDiscloseNoPersistentMemory(facts) {
34
39
  return !facts.declaredCapable;
35
40
  if (facts.hands === "none" || facts.handsReadOnly)
36
41
  return true;
37
- if (facts.remoteExecutionEnv)
42
+ if (!facts.handsWired)
43
+ return true;
44
+ if (facts.filePlaneRemote)
38
45
  return true;
39
46
  return !anyPersistenceToolSurvives(facts);
40
47
  }
@@ -1,4 +1,5 @@
1
1
  import type { SkillSpec } from "@sema-agent/core";
2
+ import type { RemoteExecProvider } from "../execution-lane-caps.js";
2
3
  /**
3
4
  * Built-in skills baked into the image, loaded ONCE at startup (no per-task disk IO). A skill is a
4
5
  * Markdown capability (frontmatter + body). core uses **progressive disclosure** (core ≥1.98.0):
@@ -57,6 +58,19 @@ export interface LoadSkillsOptions {
57
58
  * `label` = 错误文本里的对象名(center-plugins 用它约束 manifest 本体,报错不该指成 skill entry)。 */
58
59
  export declare function assertConfined(p: string, rootReal: string, label?: string): void;
59
60
  export declare function loadSkills(dir: string, opts?: LoadSkillsOptions): LoadedSkill[];
61
+ /**
62
+ * S-602 —— **出厂技能**的装载腿(`SKILLS_DIR` 自扫)。stage-07 与 run-local 两处装配点**都调这一只**:改前两处
63
+ * 各写一遍 `loadSkills(config.skillsDir, { toolsShareThisFilesystem: toolsRunOnThisHost(config) })`,测试只能
64
+ * 直传布尔去证 `loadSkills`(`baked-skills.test.ts`),证不到「config → 装配腿 → baseDir」这一段;收成一只
65
+ * 之后,联合格从 config 走到 `baseDir`(`test/capabilities-http.test.ts` S-602 段)证的就是两处装配点本人。
66
+ * 判据属主不变:`task-cwd.ts` 的 {@link toolsRunOnThisHost}(S-602 起 in-process ⇒ 假 ⇒ 目录形技能不带 `baseDir`)。
67
+ */
68
+ export declare function loadDeploymentSkills(config: {
69
+ readonly skillsDir: string;
70
+ readonly remoteExec?: {
71
+ readonly provider: RemoteExecProvider;
72
+ } | undefined;
73
+ }): LoadedSkill[];
60
74
  /** Skills applicable to a scenario: those tagged with it, plus untagged (global) ones.
61
75
  * [ref]:alias 窗撤销,单键查询——退役旧名标签不再命中(迁移面在 7.44 发车帖/MIGRATION 记载)。 */
62
76
  export declare function skillsForScenario(loaded: LoadedSkill[], scenario: string): SkillSpec[];
@@ -1,5 +1,6 @@
1
1
  import { readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
2
2
  import { join, resolve, sep } from "node:path";
3
+ import { toolsRunOnThisHost } from "../task-cwd.js";
3
4
  export function assertConfined(p, rootReal, label = "skill entry") {
4
5
  let real;
5
6
  try {
@@ -66,6 +67,9 @@ export function loadSkills(dir, opts) {
66
67
  }
67
68
  return out;
68
69
  }
70
+ export function loadDeploymentSkills(config) {
71
+ return loadSkills(config.skillsDir, { toolsShareThisFilesystem: toolsRunOnThisHost(config) });
72
+ }
69
73
  function collectAttachments(skillDir, rel, confineRoot) {
70
74
  const files = [];
71
75
  const here = rel === "" ? skillDir : join(skillDir, rel);
@@ -145,6 +145,7 @@ export const CONFIG_CATALOG = [
145
145
  r("ENGINE_LINGER_MS", "limitsHttp", "number", "父还活着时自退前三合取须持续成立的静默窗(ms;[1000,24h] 拒启带界)", { staticDefault: "60000", configKey: "engineLingerMs" }),
146
146
  r("ENGINE_ORPHAN_LINGER_MS", "limitsHttp", "number", "父进程已不在(孤儿)时的静默窗(ms;[1000,24h] 拒启带界;SEMA_PARENT_PID 缺席则不生效)", { staticDefault: "2000", configKey: "engineOrphanLingerMs" }),
147
147
  r("EXPERIMENTAL_OBSERVER_AGENTS", "orchestration", "boolean", "observer 开闸线(实验面 ship-dark;多租户只认 center caps,env 被忽略)", { staticDefault: "false", configKey: "experimentalObserverAgents" }),
148
+ r("FILE_HISTORY_CAPTURE", "store", "boolean", "文件历史**捕获**总开关(三条车道同义)。交换:关 = 不再捕获任何改动文件的首次快照,同时没有 rewind / restoreFiles / rewindFilesTo / 2c session-sync(对应能力位与 `fileHistoryCapture` 同源翻 off)。关掉 ≠ 删掉:已捕获的历史不被批量删除、也不再被读用,仍按会话删除与留存规则清理 —— 要清存量走 DELETE 会话或留存旋钮", { staticDefault: "true", danger: DATA, configKey: "fileHistoryCapture" }),
148
149
  r("FILE_HISTORY_RETENTION_KEEP", "store", "number", "per-file rewind 历史的保留上限(与 core FileStorageBackend 同域键形,三条车道一套语义)", { danger: DATA, derivedDefaultNote: "缺席 ⇒ core 缺省 100" }),
149
150
  r("FLEET_ADVERTISE_ADDRESS", "integrations", "url", "fleet 面向 center 公告的本 worker 可达地址(与 center 腿同在 ⇒ 启 fleet client;坏形拒启)"),
150
151
  r("FORK_ENABLED", "orchestration", "boolean", "CC /fork(core Fork 工具)装配开关", { derivedDefaultNote: "posture 三态:缺席 = 单用户 ⇒ on / 多租户 ⇒ off(per-principal entitlement 另判)", configKey: "forkEnabled" }),
@@ -956,6 +956,13 @@ export interface ServiceConfigFlat {
956
956
  fileHistory?: {
957
957
  retention?: FileHistoryRetentionPolicy;
958
958
  };
959
+ /** S-589:文件历史**捕获**总开关(`FILE_HISTORY_CAPTURE`,布尔词表,缺省 true,未知词拒启)。三条车道
960
+ * (local / mysql / pg)同一语义:`false` ⇒ 装配点不把历史店交给 core(`RunnerDeps.fileHistoryStore` 缺席 ⇒
961
+ * 首次触碰捕获结构上不发生),同时没有 rewind / restore / `rewindFilesTo` / 2c session-sync(那几位能力位与
962
+ * 能力词 `fileHistoryCapture:"off"` 同源翻关)。**只管写入,不管擦除**:已捕获的历史照旧随会话删除 / 留存
963
+ * 规则清理,翻旋钮本身不做批量删除。判定的唯一属主是 `file-history-capture.ts` 的 `assembleFileHistory`。
964
+ * 刻意**不**并进上面的 `fileHistory` 键:那一格是 core 店构造选项的同域键形,原样透传给 core。 */
965
+ fileHistoryCapture: boolean;
959
966
  /** plugins 域 clone host 白名单(`PLUGINS_ALLOW_HOSTS`,缺省 github.com;契约 [ref]① 双保险面,
960
967
  * 消费在 capabilities/center-plugins.ts)。 */
961
968
  pluginsAllowHosts: string[];
@@ -1905,7 +1912,7 @@ export interface ServiceConfigFlat {
1905
1912
  configLocalDir?: string;
1906
1913
  }
1907
1914
  /** 组:store(持久化)—— DB 引擎三态、session 后端、SQL coords、快照 BLOB / SendUserFile 对象存储。 */
1908
- export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "sessionCacheTtlSec" | "storeProbeIntervalMs" | "storeLockWaitMs" | "dbBackend" | "dbBackendExplicit" | "localDataRoot" | "runStoreStrictHydrate" | "tidb" | "pg" | "dbQueryTimeoutMs" | "snapshotBlobStore" | "snapshotBlobSqlMaxBytes" | "snapshotBlobAllowSql" | "fileHistory" | "sendUserFile">;
1915
+ export type ServiceStoreConfig = Pick<ServiceConfigFlat, "sessionBackend" | "sessionCacheTtlSec" | "storeProbeIntervalMs" | "storeLockWaitMs" | "dbBackend" | "dbBackendExplicit" | "localDataRoot" | "runStoreStrictHydrate" | "tidb" | "pg" | "dbQueryTimeoutMs" | "snapshotBlobStore" | "snapshotBlobSqlMaxBytes" | "snapshotBlobAllowSql" | "fileHistory" | "fileHistoryCapture" | "sendUserFile">;
1909
1916
  /** 组:modelPlane(模型面)—— 网关坐标、Anthropic 路线、韧性旋钮、主/廉价 model entry、role 表、降级梯。 */
1910
1917
  export type ServiceModelPlaneConfig = Pick<ServiceConfigFlat, "gatewayBaseUrl" | "gatewayApiKey" | "gatewayFallbackUrls" | "gatewayMaxRetries" | "anthropic" | "resilience" | "model" | "envModel" | "models" | "modelApiKeyEnv" | "modelApiKeys" | "modelQuotaWeights" | "tiers" | "projects" | "roles" | "atModelAllowlist" | "cascadeLadder" | "degrade">;
1911
1918
  /** 组:approval(审批 / HITL 门)。`directDoorActive` 无 env 解析腿(装配层三域合取的产物),但语义上
package/dist/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type Model, type PathRuleBases, type RuleBehavior } from "@sema-agent/core";
2
+ import { type RemoteExecProvider } from "./execution-lane-caps.js";
2
3
  import { type Autonomy } from "./runtime-governance.js";
3
4
  import type { ServiceConfig, ServiceConfigFlat, ServiceConfigGroups } from "./config-types.js";
4
5
  /** [ref] A1: the config TYPE face lives in the leaf module config-types.ts; re-exported here so
@@ -215,6 +216,21 @@ export declare const configLkgEnabled: () => boolean;
215
216
  export declare const hostBackgroundShellEnabled: () => boolean;
216
217
  /** The `host` exec lane's spool-file stdio form; false ⇒ the pre-1.226 pipe fallback (read per exec). */
217
218
  export declare const hostExecSpoolEnabled: () => boolean;
219
+ /**
220
+ * 每条执行车道**必需的 env 键名**([ref] 的 fail-closed 拒启门按它点名缺件;见 `parseOrchestrationDomain`
221
+ * 里那处 `REMOTE_EXEC` 校验)。
222
+ *
223
+ * 键集 = `RemoteExecProvider`(判别联合派生,`execution-lane-caps.ts`):配置面加一条 provider 臂而
224
+ * 这里不加行 = **编译红**(codex R1-F2)。此前是 `Record<string, …>`,漏一行只表现为该词在 boot 期被判
225
+ * 「不是执行车道」—— fail-closed 但把「新车道」讲成「拼错了」,运维照着报文永远查不到成因。
226
+ *
227
+ * 🔴 **S-599:提到模块顶层导出**(此前是 `parseOrchestrationDomain` 的函数内局部)。零语义变,动机是
228
+ * 部署面需要**同一张表**:`deploy/sema-up/lib/lanes.json` 的每行必须登记它那条车道的必需键名,根 /
229
+ * review compose 的 environment 白名单必须把**全部**车道的必需键透传出去 —— 那两处若各手抄一份名单,
230
+ * 加一条车道就会静默分叉(选得到、起不来,而 config 的拒启报文里那个键在 compose 里根本不存在)。
231
+ * 门 = `test/deploy-lanes-contract.test.ts`,它读的是本表而不是任何副本。
232
+ */
233
+ export declare const REQUIRED_ENVS_BY_LANE: Record<RemoteExecProvider, readonly string[]>;
218
234
  /** [ref]① 1M dual-window (core 1.289 `Model.autoCompactTokens`): an EXPLICIT positive value wins (env
219
235
  * `MODEL_AUTO_COMPACT_TOKENS` / center roster `autoCompactTokens`); else the **model-id table** applies —
220
236
  * sonnet-5-family id ∧ contextWindow>=1e6 ⇒ 967000 (the faithful shape of CC's single-entry per-model