@sema-agent/server 7.25.0 → 7.26.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -38,6 +38,35 @@
38
38
  * `TaskSpec.memoryPersistenceCapable` 一条路进去(`boot/resolve-spec.ts` 直通),我方显式**不铸**受限会话、
39
39
  * 也**不投**这个读面(硬造一个消费点 = 造给自己看的 wire 键)。哪天真要在诊断面露它,先有消费方需求。
40
40
  */
41
+ /**
42
+ * #266 A3 源头修(黑板 [4080]/[4082]/[4083],A-044)—— `memoryPersistenceCapable` 的**装配层有效值**:
43
+ * operator 显式 env 恒赢;env 缺席时,`REMOTE_EXEC=host` 腿自动补 `true`;其余一律缺席(现行为)。
44
+ *
45
+ * ## 为什么 host 腿可以自动声明(而且只有 host 腿)
46
+ * core r13/r14 的 withhold 判据是「执行环境暴露 remote 面」——它对 host 腿是**误伤**:host 腿的文件
47
+ * 工具就跑在**这台机器**的 fs 上(`hostSemanticsLane`,与 core 的 remote 判决是两根轴,见下方 facts 注),
48
+ * 记忆根也在这台机器上,「执行车道与记忆根共享文件平面」这个 capable=true 所断言的**车道级事实**由
49
+ * 构造成立。剩下的围栏维(记忆根是否落在该次任务的 fs 授权边界内)在 in-process 上**同样存在**而 core
50
+ * 并不 withhold——它由本模块的 ② 号 warn、fs 门的 `path_not_in_root` 拒绝、`additionalDirectories`
51
+ * 旋钮这套既有处置管,不归 withhold 判据管。所以 host 腿自动 true 与 in-process 的默认行为**同等诚实**。
52
+ * 沙箱腿(e2b/k8s/ssh/local-docker)文件平面在沙箱盘,withhold 是对的,恒不自动。
53
+ *
54
+ * ## 为什么不是「围栏覆盖记忆根」的更严合取
55
+ * boot 期对 host 腿**算不出**围栏根(host factory 每任务另铸 `sema-host-*` 子目录、per-session cwd 可整个
56
+ * 换掉 workspaceBase——见 boot/stores.ts 的 codex 已核注释),那个合取在恰恰需要它的腿上永不可满足;
57
+ * 而且它断言的是 per-task 事实,不是本键的车道级语义。
58
+ *
59
+ * ## 位置与纪律
60
+ * 单源:resolve-spec 的 spec 直通、`shouldDiscloseNoPersistentMemory` 判别式、boot 审计的 declared 位
61
+ * 三处都必须读本函数,禁各自重算(operator-knob 律:显式 env 的优先权在这一个函数里执法一次)。
62
+ * 自动补位在 boot 日志上必须可见(stores.ts 的 info 行),部署方随时可用显式 env 覆盖。
63
+ */
64
+ export declare function effectiveMemoryPersistenceCapable(config: {
65
+ remoteExec?: {
66
+ provider?: string;
67
+ };
68
+ memoryPersistenceCapable?: boolean;
69
+ }): boolean | undefined;
41
70
  /** boot 期可知的全部事实。每一位都必须由调用点从真配置/真装配结果取,禁在本模块内重算(重算=第二真源)。 */
42
71
  export interface MemoryWriteBoundaryFacts {
43
72
  /** 记忆引擎的根(`memoryEngine.root`)。`undefined` = 引擎未接 ⇒ 本判据整体不适用。 */
@@ -39,6 +39,34 @@
39
39
  * 也**不投**这个读面(硬造一个消费点 = 造给自己看的 wire 键)。哪天真要在诊断面露它,先有消费方需求。
40
40
  */
41
41
  import { resolve } from "node:path";
42
+ /**
43
+ * #266 A3 源头修(黑板 [4080]/[4082]/[4083],A-044)—— `memoryPersistenceCapable` 的**装配层有效值**:
44
+ * operator 显式 env 恒赢;env 缺席时,`REMOTE_EXEC=host` 腿自动补 `true`;其余一律缺席(现行为)。
45
+ *
46
+ * ## 为什么 host 腿可以自动声明(而且只有 host 腿)
47
+ * core r13/r14 的 withhold 判据是「执行环境暴露 remote 面」——它对 host 腿是**误伤**:host 腿的文件
48
+ * 工具就跑在**这台机器**的 fs 上(`hostSemanticsLane`,与 core 的 remote 判决是两根轴,见下方 facts 注),
49
+ * 记忆根也在这台机器上,「执行车道与记忆根共享文件平面」这个 capable=true 所断言的**车道级事实**由
50
+ * 构造成立。剩下的围栏维(记忆根是否落在该次任务的 fs 授权边界内)在 in-process 上**同样存在**而 core
51
+ * 并不 withhold——它由本模块的 ② 号 warn、fs 门的 `path_not_in_root` 拒绝、`additionalDirectories`
52
+ * 旋钮这套既有处置管,不归 withhold 判据管。所以 host 腿自动 true 与 in-process 的默认行为**同等诚实**。
53
+ * 沙箱腿(e2b/k8s/ssh/local-docker)文件平面在沙箱盘,withhold 是对的,恒不自动。
54
+ *
55
+ * ## 为什么不是「围栏覆盖记忆根」的更严合取
56
+ * boot 期对 host 腿**算不出**围栏根(host factory 每任务另铸 `sema-host-*` 子目录、per-session cwd 可整个
57
+ * 换掉 workspaceBase——见 boot/stores.ts 的 codex 已核注释),那个合取在恰恰需要它的腿上永不可满足;
58
+ * 而且它断言的是 per-task 事实,不是本键的车道级语义。
59
+ *
60
+ * ## 位置与纪律
61
+ * 单源:resolve-spec 的 spec 直通、`shouldDiscloseNoPersistentMemory` 判别式、boot 审计的 declared 位
62
+ * 三处都必须读本函数,禁各自重算(operator-knob 律:显式 env 的优先权在这一个函数里执法一次)。
63
+ * 自动补位在 boot 日志上必须可见(stores.ts 的 info 行),部署方随时可用显式 env 覆盖。
64
+ */
65
+ export function effectiveMemoryPersistenceCapable(config) {
66
+ if (config.memoryPersistenceCapable !== undefined)
67
+ return config.memoryPersistenceCapable;
68
+ return config.remoteExec?.provider === "host" ? true : undefined;
69
+ }
42
70
  /** 词法包含判定:`child` 是否落在 `root` 之内(含相等)。纯词法,不碰 fs —— boot 期这些路径可能还没被
43
71
  * 创建,而 `realpath` 会把「同一棵树的两种写法」这个我们**不想**放过的差别悄悄抹平。
44
72
  * 先过 `resolve` 折掉 `.`/`..`/重复分隔符(codex 复审:`/srv/ws/../mem` 不该被读成落在 `/srv/ws` 里),
@@ -51,6 +51,7 @@ import { MAX_SETTINGS_OUTPUT_STYLE_CHARS, acceptAppendSystemPrompt, applyTaskSet
51
51
  import { enableForkFromBody, normalizeRetainSubagentSessions, selfOrchestrationFromBody } from "../task-workflow.js";
52
52
  import { redactSecrets } from "../trace/redact.js";
53
53
  import { DeferredSandboxPathEnv, isSandboxPathAdjudicationLane, sandboxPathEnvSlots } from "./deferred-sandbox-path-env.js";
54
+ import { effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
54
55
  export function createResolveSpec(ctx) {
55
56
  const { config, logger, metrics, localRoot, scenarios, principalCaps, centerRuntimeCapsResolver, handsLanes, getCenterPrompts, getKeyResolver, taskAttachmentStore, perSessionCwd, setSessionCwd, setSessionShellEnv, hookLlm, hookAgent, fleetBus, hookWakeBus, resumeAnchorStore, ownerAware, taskLimitCaps, taskTimeoutSec, selectEnvTool, sendUserFileToolSpec, memoryEngine, durableEnabled, approvalExemptionStore, singleUserAutoAcceptBaseline, checkpointStore, deploymentHooks, imageIndex, perTaskImage, sessionEnvSelection, liveQuestionFace, lockedKeys, } = ctx;
56
57
  // ── #177 的两件 boot 期收口(把守卫集搬到 governance 拍之后才成立的两条)────────────────────────
@@ -788,7 +789,11 @@ export function createResolveSpec(ctx) {
788
789
  // 无事务背书的分歧不收编不供给(留盘 + `restricted_divergence` 点名)。**受限是会话级的这一个键**,
789
790
  // 与上面 `memoryWrite:false` 铸出的 `writeScope:null` **只读平面**不是一回事 —— 那条平面照旧
790
791
  // adopt-on-read(暂停的只是 harvest)。两轴各自的完整口径见 `config-types.ts` 的字段注。
791
- ...(config.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: config.memoryPersistenceCapable } : {}),
792
+ // 🔴 #266 A3 源头修([4082],A-044):有效值经 effectiveMemoryPersistenceCapable 单源——env 显式恒赢;
793
+ // env 缺席且 `REMOTE_EXEC=host` ⇒ 自动 true(host 腿文件平面=本机,capable 断言的车道级事实由构造
794
+ // 成立;core 按 remote 面 withhold 对这条腿是误伤,裸装默认形的记忆写指令曾因此静默消失)。
795
+ // 沙箱腿/in-process 逐位不变。理由全文与「为什么不是围栏合取」见该函数头注。
796
+ ...((v) => (v !== undefined ? { memoryPersistenceCapable: v } : {}))(effectiveMemoryPersistenceCapable(config)),
792
797
  // Scenario-provided capabilities (e.g. code-review = repo tools + reviewer subagents + prompt).
793
798
  // RFC A2: the SelectEnvironment tool rides after the scenario's tools (spec.tools is ADDITIVE to core's
794
799
  // built-in roster — prepare-task mounts first-party tools separately). Only when the image chain is live.
@@ -1123,7 +1128,7 @@ export function createResolveSpec(ctx) {
1123
1128
  // 由 autonomy(governance 拍)或 permissionMode:plan(settings 拍)才被点亮;在阶段④判会漏掉这两条腿。
1124
1129
  if (shouldDiscloseNoPersistentMemory({
1125
1130
  memory: governed.memory,
1126
- declaredCapable: config.memoryPersistenceCapable,
1131
+ declaredCapable: effectiveMemoryPersistenceCapable(config), // #266:同一有效值(host 腿自动 true 后披露判别式同视角)
1127
1132
  hands: gated.hands,
1128
1133
  handsReadOnly: governed.handsReadOnly === true,
1129
1134
  excludeTools: governed.excludeTools,
@@ -32,7 +32,7 @@ import { createSessionStore } from "../plugins/session-store.js";
32
32
  import { assertCloudSnapshotBlobPosture, openStoreBackendWithFallback } from "../plugins/store-backend.js";
33
33
  import { buildMemoryRemoteLaneWarn, memoryEngineBackendFor, memoryEngineRemoteLanePosture } from "../memory-scope.js";
34
34
  import { assertToolResultProvenanceSchema } from "../plugins/tool-result-store-sql.js";
35
- import { buildMemoryWriteBoundaryAudit } from "./memory-boundary.js";
35
+ import { buildMemoryWriteBoundaryAudit, effectiveMemoryPersistenceCapable } from "./memory-boundary.js";
36
36
  import { buildMemoryPosture } from "../memory-posture.js";
37
37
  /** #252:dark 三词 → 启动日志的人话(词表是机器面的真源,散文是它的**投影**,不是第二个真源)。
38
38
  * 穷举 `Record<MemoryDarkReason, …>`:词表加员 ⇒ 这里编译红,不会静默漏一句人话。 */
@@ -254,12 +254,22 @@ export async function openStores(ctx) {
254
254
  lane: lane ?? "in-process",
255
255
  hostSemanticsLane,
256
256
  coreRemoteExecutionEnv,
257
- declaredCapable: config.memoryPersistenceCapable,
257
+ // #266 A3 源头修:审计读**有效值**(host 腿 env 缺席时装配层自动 true)——自动补位后 ① 号 warn
258
+ // 不再响(它警的正是「写指令被撤且无人声明」,现在装配层声明了);显式 env 恒赢在 derive 单源里执法。
259
+ declaredCapable: effectiveMemoryPersistenceCapable(config),
258
260
  containmentRoots,
259
261
  engineBackend: config.memoryEngineBackend, // 只进文案:哪个旋钮真能挪动这条腿的根
260
262
  });
261
263
  for (const w of audit.warnings)
262
264
  logger.warn(w.tag, { detail: w.detail, lane: lane ?? "in-process", root: memoryEngine?.root ?? null });
265
+ // #266:自动补位必须在 boot 日志可见(感知链路纪律)——部署方一眼可核「这台机的 capable 是装配层
266
+ // 按 host 腿车道事实补的」,并知道显式 MEMORY_PERSISTENCE_CAPABLE 可覆盖。只在真自动时发一行。
267
+ if (config.memoryPersistenceCapable === undefined && effectiveMemoryPersistenceCapable(config) === true) {
268
+ logger.info?.("memory_persistence_capable_auto_declared", {
269
+ lane: lane ?? "in-process",
270
+ 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.",
271
+ });
272
+ }
263
273
  }
264
274
  // RosterStore(agent-team S1 server 半场,[1070]① 提货单;core 1.316 `RunnerDeps.rosterStore`):
265
275
  // 具名子 agent 持久名册。形态跟 StoreBackend:tidb/pg=SQL twins(语义三条+真双库验证,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.25.0",
3
+ "version": "7.26.0-rc.1",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -69,7 +69,7 @@
69
69
  "sharp": "^0.35.3"
70
70
  },
71
71
  "devDependencies": {
72
- "@sema-agent/sdk": "7.1.0",
72
+ "@sema-agent/sdk": "7.2.0-rc.1",
73
73
  "@types/libsodium-wrappers": "^0.7.14",
74
74
  "@types/node": "22.10.2",
75
75
  "@types/pg": "^8.20.0",