@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.
- package/package.json +7 -6
- package/src/__tests__/cross-package-subagent-core.test.ts +34 -25
- package/src/__tests__/entry-anchor.test.ts +3 -3
- package/src/__tests__/execution-tree.test.ts +85 -86
- package/src/__tests__/family-zcode.test.ts +24 -14
- package/src/__tests__/family.test.ts +4 -4
- package/src/__tests__/find.test.ts +50 -5
- package/src/__tests__/index.test.ts +14 -3
- package/src/__tests__/rebuild-manifest-dual-write.test.ts +29 -22
- package/src/__tests__/render.test.ts +2 -2
- package/src/__tests__/result.test.ts +20 -11
- package/src/__tests__/roots.test.ts +64 -26
- package/src/__tests__/subagents.test.ts +108 -52
- package/src/__tests__/summary.test.ts +223 -0
- package/src/__tests__/tool-handler.test.ts +133 -83
- package/src/__tests__/workflow.test.ts +5 -5
- package/src/__tests__/zcode-manifest.test.ts +18 -10
- package/src/__tests__/zcode-routing.test.ts +21 -19
- package/src/__tests__/zcode-whitelist.test.ts +1 -1
- package/src/core/family.ts +6 -6
- package/src/core/render.ts +2 -2
- package/src/core/toolcall.ts +1 -1
- package/src/core/workflow.ts +9 -9
- package/src/discovery/entry-anchor.ts +5 -5
- package/src/discovery/find.ts +9 -1
- package/src/discovery/roots.ts +95 -7
- package/src/discovery/subagents.ts +24 -17
- package/src/discovery/whitelist.ts +4 -4
- package/src/discovery/workflows.ts +8 -8
- package/src/discovery/zcode-manifest.ts +22 -27
- package/src/extract.ts +1 -1
- package/src/index.ts +12 -4
- package/src/no-match.ts +1 -1
- package/src/result-action.ts +1 -1
- package/src/search-across.ts +3 -3
- package/src/summary.ts +178 -0
- package/src/tool-format.ts +12 -12
- package/src/tool-handler.ts +27 -19
- package/src/tui/session-command.ts +1 -1
- package/src/tui/tool-render.ts +48 -0
- package/src/zcode-anchor-classify.ts +2 -2
|
@@ -9,6 +9,11 @@ import {
|
|
|
9
9
|
type SessionReadParams,
|
|
10
10
|
type SessionReadSignals,
|
|
11
11
|
} from '../tool-handler.js'
|
|
12
|
+
import {
|
|
13
|
+
resolveSessionStoreRoot,
|
|
14
|
+
resolveStoreSubagentSessionsDir,
|
|
15
|
+
resolveStoreSubagentRecordsDir,
|
|
16
|
+
} from '../discovery/roots.js'
|
|
12
17
|
import { setPiHandle } from '@zhushanwen/pi-extension-logger'
|
|
13
18
|
import { type DoctorDetails } from '../doctor.js'
|
|
14
19
|
import { levenshtein } from '../no-match.js'
|
|
@@ -41,7 +46,8 @@ vi.mock('node:os', async (importOriginal) => {
|
|
|
41
46
|
*/
|
|
42
47
|
|
|
43
48
|
describe('handleSessionRead', () => {
|
|
44
|
-
let
|
|
49
|
+
let dataDir: string
|
|
50
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻)
|
|
45
51
|
const SLUG = '--demo-cwd--'
|
|
46
52
|
// 家族 fixture 组:根 FAM → fork 子代 FORK(跨 cwd,parentSession 指 FAM 文件)
|
|
47
53
|
// → 隔代 subagent SUB2(rootSessionId=FORK)。前缀互异,断言按 id 精确区分层级。
|
|
@@ -106,11 +112,12 @@ describe('handleSessionRead', () => {
|
|
|
106
112
|
}
|
|
107
113
|
|
|
108
114
|
beforeEach(async () => {
|
|
109
|
-
|
|
115
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-main-'))
|
|
116
|
+
dir = join(dataDir, 'agent')
|
|
110
117
|
await writeMainFixtures()
|
|
111
118
|
})
|
|
112
119
|
afterEach(async () => {
|
|
113
|
-
await rm(
|
|
120
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
114
121
|
})
|
|
115
122
|
|
|
116
123
|
it('1. find by uuid fragment returns matching session', async () => {
|
|
@@ -158,21 +165,22 @@ describe('handleSessionRead', () => {
|
|
|
158
165
|
expect(typeof d.hits[0].matchSnippet).toBe('string')
|
|
159
166
|
})
|
|
160
167
|
|
|
161
|
-
it('6. export outline materializes a .md file', async () => {
|
|
162
|
-
// export 写目标 = <
|
|
163
|
-
//
|
|
164
|
-
|
|
168
|
+
it('6. export outline materializes a .md file under the session store tree', async () => {
|
|
169
|
+
// export 写目标 = <dataDir>/ext-sessions/<sid>/session-view.md(session-store-layout-
|
|
170
|
+
// inversion 域 6):agentDir 参数 = <dataDir>/agent(mkdtemp tmp 子目录,导出树随 tmp
|
|
171
|
+
// 整体清理);读侧经 liveSessionDir=[live] 根扫本 describe 的 fixture sessions 目录。
|
|
172
|
+
const dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-export-'))
|
|
165
173
|
try {
|
|
166
174
|
const r = await handleSessionRead(
|
|
167
175
|
{ action: 'export', session: FIX_E6, format: 'outline' },
|
|
168
|
-
{ agentDir:
|
|
176
|
+
{ agentDir: join(dataDir, 'agent'), liveSessionDir: join(dir, 'sessions') },
|
|
169
177
|
)
|
|
170
178
|
const d = r.details as { path: string; sizeBytes: number }
|
|
171
|
-
expect(d.path).
|
|
179
|
+
expect(d.path).toBe(join(dataDir, 'ext-sessions', FIX_E6, 'session-view.md'))
|
|
172
180
|
expect(existsSync(d.path)).toBe(true)
|
|
173
181
|
expect(d.sizeBytes).toBeGreaterThan(0)
|
|
174
182
|
} finally {
|
|
175
|
-
await rm(
|
|
183
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
176
184
|
}
|
|
177
185
|
})
|
|
178
186
|
|
|
@@ -213,6 +221,7 @@ describe('handleSessionRead', () => {
|
|
|
213
221
|
|
|
214
222
|
describe('extract (v2 O4)', () => {
|
|
215
223
|
let dir: string
|
|
224
|
+
let dataDir: string
|
|
216
225
|
const SLUG = '--demo-cwd--'
|
|
217
226
|
|
|
218
227
|
/**
|
|
@@ -308,11 +317,12 @@ describe('extract (v2 O4)', () => {
|
|
|
308
317
|
}
|
|
309
318
|
|
|
310
319
|
beforeEach(async () => {
|
|
311
|
-
|
|
320
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-extract-'))
|
|
321
|
+
dir = join(dataDir, 'agent')
|
|
312
322
|
await writeExtractFixture()
|
|
313
323
|
})
|
|
314
324
|
afterEach(async () => {
|
|
315
|
-
await rm(
|
|
325
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
316
326
|
})
|
|
317
327
|
|
|
318
328
|
it('user-messages returns all user entries with turn + full text', async () => {
|
|
@@ -514,13 +524,15 @@ async function makeFixtureSession(
|
|
|
514
524
|
}
|
|
515
525
|
|
|
516
526
|
describe('F2 多匹配消歧(fixture,MF-9)', () => {
|
|
517
|
-
let
|
|
527
|
+
let dataDir: string
|
|
528
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻,[session-store-layout-inversion])
|
|
518
529
|
|
|
519
530
|
beforeEach(async () => {
|
|
520
|
-
|
|
531
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-f2-'))
|
|
532
|
+
dir = join(dataDir, 'agent')
|
|
521
533
|
})
|
|
522
534
|
afterEach(async () => {
|
|
523
|
-
await rm(
|
|
535
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
524
536
|
})
|
|
525
537
|
|
|
526
538
|
it('共享 uuid 片段 → 不抛错,content 含两候选 + 👉,details.ambiguous=true', async () => {
|
|
@@ -564,13 +576,15 @@ describe('F2 多匹配消歧(fixture,MF-9)', () => {
|
|
|
564
576
|
|
|
565
577
|
describe('outline skippedLines 报告(fixture,D8d 有检测必有报告)', () => {
|
|
566
578
|
let dir: string
|
|
579
|
+
let dataDir: string
|
|
567
580
|
const SID = '019e6c96-bbbb-cccc-dddd-00000000000b'
|
|
568
581
|
|
|
569
582
|
beforeEach(async () => {
|
|
570
|
-
|
|
583
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-skipped-'))
|
|
584
|
+
dir = join(dataDir, 'agent')
|
|
571
585
|
})
|
|
572
586
|
afterEach(async () => {
|
|
573
|
-
await rm(
|
|
587
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
574
588
|
})
|
|
575
589
|
|
|
576
590
|
it('坏行计入 stats.skippedLines 且文本尾部可见(不静默跳过)', async () => {
|
|
@@ -612,6 +626,7 @@ describe('outline skippedLines 报告(fixture,D8d 有检测必有报告)',
|
|
|
612
626
|
// ============================================================
|
|
613
627
|
describe('E7 行渲染统一(outline 与 export outline 同一渲染行)', () => {
|
|
614
628
|
let dir: string
|
|
629
|
+
let dataDir: string
|
|
615
630
|
const SID = '019e6c96-bbbb-cccc-dddd-00000000000e'
|
|
616
631
|
|
|
617
632
|
/** 写含 fork 旁支的 session:主链 R→m1(user)→m2(assistant),b1/b2 挂 m1 下成
|
|
@@ -637,10 +652,11 @@ describe('E7 行渲染统一(outline 与 export outline 同一渲染行)', (
|
|
|
637
652
|
}
|
|
638
653
|
|
|
639
654
|
beforeEach(async () => {
|
|
640
|
-
|
|
655
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-e7-'))
|
|
656
|
+
dir = join(dataDir, 'agent')
|
|
641
657
|
})
|
|
642
658
|
afterEach(async () => {
|
|
643
|
-
await rm(
|
|
659
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
644
660
|
})
|
|
645
661
|
|
|
646
662
|
it('① allBranches:true → 旁支行标记 `[旁支 N entries]`(N=forkPoint 旁支子树 entry 数)+ 尾段同改前语义', async () => {
|
|
@@ -677,14 +693,16 @@ describe('E7 行渲染统一(outline 与 export outline 同一渲染行)', (
|
|
|
677
693
|
|
|
678
694
|
describe('search 灾难性正则降级 + abort(fixture,MF-5 回归)', () => {
|
|
679
695
|
let dir: string
|
|
696
|
+
let dataDir: string
|
|
680
697
|
const SID = '019e6c96-bbbb-cccc-dddd-00000000000a'
|
|
681
698
|
|
|
682
699
|
beforeEach(async () => {
|
|
683
|
-
|
|
700
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-search-'))
|
|
701
|
+
dir = join(dataDir, 'agent')
|
|
684
702
|
await makeFixtureSession(dir, SID, 'aaa plugin 内容')
|
|
685
703
|
})
|
|
686
704
|
afterEach(async () => {
|
|
687
|
-
await rm(
|
|
705
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
688
706
|
})
|
|
689
707
|
|
|
690
708
|
it('嵌套量词 pattern (a+)+ → 降级字面子串(不挂死,零命中)', async () => {
|
|
@@ -786,13 +804,15 @@ describe('search 灾难性正则降级 + abort(fixture,MF-5 回归)', () =
|
|
|
786
804
|
|
|
787
805
|
describe('extract commits 双路径(fixture:git-cmd 主路径 + commit-context 次路径 + 去重)', () => {
|
|
788
806
|
let dir: string
|
|
807
|
+
let dataDir: string
|
|
789
808
|
const SID = '019e6c96-bbbb-cccc-dddd-00000000000c'
|
|
790
809
|
|
|
791
810
|
beforeEach(async () => {
|
|
792
|
-
|
|
811
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-commits-'))
|
|
812
|
+
dir = join(dataDir, 'agent')
|
|
793
813
|
})
|
|
794
814
|
afterEach(async () => {
|
|
795
|
-
await rm(
|
|
815
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
796
816
|
})
|
|
797
817
|
|
|
798
818
|
/** 写单个 session JSONL(commits 用例行内联传入)。 */
|
|
@@ -975,7 +995,7 @@ describe('renderExtractItems F9 截断(S3 首项超大 + S4 文案)', () =>
|
|
|
975
995
|
|
|
976
996
|
// ============================================================
|
|
977
997
|
// w2 新增:resolveSessionId 三形态(① 绝对路径 / ② sa-id / ③ findSessions 透传 source)
|
|
978
|
-
// + ES1/ES2 错误契约 + source 透传 +
|
|
998
|
+
// + ES1/ES2 错误契约 + source 透传 + 真实数据检查(design TC2-TC18)
|
|
979
999
|
// ============================================================
|
|
980
1000
|
|
|
981
1001
|
/**
|
|
@@ -996,10 +1016,12 @@ async function makeFixtureSubagent(
|
|
|
996
1016
|
slug?: string
|
|
997
1017
|
},
|
|
998
1018
|
): Promise<string> {
|
|
999
|
-
|
|
1000
|
-
|
|
1019
|
+
// [session-store-layout-inversion] 新布局存储树:分区键 = rootSessionId(无 encodeCwd
|
|
1020
|
+
// 目录层,opts.slug 已无布局语义,保留入参兼容旧调用点但不参与路径推导)
|
|
1021
|
+
const rootSid = opts.rootSessionId ?? 'root-session-1'
|
|
1022
|
+
const sessionFile = join(resolveStoreSubagentSessionsDir(dir, rootSid), `${opts.realSessionId}.jsonl`)
|
|
1001
1023
|
if (opts.sessionFileExists !== false) {
|
|
1002
|
-
await mkdir(
|
|
1024
|
+
await mkdir(resolveStoreSubagentSessionsDir(dir, rootSid), { recursive: true })
|
|
1003
1025
|
const lines = [
|
|
1004
1026
|
JSON.stringify({ type: 'session', id: opts.realSessionId, cwd: '/demo' }),
|
|
1005
1027
|
JSON.stringify({
|
|
@@ -1014,13 +1036,13 @@ async function makeFixtureSubagent(
|
|
|
1014
1036
|
]
|
|
1015
1037
|
await writeFile(sessionFile, lines.join('\n') + '\n')
|
|
1016
1038
|
}
|
|
1017
|
-
const recordsDir =
|
|
1039
|
+
const recordsDir = resolveStoreSubagentRecordsDir(dir, rootSid)
|
|
1018
1040
|
await mkdir(recordsDir, { recursive: true })
|
|
1019
1041
|
await writeFile(
|
|
1020
1042
|
join(recordsDir, `${saId}.json`),
|
|
1021
1043
|
JSON.stringify({
|
|
1022
1044
|
id: saId,
|
|
1023
|
-
rootSessionId:
|
|
1045
|
+
rootSessionId: rootSid,
|
|
1024
1046
|
agentName: opts.agentName ?? 'explorer',
|
|
1025
1047
|
sessionFile,
|
|
1026
1048
|
}),
|
|
@@ -1029,13 +1051,15 @@ async function makeFixtureSubagent(
|
|
|
1029
1051
|
}
|
|
1030
1052
|
|
|
1031
1053
|
describe('resolveSessionId ① 绝对路径形态(w2 TC2-TC6)', () => {
|
|
1032
|
-
let
|
|
1054
|
+
let dataDir: string
|
|
1055
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻,[session-store-layout-inversion])
|
|
1033
1056
|
|
|
1034
1057
|
beforeEach(async () => {
|
|
1035
|
-
|
|
1058
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-path-'))
|
|
1059
|
+
dir = join(dataDir, 'agent')
|
|
1036
1060
|
})
|
|
1037
1061
|
afterEach(async () => {
|
|
1038
|
-
await rm(
|
|
1062
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
1039
1063
|
})
|
|
1040
1064
|
|
|
1041
1065
|
it('TC2: 绝对路径 outline → sessionId=header 真实 id(非文件名)', async () => {
|
|
@@ -1138,13 +1162,15 @@ describe('resolveSessionId ① 绝对路径形态(w2 TC2-TC6)', () => {
|
|
|
1138
1162
|
})
|
|
1139
1163
|
|
|
1140
1164
|
describe('resolveSessionId ② sa-id 形态(w2 TC7-TC10 + CQ3)', () => {
|
|
1141
|
-
let
|
|
1165
|
+
let dataDir: string
|
|
1166
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻,[session-store-layout-inversion])
|
|
1142
1167
|
|
|
1143
1168
|
beforeEach(async () => {
|
|
1144
|
-
|
|
1169
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-said-'))
|
|
1170
|
+
dir = join(dataDir, 'agent')
|
|
1145
1171
|
})
|
|
1146
1172
|
afterEach(async () => {
|
|
1147
|
-
await rm(
|
|
1173
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
1148
1174
|
})
|
|
1149
1175
|
|
|
1150
1176
|
it('TC7: sa-id 恰 1 命中 + sessionFile 存在 → 成功,sessionId=header 真实 id(非 sa-)', async () => {
|
|
@@ -1183,7 +1209,7 @@ describe('resolveSessionId ② sa-id 形态(w2 TC7-TC10 + CQ3)', () => {
|
|
|
1183
1209
|
}
|
|
1184
1210
|
})
|
|
1185
1211
|
|
|
1186
|
-
it('TC9: sa-id 0 命中(可能 running)→ zcode_record_not_found(U9:sa-id
|
|
1212
|
+
it('TC9: sa-id 0 命中(可能 running)→ zcode_record_not_found(U9:sa-id 语境统一错误范围,含 family 指引 + 👉)', async () => {
|
|
1187
1213
|
await expect(
|
|
1188
1214
|
handleSessionRead({ action: 'outline', session: 'sa-nonexist-9999' }, { agentDir: dir }),
|
|
1189
1215
|
).rejects.toThrow('zcode_record_not_found')
|
|
@@ -1197,7 +1223,7 @@ describe('resolveSessionId ② sa-id 形态(w2 TC7-TC10 + CQ3)', () => {
|
|
|
1197
1223
|
}
|
|
1198
1224
|
})
|
|
1199
1225
|
|
|
1200
|
-
it('TC10: sa-id 片段输入(精确相等不命中)→ zcode_record_not_found + 完整 sa- id 重试指引(U9
|
|
1226
|
+
it('TC10: sa-id 片段输入(精确相等不命中)→ zcode_record_not_found + 完整 sa- id 重试指引(U9 统一错误范围)', async () => {
|
|
1201
1227
|
await makeFixtureSubagent(dir, 'sa-c8c8dfa8', {
|
|
1202
1228
|
realSessionId: '019e6c96-dddd-eeee-ffff-0000000010a3',
|
|
1203
1229
|
})
|
|
@@ -1212,24 +1238,26 @@ describe('resolveSessionId ② sa-id 形态(w2 TC7-TC10 + CQ3)', () => {
|
|
|
1212
1238
|
})
|
|
1213
1239
|
|
|
1214
1240
|
it('sa-id 多 manifest 命中(数据异常)→ ES2 ambiguous(C4)', async () => {
|
|
1215
|
-
// 同 sa-id 的两个 manifest(不同
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
['
|
|
1241
|
+
// 同 sa-id 的两个 manifest(不同 rootSessionId 分区,模拟数据异常——新布局分区键 =
|
|
1242
|
+
// rootSessionId,同分区同名 manifest 会互覆,双 manifest 只能跨分区构造)
|
|
1243
|
+
for (const [rootSid, realId] of [
|
|
1244
|
+
['0aaaaaaa-bbbb-7ccc-dddd-0000000004r1', '019e6c96-dddd-eeee-ffff-0000000004a4'],
|
|
1245
|
+
['0aaaaaaa-bbbb-7ccc-dddd-0000000004r2', '019e6c96-dddd-eeee-ffff-0000000004a5'],
|
|
1219
1246
|
] as const) {
|
|
1220
|
-
const
|
|
1221
|
-
|
|
1247
|
+
const sessionsDir = resolveStoreSubagentSessionsDir(dir, rootSid)
|
|
1248
|
+
const sessionFile = join(sessionsDir, `${realId}.jsonl`)
|
|
1249
|
+
await mkdir(sessionsDir, { recursive: true })
|
|
1222
1250
|
await writeFile(
|
|
1223
1251
|
sessionFile,
|
|
1224
1252
|
JSON.stringify({ type: 'session', id: realId, cwd: '/demo' }) + '\n',
|
|
1225
1253
|
)
|
|
1226
|
-
const recordsDir =
|
|
1254
|
+
const recordsDir = resolveStoreSubagentRecordsDir(dir, rootSid)
|
|
1227
1255
|
await mkdir(recordsDir, { recursive: true })
|
|
1228
1256
|
await writeFile(
|
|
1229
1257
|
join(recordsDir, 'sa-dup.json'),
|
|
1230
1258
|
JSON.stringify({
|
|
1231
1259
|
id: 'sa-dup',
|
|
1232
|
-
rootSessionId:
|
|
1260
|
+
rootSessionId: rootSid,
|
|
1233
1261
|
agentName: 'a',
|
|
1234
1262
|
sessionFile,
|
|
1235
1263
|
}),
|
|
@@ -1242,11 +1270,11 @@ describe('resolveSessionId ② sa-id 形态(w2 TC7-TC10 + CQ3)', () => {
|
|
|
1242
1270
|
|
|
1243
1271
|
it('CQ3: sa-id 命中但 sessionFile header 读不出 → F6 风格(不降级 record.id 当 sessionId)', async () => {
|
|
1244
1272
|
// sessionFile 存在但首行非 session header → readSessionHeaderIdSync 返 undefined
|
|
1245
|
-
const
|
|
1246
|
-
const sessionFile = join(dir,
|
|
1247
|
-
await mkdir(
|
|
1273
|
+
const rootSid = 'r' // 合法 pi sid 形态(单字符分区)
|
|
1274
|
+
const sessionFile = join(resolveStoreSubagentSessionsDir(dir, rootSid), 'bad.jsonl')
|
|
1275
|
+
await mkdir(dirname(sessionFile), { recursive: true })
|
|
1248
1276
|
await writeFile(sessionFile, JSON.stringify({ type: 'custom', customType: 'x' }) + '\n')
|
|
1249
|
-
const recordsDir =
|
|
1277
|
+
const recordsDir = resolveStoreSubagentRecordsDir(dir, rootSid)
|
|
1250
1278
|
await mkdir(recordsDir, { recursive: true })
|
|
1251
1279
|
await writeFile(
|
|
1252
1280
|
join(recordsDir, 'sa-bad.json'),
|
|
@@ -1264,7 +1292,7 @@ describe('resolveSessionId ② sa-id 形态(w2 TC7-TC10 + CQ3)', () => {
|
|
|
1264
1292
|
})
|
|
1265
1293
|
})
|
|
1266
1294
|
|
|
1267
|
-
describe('sa-id entry 兜底 v2 终态条锚定(W1 D1
|
|
1295
|
+
describe('sa-id entry 兜底 v2 终态条锚定(W1 D1 版本门补齐,第三层兜底消费方)', () => {
|
|
1268
1296
|
// fixture 骨架对齐 zcode-routing.test.ts:root/agent = agentDir、root/live-sessions =
|
|
1269
1297
|
// liveSessionDir(entry 兜底候选根)、root/engines/zcode/session-db/db.sqlite = 隔离库
|
|
1270
1298
|
// 白名单形态路径(不落文件——正向用例锚命中后进 zcode 读链,在白名单存在性检查处
|
|
@@ -1361,7 +1389,7 @@ describe('sa-id entry 兜底 v2 终态条锚定(W1 D1 版本门补齐,第三
|
|
|
1361
1389
|
await expect(
|
|
1362
1390
|
handleSessionRead({ action: 'outline', session: 'sa-v2-1' }, { agentDir, liveSessionDir: liveDir }),
|
|
1363
1391
|
).rejects.toThrow('zcode_anchor_missing')
|
|
1364
|
-
// 归因进结构化日志不进 LLM
|
|
1392
|
+
// 归因进结构化日志不进 LLM 可见部分(§3.4):firstIncompleteAnchorReason 的 v2 分支
|
|
1365
1393
|
//(tool-handler 唯一消费点)产出 missing-dbPath
|
|
1366
1394
|
expect(logCallsWithReason('missing-dbPath')).toBeGreaterThanOrEqual(1)
|
|
1367
1395
|
})
|
|
@@ -1390,13 +1418,15 @@ describe('sa-id entry 兜底 v2 终态条锚定(W1 D1 版本门补齐,第三
|
|
|
1390
1418
|
})
|
|
1391
1419
|
|
|
1392
1420
|
describe('source 透传(w2 TC12-TC13,依赖 w1 findSessions opts.source)', () => {
|
|
1393
|
-
let
|
|
1421
|
+
let dataDir: string
|
|
1422
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻,[session-store-layout-inversion])
|
|
1394
1423
|
|
|
1395
1424
|
beforeEach(async () => {
|
|
1396
|
-
|
|
1425
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-src-'))
|
|
1426
|
+
dir = join(dataDir, 'agent')
|
|
1397
1427
|
})
|
|
1398
1428
|
afterEach(async () => {
|
|
1399
|
-
await rm(
|
|
1429
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
1400
1430
|
})
|
|
1401
1431
|
|
|
1402
1432
|
it('TC12: find source 过滤——subagent 只返回 subagent 候选,main 只返回 main', async () => {
|
|
@@ -1466,7 +1496,8 @@ describe('source 透传(w2 TC12-TC13,依赖 w1 findSessions opts.source)',
|
|
|
1466
1496
|
})
|
|
1467
1497
|
|
|
1468
1498
|
describe('subagent sa-id(w2 TC14-TC17,fixture)', () => {
|
|
1469
|
-
let
|
|
1499
|
+
let dataDir: string
|
|
1500
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻)
|
|
1470
1501
|
const TC14_ID = '0aaaaaaa-0000-7000-8000-0000000014a1'
|
|
1471
1502
|
const TC15_ID = '0aaaaaaa-0000-7000-8000-0000000015a2'
|
|
1472
1503
|
|
|
@@ -1478,7 +1509,8 @@ describe('subagent sa-id(w2 TC14-TC17,fixture)', () => {
|
|
|
1478
1509
|
* - TC17 的 manifest sessionFile 指向不存在路径(模拟 .jsonl 被 GC)
|
|
1479
1510
|
*/
|
|
1480
1511
|
beforeEach(async () => {
|
|
1481
|
-
|
|
1512
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-tc14-'))
|
|
1513
|
+
dir = join(dataDir, 'agent')
|
|
1482
1514
|
await makeFixtureSubagent(dir, 'sa-tc14', {
|
|
1483
1515
|
realSessionId: TC14_ID,
|
|
1484
1516
|
firstUserText: 'subagent task',
|
|
@@ -1494,7 +1526,7 @@ describe('subagent sa-id(w2 TC14-TC17,fixture)', () => {
|
|
|
1494
1526
|
})
|
|
1495
1527
|
})
|
|
1496
1528
|
afterEach(async () => {
|
|
1497
|
-
await rm(
|
|
1529
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
1498
1530
|
})
|
|
1499
1531
|
|
|
1500
1532
|
it('TC14: completed subagent sa-id outline 成功(场景 1,sessionId=header 真实 id)', async () => {
|
|
@@ -1511,7 +1543,10 @@ describe('subagent sa-id(w2 TC14-TC17,fixture)', () => {
|
|
|
1511
1543
|
it('TC16: 嵌套后代直接 outline(场景 1c,绝对路径形态读任意节点)', async () => {
|
|
1512
1544
|
// 绝对路径形态直接读(M0 入口不依赖 findSessions)
|
|
1513
1545
|
const r = await handleSessionRead(
|
|
1514
|
-
{
|
|
1546
|
+
{
|
|
1547
|
+
action: 'outline',
|
|
1548
|
+
session: join(resolveStoreSubagentSessionsDir(dir, 'root-session-1'), `${TC14_ID}.jsonl`),
|
|
1549
|
+
},
|
|
1515
1550
|
{ agentDir: dir },
|
|
1516
1551
|
)
|
|
1517
1552
|
expect(((r.details as { turns: unknown[] }).turns).length).toBeGreaterThan(0)
|
|
@@ -1571,13 +1606,15 @@ async function wfStateFile(
|
|
|
1571
1606
|
}
|
|
1572
1607
|
|
|
1573
1608
|
describe('doWorkflow(w6,fixture)', () => {
|
|
1574
|
-
let
|
|
1609
|
+
let dataDir: string
|
|
1610
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻,[session-store-layout-inversion])
|
|
1575
1611
|
|
|
1576
1612
|
beforeEach(async () => {
|
|
1577
|
-
|
|
1613
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-wf-'))
|
|
1614
|
+
dir = join(dataDir, 'agent')
|
|
1578
1615
|
})
|
|
1579
1616
|
afterEach(async () => {
|
|
1580
|
-
await rm(
|
|
1617
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
1581
1618
|
})
|
|
1582
1619
|
|
|
1583
1620
|
// ---- [D16③] v2 record 流档概览(recordPath 锚点 = record 流路径)----
|
|
@@ -1603,7 +1640,7 @@ describe('doWorkflow(w6,fixture)', () => {
|
|
|
1603
1640
|
}
|
|
1604
1641
|
|
|
1605
1642
|
/** 构造 v2 record 流文件(run-created + agent-settled[+终局] 帧提 calls)并写注册条目——
|
|
1606
|
-
* [ADR-0095] v1
|
|
1643
|
+
* [ADR-0095] v1 读侧删除后的单档 fixture 形态(替代 wf-state 快照 + link 指针)。 */
|
|
1607
1644
|
async function wfV2Run(
|
|
1608
1645
|
dir: string,
|
|
1609
1646
|
slug: string,
|
|
@@ -1798,7 +1835,7 @@ describe('doWorkflow(w6,fixture)', () => {
|
|
|
1798
1835
|
expect(text).toContain('family')
|
|
1799
1836
|
})
|
|
1800
1837
|
|
|
1801
|
-
// [ADR-0095] TC-w6-snapshot-skip(快照不可读 → skippedRuns)随 v1
|
|
1838
|
+
// [ADR-0095] TC-w6-snapshot-skip(快照不可读 → skippedRuns)随 v1 读侧删除:
|
|
1802
1839
|
// 发现链收敛 v2 后 run 一律 record 流直读(读失败 → running 兜底不 skipped),
|
|
1803
1840
|
// skippedRuns 快照链对 v2 档不可达。
|
|
1804
1841
|
|
|
@@ -1831,7 +1868,7 @@ describe('doWorkflow(w6,fixture)', () => {
|
|
|
1831
1868
|
const slug = '--wf-subagent--'
|
|
1832
1869
|
// subagent session 放 subagents/ 下(不在 sessions/——buildFamilyFromFs 的 main byId
|
|
1833
1870
|
// 索引外,旧实现 resolveFamily 找不到会抛「session not found in family index」)
|
|
1834
|
-
const subDir =
|
|
1871
|
+
const subDir = resolveStoreSubagentSessionsDir(dir, SUB_WF_ROOT)
|
|
1835
1872
|
await mkdir(subDir, { recursive: true })
|
|
1836
1873
|
const subPath = join(subDir, `${SUB_WF_ROOT}.jsonl`)
|
|
1837
1874
|
await writeFile(
|
|
@@ -1873,12 +1910,14 @@ describe('doWorkflow(w6,fixture)', () => {
|
|
|
1873
1910
|
// ============================================================
|
|
1874
1911
|
|
|
1875
1912
|
describe('doFamily recursive(m3b U8 接入)', () => {
|
|
1876
|
-
let
|
|
1913
|
+
let dataDir: string
|
|
1914
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻)
|
|
1877
1915
|
const MAIN = '019e6c96-cccc-dddd-eeee-000000000001'
|
|
1878
1916
|
const SUB = '019e6c96-cccc-dddd-eeee-000000000002'
|
|
1879
1917
|
|
|
1880
1918
|
beforeEach(async () => {
|
|
1881
|
-
|
|
1919
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-recursive-'))
|
|
1920
|
+
dir = join(dataDir, 'agent')
|
|
1882
1921
|
await makeFixtureSession(dir, MAIN, 'main session 内容')
|
|
1883
1922
|
// 顶层 subagent(rootSessionId=MAIN,无 parentRecordId → flat 回退)
|
|
1884
1923
|
await makeFixtureSubagent(dir, `sa-${SUB.slice(0, 8)}`, {
|
|
@@ -1889,7 +1928,7 @@ describe('doFamily recursive(m3b U8 接入)', () => {
|
|
|
1889
1928
|
})
|
|
1890
1929
|
})
|
|
1891
1930
|
afterEach(async () => {
|
|
1892
|
-
await rm(
|
|
1931
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
1893
1932
|
})
|
|
1894
1933
|
|
|
1895
1934
|
it('TC-m3b-dofamily-recursive-false:不传 recursive → flat family(m0-m2 零回归)', async () => {
|
|
@@ -1939,20 +1978,22 @@ describe('doFamily recursive(m3b U8 接入)', () => {
|
|
|
1939
1978
|
|
|
1940
1979
|
// ===========================================================================
|
|
1941
1980
|
// ext-simplify-04 U4/E2(D2②):formatFamilyText subagents 行富字段展示
|
|
1942
|
-
// status 终态短标签 + agent 名 + task 截 60 摘要;孤儿只标 [已清理];P4
|
|
1981
|
+
// status 终态短标签 + agent 名 + task 截 60 摘要;孤儿只标 [已清理];P4 输出量级检查。
|
|
1943
1982
|
// ===========================================================================
|
|
1944
1983
|
|
|
1945
1984
|
describe('formatFamilyText subagents 行富字段(ext-simplify-04 U4 / E2)', () => {
|
|
1946
1985
|
const MAIN = '0aaaaaaa-bbbb-7ccc-dddd-0000000000f1'
|
|
1947
1986
|
const SUB = '0aaaaaaa-bbbb-7ccc-dddd-0000000000f2'
|
|
1948
|
-
let
|
|
1987
|
+
let dataDir: string
|
|
1988
|
+
let dir: string // agentDir = <dataDir>/agent(生产拓扑复刻)
|
|
1949
1989
|
|
|
1950
1990
|
beforeEach(async () => {
|
|
1951
|
-
|
|
1991
|
+
dataDir = await mkdtemp(join(tmpdir(), 'tool-handler-family-rich-'))
|
|
1992
|
+
dir = join(dataDir, 'agent')
|
|
1952
1993
|
await makeFixtureSession(dir, MAIN, 'main session')
|
|
1953
1994
|
})
|
|
1954
1995
|
afterEach(async () => {
|
|
1955
|
-
await rm(
|
|
1996
|
+
await rm(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 })
|
|
1956
1997
|
})
|
|
1957
1998
|
|
|
1958
1999
|
/** alive subagent:session 文件(header + 可选 identity 尾行)+ 可选 manifest 富字段 */
|
|
@@ -1964,7 +2005,7 @@ describe('formatFamilyText subagents 行富字段(ext-simplify-04 U4 / E2)',
|
|
|
1964
2005
|
identityAgent?: string
|
|
1965
2006
|
} = {},
|
|
1966
2007
|
): Promise<void> {
|
|
1967
|
-
const sessionDir =
|
|
2008
|
+
const sessionDir = resolveStoreSubagentSessionsDir(dir, MAIN)
|
|
1968
2009
|
await mkdir(sessionDir, { recursive: true })
|
|
1969
2010
|
const subPath = join(sessionDir, `${realId}.jsonl`)
|
|
1970
2011
|
const lines = [JSON.stringify({ type: 'session', id: realId, cwd: '/demo' })]
|
|
@@ -1985,7 +2026,7 @@ describe('formatFamilyText subagents 行富字段(ext-simplify-04 U4 / E2)',
|
|
|
1985
2026
|
}
|
|
1986
2027
|
await writeFile(subPath, lines.join('\n') + '\n')
|
|
1987
2028
|
if (opts.manifest) {
|
|
1988
|
-
const recordsDir =
|
|
2029
|
+
const recordsDir = resolveStoreSubagentRecordsDir(dir, MAIN)
|
|
1989
2030
|
await mkdir(recordsDir, { recursive: true })
|
|
1990
2031
|
await writeFile(
|
|
1991
2032
|
join(recordsDir, `sa-${realId}.json`),
|
|
@@ -2004,7 +2045,7 @@ describe('formatFamilyText subagents 行富字段(ext-simplify-04 U4 / E2)',
|
|
|
2004
2045
|
saId: string,
|
|
2005
2046
|
fields: { agentName?: string; task?: string; slug?: string; status?: string } = {},
|
|
2006
2047
|
): Promise<void> {
|
|
2007
|
-
const recordsDir =
|
|
2048
|
+
const recordsDir = resolveStoreSubagentRecordsDir(dir, MAIN)
|
|
2008
2049
|
await mkdir(recordsDir, { recursive: true })
|
|
2009
2050
|
await writeFile(
|
|
2010
2051
|
join(recordsDir, `${saId}.json`),
|
|
@@ -2107,7 +2148,10 @@ describe('doctor action(u8:环境判定 + 根表 + 告警 + 残留 glob)',
|
|
|
2107
2148
|
await writeJsonl(join(agentDir, 'sessions', SLUG, 'a.jsonl'), 'aaaaaaaa')
|
|
2108
2149
|
await writeJsonl(join(agentDir, 'sessions', SLUG, 'b.jsonl'), 'bbbbbbbb')
|
|
2109
2150
|
await writeJsonl(join(dataDir, 'sessions', 'old.jsonl'), 'cccccccc') // legacy 非空
|
|
2110
|
-
await writeJsonl(
|
|
2151
|
+
await writeJsonl(
|
|
2152
|
+
join(resolveStoreSubagentSessionsDir(agentDir, '0aaaaaaa-bbbb-7ccc-dddd-000000000d1'), 's.jsonl'),
|
|
2153
|
+
'dddddddd',
|
|
2154
|
+
)
|
|
2111
2155
|
return {
|
|
2112
2156
|
dataDir,
|
|
2113
2157
|
agentDir,
|
|
@@ -2219,7 +2263,7 @@ describe('doctor action(u8:环境判定 + 根表 + 告警 + 残留 glob)',
|
|
|
2219
2263
|
it('无缓存形态(ext-simplify-04 U3):重复调用输出一致(每次实扫),新落盘文件立即可见', async () => {
|
|
2220
2264
|
// 原三个用例(缓存命中不重扫 / TTL 到期失效 / find 不读 doctor 缓存 PS-14)随
|
|
2221
2265
|
// doctor 缓存机删除而合并为本用例——无缓存层后「find/doctor 读到旧计数」的形态
|
|
2222
|
-
//
|
|
2266
|
+
// 在结构上不存在,检查点收敛为:输出无「缓存命中」行 + 每次调用实扫(新增文件
|
|
2223
2267
|
// 下一次调用即见,反而更实时)
|
|
2224
2268
|
const agentDir = join(tmp, 'agent')
|
|
2225
2269
|
await writeJsonl(join(agentDir, 'sessions', SLUG, 'a.jsonl'))
|
|
@@ -2286,8 +2330,9 @@ describe('u9 F1 重写 + uuid 归一化(fixture,§5.2 / §6.7 / §11.5)',
|
|
|
2286
2330
|
id: string,
|
|
2287
2331
|
opts?: { subagent?: boolean; firstUserText?: string },
|
|
2288
2332
|
): Promise<void> {
|
|
2333
|
+
// [session-store-layout-inversion] subagent 会话在新布局存储树(分区 sid 固定合法形态)
|
|
2289
2334
|
const dir = opts?.subagent
|
|
2290
|
-
? join(tmp, 'agent', '
|
|
2335
|
+
? resolveStoreSubagentSessionsDir(join(tmp, 'agent'), '0aaaaaaa-bbbb-7ccc-dddd-0000000000s1')
|
|
2291
2336
|
: join(tmp, 'agent', 'sessions', SLUG)
|
|
2292
2337
|
await mkdir(dir, { recursive: true })
|
|
2293
2338
|
const lines = [JSON.stringify({ type: 'session', id, cwd: '/demo' })]
|
|
@@ -2436,7 +2481,7 @@ describe('u9 F1 重写 + uuid 归一化(fixture,§5.2 / §6.7 / §11.5)',
|
|
|
2436
2481
|
)
|
|
2437
2482
|
const text = r.content[0].text
|
|
2438
2483
|
expect(text).toContain(`${join(agentDir, 'sessions')}:2 文件`)
|
|
2439
|
-
expect(text).toContain(`${
|
|
2484
|
+
expect(text).toContain(`${resolveSessionStoreRoot(agentDir)}:1 文件`)
|
|
2440
2485
|
expect(text).toContain('候选集非空(共 3 文件)')
|
|
2441
2486
|
|
|
2442
2487
|
// 完整信号包(live 与 default 同路径)→ 去重根注记,不重复计数
|
|
@@ -2560,8 +2605,9 @@ describe('u10 find 分组输出(fixture,§6.7 子决策 2/3 + §8.2 回归
|
|
|
2560
2605
|
id: string,
|
|
2561
2606
|
opts?: { subagent?: boolean; firstUserText?: string },
|
|
2562
2607
|
): Promise<void> {
|
|
2608
|
+
// [session-store-layout-inversion] subagent 会话在新布局存储树(分区 sid 固定合法形态)
|
|
2563
2609
|
const dir = opts?.subagent
|
|
2564
|
-
? join(tmp, 'agent', '
|
|
2610
|
+
? resolveStoreSubagentSessionsDir(join(tmp, 'agent'), '0aaaaaaa-bbbb-7ccc-dddd-0000000000s1')
|
|
2565
2611
|
: join(tmp, 'agent', 'sessions', SLUG)
|
|
2566
2612
|
await mkdir(dir, { recursive: true })
|
|
2567
2613
|
const lines = [JSON.stringify({ type: 'session', id, cwd: '/demo' })]
|
|
@@ -2582,7 +2628,10 @@ describe('u10 find 分组输出(fixture,§6.7 子决策 2/3 + §8.2 回归
|
|
|
2582
2628
|
* 供 result action 批量调用(回归基线:批量头行 8 字符短 id 不受 u10 影响)。
|
|
2583
2629
|
*/
|
|
2584
2630
|
async function writeSubagentWithResult(saId: string, id: string, body: string): Promise<void> {
|
|
2585
|
-
const sessionFile = join(
|
|
2631
|
+
const sessionFile = join(
|
|
2632
|
+
resolveStoreSubagentSessionsDir(join(tmp, 'agent'), 'root-u10'),
|
|
2633
|
+
`${id}.jsonl`,
|
|
2634
|
+
)
|
|
2586
2635
|
await mkdir(dirname(sessionFile), { recursive: true })
|
|
2587
2636
|
const lines = [
|
|
2588
2637
|
JSON.stringify({ type: 'session', id, cwd: '/demo' }),
|
|
@@ -2598,7 +2647,7 @@ describe('u10 find 分组输出(fixture,§6.7 子决策 2/3 + §8.2 回归
|
|
|
2598
2647
|
}),
|
|
2599
2648
|
]
|
|
2600
2649
|
await writeFile(sessionFile, lines.join('\n') + '\n')
|
|
2601
|
-
const recordsDir = join(tmp, 'agent', '
|
|
2650
|
+
const recordsDir = resolveStoreSubagentRecordsDir(join(tmp, 'agent'), 'root-u10')
|
|
2602
2651
|
await mkdir(recordsDir, { recursive: true })
|
|
2603
2652
|
await writeFile(
|
|
2604
2653
|
join(recordsDir, `${saId}.json`),
|
|
@@ -2804,8 +2853,9 @@ describe('E1 find 单次根解析(探针 P1:resolveSessionRoots 恰 1 次)
|
|
|
2804
2853
|
|
|
2805
2854
|
/** 写 session 文件(subagent:true 写入 subagent 根),形态同 u10 段 writeSession。 */
|
|
2806
2855
|
async function writeSession(id: string, opts?: { subagent?: boolean }): Promise<void> {
|
|
2856
|
+
// [session-store-layout-inversion] subagent 会话在新布局存储树(分区 sid 固定合法形态)
|
|
2807
2857
|
const dir = opts?.subagent
|
|
2808
|
-
? join(tmp, 'agent', '
|
|
2858
|
+
? resolveStoreSubagentSessionsDir(join(tmp, 'agent'), '0aaaaaaa-bbbb-7ccc-dddd-0000000000s1')
|
|
2809
2859
|
: join(tmp, 'agent', 'sessions', SLUG)
|
|
2810
2860
|
await mkdir(dir, { recursive: true })
|
|
2811
2861
|
await writeFile(
|
|
@@ -3038,7 +3088,7 @@ describe('u12 跨会话内容检索(fixture,V8)', () => {
|
|
|
3038
3088
|
opts?: { subagent?: boolean },
|
|
3039
3089
|
): Promise<void> {
|
|
3040
3090
|
const dir = opts?.subagent
|
|
3041
|
-
? join(tmp, 'agent', '
|
|
3091
|
+
? resolveStoreSubagentSessionsDir(join(tmp, 'agent'), '0aaaaaaa-bbbb-7ccc-dddd-0000000000s2')
|
|
3042
3092
|
: join(tmp, 'agent', 'sessions')
|
|
3043
3093
|
await mkdir(dir, { recursive: true })
|
|
3044
3094
|
const lines = [JSON.stringify({ type: 'session', id, cwd: '/demo' })]
|
|
@@ -57,7 +57,7 @@ const NEW_SNAPSHOT_FIXTURE = {
|
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* v2 格式 fixture(pi-subagent-workflow 8.x 一次性生命周期写入的 wf-run-v2 快照)。
|
|
60
|
-
*
|
|
60
|
+
* 读侧形状与 v1 一致(state.calls[].sessionFile/result 保留),差异仅版本字面量与
|
|
61
61
|
* status 两态(running/done)、meta 无 pausedAt——v2 不该因版本字面量被挡在读之外。
|
|
62
62
|
*/
|
|
63
63
|
const V2_SNAPSHOT_FIXTURE = {
|
|
@@ -206,7 +206,7 @@ describe('parseRunSnapshot', () => {
|
|
|
206
206
|
|
|
207
207
|
it('TC-w5-parse-malformed:既非 NEW 也非 OLD(缺关键字段)返回 null', () => {
|
|
208
208
|
// (a) 有 v 但 v 非 v1/v2 且无 callCache(未来版本 wf-run-v3 及之后——届时须评估
|
|
209
|
-
//
|
|
209
|
+
// 新版本读侧形状再扩判定,v2 因形状兼容被接受)
|
|
210
210
|
expect(parseRunSnapshot({ v: 'wf-run-v3', state: { calls: [] } }, 'r', 's')).toBeNull()
|
|
211
211
|
// (b) 无 v 无 callCache 无 status(异构对象)
|
|
212
212
|
expect(parseRunSnapshot({ foo: 'bar', baz: 1 }, 'r', 's')).toBeNull()
|
|
@@ -327,7 +327,7 @@ describe('readRunSnapshot', () => {
|
|
|
327
327
|
})
|
|
328
328
|
|
|
329
329
|
// ============================================================
|
|
330
|
-
// resolveWorkflows 发现链([ADR-0095] v1
|
|
330
|
+
// resolveWorkflows 发现链([ADR-0095] v1 读侧删除后的单档形态:v2 注册条目
|
|
331
331
|
// recordPath 主源;历史格式条目——v1 快照 / workflow-state-link 指针——静默消失)
|
|
332
332
|
// ============================================================
|
|
333
333
|
|
|
@@ -549,8 +549,8 @@ describe('resolveWorkflows(v2 注册条目单档发现链)', () => {
|
|
|
549
549
|
expect(workflows).toEqual([])
|
|
550
550
|
})
|
|
551
551
|
|
|
552
|
-
it('
|
|
553
|
-
//
|
|
552
|
+
it('跨包契约检查:发现链判别的本地字面量 === subagent-core 写侧单源([D1] record 后缀)', () => {
|
|
553
|
+
// 源码本地持有磁盘协议字符串与版本/尾段(生产依赖范围不引 subagent-core),锚定两侧同值
|
|
554
554
|
expect(WORKFLOW_RECORD_CUSTOM_TYPE).toBe('workflow-record')
|
|
555
555
|
expect(WORKFLOW_RECORD_ENTRY_VERSION).toBe(2)
|
|
556
556
|
expect(RUN_RECORD_STREAM_SUFFIX).toBe('.record.jsonl')
|