@zhushanwen/pi-session-reader 0.11.0 → 0.13.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 (42) hide show
  1. package/package.json +5 -5
  2. package/src/__tests__/archive-ledger.test.ts +447 -0
  3. package/src/__tests__/cross-package-subagent-core.test.ts +34 -25
  4. package/src/__tests__/entry-anchor.test.ts +3 -3
  5. package/src/__tests__/execution-tree.test.ts +85 -86
  6. package/src/__tests__/family-zcode.test.ts +24 -14
  7. package/src/__tests__/family.test.ts +4 -4
  8. package/src/__tests__/find.test.ts +50 -5
  9. package/src/__tests__/index.test.ts +3 -3
  10. package/src/__tests__/rebuild-manifest-dual-write.test.ts +29 -22
  11. package/src/__tests__/render.test.ts +2 -2
  12. package/src/__tests__/result.test.ts +20 -11
  13. package/src/__tests__/roots.test.ts +64 -26
  14. package/src/__tests__/subagents.test.ts +108 -52
  15. package/src/__tests__/tool-handler.test.ts +133 -83
  16. package/src/__tests__/workflow.test.ts +5 -5
  17. package/src/__tests__/zcode-manifest.test.ts +18 -10
  18. package/src/__tests__/zcode-routing.test.ts +21 -19
  19. package/src/__tests__/zcode-whitelist.test.ts +1 -1
  20. package/src/core/family.ts +6 -6
  21. package/src/core/render.ts +2 -2
  22. package/src/core/toolcall.ts +1 -1
  23. package/src/core/workflow.ts +9 -9
  24. package/src/discovery/archive-ledger.ts +221 -0
  25. package/src/discovery/archive-match.ts +84 -0
  26. package/src/discovery/entry-anchor.ts +5 -5
  27. package/src/discovery/find.ts +12 -1
  28. package/src/discovery/roots.ts +149 -14
  29. package/src/discovery/subagents.ts +24 -17
  30. package/src/discovery/whitelist.ts +4 -4
  31. package/src/discovery/workflows.ts +8 -8
  32. package/src/discovery/zcode-manifest.ts +22 -27
  33. package/src/doctor.ts +3 -0
  34. package/src/extract.ts +1 -1
  35. package/src/index.ts +5 -2
  36. package/src/no-match.ts +29 -4
  37. package/src/result-action.ts +7 -1
  38. package/src/search-across.ts +3 -3
  39. package/src/tool-format.ts +24 -13
  40. package/src/tool-handler.ts +68 -29
  41. package/src/tui/session-command.ts +1 -1
  42. package/src/zcode-anchor-classify.ts +2 -2
@@ -11,7 +11,7 @@
11
11
  * 域模块拆分(max-lines 拆分轮机械提取,零行为变更,result-action.ts 先例同型):
12
12
  * result-action.ts(result)/ doctor.ts(doctor + SessionReadSignals)/
13
13
  * search-across.ts(search 管线 + u12 跨会话)/ extract.ts(extract 预设)/
14
- * tool-format.ts(各 action 输出文本渲染 + 错误面 message + F2 消歧与
14
+ * tool-format.ts(各 action 输出文本渲染 + 错误范围 message + F2 消歧与
15
15
  * find 零匹配包装)/ no-match.ts(F1 自检行)/ zcode-anchor-classify.ts(entry
16
16
  * 兜底归因的 entry 级纯判定)/ handler-utils.ts(pad/err/
17
17
  * stripHash/requireStr/SESSION_ID_PREFIX_LEN/turn 索引解析低层小工具)。
@@ -20,13 +20,15 @@
20
20
  * 本模块 re-export 供 index.ts 生产消费)。
21
21
  *
22
22
  * 错误规格 F1-F6:handler 抛 Error(message 含 👉 恢复指引),index.ts 的 execute 闭包
23
- * 原样传播给 pi——pi-agent-core 只对 execute throw 置 isError:true(返回值里的 isError
24
- * 字段被丢弃,agent-loop.js:453-483)。handler 可抛(纯逻辑可测)。
23
+ * 原样传播给 pi——execute throw → agent-loop catch 置 isError:true(pi-agent-core dist/
24
+ * agent-loop.js executePreparedToolCall,1.0.0 实读 catch :581-588;1.0.0 起正常 return 的
25
+ * isError 也被尊重——:579 isError: result.isError === true,0.84.4 才硬编码 false——本包
26
+ * 失败恒走 throw,行为不变)。handler 可抛(纯逻辑可测)。
25
27
  * 例外:F2 多匹配与 F1 find 零匹配「不视为错误」,返回消歧/提示结果而非抛错。
26
28
  */
27
29
  import { existsSync } from 'node:fs'
28
30
  import { mkdir, readFile, stat, writeFile } from 'node:fs/promises'
29
- import { join, isAbsolute } from 'node:path'
31
+ import { join, dirname, isAbsolute } from 'node:path'
30
32
  import { homedir } from 'node:os'
31
33
  import { toErrorMessage } from '@zhushanwen/pi-ext-guards'
32
34
  import { getLogger } from '@zhushanwen/pi-extension-logger'
@@ -38,6 +40,10 @@ import {
38
40
  type SessionMetadataEntry,
39
41
  type SessionMetadataProvider,
40
42
  } from './discovery/find.js'
43
+ import {
44
+ resolveArchiveFragment,
45
+ type ArchiveResolve,
46
+ } from './discovery/archive-match.js'
41
47
  import { resolveSessionRoots } from './discovery/roots.js'
42
48
  import { readSessionHeaderIdSync } from './discovery/session-header.js'
43
49
  import { findZcodeEntryAnchor, type ZcodeAnchor } from './discovery/entry-anchor.js'
@@ -102,7 +108,7 @@ import {
102
108
  extractUserMessages,
103
109
  type ExtractWhat,
104
110
  } from './extract.js'
105
- // 同轮拆分的渲染域模块(format* 纯函数簇,含 zcode 错误面 message 与
111
+ // 同轮拆分的渲染域模块(format* 纯函数簇,含 zcode 错误范围 message 与
106
112
  // zcodeReadErrorMessage 错误映射):运行时依赖方向本模块 → tool-format,
107
113
  // tool-format 对本模块仅 type import(ToolResult),无循环。
108
114
  import {
@@ -122,6 +128,7 @@ import {
122
128
  formatZcodeRecordNotFound,
123
129
  formatZcodeSessionNotFound,
124
130
  isToolResultSummary,
131
+ resolveNonOkResult,
125
132
  zcodeReadErrorMessage,
126
133
  } from './tool-format.js'
127
134
  import { doDoctor, type SessionReadSignals } from './doctor.js'
@@ -132,7 +139,7 @@ import { firstIncompleteAnchorReason } from './zcode-anchor-classify.js'
132
139
  export type { SessionReadSignals }
133
140
 
134
141
  // zcode 读链的结构化日志(「事后排查」通道:degradations 留痕 / L2-L3 恢复成功 /
135
- // anchor-missing 归因——§3.4 可观测性 + P2 降级隔离契约 F13,均不进 LLM 可见面)。
142
+ // anchor-missing 归因——§3.4 可观测性 + P2 降级隔离契约 F13,均不进 LLM 可见部分)。
136
143
  // pi handle 由 index.ts 初始化时 setPiHandle 注入(appendEntry 通道);未注入时
137
144
  // warn/error 降级文件日志,双开关缺省 no-op(纯 pi 独立用户零行为影响)。
138
145
  const logger = getLogger('session-reader')
@@ -215,6 +222,13 @@ export type ResolveResult =
215
222
  zcodeAnchor?: ZcodeAnchor
216
223
  }
217
224
  | { kind: 'multi'; query: string; candidates: MatchedSession[] }
225
+ /**
226
+ * [session-archive D7] 片段命中归档 session(在场零命中后的归档回退层,两支
227
+ * 形态见 ArchiveResolve):消费方(各 do* 的 non-ok 分发 resolveNonOkResult /
228
+ * result-action)统一返回归档态摘要——不读压缩体,头部显式标注归档态
229
+ * (agent 区分摘要与完整历史的硬契约)。
230
+ */
231
+ | ArchiveResolve
218
232
 
219
233
  // readSessionHeaderIdSync(同步读首行 header 取 id,resolveSessionId 形态①/②消费)
220
234
  // 在 discovery/session-header.ts(基座 readFirstJsonlLineSync 的谓词/降级薄包装,G2 单源)。
@@ -267,7 +281,7 @@ async function resolveSessionId(
267
281
 
268
282
  // §3.4 第 0 段:sess_ 形态 id 无第一梯队直读入口(agent 从不持有 sess id,F14;
269
283
  // 且无 zcode 候选发现,D6)→ zcode_param_invalid。pi 的 session id 是 uuid(无
270
- // sess_ 前缀),此检测只改变错误输入的错误面文案。
284
+ // sess_ 前缀),此检测只改变错误输入的错误范围文案。
271
285
  if (session.startsWith('sess_')) {
272
286
  throw err(formatZcodeParamInvalid(session))
273
287
  }
@@ -287,7 +301,7 @@ async function resolveSessionId(
287
301
  * `resolveByRecordId`(engine 缺省/'pi' 形态,行为与今天逐字节一致)。
288
302
  * ③ pi 无命中(今天此处直接 ES2)→ entry 兜底(§3.5 第②层,仅 liveSessionDir 内,
289
303
  * 越界显式失败):命中 → zcode 读链;未命中 → `zcode_record_not_found` + 👉
290
- * (§3.4:sa-id 语境的统一错误面——F14 agent 唯一持有的 id 就是 sa-id,指引动作
304
+ * (§3.4:sa-id 语境的统一错误范围——F14 agent 唯一持有的 id 就是 sa-id,指引动作
291
305
  * 覆盖除 find 指针外的全部恢复路径(find 按 §3.4 刻意不保留)。
292
306
  */
293
307
  async function resolveSaIdRoute(
@@ -299,7 +313,7 @@ async function resolveSaIdRoute(
299
313
  ): Promise<ResolveResult> {
300
314
  const zm = await readZcodeManifest(agentDir, session)
301
315
  if (zm.kind === 'anchor-missing') {
302
- // 归因进结构化日志不进 LLM 可见面(§3.4):reason 逐键归因(missing-engineHandle /
316
+ // 归因进结构化日志不进 LLM 可见部分(§3.4):reason 逐键归因(missing-engineHandle /
303
317
  // missing-sessionRef / missing-sessionId / missing-dbPath,见 zcode-manifest.ts)
304
318
  logger.warn('zcode manifest anchor-missing', { saId: session, reason: zm.reason })
305
319
  throw err(formatZcodeAnchorMissing(session))
@@ -391,7 +405,7 @@ async function resolveZcodeRoute(
391
405
  const zcodeNodes = await listZcodeManifests(agentDir)
392
406
  const rootSessionId = zcodeNodes.find((n) => n.id === saId)?.rootSessionId
393
407
  // 枚举未含该 id(直读命中与枚举之间 manifest 被迁移的极端窗口)→ 退回锚 sessionId,
394
- // 后续 byId 查不到走既有 not-found 错误面(不静默伪造家族)
408
+ // 后续 byId 查不到走既有 not-found 错误范围(不静默伪造家族)
395
409
  if (rootSessionId === undefined) {
396
410
  return { kind: 'ok', sessionId: anchor.sessionId, fileName: '' }
397
411
  }
@@ -467,6 +481,11 @@ async function resolveByRecordId(session: string, agentDir: string, prefetchedMa
467
481
  * 形态③:其余片段 → findSessions 透传 source/liveSessionDir 沿用 F1(零匹配)/ F2(多匹配消歧)。
468
482
  * liveSessionDir 透传保证片段匹配与 find 同一 roots;F1 自检行同理须含 [live] 根行
469
483
  *(否则 [live]≠[default] 环境下自检行看不到最高优先级根,计数失真)。
484
+ *
485
+ * [session-archive D7] 归档回退层:在场匹配零命中 → 查归档簿记(archive-match)。
486
+ * 在场优先保证既有行为零变化(归档 session 不在 sessions/ 扫描范围,「在场零命中」
487
+ * 与「已归档」对同一 session 互斥成立——D9 不变量 1);命中归档不走 F1(历史引用
488
+ * 不断链,D7 方案否决项「agent 完全不可见」)。
470
489
  */
471
490
  async function resolveByFragment(
472
491
  session: string,
@@ -481,6 +500,10 @@ async function resolveByFragment(
481
500
  }
482
501
  const { matches } = await findSessions(session, agentDir, opts)
483
502
  if (matches.length === 0) {
503
+ // [session-archive D7] 归档回退层:单命中返回归档态、多命中返回归档消歧
504
+ //(实现与匹配口径在 discovery/archive-match.ts);无簿记命中落 F1。
505
+ const archive = await resolveArchiveFragment(session, agentDir)
506
+ if (archive !== undefined) return archive
484
507
  // F1 自检行需要发现层实况:无 options 的 resolveSessionRoots 恒实扫(根扫描无
485
508
  // 缓存——doctor 缓存机已删除,ext-simplify-04 U3),与 find 刚完成的扫描同一数据
486
509
  // 源(roots.ts 薄包装语义);信号包同源(liveSessionDir 透传)——findSessions
@@ -501,8 +524,8 @@ async function resolveByFragment(
501
524
  /**
502
525
  * zcode 锚 → ParseResult(既有 turns/render 管线的统一内容入口)。
503
526
  *
504
- * 检查顺序三段递进(§3.4,实现与测试双锚定):第 1 段存在性 → 第 2 段路径闸
505
- * (whitelist.ts)→ 第 3 段开库与查询(四级恢复阶梯 + schema 已知集闸门在
527
+ * 检查顺序三段递进(§3.4,实现与测试双锚定):第 1 段存在性 → 第 2 段路径门禁
528
+ * (whitelist.ts)→ 第 3 段开库与查询(四级恢复阶梯 + schema 已知集门禁在
506
529
  * openZcodeSessionDb 单点)。读库发生在 ext 进程内(§3.5 关键论断),不经 taiji。
507
530
  */
508
531
  async function loadZcodeParsed(anchor: ZcodeAnchor, agentDir: string): Promise<ParseResult> {
@@ -515,7 +538,7 @@ async function loadZcodeParsed(anchor: ZcodeAnchor, agentDir: string): Promise<P
515
538
  }
516
539
  try {
517
540
  // 恢复成功不再是静默事件(§3.4 硬要求):结构化日志(恢复方式 + db 路径),不进
518
- // LLM 可见面。用 warn 而非 debug——L2/L3 是恢复降级路径,语义表 warn=内部降级与
541
+ // LLM 可见部分。用 warn 而非 debug——L2/L3 是恢复降级路径,语义表 warn=内部降级与
519
542
  // 失败(appendEntry 持久化);debug 缺省 no-op 会让恢复成功重新变回静默事件。
520
543
  if (handle.via !== 'L1-direct') {
521
544
  logger.warn('zcode session db recovered via recovery ladder', {
@@ -545,7 +568,7 @@ async function loadZcodeParsed(anchor: ZcodeAnchor, agentDir: string): Promise<P
545
568
  // converter 保证),totalBytes/skippedLines 恒 0
546
569
  return { entries: normalized.entries, totalBytes: 0, skippedLines: 0, lastLinePartial: false }
547
570
  } catch (e) {
548
- if (e instanceof Error && e.message.startsWith('[zcode_')) throw e // 本函数产出的错误面原样传播
571
+ if (e instanceof Error && e.message.startsWith('[zcode_')) throw e // 本函数产出的错误范围原样传播
549
572
  throw err(zcodeReadErrorMessage(e, agentDir))
550
573
  } finally {
551
574
  handle.dispose()
@@ -719,7 +742,8 @@ async function doFamily(
719
742
  undefined,
720
743
  liveSessionDir,
721
744
  )
722
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
745
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
746
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
723
747
 
724
748
  // recursive=true:嵌套执行树(U7/U8)
725
749
  if (params.recursive) {
@@ -766,7 +790,8 @@ async function doOutline(
766
790
  undefined,
767
791
  liveSessionDir,
768
792
  )
769
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
793
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
794
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
770
795
  const { entries, totalBytes, skippedLines } = await loadParsed(resolved, agentDir)
771
796
  const tree = buildTreeView(entries)
772
797
  const turns = segmentTurns(entries, new Set(tree.leafPath))
@@ -802,7 +827,8 @@ async function doExpand(
802
827
  undefined,
803
828
  liveSessionDir,
804
829
  )
805
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
830
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
831
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
806
832
  const turnIdx = parseTurnIndex(requireStr(params.turn, 'turn', 'expand'))
807
833
  const { entries } = await loadParsed(resolved, agentDir)
808
834
  const tree = buildTreeView(entries)
@@ -835,7 +861,8 @@ async function doDetail(
835
861
  undefined,
836
862
  liveSessionDir,
837
863
  )
838
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
864
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
865
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
839
866
  const range = parseTurnsRange(requireStr(params.turns, 'turns', 'detail'))
840
867
  const { entries } = await loadParsed(resolved, agentDir)
841
868
  const tree = buildTreeView(entries)
@@ -893,7 +920,8 @@ async function doSearch(
893
920
  undefined,
894
921
  signals.liveSessionDir,
895
922
  )
896
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
923
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
924
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
897
925
  const scope = params.scope ?? 'all'
898
926
  const limit = params.limit ?? SEARCH_DEFAULT_LIMIT
899
927
  const { entries } = await loadParsed(resolved, agentDir)
@@ -912,7 +940,7 @@ async function doSearch(
912
940
  /** export full 模式的 entry 分隔线('=' 重复)宽度。 */
913
941
  const EXPORT_SEPARATOR_LEN = 40
914
942
 
915
- /** export:物化摘要到 <agentDir>/tmp/session-view-<id>.md(design §3.4 export,D-8)。 */
943
+ /** export:物化摘要到 <dataDir>/ext-sessions/<id>/session-view.md(session-store-layout-inversion 域 6;文件名去 sid——sid 已在路径)。 */
916
944
  async function doExport(
917
945
  params: SessionReadParams,
918
946
  agentDir: string,
@@ -927,7 +955,8 @@ async function doExport(
927
955
  undefined,
928
956
  liveSessionDir,
929
957
  )
930
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
958
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
959
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
931
960
 
932
961
  let text: string
933
962
  let label: string
@@ -972,8 +1001,16 @@ async function doExport(
972
1001
  label = 'outline'
973
1002
  }
974
1003
 
975
- const outDir = join(agentDir, 'tmp')
976
- const outPath = join(outDir, `session-view-${resolved.sessionId}.md`)
1004
+ // [session-store-layout-inversion 域 6] 导出落 session 存储树(<root>/<sid>/session-view.md,
1005
+ // 文件名去 sid——sid 已在路径):基址 = agentDir 剥一层(taiji dataDir,托管态
1006
+ // PI_CODING_AGENT_DIR = <dataDir>/agent)。session-reader 零 shared 依赖(extension 包
1007
+ // 边界),公式与 shared getSessionViewPath 同字面镜像;sessionId 过 isPiSessionId 同域
1008
+ // 校验(解析器产出的 sid 正常路径恒合法,此处是路径穿越纵深防线)。
1009
+ if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(resolved.sessionId)) {
1010
+ throw err(`导出拒绝:sessionId 形态非法(${resolved.sessionId})👉 检查 session 参数或用 find 重新定位`)
1011
+ }
1012
+ const outDir = join(dirname(agentDir), 'ext-sessions', resolved.sessionId)
1013
+ const outPath = join(outDir, 'session-view.md')
977
1014
  await mkdir(outDir, { recursive: true })
978
1015
  await writeFile(outPath, text, 'utf8')
979
1016
  const sizeBytes = Buffer.byteLength(text, 'utf8')
@@ -996,7 +1033,7 @@ async function doExport(
996
1033
  //(max-lines 拆分轮机械提取);本段保留 F7 what 校验与 doExtract 编排(定位依赖
997
1034
  // tool-handler 私有的 resolveSessionId/safeParse/segmentTurns)。纯提取,不调 LLM。
998
1035
 
999
- /** what 类型守卫(直接比较,避开不安全断言;schema 已校验,此处防御 + 可单测绕过)。 */
1036
+ /** what 类型检查(直接比较,避开不安全断言;schema 已校验,此处防御 + 可单测绕过)。 */
1000
1037
  function isExtractWhat(v: unknown): v is ExtractWhat {
1001
1038
  return (
1002
1039
  v === 'user-messages' ||
@@ -1026,7 +1063,8 @@ async function doExtract(
1026
1063
  undefined,
1027
1064
  liveSessionDir,
1028
1065
  )
1029
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
1066
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
1067
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
1030
1068
  const { entries } = await loadParsed(resolved, agentDir)
1031
1069
  // extract 遍历全量 entry(含旁支/压缩历史),与 outline/expand/detail 的 leaf 视图不同:
1032
1070
  // 素材提取要全量(design §2.3 实测全量 519 toolCall / 26 user / 515 toolResult),
@@ -1109,7 +1147,7 @@ interface SkippedRun {
1109
1147
  * session 会抛「session not found in family index」)——resolveSessionId 已把 session 解析到
1110
1148
  * 真实文件(kind==='ok' 保证文件存在,三形态:绝对路径/sa-id 均 existsSync 校验,片段匹配
1111
1149
  * 来自实际 fs 扫描),直接用 resolved.fileName 构造单条目 sessionIdToPath 调 resolveWorkflows
1112
- *(与 buildFamilyFromFs 步骤 6 的 workflow 腿同源)。pathToRef 传空 Map(单条目链路无其他
1150
+ *(与 buildFamilyFromFs 步骤 6 的 workflow 路径同源)。pathToRef 传空 Map(单条目链路无其他
1113
1151
  * 文件可反查),call 引用 100% 走 sessionRefFromPath 文件名最小回退(sessionId+fileName,
1114
1152
  * 足够 LLM 跳 outline/detail 深读)。
1115
1153
  *
@@ -1131,7 +1169,7 @@ async function renderSingleWorkflowRun(
1131
1169
  ): Promise<void> {
1132
1170
  // [D16③] 概览链换源:v2 档的 stateFile = record 流路径(recordPath 锚点语义
1133
1171
  // 重定义)——直读流经 parseRunRecordStream 概览([D2] 三态 status;空流/读失败
1134
- // 投影 running,活跃 run 概览不退化为 skipped——红线)。record 后缀以外的
1172
+ // 映射 running,活跃 run 概览不退化为 skipped——红线)。record 后缀以外的
1135
1173
  // stateFile(v1 快照档空串 / 旧指针档 link path)走原快照链。
1136
1174
  if (wf.stateFile.endsWith('.record.jsonl')) {
1137
1175
  await renderRunFromRecordStream(wf, ctx)
@@ -1205,7 +1243,8 @@ async function doWorkflow(
1205
1243
  undefined,
1206
1244
  liveSessionDir,
1207
1245
  )
1208
- if (resolved.kind === 'multi') return disambiguate(resolved.query, resolved.candidates)
1246
+ // non-ok 支统一分发(multi 消歧 / 归档态两支,[session-archive D7] 归并点)
1247
+ if (resolved.kind !== 'ok') return resolveNonOkResult(resolved)
1209
1248
 
1210
1249
  let workflows: WorkflowRef[]
1211
1250
  try {
@@ -1391,7 +1430,7 @@ export async function handleSessionRead(
1391
1430
  case 'workflow':
1392
1431
  return doWorkflow(params, agentDir, signals.liveSessionDir)
1393
1432
  case 'result': {
1394
- // per-call 构造注入面(仅剩 tool-handler 文件私有 helper,纯函数经 handler-utils
1433
+ // per-call 构造注入点(仅剩 tool-handler 文件私有 helper,纯函数经 handler-utils
1395
1434
  // 直接 import——ext-simplify-04 E5):resolveSessionId 包装把信号包中的
1396
1435
  // liveSessionDir 闭包进解析调用(ResultActionDeps 接口签名固定 5 参,包装保持
1397
1436
  // 同形、末位补传),result 的片段形态与 find/outline 消费同一 roots
@@ -77,7 +77,7 @@ export function createSessionCommand(
77
77
  const chosen = await ctx.ui.select('选择一个 session 插入 # 引用', labels)
78
78
  if (chosen === undefined) return
79
79
  // chosen 必属 labels(select 契约返回列表成员或 undefined,后者上一行已拦截),
80
- // indexOf 恒命中——不再保留 idx<0 死防御分支
80
+ // indexOf 恒命中——不再保留 idx<0 永不命中的防御机制分支
81
81
  const idx = labels.indexOf(chosen)
82
82
  // 选中后插入 # + 完整 uuid(剥 # 算片段,再补 # 插入编辑器)
83
83
  const frag = toCandidate(top[idx]).insertText.slice(1)
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * 判定与 entry-anchor.ts 的 zcodeAnchorOfEntry 双键判据同构(v1 快照 / v2 终态条
7
7
  * 双版本门),协议字面量同值(写侧 subagent-core record-entry.ts
8
- * SUBAGENT_RECORD_CUSTOM_TYPE,漂移由 entry-anchor.test.ts 守卫)。
8
+ * SUBAGENT_RECORD_CUSTOM_TYPE,漂移由 entry-anchor.test.ts 检查)。
9
9
  */
10
10
  import type { Entry } from '@zhushanwen/session-core'
11
11
  import type { ZcodeAnchorMissingReason } from './discovery/zcode-manifest.js'
@@ -13,7 +13,7 @@ import type { ZcodeAnchorMissingReason } from './discovery/zcode-manifest.js'
13
13
  /**
14
14
  * v2 条目 data 的认识版本词表(W1 起 = 2)。与写侧 subagent-core
15
15
  * SUBAGENT_RECORD_ENTRY_VERSION 同值——本地持有 + entry-anchor.test.ts 跨包断言
16
- * 守卫漂移(生产依赖面不引 subagent-core,同 entry-anchor.ts 的协议字面量模式)。
16
+ * 检查漂移(生产依赖范围不引 subagent-core,同 entry-anchor.ts 的协议字面量模式)。
17
17
  */
18
18
  const SUBAGENT_RECORD_ENTRY_V2 = 2
19
19