@zhushanwen/pi-session-reader 0.10.4 → 0.12.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 (41) hide show
  1. package/package.json +7 -6
  2. package/src/__tests__/cross-package-subagent-core.test.ts +34 -25
  3. package/src/__tests__/entry-anchor.test.ts +3 -3
  4. package/src/__tests__/execution-tree.test.ts +85 -86
  5. package/src/__tests__/family-zcode.test.ts +24 -14
  6. package/src/__tests__/family.test.ts +4 -4
  7. package/src/__tests__/find.test.ts +50 -5
  8. package/src/__tests__/index.test.ts +14 -3
  9. package/src/__tests__/rebuild-manifest-dual-write.test.ts +29 -22
  10. package/src/__tests__/render.test.ts +2 -2
  11. package/src/__tests__/result.test.ts +20 -11
  12. package/src/__tests__/roots.test.ts +64 -26
  13. package/src/__tests__/subagents.test.ts +108 -52
  14. package/src/__tests__/summary.test.ts +223 -0
  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/entry-anchor.ts +5 -5
  25. package/src/discovery/find.ts +9 -1
  26. package/src/discovery/roots.ts +95 -7
  27. package/src/discovery/subagents.ts +24 -17
  28. package/src/discovery/whitelist.ts +4 -4
  29. package/src/discovery/workflows.ts +8 -8
  30. package/src/discovery/zcode-manifest.ts +22 -27
  31. package/src/extract.ts +1 -1
  32. package/src/index.ts +12 -4
  33. package/src/no-match.ts +1 -1
  34. package/src/result-action.ts +1 -1
  35. package/src/search-across.ts +3 -3
  36. package/src/summary.ts +178 -0
  37. package/src/tool-format.ts +12 -12
  38. package/src/tool-handler.ts +27 -19
  39. package/src/tui/session-command.ts +1 -1
  40. package/src/tui/tool-render.ts +48 -0
  41. package/src/zcode-anchor-classify.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-session-reader",
3
- "version": "0.10.4",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "taiji": {
@@ -16,15 +16,16 @@
16
16
  "pi-package"
17
17
  ],
18
18
  "dependencies": {
19
- "@zhushanwen/pi-extension-logger": "0.6.2",
19
+ "@zhushanwen/extension-protocol": "0.22.0",
20
20
  "@zhushanwen/pi-ext-guards": "0.5.0",
21
+ "@zhushanwen/pi-extension-logger": "0.6.2",
21
22
  "@zhushanwen/session-core": "0.12.0",
22
23
  "@zhushanwen/zcode-session-source": "0.2.7"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@vitest/coverage-v8": "^4.1.9",
26
27
  "vitest": "^4.1.8",
27
- "@zhushanwen/subagent-core": "1.5.2"
28
+ "@zhushanwen/subagent-core": "1.6.0"
28
29
  },
29
30
  "files": [
30
31
  "index.ts",
@@ -32,16 +33,16 @@
32
33
  "vitest.config.ts"
33
34
  ],
34
35
  "peerDependencies": {
35
- "@earendil-works/pi-coding-agent": "^1.0.0",
36
36
  "@earendil-works/pi-ai": "^1.0.0",
37
+ "@earendil-works/pi-coding-agent": "^1.0.0",
37
38
  "@earendil-works/pi-tui": "^1.0.0",
38
39
  "typebox": "1.3.27"
39
40
  },
40
41
  "peerDependenciesMeta": {
41
- "@earendil-works/pi-coding-agent": {
42
+ "@earendil-works/pi-ai": {
42
43
  "optional": true
43
44
  },
44
- "@earendil-works/pi-ai": {
45
+ "@earendil-works/pi-coding-agent": {
45
46
  "optional": true
46
47
  },
47
48
  "@earendil-works/pi-tui": {
@@ -21,16 +21,16 @@ import {
21
21
  } from '@zhushanwen/subagent-core/execution/assembly/path-encoding.ts'
22
22
 
23
23
  /**
24
- * W4 跨包集成测试([collect 退役] 改造保留:原「E1 落标出口产出 manifest」驱动面
25
- * 随 sync 批机制删除,现行真实投影出口 = store 重建通道 rebuildIndexes——manifest
26
- * 是可丢缓存,boot 全量腿按磁盘子文件锚幂等补缺)。「subagent-core 真实 tmpdir
24
+ * W4 跨包集成测试([collect 退役] 改造保留:原「E1 落标出口产出 manifest」驱动侧
25
+ * 随 sync 批机制删除,现行真实派生视图出口 = store 重建通道 rebuildIndexes——manifest
26
+ * 是可丢缓存,boot 全量路径按磁盘子文件锚幂等补缺)。「subagent-core 真实 tmpdir
27
27
  * 产出磁盘状态 → session-reader result action sa- id 反查,不 mock manifest 写入」
28
28
  * 的跨包契约保持不变。
29
29
  *
30
30
  * 与 result.test.ts 的分工:result.test.ts 的 fixture **手工预写** manifest;本文件
31
- * manifest 内容必须来自投影代码路径——subagent-core 侧真实驱动(真实 RecordStore
31
+ * manifest 内容必须来自派生视图代码路径——subagent-core 侧真实驱动(真实 RecordStore
32
32
  * 落盘 register/轮终 entry → initSession 内 orphan 覆写真实跑 → rebuildIndexes
33
- * derivedManifestRecord 投影真写 records/<sa-id>.json),session-reader 侧零 mock
33
+ * derivedManifestRecord 派生视图真写 records/<sa-id>.json),session-reader 侧零 mock
34
34
  * 读同一 tmpdir 断言反查闭环。
35
35
  *
36
36
  * 构造形态取自 subagent-core sync-collect-recovery.test.ts 的 kill -9 同构主用例
@@ -42,17 +42,14 @@ import {
42
42
  * mkdtemp(tmpdir) 自建自删(红线:零真实数据目录触碰)。
43
43
  */
44
44
 
45
- /** 剥身份/relay env(subagent-core 测试纪律同款):身份 env 会让 service 误判自己是
46
- * 子进程跳过恢复扫描,relay env 改道 pi-invocation 的 relay 分支。 */
45
+ /** 剥身份 env(subagent-core 测试纪律同款):身份 env 会让 service 误判自己是
46
+ * 子进程跳过恢复扫描。 */
47
47
  const ENV_KEYS_TO_STRIP = [
48
48
  'PI_SUBAGENT_ROOT_SESSION_ID',
49
49
  'PI_SUBAGENT_SELF_RECORD_ID',
50
50
  'PI_SUBAGENT_DEPTH',
51
51
  'PI_SUBAGENT_ROOT_CWD',
52
52
  'PI_SUBAGENT_FORK_DEPTH',
53
- 'TAIJI_SUBAGENT_RELAY_SOCKET',
54
- 'TAIJI_SUBAGENT_RELAY_NODE',
55
- 'TAIJI_SUBAGENT_RELAY_SCRIPT',
56
53
  ] as const
57
54
 
58
55
  const ROOT_SESSION = 'root-cross-pkg'
@@ -119,30 +116,42 @@ interface MemberSeed {
119
116
  }
120
117
 
121
118
  describe('跨包集成:subagent-core 重建 manifest → session-reader result 反查(W4)', () => {
119
+ let dataDir: string
122
120
  let agentDir: string
123
121
  let mainFile: string
124
122
  let services: SubagentService[]
125
123
 
126
124
  beforeEach(() => {
127
125
  for (const k of ENV_KEYS_TO_STRIP) delete process.env[k]
128
- agentDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cross-pkg-subagent-core-'))
129
- fs.mkdirSync(getSubagentSessionDir(agentDir, agentDir), { recursive: true })
126
+ // 生产拓扑复刻([session-store-layout-inversion 域 9]):agentDir = <dataDir>/agent
127
+ //(session-reader 的 agentDir 语义),subagent-core 写侧经新公式落存储树
128
+ // <dataDir>/ext-sessions/<rootSid>/subagents/——listRecordManifests 双根扫描的新根
129
+ //(dirname(agentDir)/ext-sessions)与写侧落点相遇
130
+ dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cross-pkg-subagent-core-'))
131
+ agentDir = path.join(dataDir, 'agent')
132
+ fs.mkdirSync(agentDir, { recursive: true })
133
+ fs.mkdirSync(getSubagentSessionDir(ROOT_SESSION, dataDir), { recursive: true })
134
+ // 恢复 service 的内部 store 经 getSubagentRecordsDir(resolveStoreRootSessionId())
135
+ //(env dataDir 兜底)解析——钉 env 把写删目标收进本 mkdtemp 树(自建自删红线,
136
+ // subagent-core 同族测试先例 subagent-service-model-switch.test.ts)
137
+ process.env['TAIJI_AGENT_DATA_DIR'] = dataDir
130
138
  mainFile = path.join(agentDir, 'main-session.jsonl')
131
139
  services = []
132
140
  })
133
141
 
134
142
  afterEach(() => {
135
143
  for (const s of services) s.dispose()
136
- fs.rmSync(agentDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
144
+ delete process.env['TAIJI_AGENT_DATA_DIR']
145
+ fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
137
146
  })
138
147
 
139
148
  /** 真实 RecordStore(写文件 pi:种子经真实 register 通路 + v2 条目构造器
140
149
  * (toRegisteredEntryData,[v1 兼容层删除] v1 快照写点 toSubagentRecordEntry 已删)
141
- * 落主文件)。 */
150
+ * 落主文件)。[u-subagent dir 晚绑定] store/manifest 构造 = 目录取值函数。 */
142
151
  function makeSeedStore(): RecordStore {
143
152
  return new RecordStore(
144
- getSubagentSessionDir(agentDir, agentDir),
145
- new ManifestStore(getSubagentRecordsDir(agentDir, agentDir)),
153
+ () => getSubagentSessionDir(ROOT_SESSION, dataDir),
154
+ new ManifestStore(() => getSubagentRecordsDir(ROOT_SESSION, dataDir)),
146
155
  makeWritingPi(mainFile),
147
156
  )
148
157
  }
@@ -172,7 +181,7 @@ describe('跨包集成:subagent-core 重建 manifest → session-reader result
172
181
  * 形态同 subagent-core W1 用例)——**不写**三 sidecar,保证恢复走
173
182
  * finalizeOrphanRecord 真实路径。 */
174
183
  function writeChildSessionFile(m: MemberSeed): string {
175
- const childFile = path.join(getSubagentSessionDir(agentDir, agentDir), `${m.id}.jsonl`)
184
+ const childFile = path.join(getSubagentSessionDir(ROOT_SESSION, dataDir), `${m.id}.jsonl`)
176
185
  const ts = new Date(1000).toISOString()
177
186
  const lines: string[] = [
178
187
  JSON.stringify({ type: 'session', version: 3, id: `sess-${m.id}`, timestamp: ts, cwd: agentDir }),
@@ -202,7 +211,7 @@ describe('跨包集成:subagent-core 重建 manifest → session-reader result
202
211
  data: { id: m.id, agent: '/agents/worker.md', mode: 'background', task: 'cross-package task', startedAt: 1000, rootSessionId: ROOT_SESSION, depth: 0 },
203
212
  }),
204
213
  JSON.stringify({
205
- // 登记 §3.3:主/子文件条目面 = v2 注册条(身份域;运行态事实源在 record 事件文件)。
214
+ // 登记 §3.3:主/子文件条目范围 = v2 注册条(身份域;运行态事实源在 record 事件文件)。
206
215
  type: 'custom', id: `cid-${m.id}-2`, parentId: `cid-${m.id}-1`, timestamp: ts,
207
216
  customType: 'subagent-record',
208
217
  data: {
@@ -247,7 +256,7 @@ describe('跨包集成:subagent-core 重建 manifest → session-reader result
247
256
  }
248
257
 
249
258
  /** 全链路驱动:种子 → 恢复 service(orphan 覆写)→ rebuildIndexes(现行 boot
250
- * 全量重建通道——derivedManifestRecord 投影真写 manifest)。返回子 session 文件
259
+ * 全量重建通道——derivedManifestRecord 派生视图真写 manifest)。返回子 session 文件
251
260
  * 路径表。[collect 退役] 原 E1 落标出口(recoverSyncCollectBatch →
252
261
  * appendBatchFinalizedEntry)随 sync 批机制删除。 */
253
262
  async function driveManifestRebuild(members: MemberSeed[]): Promise<Map<string, string>> {
@@ -259,11 +268,11 @@ describe('跨包集成:subagent-core 重建 manifest → session-reader result
259
268
  }
260
269
  const service = makeRecoveryService()
261
270
  service.rebuildIndexes()
262
- // derivedManifestRecord 投影为同步落盘——全成员 manifest 即刻可见(W3 投影
271
+ // derivedManifestRecord 派生视图为同步落盘——全成员 manifest 即刻可见(W3 派生视图
263
272
  // 代码路径的产物,非手工预写)。
264
273
  for (const m of members) {
265
274
  expect(
266
- fs.existsSync(path.join(getSubagentRecordsDir(agentDir, agentDir), `${m.id}.json`)),
275
+ fs.existsSync(path.join(getSubagentRecordsDir(ROOT_SESSION, dataDir), `${m.id}.json`)),
267
276
  `manifest of ${m.id}`,
268
277
  ).toBe(true)
269
278
  }
@@ -280,12 +289,12 @@ describe('跨包集成:subagent-core 重建 manifest → session-reader result
280
289
  }
281
290
  const childFiles = await driveManifestRebuild([m])
282
291
 
283
- // manifest 内容断言(rebuildIndexes → derivedManifestRecord 投影路径的产物,
284
- // 非手工预写):sessionFile 来自 W1 投影扩展。[U2/U3 两态桥接] 恢复链覆写后成员
285
- // 处于 idle(重建单规则)、无旧终态遗留位 → legacy status 如实投影 "running"
292
+ // manifest 内容断言(rebuildIndexes → derivedManifestRecord 派生视图路径的产物,
293
+ // 非手工预写):sessionFile 来自 W1 派生视图扩展。[U2/U3 两态桥接] 恢复链覆写后成员
294
+ // 处于 idle(重建单规则)、无旧终态遗留位 → legacy status 如实派生 "running"
286
295
  //(legacyManifestStatusFields 单点派生,session-reader 视角的活跃成员)。
287
296
  const manifest = JSON.parse(
288
- fs.readFileSync(path.join(getSubagentRecordsDir(agentDir, agentDir), 'sa-cross-1.json'), 'utf-8'),
297
+ fs.readFileSync(path.join(getSubagentRecordsDir(ROOT_SESSION, dataDir), 'sa-cross-1.json'), 'utf-8'),
289
298
  ) as Record<string, unknown>
290
299
  expect(manifest).toMatchObject({
291
300
  id: 'sa-cross-1',
@@ -370,13 +370,13 @@ describe('候选文件列表:顺序回退 + 命中即止(越界语义由调
370
370
  })
371
371
  })
372
372
 
373
- describe('协议字面量漂移守卫(跨包契约)', () => {
373
+ describe('协议字面量漂移检查(跨包契约)', () => {
374
374
  it('entry-anchor 本地 customType 字面量 === subagent-core 写侧单源', () => {
375
- // 源码本地持有磁盘协议字符串(生产依赖面不引 subagent-core),此处锚定两侧同值
375
+ // 源码本地持有磁盘协议字符串(生产依赖范围不引 subagent-core),此处锚定两侧同值
376
376
  expect(SUBAGENT_RECORD_CUSTOM_TYPE).toBe('subagent-record')
377
377
  })
378
378
 
379
- it('entry-anchor 本地 v2 认识版本 === subagent-core 写侧单源(写侧再升版本本守卫先红)', () => {
379
+ it('entry-anchor 本地 v2 认识版本 === subagent-core 写侧单源(写侧再升版本本检查先红)', () => {
380
380
  expect(SUBAGENT_RECORD_ENTRY_VERSION).toBe(2)
381
381
  })
382
382
  })