@sema-agent/server 7.143.0 → 7.144.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 (94) hide show
  1. package/MIGRATION.md +166 -0
  2. package/USAGE.md +24 -14
  3. package/dist/approval-card.d.ts +31 -14
  4. package/dist/approval-card.js +8 -8
  5. package/dist/boot/execution-env.js +1 -1
  6. package/dist/boot/runner-deps.d.ts +10 -5
  7. package/dist/boot/runner-deps.js +5 -4
  8. package/dist/boot/shutdown.d.ts +1 -1
  9. package/dist/boot/shutdown.js +4 -2
  10. package/dist/boot/stage-01-config.js +2 -0
  11. package/dist/boot/stage-07-capability-layer.js +1 -1
  12. package/dist/boot/stage-10-http-server.js +2 -7
  13. package/dist/capabilities/scenarios.d.ts +14 -18
  14. package/dist/capabilities/scenarios.js +1 -2
  15. package/dist/config-d-migration.d.ts +2 -0
  16. package/dist/delegated-session-owner.d.ts +33 -151
  17. package/dist/delegated-session-owner.js +1 -83
  18. package/dist/entry-mode-clamp-notice.d.ts +1 -1
  19. package/dist/execution-placement.d.ts +7 -4
  20. package/dist/hooks/classic-stations.d.ts +3 -1
  21. package/dist/hooks/classic-stations.js +2 -0
  22. package/dist/http/principal-gate.d.ts +1 -1
  23. package/dist/http/refusal-body.d.ts +7 -3
  24. package/dist/http/refusal-body.js +1 -0
  25. package/dist/http/resume-legs.js +11 -4
  26. package/dist/http/routes/approvals-assistant.js +1 -0
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +1 -0
  29. package/dist/lane-home-probe.d.ts +15 -0
  30. package/dist/lane-home-probe.js +4 -0
  31. package/dist/observability/corrupt-read-seat.d.ts +5 -9
  32. package/dist/observability/corrupt-read-seat.js +1 -2
  33. package/dist/observability/fail-open.d.ts +12 -8
  34. package/dist/observability/fail-open.js +12 -8
  35. package/dist/observability/run-terminal.d.ts +1 -1
  36. package/dist/observability/run-terminal.js +2 -2
  37. package/dist/parked-revive-surface.d.ts +6 -7
  38. package/dist/peer-inbound-face.d.ts +29 -34
  39. package/dist/peer-inbound-face.js +28 -63
  40. package/dist/plugins/caching-session-store.d.ts +0 -3
  41. package/dist/plugins/caching-session-store.js +0 -3
  42. package/dist/plugins/checkpoint-store-sql.d.ts +41 -34
  43. package/dist/plugins/checkpoint-store-sql.js +99 -38
  44. package/dist/plugins/fork-routing-session-store.d.ts +8 -1
  45. package/dist/plugins/fork-routing-session-store.js +6 -0
  46. package/dist/plugins/local-checkpoint-store.d.ts +6 -8
  47. package/dist/plugins/local-checkpoint-store.js +3 -2
  48. package/dist/plugins/local-session-store.d.ts +64 -29
  49. package/dist/plugins/local-session-store.js +126 -56
  50. package/dist/plugins/local-session-window.d.ts +4 -2
  51. package/dist/plugins/one-time-migrations.d.ts +12 -12
  52. package/dist/plugins/one-time-migrations.js +12 -12
  53. package/dist/plugins/pg-session-storage.d.ts +1 -9
  54. package/dist/plugins/pg-session-storage.js +2 -19
  55. package/dist/plugins/remote-env-host.d.ts +8 -4
  56. package/dist/plugins/remote-env-host.js +7 -9
  57. package/dist/plugins/remote-shell.js +12 -2
  58. package/dist/plugins/schema-consistency.d.ts +5 -1
  59. package/dist/plugins/session-fork.d.ts +1 -1
  60. package/dist/plugins/session-ownership.d.ts +9 -0
  61. package/dist/plugins/session-ownership.js +6 -0
  62. package/dist/plugins/session-store.d.ts +13 -16
  63. package/dist/plugins/session-store.js +9 -10
  64. package/dist/plugins/sql-json-column.d.ts +24 -2
  65. package/dist/plugins/sql-json-column.js +38 -3
  66. package/dist/plugins/store-backend.js +3 -2
  67. package/dist/plugins/tidb-session-store.d.ts +2 -14
  68. package/dist/plugins/tidb-session-store.js +2 -20
  69. package/dist/run-local.js +2 -1
  70. package/dist/runs.js +4 -0
  71. package/dist/security.d.ts +0 -4
  72. package/dist/session-error-wire.d.ts +24 -25
  73. package/dist/session-error-wire.js +4 -5
  74. package/dist/session-incarnation.d.ts +2 -2
  75. package/dist/session-notify-door.d.ts +2 -2
  76. package/dist/session-notify-door.js +5 -1
  77. package/dist/tool-approval.d.ts +3 -1
  78. package/dist/tool-approval.js +47 -18
  79. package/dist/trace/core-keyset-guard.d.ts +1 -1
  80. package/dist/trace/engine-notice-wire.d.ts +1 -1
  81. package/dist/trace/engine-notice-wire.js +1 -1
  82. package/dist/trace/ledger-sink.js +12 -4
  83. package/dist/trace/model-segment.d.ts +25 -6
  84. package/dist/trace/model-segment.js +47 -6
  85. package/dist/trace/project.d.ts +8 -2
  86. package/dist/trace/project.js +6 -4
  87. package/dist/trace/redact.d.ts +11 -0
  88. package/dist/trace/redact.js +19 -3
  89. package/dist/trace/self-minted-wire.d.ts +88 -9
  90. package/dist/trace/self-minted-wire.js +25 -3
  91. package/package.json +2 -2
  92. package/wire-changes/server@7.144.0.json +291 -0
  93. package/dist/session-records-skipped-notice.d.ts +0 -21
  94. package/dist/session-records-skipped-notice.js +0 -27
@@ -13,7 +13,7 @@ import { IS_WIN32, resolveHostShell, hostShell, pinHostShell, spawnGroupOptions,
13
13
  import { BackgroundShellManager, seedMemStream, feedMemStream, drainMemStream } from "./background-shell-support.js";
14
14
  import { FileError, NodeExecutionEnv, inheritEnvPolicyProblem, ExecutionError, RemoteExecutionError, RollingTailBuffer, walkSymlinkChain, markTruncated, SchedulerError, BackgroundShellError, } from "@sema-agent/core";
15
15
  import { capabilitiesFor } from "../execution-lane-caps.js";
16
- import { LaneHomeSeat } from "../lane-home-probe.js";
16
+ import { LaneHomeSeat, laneHomeRefusal } from "../lane-home-probe.js";
17
17
  import { composeHostSubprocessEnv, hostSubprocessEnv, withheldFromHostSubprocess } from "../server-secret-env.js";
18
18
  import { nameTooLongCode } from "./remote-env-file-error.js";
19
19
  import { trackEphemeralHostWorkspace, untrackEphemeralHostWorkspace } from "./host-workspace-registry.js";
@@ -64,11 +64,13 @@ export class RemoteHostExecutionEnv {
64
64
  return this.homeSeat.home;
65
65
  }
66
66
  homeSeat;
67
- homeRefusal;
68
67
  get cdPath() {
69
68
  const value = this.childEnv(undefined, undefined).CDPATH;
70
69
  return value === undefined || value === "" ? [] : value.split(":");
71
70
  }
71
+ get receivedEnvNames() {
72
+ return Object.entries(this.childEnv(undefined, undefined)).flatMap(([name, value]) => (value === undefined ? [] : [name]));
73
+ }
72
74
  get withheldEnv() {
73
75
  return withheldFromHostSubprocess({ policy: this.cfg.inheritEnv, allow: [] }, this.cfg.env);
74
76
  }
@@ -148,7 +150,9 @@ export class RemoteHostExecutionEnv {
148
150
  this.cwd = this.workspaceDir;
149
151
  this.handle = { sandboxId: this.workspaceDir, provider: PROVIDER, mountPath: this.workspaceDir };
150
152
  this.homeSeat = new LaneHomeSeat(PROVIDER, config, IS_WIN32 ? "win32" : "posix");
151
- this.homeRefusal = this.homeSeat.settle(this.childHome())?.refusal;
153
+ const homeVerdict = this.homeSeat.settle(this.childHome());
154
+ if (homeVerdict !== undefined)
155
+ throw laneHomeRefusal(homeVerdict);
152
156
  }
153
157
  workspaceHandle() {
154
158
  return this.handle;
@@ -158,8 +162,6 @@ export class RemoteHostExecutionEnv {
158
162
  return unsupported("connect(snapshotId)");
159
163
  if (this.destroyed)
160
164
  return { ok: false, error: new RemoteExecutionError("connect_failed", "execution env already destroyed") };
161
- if (this.homeRefusal !== undefined)
162
- return { ok: false, error: new RemoteExecutionError("connect_failed", this.homeRefusal) };
163
165
  if (config?.abortSignal?.aborted)
164
166
  return { ok: false, error: new RemoteExecutionError("aborted", "connect aborted") };
165
167
  try {
@@ -1262,8 +1264,6 @@ export class RemoteHostExecutionEnv {
1262
1264
  async shellReady() {
1263
1265
  if (this.destroyed)
1264
1266
  return { ok: false, error: new ExecutionError("shell_unavailable", "execution env already destroyed") };
1265
- if (this.homeRefusal !== undefined)
1266
- return { ok: false, error: new ExecutionError("shell_unavailable", this.homeRefusal) };
1267
1267
  try {
1268
1268
  try {
1269
1269
  await resolveHostShell();
@@ -1295,8 +1295,6 @@ export class RemoteHostExecutionEnv {
1295
1295
  return (this.spoolRootDir !== undefined && inside(this.spoolRootDir)) || (this.persistent && inside(this.workspaceDir));
1296
1296
  }
1297
1297
  async fsReady(p, opts) {
1298
- if (this.homeRefusal !== undefined)
1299
- return { ok: false, error: new FileError("target_unavailable", this.homeRefusal, this.resolve(p)) };
1300
1298
  if (this.destroyed) {
1301
1299
  const lingering = opts?.lingeringBgWrite === true && this._bgManager?.hasShells() === true && this.isLingeringMirrorTarget(p);
1302
1300
  if (!lingering)
@@ -157,7 +157,7 @@ export async function runSymlinkChain(absPath, run) {
157
157
  const STAGED_FILE = "data";
158
158
  const STAGE_DIR_REFUSED_EXIT = 3;
159
159
  const STAGE_DIR_UNSEALED_EXIT = 4;
160
- const STAGE_DIR_UNSEALED_MESSAGE = "sema: the private staging directory could not be made private (chmod 700 failed or did not take effect, or the mode or owner read back differently — the filesystem ignores permission bits or the parent directory carries a default ACL); nothing was staged";
160
+ const STAGE_DIR_UNSEALED_MESSAGE = "sema: the private staging directory could not be made private (chmod 700 failed or did not take effect, the mode or owner read back differently, or an inherited ACL entry could not be removed or read on this platform — the filesystem ignores permission bits or the parent directory carries a default or inheritable ACL); nothing was staged";
161
161
  export const STAGE_DIR_LEFT_MARK = "sema: staging directory left behind:";
162
162
  export function buildStagingPaths(abs, nonce = randomBytes(6).toString("hex")) {
163
163
  const cut = abs.lastIndexOf("/");
@@ -174,7 +174,17 @@ function privateMkdir(dir) {
174
174
  }
175
175
  function hardenPrivate(dir) {
176
176
  const statOf = (gnu, bsd) => `$(stat -c ${gnu} -- ${dir} 2>/dev/null || stat -f ${bsd} -- ${dir} 2>/dev/null)`;
177
- return `( chmod -- 700 ${dir} 2>/dev/null && m=${statOf("%a", "%Lp")} && u=${statOf("%u", "%u")} && case "$m" in 700|[1-7]700) [ "$u" = "$(id -u)" ];; *) false;; esac )`;
177
+ return `( chmod -- 700 ${dir} 2>/dev/null && m=${statOf("%a", "%Lp")} && u=${statOf("%u", "%u")} && case "$m" in 700|[1-7]700) [ "$u" = "$(id -u)" ];; *) false;; esac && ${aclCleared(dir)} )`;
178
+ }
179
+ function aclCleared(dir) {
180
+ const marker = `ls_out=$(cd ${dir} && ls -ld . 2>&1) && [ -z "$(printf '%s\\n' "$ls_out" | sed -n 2p)" ] && mode_word=\${ls_out%% *} && ` +
181
+ `case "$mode_word" in d?????????*+*) echo +;; d?????????*) ;; *) false;; esac`;
182
+ return (`plat=$(uname -s 2>/dev/null) && case "$plat" in ` +
183
+ `Linux) acl_read() { :; }; acl_strip() { false; };; ` +
184
+ `Darwin) acl_read() { ls_out=$(cd ${dir} && ls -led . 2>&1) && case "$ls_out" in d?????????*) ;; *) false;; esac && printf '%s\\n' "$ls_out" | sed 1d; }; acl_strip() { chmod -N ${dir}; };; ` +
185
+ `FreeBSD) acl_read() { ${marker}; }; acl_strip() { setfacl -b ${dir}; };; ` +
186
+ `SunOS) acl_read() { ${marker}; }; acl_strip() { chmod A- ${dir}; };; ` +
187
+ `*) false;; esac && acl=$(acl_read 2>/dev/null) && { [ -z "$acl" ] || { acl_strip 2>/dev/null && acl=$(acl_read 2>/dev/null) && [ -z "$acl" ]; }; }`);
178
188
  }
179
189
  function emptyPrivate(dir) {
180
190
  return `[ ! -e ${dir}/${STAGED_FILE} ] && [ ! -L ${dir}/${STAGED_FILE} ]`;
@@ -73,7 +73,11 @@ export interface ParsedTable {
73
73
  export declare function parseCreateTable(stmt: string): ParsedTable | undefined;
74
74
  /** 启动时真会建出来的**全部**表形(录制 → 解析)。同名表二次出现以**先到**为准(`IF NOT EXISTS` 的真语义)。 */
75
75
  export declare function expectedTableShapes(dialect: SqlDialect): Promise<Map<string, ParsedTable>>;
76
- /** 拒启码(机读)。附录 A / 拒启码表的登记名。 */
76
+ /**
77
+ * 拒启码(机读)。附录 A / 拒启码表的登记名。含义 = 「本构建读不出的持久形,开始服务之前判」,一个码三只臂:本文件的列册对账与退役 schema 两道(SQL),
78
+ * 以及 local 数据根的严格边档对账(`local-session-store.ts` 的 `LocalDataRootSchemaMismatchError`,`LocalBackend.ensureSchema()` 调;拒句同形:
79
+ * 码开头的表头 · 逐坐标一段 · 「NEWER than this build」方向句 · 闭集出路)。
80
+ */
77
81
  export declare const SCHEMA_MISMATCH_ERROR_CODE = "config.schema_mismatch";
78
82
  /** 一张表的对账结论。 */
79
83
  export interface TableMismatch {
@@ -28,7 +28,7 @@ export interface ForkLogRow {
28
28
  readonly ts: unknown;
29
29
  }
30
30
  /**
31
- * 两只 SQL 孪生的行怎么交给引擎的 `selectForkRows`(按答案挑行 + 新会话的叶;分叉点不在日志里 ⇒ `invalid_fork_target`、路径中途悬空父 / 父链成环 ⇒ `invalid_session`,
31
+ * 两只 SQL 孪生的行怎么交给引擎的 `selectForkRows`(按答案挑行 + 新会话的叶;分叉点不在日志里 ⇒ `invalid_fork_target`、路径中途悬空父 / 父链成环 ⇒ `corrupt_session`〔core 7.61.x 及以前 `invalid_session`〕,
32
32
  * 引擎的原词原句)。本仓不再自写父链走法 —— 那曾是与引擎 `getPathToRoot` 同义的第二份。
33
33
  */
34
34
  export declare const FORK_LOG_ROW_SHAPE: ForkRowShape<ForkLogRow>;
@@ -22,6 +22,15 @@ export declare function forkOwnerOf(requested: string | null | undefined, source
22
22
  * 「首写」由调用方判(已记下属主的会话上的调用照旧静默不改,不经这里)。判官 = {@link isSessionOwnerValue}(core 7.56.0 [ref] `principalNameOf`)。
23
23
  */
24
24
  export declare function assertFirstOwnerWritable(sessionId: string, owner: unknown): asserts owner is string | null;
25
+ /**
26
+ * placed 行属主格的唯一读法(core 7.62.0 [ref] 盒义务 7;三只持久会话店的 placing write 同调):记下的属主 = 引擎交来的 `placement.owner` **原样** ——
27
+ * 委派树的属主由引擎判(没人派生的 run 读自己那一行、按化身绑一次,原样带给每一层子代;`null` = 根匿名),店只记、不判、不推断,也不进元组
28
+ * (`SessionPlacementRecord` 没有属主)。值不是主体名也不是 `null`(空串、空白、缺席 —— 绕过类型的调用)⇒ 响亮拒(`SessionError("storage")`,一个字节不写):
29
+ * 一只谁也不指的属主会让行要么谁都读得到、要么谁都读不到。判官 = {@link isSessionOwnerValue},与 `register` 首写同一只。
30
+ */
31
+ export declare function placedSessionOwnerOf(sessionId: string, placement: {
32
+ readonly owner?: unknown;
33
+ }): string | null;
25
34
  /**
26
35
  * 导入者的属主 —— 换装的写腿(公开 `importEntries` / `replaceEntries` 与暂存提交)把调用方给的属主重戳进会话行之前的唯一读法:
27
36
  * 主体名或 `null`(匿名)⇒ 原样交回;别的一切(空串、空白串、带控制 / 分隔 / 非连接符格式字符的串、非串)⇒ `TypeError` 响亮拒 ——
@@ -22,6 +22,12 @@ export function assertFirstOwnerWritable(sessionId, owner) {
22
22
  throw new SessionError("storage", `register(${sessionId}): a first ownership write must carry a principal name or null (anonymous), got ${ownerValueFault(owner)} — ` +
23
23
  "a value that names nobody would leave the session claimable by anyone or ownable by no one; refusing, nothing written");
24
24
  }
25
+ export function placedSessionOwnerOf(sessionId, placement) {
26
+ const owner = placement.owner;
27
+ if (isSessionOwnerValue(owner))
28
+ return owner;
29
+ throw new SessionError("storage", `acquire(${sessionId}): a placed session's owner (placement.owner, the delegation tree's owner the engine judged) must be a principal name or null (an anonymous tree), got ${ownerValueFault(owner)} — refusing, nothing written`);
30
+ }
25
31
  export function importerOwnerOf(owner) {
26
32
  if (isSessionOwnerValue(owner))
27
33
  return owner;
@@ -1,8 +1,6 @@
1
1
  import { type SessionStore } from "@sema-agent/core";
2
2
  import type { ServiceConfig } from "../config-types.js";
3
3
  import type { StoreBackend } from "./store-backend.js";
4
- import { type DelegationRootWitness, type OrphanOwnerLog } from "../delegated-session-owner.js";
5
- import type { OwnerAwareSessionStore } from "../security.js";
6
4
  /**
7
5
  * Session-store plugin seam.
8
6
  *
@@ -43,23 +41,22 @@ export declare function createSessionStore(config: Pick<ServiceConfig, "sessionB
43
41
  */
44
42
  export declare function ensureSessionDurable(hostStore: SessionStore, sessionId: string): Promise<void>;
45
43
  /**
46
- * core `SubagentToolOptions.background.ensureChildSessionDurable` 的实现体 —— 停驻挂点的**见证**:这只后台子代的转录此刻是 host 店里一只有真内容、
47
- * 属主按委派继承律定得下来的会话,才放行停驻;任何一步不成立 ⇒ 抛 = 否决(core 收 veto 即 expire checkpoint + settle failed,pre-153 行为,不放大)。
44
+ * core `SubagentToolOptions.background.ensureChildSessionDurable(sessionId, given)` 的实现体 —— 停驻挂点的**见证**:这只后台子代的转录此刻是 host 店里一只有真内容、
45
+ * 属主恰是引擎交来的委派树属主(`given.owner`)的会话,才放行停驻;任何一步不成立 ⇒ 抛 = 否决(core 收 veto 即 expire checkpoint + settle failed,pre-153 行为,不放大)。
48
46
  *
49
47
  * 规则(`fork-routing-session-store.ts` 头注):一只后台子代的 durable 行在,它的转录就是 host 店委派分区里的一只 placed 行。挂点只装在声明了
50
48
  * durable 委派分区的部署上(stage-07:core `resolveSubagentTranscriptTier(...) === "full"`),那里 core 给每只新起的后台子代铸 placement、
51
- * 转录生来就在 host 店 —— 所以这里**只验、不搬**(此前的「晋升」把私店的转录整树拷进 host 店,随这条规则退役)。
52
- * 0. 属主(`delegated-session-owner.ts`):按委派继承律判一次(唯一判据 `judgeDelegatedOwner`)—— 来源集 = 见证的根这一只(调用方交出的委派树根 + 调用方判断那一刻
53
- * 读到的根行住客:请求腿 = 本请求经鉴权解析的会话与受理时那一读,`/decide` 赎回腿 = 行上的 `rootSessionId` 与写口放行的那一位(认领之前解析器那一读);core 的挂点签名只给子代 id,
54
- * 场景层把见证绑进闭包交下来)。此刻根行在而住客不是见证的那一位(删了、同 id 被重新登记)⇒ **拒**(`assertRootStillWitnessed`);
55
- * 缺席(行上没有根)或根行此刻不在 ⇒ 孤行哨兵。
49
+ * 转录生来就在 host 店 —— 所以这里**只验、不搬、不判属主**:属主是引擎判的(core 7.62.0 [ref] L1:没人派生的 run 读自己那一行、按化身绑一次,原样带给每一层子代;
50
+ * 复活的子代 = 它自己那一行的属主),placed 行建行那一笔原样记下了它(盒义务 7,三只持久会话店 + `session-ownership.ts` 的 `placedSessionOwnerOf`)。
51
+ * 挂点不认领、不推断、不收委派树根见证([ref] P-5;本仓的委派继承律在这一写点退役 —— 存量无主 placed 行:SQL 两店由一次性迁移 M23 在窗内回填,本机文件店没有回填臂,见 `local-session-store.ts` 头注)。
52
+ * 0. 宿主店读不出属主(没有 `headOf`,唯一判法 `headReadOf`)⇒ 响亮拒,一次读都不发。
56
53
  * 1. 在不在、有没有真内容:{@link ensureSessionDurable}(`requireExisting` 打开 + 叶子非空 + forget 收尾)。任何失败(不在、空壳、读故障)⇒ 原样上抛 = 否决
57
- * (读故障也否决:说不准转录在不在,就不让一次审批押在它上面)。这里**不判它是不是 placed 行**:复活的是旧行时 id 取行上的会话(升级前停驻、当年晋升进来的普通行),
58
- * 它照样是这只子代的转录;判据只问「host 店里有这份真内容」。
59
- * 2. 属主定夺(`settleDelegatedOwner`):记着别的委派树 ⇒ 抛;记着这一棵 ⇒ 按它记录的根、父再判一次(与 M23 同读数),无主 ⇒ 认领(CAS 子行,写判断时的读数)、没落下 ⇒ 抛;
60
- * 此刻属主不是律给的那一只 ⇒ 那是别人的同 id 会话,不是这只子代的转录 ⇒ 抛 = 停驻否决。认领写下的是哨兵 ⇒ 写落下之后一行 warn(`site: "park"`)。
61
- * 宿主店必须读得出属主(`delegatedOwnerHostOf`:`headOf`;记录委派树的店另交 `placementOf` + `claimOwnerless`,两只同在或同缺;生产的 SQL / local 店与缓存包装层都满足),
62
- * 否则响亮拒。
54
+ * (读故障也否决:说不准转录在不在,就不让一次审批押在它上面)。不判它是不是 placed 行:复活的是旧行时 id 取行上的会话(升级前停驻、当年晋升进来的普通行),
55
+ * 它照样是这只子代的转录。
56
+ * 2. 此刻行上的属主 ≠ `given.owner` ⇒ 那是别人的同 id 会话,或这一行在引擎读之后被改写 ⇒ 抛 = 停驻否决(拒句不带任何一方的属主值 —— 它会进停驻否决的错误面)。
57
+ * 契约只承诺属主值相等(化身不比:复活腿的 `given` 只带属主)。
63
58
  */
64
- export declare function attestChildSessionDurable(hostStore: OwnerAwareSessionStore, sessionId: string, root: DelegationRootWitness | undefined, log: OrphanOwnerLog): Promise<void>;
59
+ export declare function attestChildSessionDurable(hostStore: SessionStore, sessionId: string, given: {
60
+ readonly owner: string | null;
61
+ }): Promise<void>;
65
62
  //# sourceMappingURL=session-store.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { TtlSessionStore } from "@sema-agent/core";
2
2
  import { CachingSessionStore } from "./caching-session-store.js";
3
- import { assertRootStillWitnessed, delegatedOwnerHostOf, readDelegatedOwner, reportWrittenOwner, settleDelegatedOwner, sourceFacesVia, } from "../delegated-session-owner.js";
3
+ import { headReadOf } from "../session-incarnation.js";
4
4
  export function createSessionStore(config, backend, metrics) {
5
5
  if (config.sessionBackend === "tidb") {
6
6
  if (!backend)
@@ -23,16 +23,15 @@ export async function ensureSessionDurable(hostStore, sessionId) {
23
23
  await hostStore.forget?.(acquired.sessionId);
24
24
  }
25
25
  }
26
- export async function attestChildSessionDurable(hostStore, sessionId, root, log) {
27
- const host = delegatedOwnerHostOf(hostStore);
28
- if (host === undefined) {
29
- throw new Error(`session "${sessionId}" cannot be parked: the host session store cannot read a session's owner (headOf), or records delegation trees without being able to claim under them (placementOf / claimOwnerless), so the delegated transcript's owner cannot be derived or checked`);
26
+ export async function attestChildSessionDurable(hostStore, sessionId, given) {
27
+ const headOf = headReadOf(hostStore);
28
+ if (headOf === undefined) {
29
+ throw new Error(`session "${sessionId}" cannot be parked: the host session store cannot read a session's owner (headOf), so the delegated transcript's owner cannot be checked`);
30
30
  }
31
- const judged = await readDelegatedOwner(sessionId, root === undefined ? {} : { rootSessionId: root.sessionId }, sourceFacesVia(host.headOf));
32
- assertRootStillWitnessed(sessionId, root, judged);
33
31
  await ensureSessionDurable(hostStore, sessionId);
34
- const settled = await settleDelegatedOwner(host, sessionId, judged.inherited.owner, judged.tree);
35
- if (settled.claimed)
36
- reportWrittenOwner(log, "park", sessionId, settled.judged ?? judged);
32
+ if ((await headOf(sessionId))?.owner !== given.owner) {
33
+ throw new Error(`session "${sessionId}" cannot be parked as this delegated session: the host session store holds a session with this id whose owner is not this delegation tree's owner, ` +
34
+ "so it does not attest this delegated transcript");
35
+ }
37
36
  }
38
37
  //# sourceMappingURL=session-store.js.map
@@ -68,12 +68,13 @@ export interface JsonColumnMember {
68
68
  * 同一只投影的**裸表达式**(不带别名)—— 给 `WHERE` / `UPDATE … WHERE` 这种**没有别名位**的地方用。
69
69
  *
70
70
  * 🔴 存在的唯一理由是「别名位在那里不合法」,**不是**给读腿留第二条路:读腿一律用 {@link jsonColumnText}
71
- * (别名是它的产物的一部分,理由见那只函数)。今天整仓**两处**消费方,各自的「别名位不合法」都是真的:
71
+ * (别名是它的产物的一部分,理由见那只函数)。今天整仓**三处**消费方,各自的「别名位不合法」都是真的:
72
72
  * · `one-time-migrations.ts` 的几条改写(M8 / M17 / M18 / M21 的候选扫描与按原文 CAS 回写)——
73
73
  * 它们要在 `WHERE` 里判这一列的**文本**,而 `WHERE` 没有别名位;
74
74
  * · `workflow-journal-store-sql.ts` 的 `loadPage`([ref] 车③a)—— 那一列的投影裹在一条
75
75
  * `CASE WHEN LENGTH(result) > ? THEN NULL ELSE <expr> END AS result_json` 里:**别名属于整条 CASE**
76
76
  * (`result_json`,因为超字节门的那一支要给 `NULL`),CASE 里面的表达式位上写别名是语法错。
77
+ * · 本模块的 {@link jsonColumnTextSha256} —— 投影裹在摘要函数里,**别名属于整条摘要表达式**(产物是摘要不是体)。
77
78
  * 而「怎么把一列 JSON 取成文本」这件事整仓只能有一种写法:两种承载(真 JSON 列 / LONGTEXT 别名)在这条
78
79
  * 表达式下合成同一只串,迁移的判等、分页腿的取文本因此与读腿的判形**看同一份字节**。
79
80
  *
@@ -82,6 +83,20 @@ export interface JsonColumnMember {
82
83
  * 误用的落点是真库上当场的 `SqlJsonColumnError("not-text")` 响亮抛(行上没有那个键 ⇒ `undefined`),不是静默。
83
84
  */
84
85
  export declare function jsonColumnTextExpr(dialect: SqlDialect, column: string, memberKey?: string): string;
86
+ /**
87
+ * 一列 JSON 的**投影文本的 sha256**(十六进制小写,引擎内算 —— 整只文档不出库),带别名。输入恰是 {@link jsonColumnTextExpr} 那一只投影:
88
+ * 同一行、同一次读里,本表达式的摘要就是 {@link jsonColumnText} 交给读腿的那串字节的摘要,于是「字节没变 ⇒ 摘要没变」只有一种读法。
89
+ * MySQL 协议 `SHA2(<文本>, 256)`(TiDB / MySQL / MariaDB 都认);PG `encode(sha256(convert_to(<文本>, 'UTF8')), 'hex')`(PG 11+)。
90
+ *
91
+ * 读者今天一处:`checkpoint-store-sql.ts` 的停驻行投影记忆(运维队列 / 调度视图对一份停驻行字节的判决按 (摘要, 引擎构建) 记,
92
+ * 记忆命中就不拉整只文档)。产物是摘要不是体,所以它**不是**体的第二条投影路;回读走 {@link readJsonColumnTextSha256}。
93
+ */
94
+ export declare function jsonColumnTextSha256(dialect: SqlDialect, column: string, as: string): string;
95
+ /**
96
+ * {@link jsonColumnTextSha256} 那一格的回读:恒是 64 位十六进制小写串。别的一切(键不在 = 读腿漏了投影;串形不对 = 驱动换了行为)都是**码坏**,
97
+ * 响亮抛 —— 不当作「这一行没有摘要」退成每次整行读(那会让记忆静默失效,代价无声地涨回去)。
98
+ */
99
+ export declare function readJsonColumnTextSha256(cell: unknown, where: string): string;
85
100
  /**
86
101
  * 一次读回失败的**种别**(店层码,**闭集**;运行期词表在这里,类型从它推导 —— 一张词表只有一个写者):
87
102
  * · `not-text` —— 拿到的不是串 = **读腿漏了投影**,那是**码的问题不是数据的问题** ⇒ 响亮的程序错,
@@ -114,7 +129,12 @@ export declare class SqlJsonColumnError extends Error {
114
129
  readonly problem: SqlJsonColumnProblem;
115
130
  /** 出事的坐标(`<table>.<column> for <identity>`)—— 进错误文案,让运维知道去看哪一行。 */
116
131
  readonly where: string;
117
- constructor(problem: SqlJsonColumnProblem, where: string, detail: string);
132
+ /**
133
+ * `malformed` 时形读者交出的「它不认得的键」({@link JsonColumnShapeBad.unknownKeys};别的种别 / 读者说不出 ⇒ 空)—— 体是更新的写者写下的那一形的判据
134
+ * (例:local 数据根的严格边档据它把拒句点名成「比本构建新」)。判词只有形读者一个写者,本类原样携带。
135
+ */
136
+ readonly unknownKeys: readonly string[];
137
+ constructor(problem: SqlJsonColumnProblem, where: string, detail: string, unknownKeys?: readonly string[]);
118
138
  }
119
139
  /** 形读者认了:带上收窄后的值。 */
120
140
  export interface JsonColumnShapeOk<T> {
@@ -125,6 +145,8 @@ export interface JsonColumnShapeOk<T> {
125
145
  export interface JsonColumnShapeBad {
126
146
  readonly value?: undefined;
127
147
  readonly problem: string;
148
+ /** 读者因为**不认得**而拒的键(去重、排序;没有 / 读者说不出 ⇒ 缺)—— 更新的写者给这一形加了位。{@link decodeJsonColumn} 原样交给 {@link SqlJsonColumnError.unknownKeys}。 */
149
+ readonly unknownKeys?: readonly string[];
128
150
  }
129
151
  /** {@link JsonColumnReader} 的答复。 */
130
152
  export type JsonColumnShape<T> = JsonColumnShapeOk<T> | JsonColumnShapeBad;
@@ -20,15 +20,30 @@ export function jsonColumnTextExpr(dialect, column, memberKey) {
20
20
  }
21
21
  return dialect === "pg" ? `(${column}::jsonb -> '${memberKey}')::text` : `CAST(JSON_EXTRACT(${column}, '$.${memberKey}') AS CHAR)`;
22
22
  }
23
+ export function jsonColumnTextSha256(dialect, column, as) {
24
+ if (!MEMBER_ALIAS_RE.test(as)) {
25
+ throw new Error(`jsonColumnTextSha256: alias ${JSON.stringify(as)} must be a lower-case bare identifier — PostgreSQL folds unquoted aliases to lower case, so any other spelling would give the two dialects different row keys`);
26
+ }
27
+ const text = jsonColumnTextExpr(dialect, column);
28
+ return dialect === "pg" ? `encode(sha256(convert_to(${text}, 'UTF8')), 'hex') AS ${as}` : `SHA2(${text}, 256) AS ${as}`;
29
+ }
30
+ const SHA256_HEX_RE = /^[0-9a-f]{64}$/;
31
+ export function readJsonColumnTextSha256(cell, where) {
32
+ if (typeof cell === "string" && SHA256_HEX_RE.test(cell))
33
+ return cell;
34
+ throw new Error(`${where}: expected a lower-case sha256 hex digest from the read leg, got ${cell === undefined ? "no such key (the SELECT dropped the projection)" : typeof cell} — refusing rather than guess`);
35
+ }
23
36
  export const SQL_JSON_COLUMN_PROBLEMS = ["not-text", "not-json", "malformed"];
24
37
  export class SqlJsonColumnError extends Error {
25
38
  problem;
26
39
  where;
27
- constructor(problem, where, detail) {
40
+ unknownKeys;
41
+ constructor(problem, where, detail, unknownKeys = []) {
28
42
  super(`${where}: ${detail}`);
29
43
  this.name = "SqlJsonColumnError";
30
44
  this.problem = problem;
31
45
  this.where = where;
46
+ this.unknownKeys = unknownKeys;
32
47
  }
33
48
  }
34
49
  function describeBody(body) {
@@ -53,7 +68,7 @@ export function decodeJsonColumn(text, read, where) {
53
68
  }
54
69
  const shape = read(body);
55
70
  if (shape.problem !== undefined) {
56
- throw new SqlJsonColumnError("malformed", where, `carries ${describeBody(body)} that this store cannot read (${shape.problem}) — refusing rather than serve it as an empty/partial value`);
71
+ throw new SqlJsonColumnError("malformed", where, `carries ${describeBody(body)} that this store cannot read (${shape.problem}) — refusing rather than serve it as an empty/partial value`, shape.unknownKeys);
57
72
  }
58
73
  return shape.value;
59
74
  }
@@ -85,8 +100,28 @@ export function assertJsonColumnWritable(text, read, where) {
85
100
  export function createZodColumnReader(schema) {
86
101
  return (body) => {
87
102
  const parsed = schema.safeParse(body);
88
- return parsed.success ? { value: parsed.data } : { problem: parsed.error.message };
103
+ if (parsed.success)
104
+ return { value: parsed.data };
105
+ const unknownKeys = unrecognizedKeysOf(parsed.error.issues);
106
+ return { problem: parsed.error.message, ...(unknownKeys.length > 0 ? { unknownKeys } : {}) };
107
+ };
108
+ }
109
+ function unrecognizedKeysOf(issues) {
110
+ const keys = new Set();
111
+ const walk = (list) => {
112
+ for (const issue of list) {
113
+ if (issue.code === "unrecognized_keys")
114
+ for (const key of issue.keys)
115
+ keys.add(key);
116
+ else if (issue.code === "invalid_union")
117
+ for (const branch of issue.errors)
118
+ walk(branch);
119
+ else if (issue.code === "invalid_key" || issue.code === "invalid_element")
120
+ walk(issue.issues);
121
+ }
89
122
  };
123
+ walk(issues);
124
+ return [...keys].sort();
90
125
  }
91
126
  export function createObjectColumnReader() {
92
127
  return (body) => (isJsonObject(body) ? { value: body } : { problem: "expected a JSON object" });
@@ -165,7 +165,7 @@ class TiDBBackend {
165
165
  breaker(onWriteFail) { return new TiDBBreakerState(this.pool, onWriteFail); }
166
166
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new TiDBCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
167
167
  rateLimiter(limit, onDegraded) { return new TiDBRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
168
- session() { return new TiDBSessionStore(this.pool, { log: createLogger(this.config.logLevel), retention: sqlStoreRetention(this.config.retentionSweep) }); }
168
+ session() { return new TiDBSessionStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
169
169
  adoptionLog() { return new TiDBAdoptionLogStore(this.pool); }
170
170
  mysqlPool() { return this.pool; }
171
171
  pgPool() { return undefined; }
@@ -219,7 +219,7 @@ class PgBackend {
219
219
  breaker(onWriteFail) { return new PgBreakerState(this.pool, onWriteFail); }
220
220
  costQuota(limitMicroUsd, windowMs, onDegraded) { return new PgCostQuota(this.pool, limitMicroUsd, windowMs, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
221
221
  rateLimiter(limit, onDegraded) { return new PgRateLimiter(this.pool, limit, undefined, undefined, onDegraded, this.config.dbQueryTimeoutMs); }
222
- session() { return new PgSessionStore(this.pool, { log: createLogger(this.config.logLevel), retention: sqlStoreRetention(this.config.retentionSweep) }); }
222
+ session() { return new PgSessionStore(this.pool, { retention: sqlStoreRetention(this.config.retentionSweep) }); }
223
223
  adoptionLog() { return new PgAdoptionLogStore(this.pool); }
224
224
  mysqlPool() { return undefined; }
225
225
  pgPool() { return this.pool; }
@@ -294,6 +294,7 @@ class LocalBackend {
294
294
  : (plane) => fileSessionCaptureRecordStore(plane?.controlDir ?? ownControlDir);
295
295
  }
296
296
  async ensureSchema() {
297
+ await this.sessionStore.assertStrictSidecarsReadable();
297
298
  await this.sessionStore.settleArchiveSnapshots();
298
299
  }
299
300
  async runOneTimeMigrations() {
@@ -17,7 +17,6 @@ import type { SessionWorkspace } from "../session-workspace.js";
17
17
  import type { RetentionDeclaration } from "@sema-agent/core";
18
18
  import type { SessionSummary } from "./store-contracts.js";
19
19
  import { type ClientSessionRowWrite, type ReplaySessionRowWrite, type ReplaySessionRowWriteOutcome, type SessionRowPatch, type SessionRowWriteOutcome } from "../session-row.js";
20
- import { type OrphanOwnerLog } from "../delegated-session-owner.js";
21
20
  import { type PlacementAcquireOpts } from "./session-placement.js";
22
21
  import { type StagingHandle } from "../session-sync-kernel.js";
23
22
  import { type ForkTarget, type ForkToOutcome } from "./session-fork.js";
@@ -40,8 +39,6 @@ export declare class TiDBSessionStore implements SessionStore {
40
39
  readonly durability: "durable";
41
40
  /** In-flight acquisitions keyed by id, so concurrent acquire(sameId) in one process share one. */
42
41
  private readonly pending;
43
- /** 委派子代孤行那一行 warn 的去处(`delegated-session-owner.ts`;装配给部署的 logger,缺席 ⇒ 缺省 logger,不静默)。 */
44
- private readonly log;
45
42
  /**
46
43
  * 执行席 —— 根会话行上这一位住客的那一格的唯一读口与 place / markMirror 两只写口(规则在 `src/execution-placement.ts`;
47
44
  * 实现与语句的唯一属主 = `execution-placement-sql.ts`,两孪生同一只实现,本店只交自己的池)。
@@ -50,7 +47,6 @@ export declare class TiDBSessionStore implements SessionStore {
50
47
  /** 本店的池包成的 SqlDriver:执行席与会话行格写原语的事务都经它借连接(连接初始化钉住事务读语义,`sql-driver.ts`)。 */
51
48
  private readonly db;
52
49
  constructor(pool: Pool, opts?: {
53
- readonly log?: OrphanOwnerLog;
54
50
  readonly retention?: RetentionDeclaration;
55
51
  });
56
52
  acquire(sessionId?: string, opts?: PlacementAcquireOpts): Promise<AcquiredSession>;
@@ -58,18 +54,10 @@ export declare class TiDBSessionStore implements SessionStore {
58
54
  /** Idempotently create a session_meta row with an owner (core `SessionStore.register`). Existing owner is never overwritten. */
59
55
  register(sessionId: string, owner: string | null): Promise<void>;
60
56
  /**
61
- * acquire 的建行(唯一一处):普通会话 ⇒ 无主(按 id 首建的既有读法);placed 会话 ⇒ 属主按委派继承律在**这一笔**里写入
62
- * ([ref]:根会话的属主,根行不在取父,都不在 ⇒ 孤行哨兵 + warn;属主 = 判断时见证到的,读来源 → 判 → INSERT —— 读法与顺序只住在
63
- * `delegated-session-owner.ts` 的 `createPlacedSessionRow`,本店只交既有读写口,PG 孪生同调)。
57
+ * acquire 的建行(唯一一处):普通会话 ⇒ 无主(按 id 首建的既有读法);placed 会话 ⇒ 属主 = `placement.owner` 原样,与元组同一条 INSERT 落库
58
+ * (core 7.62.0 [ref] 盒义务 7:委派树的属主是引擎判的,店只记、不判、不推断;唯一读法 `placedSessionOwnerOf`,值不是主体名也不是 null ⇒ 响亮拒、不留行。PG 孪生同调)。
64
59
  */
65
60
  private createOnAcquire;
66
- /**
67
- * [ref] 委派继承律的**认领口**:这只会话的那一位住客(判断时读到的化身)仍是无主的 placed 行 ⇒ 写 `owner`,返回是否写成
68
- * (句 = `CLAIM_OWNERLESS_PLACED_SQL`,只 CAS 子行自己,与停驻挂点 / M23 同一只;规则在 `delegated-session-owner.ts`)。普通会话行不在射程(placed 判别位)。
69
- */
70
- claimOwnerless(sessionId: string, incarnation: string, owner: string): Promise<boolean>;
71
- /** 若干来源行的会话面(属主 + 化身)一次读回(委派继承律的来源读;本店没有的 id 不在表里;活住客的投影 `sessionFaceFromRow`,空化身 ⇒ 响亮拒)。 */
72
- private sourceFaces;
73
61
  /** The one session_meta INSERT. Existing owner is never overwritten.
74
62
  * [ref]:`placement` 在场时与行同一条 INSERT 落六列;ON DUPLICATE 臂是 no-op ⇒ 已存在的行的元组
75
63
  * **永不被重写**(first-write 不可变,义务 5 的写侧半场)。
@@ -10,11 +10,9 @@ import { carriedRowCells, headFromRow, incarnationFromCell, mintSessionIncarnati
10
10
  import { sessionRowTagKey } from "../session-row.js";
11
11
  import { writeSessionRowCellsSql } from "./session-row-sql.js";
12
12
  import { contentForkRelation, fastForwardSharedContentDiverged } from "../session-sync-content.js";
13
- import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, createPlacedSessionRow } from "../delegated-session-owner.js";
14
- import { createLogger } from "../observability/logger.js";
15
13
  import { placementCapabilityOf, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SESSION_DURABILITY, SESSION_PLACEMENTS, } from "./session-placement.js";
16
14
  import { classifySyncRelationshipByIds, SyncConflictError, StagedCommitOwnerChangedError, stagingIdFor, STAGING_ID_MARKER, STAGING_GC_GRACE_MS, } from "../session-sync-kernel.js";
17
- import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue } from "./session-ownership.js";
15
+ import { assertFirstOwnerWritable, forkOwnerOf, isSessionOwnerValue, placedSessionOwnerOf } from "./session-ownership.js";
18
16
  import { selectForkRows } from "@sema-agent/core";
19
17
  import { FORK_LOG_ROW_SHAPE, ForkTargetUnsettledError, classifyForkTarget, forkByFreshId, forkPointOf } from "./session-fork.js";
20
18
  import { isMysqlDupKeyError } from "./sql-errors.js";
@@ -28,12 +26,10 @@ export class TiDBSessionStore {
28
26
  placements = SESSION_PLACEMENTS;
29
27
  durability = SESSION_DURABILITY;
30
28
  pending = new Map();
31
- log;
32
29
  executionPlacement;
33
30
  db;
34
31
  constructor(pool, opts = {}) {
35
32
  this.pool = pool;
36
- this.log = opts.log ?? createLogger();
37
33
  this.retention = opts.retention ?? UNMANAGED_RETENTION;
38
34
  this.db = mysqlDriver(pool);
39
35
  this.executionPlacement = createSqlExecutionPlacementSeat(this.db);
@@ -81,21 +77,7 @@ export class TiDBSessionStore {
81
77
  await this.insertMeta(sessionId, owner, undefined);
82
78
  }
83
79
  async createOnAcquire(sessionId, placement) {
84
- if (placement === undefined)
85
- return this.insertMeta(sessionId, null, undefined);
86
- await createPlacedSessionRow(sessionId, placement, {
87
- readFaces: (ids) => this.sourceFaces(ids),
88
- insert: (owner) => this.insertMeta(sessionId, owner, placement),
89
- readOwner: async () => (await this.headOf(sessionId))?.owner,
90
- }, this.log);
91
- }
92
- async claimOwnerless(sessionId, incarnation, owner) {
93
- const [res] = await this.pool.query(CLAIM_OWNERLESS_PLACED_SQL.mysql, [...claimOwnerlessParams(sessionId, incarnation, owner)]);
94
- return res.affectedRows > 0;
95
- }
96
- async sourceFaces(ids) {
97
- const [rows] = await this.pool.query(`SELECT session_id, owner, incarnation FROM session_meta WHERE session_id IN (${ids.map(() => "?").join(", ")})`, [...ids]);
98
- return new Map(rows.map((r) => [String(r.session_id), sessionFaceFromRow(String(r.session_id), r.owner ?? null, r.incarnation)]));
80
+ await this.insertMeta(sessionId, placement === undefined ? null : placedSessionOwnerOf(sessionId, placement), placement);
99
81
  }
100
82
  async insertMeta(sessionId, owner, placement) {
101
83
  const now = new Date();
package/dist/run-local.js CHANGED
@@ -51,7 +51,7 @@ import { declaredCredential } from "./model-route-gap.js";
51
51
  import { judgeServedModelRoute } from "./model-route-endpoint.js";
52
52
  import { startMemorySyncRunner } from "./memory-sync-client.js";
53
53
  import { buildPricing, cappedCeiling, createTracer } from "./budget.js";
54
- import { createSharedRunnerDeps } from "./boot/runner-deps.js";
54
+ import { createSharedRunnerDeps, installDefaultEngineNoticeSeat } from "./boot/runner-deps.js";
55
55
  import { createPeerDesk } from "./peer-seat.js";
56
56
  import { assertGovernanceAssemblable, createGovernanceSeams } from "./boot/governance-seams.js";
57
57
  import { assertRetentionLaneWirable } from "./boot/retention-lane.js";
@@ -352,6 +352,7 @@ export async function runLocal(argv, deps = {}) {
352
352
  const metrics = createMetrics();
353
353
  const memoryEngineIncident = createMemoryEngineIncidentSink({ logger, metrics });
354
354
  installSecretEnvScrubRecorder({ logger, metrics });
355
+ installDefaultEngineNoticeSeat(logger, metrics);
355
356
  logConfigDiagnostics(logger);
356
357
  provisionSemaConfigDir(config, logger);
357
358
  installOutboundDispatcher(logger);
package/dist/runs.js CHANGED
@@ -69,6 +69,9 @@ export function markChildrenStoppedByUserOnAbort(signal, taskId, principal) {
69
69
  }
70
70
  signal.addEventListener("abort", mark, { once: true });
71
71
  }
72
+ const EXECUTION_ENV_REFUSAL_HTTP_STATUS = {
73
+ "env.home_unconfirmed": 409,
74
+ };
72
75
  const PREPARE_REFUSAL_HTTP_STATUS = {
73
76
  "resume.session_not_found": 404,
74
77
  "rewind_snapshot.unresolvable": 422,
@@ -93,6 +96,7 @@ const PREPARE_REFUSAL_HTTP_STATUS = {
93
96
  "config.memory_project_marker_nested": 409,
94
97
  "config.read_deny_floor_unkept": 409,
95
98
  "config.mcp_server_name_collision": 409,
99
+ ...EXECUTION_ENV_REFUSAL_HTTP_STATUS,
96
100
  };
97
101
  export function resumeAtHttpStatus(result) {
98
102
  const code = failureCodeOf(result.terminal);
@@ -8,7 +8,6 @@ import type { ClientSessionRowWrite, ReplaySessionRowWrite, ReplaySessionRowWrit
8
8
  import type { ExecutionPlacementSeat } from "./execution-placement.js";
9
9
  import type { SessionUnreadable } from "./session-error-wire.js";
10
10
  import type { ForkTarget, ForkToOutcome } from "./plugins/session-fork.js";
11
- import { type OwnerlessClaim } from "./delegated-session-owner.js";
12
11
  /** One row of the session-list projection (CC /resume picker) — the durable session abstraction's enumeration
13
12
  * output (§0.5). Mirrors {@link RunStore}'s shipped `SessionSummary` shape so the wire response is unchanged. */
14
13
  export interface SessionListItem {
@@ -183,9 +182,6 @@ export declare function legacyUnownedAmnestyOpen(deployment: DeploymentShapeInpu
183
182
  export type OwnerAwareSessionStore = SessionStore & {
184
183
  /** 属主登记(core `SessionStore.register` 的契约:对没有行的 id 建行并记下属主,首写赢;对已有的行什么都不做)。读回走 `headOf`。 */
185
184
  register?: (sessionId: string, owner: string | null) => Promise<void>;
186
- /** [ref] 委派继承律的**认领口** —— 只在记录委派树的店上(三只持久会话店,只命中 placed 行;不记委派树的店没有):判断时读到的那一位住客仍是无主的 placed 行 ⇒ 写 `owner`,
187
- * 返回是否写成(只 CAS 子行自己)。唯一调用方 = 停驻挂点的 `settleDelegatedOwner`(`delegated-session-owner.ts`;M23 走同一句 SQL);规则写在那个模块。 */
188
- claimOwnerless?: OwnerlessClaim;
189
185
  /** [ref] placed 分区的元组读口(店的既有读口,一行 PK 读;`undefined` = 普通会话或无此行)。店不交它 = 不实现 placement = 没有 placed 行(无 repo 的内存店)。两个读者:
190
186
  * ① 会话面的属主门只在属主为 NULL 时读它一次(无主的 placed 行不是公共行,`http/principal-gate.ts` 的 `sessionOwnerCellOf`);
191
187
  * ② 会话视图读它投影体顶层 `agent` 键(`audit.ts` 的 `sessionAgentOf`,只在视图读上付):店没有这一面 ⇒ 键缺席,读面不凭别处另铸子代身份。 */