@zhushanwen/pi-session-reader 0.10.2 → 0.10.4

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/README.md CHANGED
@@ -54,6 +54,16 @@ pi install npm:@zhushanwen/pi-session-reader
54
54
  pi --extension <repo>/extensions/universal/session-reader/index.ts "<prompt>"
55
55
  ```
56
56
 
57
+ ## 工具激活(deferred)
58
+
59
+ `session_read` 工具为 `exposure: "deferred"`——不占常驻工具声明,由 pi 内置 `tool_search` 工具检索命中后按需激活。taiji 宿主内开箱即用(宿主启动时已激活 `tool_search`);独立 pi 使用需在 `<agentDir>/settings.json`(默认 `~/.pi/agent/settings.json`)开启:
60
+
61
+ ```json
62
+ { "defaultTools": ["+tool_search"] }
63
+ ```
64
+
65
+ `+` 前缀是增量修饰(保留 read/bash/edit/write 默认集)。未开启 `tool_search` 时该工具对模型不可见。
66
+
57
67
  ## 使用示例
58
68
 
59
69
  ```jsonc
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhushanwen/pi-session-reader",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "taiji": {
@@ -17,14 +17,14 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "@zhushanwen/pi-extension-logger": "0.6.2",
20
- "@zhushanwen/pi-ext-guards": "0.4.4",
20
+ "@zhushanwen/pi-ext-guards": "0.5.0",
21
21
  "@zhushanwen/session-core": "0.12.0",
22
22
  "@zhushanwen/zcode-session-source": "0.2.7"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@vitest/coverage-v8": "^4.1.9",
26
26
  "vitest": "^4.1.8",
27
- "@zhushanwen/subagent-core": "1.5.0"
27
+ "@zhushanwen/subagent-core": "1.5.2"
28
28
  },
29
29
  "files": [
30
30
  "index.ts",
@@ -686,50 +686,42 @@ describe('pi 穿插不受影响:同 fixture 内 pi 会话读取与 zcode 调
686
686
  // D4 schema 零变化(快照逐字节对照 + 合法形态冒烟)
687
687
  // ---------------------------------------------------------------------------
688
688
 
689
- /** `session_read` 输入 schema 的 JSON 序列化快照(U9 改动前从基线实现抓取——D4「schema 零变化」的逐字节锚)。 */
689
+ /** `session_read` 输入 schema 的 JSON 序列化快照(防无意漂移的逐字节锚)。
690
+ *
691
+ * 快照锚的意图 = 无意漂移拦截(参数增删 / 文案改动即红,迫使有意确认);
692
+ * 有意的声明面压缩须同批重新生成快照。当前快照 = tool-declaration-slimming
693
+ * U-A1 压缩终态(D1 三层分工:action 功能语义唯一承载点 = 枚举 description;
694
+ * 工具 description 只留定位 + 边界;guidelines 只留纪律)。重新生成:
695
+ * tsx 起 extension 捕获 registerTool 的 parameters 后 JSON.stringify。
696
+ */
690
697
  const SCHEMA_SNAPSHOT =
691
- '{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["find","f'
692
- + 'amily","outline","expand","detail","search","export","extract","workflow","result","doctor"],"de'
693
- + 'scription":"Action to perform: find (locate session), family (fork/subagent/workflow relations; '
694
- + 'recursive=true returns nested execution tree), outline (turn-level overview), expand (single-tur'
695
- + 'n entries), detail (full text of turns), search (full-text grep, single session or cross-session'
696
- + ' over a comma-separated id list), export (materialize to file), extract (pull user messages / co'
697
- + 'mmands / files / commits / tool results by type), workflow (workflow run overview: status/budget'
698
- + '/steps; requires session, optional runId focuses one run; step call sessionId jumps to outline/d'
699
- + 'etail), result (fetch a subagent session final result text — same content as its completion noti'
700
- + 'ce; session = single id or comma-separated batch of at most 10, optional limit caps chars per it'
701
- + 'em), doctor (show detected host environment and session-root diagnostics)."},"session":{"type":"'
702
- + 'string","description":"Session id, uuid fragment (e.g. e6c96), subagent record id (sa-xxx, preci'
703
- + 'se lookup), or absolute .jsonl path (~ or ~/ allowed). Required for family/outline/expand/detail'
704
- + '/search/export/extract/workflow/result. result also accepts a comma-separated list of up to 10 i'
705
- + 'ds. search also accepts a comma-separated list of up to 10 full ids (from find output) for cross'
706
- + '-session search. # prefix auto-stripped."},"query":{"type":"string","description":"find action: '
707
- + 'uuid fragment / filename / name keyword / \\"recent\\" (returns most recent N)."},"turns":{"type'
708
- + '":"string","description":"detail/extract action: turn range, \\"T013-T015\\" or \\"T013\\"."},"t'
709
- + 'urn":{"type":"string","description":"expand action: single turn, \\"T013\\"."},"pattern":{"type"'
710
- + ':"string","description":"search action: substring or regex."},"scope":{"type":"string","enum":["'
711
- + 'all","user","assistant","toolResult"],"description":"search action: scope filter. Default all."}'
712
- + ',"format":{"type":"string","enum":["outline","full","family"],"description":"export action: mate'
713
- + 'rialized form. Default outline."},"includeToolResult":{"type":"boolean","description":"detail/ex'
714
- + 'port: include toolResult full text. Default false (omitted as noise)."},"includeThinking":{"type'
715
- + '":"boolean","description":"detail: include thinking blocks. Default false (omitted as noise)."},'
716
- + '"allBranches":{"type":"boolean","description":"outline (and export\'s outline section): include '
717
- + 'abandoned side-branches. Not supported by family. Default false."},"granularity":{"type":"string'
718
- + '","enum":["turn","entry"],"description":"outline: turn-level or entry-flat. Default turn."},"cwd'
719
- + '":{"type":"string","description":"find: filter by cwd. Optional."},"source":{"type":"string","en'
720
- + 'um":["main","subagent"],"description":"find and session-resolving actions: filter by source. \\"'
721
- + 'main\\" = sessions/, \\"subagent\\" = subagents/. Default both (merged)."},"limit":{"type":"numb'
722
- + 'er","minimum":1,"description":"find/search: max results. Default 20. result: max chars per item,'
723
- + ' default 8000 (overlong text truncated with a pointer to the full file)."},"what":{"type":"strin'
724
- + 'g","enum":["user-messages","commands","files","commits","tool-results"],"description":"extract a'
725
- + 'ction: what to extract (required for extract)."},"tool":{"type":"string","description":"extract '
726
- + 'action: filter commands/tool-results by tool name (e.g. \\"bash\\")."},"runId":{"type":"string",'
727
- + '"description":"workflow action: focus a single run by runId (disambiguate multiple runs). Omit t'
728
- + 'o see all run overviews."},"recursive":{"type":"boolean","description":"family action: return ne'
729
- + 'sted execution tree (arbitrary-depth subagent↔workflow-call nesting, precise parentRecordId chai'
730
- + 'n with flat-fallback for legacy records). Default false (flat family)."},"includeSubagents":{"ty'
731
- + 'pe":"boolean","description":"doctor action: also scan the subagent session root (adds its file c'
732
- + 'ount). Default false (path and existence only)."}}}';
698
+ '{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["find","famil'
699
+ + 'y","outline","expand","detail","search","export","extract","workflow","result","doctor"],"descriptio'
700
+ + 'n":"find (locate session), family (relations; recursive=tree), outline (turn overview), expand (one '
701
+ + 'turn), detail (full text), search (grep, cross-session, ≤10 ids), export (to file), extract (message'
702
+ + 's/commands/files/commits/tool results), workflow (run overview), result (subagent final text, batch '
703
+ + '≤10), doctor (environment diagnostics)."},"session":{"type":"string","description":"Session id, uuid'
704
+ + ' fragment (e6c96), subagent id (sa-xxx), or .jsonl path. Required by every action except find/doctor'
705
+ + '; result/search accept ≤10 comma-separated ids. # prefix auto-stripped."},"query":{"type":"string","'
706
+ + 'description":"find: uuid fragment / filename / name keyword / \\"recent\\" (recent N)."},"turns":{"typ'
707
+ + 'e":"string","description":"detail/extract: turn range \\"T013-T015\\" or \\"T013\\"."},"turn":{"type":"s'
708
+ + 'tring","description":"expand: turn \\"T013\\"."},"pattern":{"type":"string","description":"search: sub'
709
+ + 'string or regex."},"scope":{"type":"string","enum":["all","user","assistant","toolResult"],"descript'
710
+ + 'ion":"search: filter scope. Default all."},"format":{"type":"string","enum":["outline","full","famil'
711
+ + 'y"],"description":"export: materialized form. Default outline."},"includeToolResult":{"type":"boolea'
712
+ + 'n","description":"detail/export: include toolResult text. Default false."},"includeThinking":{"type"'
713
+ + ':"boolean","description":"detail: include thinking blocks. Default false."},"allBranches":{"type":"b'
714
+ + 'oolean","description":"outline/export: include abandoned side-branches. Default false."},"granularit'
715
+ + 'y":{"type":"string","enum":["turn","entry"],"description":"outline: turn-level or entry-flat. Defaul'
716
+ + 't turn."},"cwd":{"type":"string","description":"find: filter by cwd."},"source":{"type":"string","en'
717
+ + 'um":["main","subagent"],"description":"\\"main\\" = sessions/, \\"subagent\\" = subagents/. Default both'
718
+ + '."},"limit":{"type":"number","minimum":1,"description":"find/search: max results (default 20). resul'
719
+ + 't: max chars per item (default 8000, truncated with pointer)."},"what":{"type":"string","enum":["use'
720
+ + 'r-messages","commands","files","commits","tool-results"],"description":"extract: what to pull (requi'
721
+ + 'red)."},"tool":{"type":"string","description":"extract: filter by tool name (e.g. \\"bash\\")."},"runI'
722
+ + 'd":{"type":"string","description":"workflow: focus one run (omit = all)."},"recursive":{"type":"bool'
723
+ + 'ean","description":"family: nested execution tree. Default false (flat)."},"includeSubagents":{"type'
724
+ + '":"boolean","description":"doctor: also scan the subagent session root. Default false."}}}';
733
725
 
734
726
  describe('session_read schema 快照(D4:不加任何工具参数,逐字节一致)', () => {
735
727
  it('TypeBox schema JSON 序列化与基线快照逐字节一致', () => {
@@ -749,3 +741,30 @@ describe('session_read schema 快照(D4:不加任何工具参数,逐字节
749
741
  expect(Check(schema, { action: 'outline', session: 'e6c96' })).toBe(true)
750
742
  })
751
743
  })
744
+
745
+ // ---------------------------------------------------------------------------
746
+ // D2 锚词保留清单(index.ts guidelines 上方注释登记的清单在此逐词 grep 锚定)
747
+ // ---------------------------------------------------------------------------
748
+
749
+ describe('session_read 锚词保留清单(D2:guidelines 纪律逐词锚定)', () => {
750
+ // SCHEMA_SNAPSHOT 只逐字节锚 parameters schema,不覆盖 guidelines——本块是
751
+ // guidelines 锚词的机器防线:弱模型回归(P7/PB-weak)后对本工具声明面的
752
+ // 再压缩 / 措辞改动,逐词红灯迫使有意确认。清单与 index.ts 注释登记一致:
753
+ // 1. "Progressive reading" —— 阅读纪律 outline → expand → detail
754
+ // 2. "Never read raw .jsonl" —— 禁直读原始 session 文件
755
+ // 3. "sa- ids route" —— zcode 路由对 LLM 的唯一可见面(U9)
756
+ // 4. "👉 recovery hint" —— 错误面恢复指针契约
757
+ // 5. "doctor" —— 环境自检入口(另被上方快照 action 枚举间接锚定)
758
+ it('guidelines 5 锚词逐个在位(缺一词即红)', () => {
759
+ const registerTool = vi.fn()
760
+ const pi = { registerTool, registerCommand: vi.fn(), addAutocompleteProvider: vi.fn(), on: vi.fn() }
761
+ ;(sessionReaderExtension as unknown as (p: unknown) => void)(pi)
762
+ const toolDef = registerTool.mock.calls[0][0] as { promptGuidelines: string[] }
763
+ const guidelines = toolDef.promptGuidelines.join('\n')
764
+ expect(guidelines).toContain('Progressive reading')
765
+ expect(guidelines).toContain('Never read raw .jsonl')
766
+ expect(guidelines).toContain('sa- ids route')
767
+ expect(guidelines).toContain('👉 recovery hint')
768
+ expect(guidelines).toContain('doctor')
769
+ })
770
+ })
package/src/index.ts CHANGED
@@ -11,7 +11,7 @@ import { createSessionCommand } from './tui/session-command.js'
11
11
  /**
12
12
  * pi-session-reader extension 入口(M3 工具适配层)。
13
13
  *
14
- * 分层(同 scheduler/cw-tool):
14
+ * 分层(同 scheduler):
15
15
  * - tool-handler.ts:纯逻辑 handler,agentDir 注入,零 pi 依赖,可单测
16
16
  * - index.ts(本文件):pi 依赖层,registerTool + getAgentDir() 调用 + execute 闭包
17
17
  * (错误直接 throw 给 pi——pi-agent-core agent-loop 对 execute throw 置 isError:true,
@@ -37,57 +37,57 @@ const SessionReadSchema = Type.Object({
37
37
  'doctor',
38
38
  ],
39
39
  {
40
+ // tool-declaration-slimming D1 终态:action 功能语义唯一承载点(工具 description 只留
41
+ // 一句定位 + 边界;guidelines 只留纪律)。
40
42
  description:
41
- 'Action to perform: find (locate session), family (fork/subagent/workflow relations; recursive=true returns nested execution tree), outline (turn-level overview), expand (single-turn entries), detail (full text of turns), search (full-text grep, single session or cross-session over a comma-separated id list), export (materialize to file), extract (pull user messages / commands / files / commits / tool results by type), workflow (workflow run overview: status/budget/steps; requires session, optional runId focuses one run; step call sessionId jumps to outline/detail), result (fetch a subagent session final result text — same content as its completion notice; session = single id or comma-separated batch of at most 10, optional limit caps chars per item), doctor (show detected host environment and session-root diagnostics).',
43
+ 'find (locate session), family (relations; recursive=tree), outline (turn overview), expand (one turn), detail (full text), search (grep, cross-session, ≤10 ids), export (to file), extract (messages/commands/files/commits/tool results), workflow (run overview), result (subagent final text, batch ≤10), doctor (environment diagnostics).',
42
44
  },
43
45
  ),
44
46
  session: Type.Optional(
45
47
  Type.String({
46
48
  description:
47
- 'Session id, uuid fragment (e.g. e6c96), subagent record id (sa-xxx, precise lookup), or absolute .jsonl path (~ or ~/ allowed). Required for family/outline/expand/detail/search/export/extract/workflow/result. result also accepts a comma-separated list of up to 10 ids. search also accepts a comma-separated list of up to 10 full ids (from find output) for cross-session search. # prefix auto-stripped.',
49
+ 'Session id, uuid fragment (e6c96), subagent id (sa-xxx), or .jsonl path. Required by every action except find/doctor; result/search accept ≤10 comma-separated ids. # prefix auto-stripped.',
48
50
  }),
49
51
  ),
50
52
  query: Type.Optional(
51
53
  Type.String({
52
- description:
53
- 'find action: uuid fragment / filename / name keyword / "recent" (returns most recent N).',
54
+ description: 'find: uuid fragment / filename / name keyword / "recent" (recent N).',
54
55
  }),
55
56
  ),
56
57
  turns: Type.Optional(
57
58
  Type.String({
58
- description: 'detail/extract action: turn range, "T013-T015" or "T013".',
59
+ description: 'detail/extract: turn range "T013-T015" or "T013".',
59
60
  }),
60
61
  ),
61
62
  turn: Type.Optional(
62
- Type.String({ description: 'expand action: single turn, "T013".' }),
63
+ Type.String({ description: 'expand: turn "T013".' }),
63
64
  ),
64
65
  pattern: Type.Optional(
65
- Type.String({ description: 'search action: substring or regex.' }),
66
+ Type.String({ description: 'search: substring or regex.' }),
66
67
  ),
67
68
  scope: Type.Optional(
68
69
  StringEnum(['all', 'user', 'assistant', 'toolResult'], {
69
- description: 'search action: scope filter. Default all.',
70
+ description: 'search: filter scope. Default all.',
70
71
  }),
71
72
  ),
72
73
  format: Type.Optional(
73
74
  StringEnum(['outline', 'full', 'family'], {
74
- description: 'export action: materialized form. Default outline.',
75
+ description: 'export: materialized form. Default outline.',
75
76
  }),
76
77
  ),
77
78
  includeToolResult: Type.Optional(
78
79
  Type.Boolean({
79
- description: 'detail/export: include toolResult full text. Default false (omitted as noise).',
80
+ description: 'detail/export: include toolResult text. Default false.',
80
81
  }),
81
82
  ),
82
83
  includeThinking: Type.Optional(
83
84
  Type.Boolean({
84
- description: 'detail: include thinking blocks. Default false (omitted as noise).',
85
+ description: 'detail: include thinking blocks. Default false.',
85
86
  }),
86
87
  ),
87
88
  allBranches: Type.Optional(
88
89
  Type.Boolean({
89
- description:
90
- "outline (and export's outline section): include abandoned side-branches. Not supported by family. Default false.",
90
+ description: 'outline/export: include abandoned side-branches. Default false.',
91
91
  }),
92
92
  ),
93
93
  granularity: Type.Optional(
@@ -96,12 +96,11 @@ const SessionReadSchema = Type.Object({
96
96
  }),
97
97
  ),
98
98
  cwd: Type.Optional(
99
- Type.String({ description: 'find: filter by cwd. Optional.' }),
99
+ Type.String({ description: 'find: filter by cwd.' }),
100
100
  ),
101
101
  source: Type.Optional(
102
102
  StringEnum(['main', 'subagent'], {
103
- description:
104
- 'find and session-resolving actions: filter by source. "main" = sessions/, "subagent" = subagents/. Default both (merged).',
103
+ description: '"main" = sessions/, "subagent" = subagents/. Default both.',
105
104
  }),
106
105
  ),
107
106
  limit: Type.Optional(
@@ -110,39 +109,35 @@ const SessionReadSchema = Type.Object({
110
109
  // 「无匹配 session」措辞面);result 消费侧的 resolveResultLimit 防御保留
111
110
  //(可单测绕过 schema 的调用形态)。
112
111
  minimum: 1,
113
- description:
114
- 'find/search: max results. Default 20. result: max chars per item, default 8000 (overlong text truncated with a pointer to the full file).',
112
+ description: 'find/search: max results (default 20). result: max chars per item (default 8000, truncated with pointer).',
115
113
  }),
116
114
  ),
117
115
  what: Type.Optional(
118
116
  StringEnum(
119
117
  ['user-messages', 'commands', 'files', 'commits', 'tool-results'],
120
118
  {
121
- description: 'extract action: what to extract (required for extract).',
119
+ description: 'extract: what to pull (required).',
122
120
  },
123
121
  ),
124
122
  ),
125
123
  tool: Type.Optional(
126
124
  Type.String({
127
- description: 'extract action: filter commands/tool-results by tool name (e.g. "bash").',
125
+ description: 'extract: filter by tool name (e.g. "bash").',
128
126
  }),
129
127
  ),
130
128
  runId: Type.Optional(
131
129
  Type.String({
132
- description:
133
- 'workflow action: focus a single run by runId (disambiguate multiple runs). Omit to see all run overviews.',
130
+ description: 'workflow: focus one run (omit = all).',
134
131
  }),
135
132
  ),
136
133
  recursive: Type.Optional(
137
134
  Type.Boolean({
138
- description:
139
- 'family action: return nested execution tree (arbitrary-depth subagent↔workflow-call nesting, precise parentRecordId chain with flat-fallback for legacy records). Default false (flat family).',
135
+ description: 'family: nested execution tree. Default false (flat).',
140
136
  }),
141
137
  ),
142
138
  includeSubagents: Type.Optional(
143
139
  Type.Boolean({
144
- description:
145
- 'doctor action: also scan the subagent session root (adds its file count). Default false (path and existence only).',
140
+ description: 'doctor: also scan the subagent session root. Default false.',
146
141
  }),
147
142
  ),
148
143
  })
@@ -150,23 +145,26 @@ const SessionReadSchema = Type.Object({
150
145
  // ---- guidelines(注入 LLM,design §3.4)----
151
146
 
152
147
  const guidelines = [
153
- 'Progressive reading: outline (~1500 token overview) → expand (one turn) → detail (full text). Default omits toolResult/thinking noise.',
154
- 'find first to locate a session by uuid fragment or name. TUI #references are full uuids.',
155
- 'outline before detail. Never read raw .jsonl files—use this tool.',
156
- 'family traces fork parents/children, subagent sessions, and workflow runs.',
148
+ // tool-declaration-slimming D1 终态:纪律单点——action 逐个复述条已删(功能语义在
149
+ // schema action 枚举说明一处);跨工具规则不在本工具重复(收敛点 = 注入段)。
150
+ // 锚词保留清单:Progressive reading / Never read raw .jsonl / sa- ids route /
151
+ // 👉 recovery hint / doctor——机器锚定唯一落点 = zcode-routing.test.ts 的锚词
152
+ // 保留清单 describe(逐词 toContain);doctor 另被该文件 SCHEMA_SNAPSHOT 的
153
+ // action 枚举间接锚定。tool-prompt 契约不锚本工具 guidelines(其 A8 是 subagent
154
+ // 工具内指向本工具的交叉引用,非本清单的锚)。
155
+ 'Progressive reading: outline (~1500 tokens) → expand (one turn) → detail; defaults omit toolResult/thinking noise.',
156
+ 'find first (uuid fragment / name); TUI #references are full uuids. Never read raw .jsonl—use this tool.',
157
157
  // zcode 路由(U9)对 LLM 的唯一可见面:sa-id 输入自动路由,无需任何参数;首遇
158
- // zcode_* 错误码前先有背景预告(schema 面按设计 D4 零变化,不在此重复)
159
- 'sa- ids route transparently to pi or zcode engine sessions; sess_ prefixed ids are not accepted—use the sa- id from the completion notice.',
160
- 'extract what=<type> to pull user messages / commands / files / commits / tool results across turns (optional tool= filter for commands/tool-results).',
161
- "workflow action to see workflow run overviews (status/budget/steps). Each step's call sessionId can jump to outline/detail for deep reading.",
162
- "result action to fetch a subagent's final result text (same content as its completion notice): session takes a single sa-id/uuid/path or a comma-separated batch of at most 10; optional limit caps chars per item (default 8000, truncated items carry a pointer to the full file).",
163
- 'Errors carry a 👉 recovery hint—follow it to retry in one step.',
164
- 'Unsure about the host environment (pure pi vs taiji) or where session roots live? Run action:"doctor" first—it prints the detected environment and every candidate session root.',
158
+ // zcode_* 错误码前先有背景预告。
159
+ 'sa- ids route transparently to pi or zcode engine sessions; sess_ prefixed ids are not accepted—use the completion-notice sa- id.',
160
+ 'Errors carry a 👉 recovery hint—follow it to retry.',
161
+ 'Unsure about host environment or session roots? Run action:"doctor".',
165
162
  ]
166
163
 
167
- // ---- 工具 description(design §3.4,照搬措辞)----
164
+ // ---- 工具 description(tool-declaration-slimming D1 终态:一句定位 + 阅读纪律指针 + 边界;
165
+ // action 一览与参数功能语义唯一承载点 = SessionReadSchema 的 action 枚举说明)----
168
166
 
169
- const description = `Read pi session files (conversation history) by semantic structure instead of raw bytes. Use when you need to review another session, trace a fork/subagent/workflow family, or locate a past decision. Eleven actions: find (locate by name/uuid fragment), family (fork/subagent/workflow relations), outline (turn-level overview, ~1500 token), expand (single-turn entry list), detail (full text of turns), search (full-text grep across a session, or cross-session over a comma-separated id list ≤10 from find output), export (materialize to file), extract (pull user messages / commands / files / commits / tool results by type), workflow (workflow run overview: status/budget/steps, step call sessionId jumps to outline/detail), result (fetch a subagent session final result text, same content as its completion notice; single id or comma-separated batch ≤10, optional limit chars per item default 8000), doctor (show detected host environment and session-root diagnostics). Progressive reading: outline → expand → detail. Do NOT use for the current session (the host provides current-session access) or to edit sessions (pi has /resume /fork).`
167
+ const description = `Read pi session files (conversation history) by semantic structure, not raw bytes. Progressive reading: outline → expand → detail. Do NOT use for the current session (the host provides it) or to edit sessions (pi has /resume /fork).`
170
168
 
171
169
  /**
172
170
  * 已注册过 TUI provider/command 的 pi 实例集合。
@@ -198,6 +196,9 @@ export default function sessionReaderExtension(pi: ExtensionAPI): void {
198
196
  pi.registerTool({
199
197
  name: 'session_read',
200
198
  label: 'Session Reader',
199
+ // deferred(tool-declaration-slimming D4):不常驻声明,tool_search 命中即激活;
200
+ // 迁移写入的 +tool_search 条目(runtime pi-codemode-settings)承载检索入口
201
+ exposure: 'deferred',
201
202
  description,
202
203
  parameters: SessionReadSchema,
203
204
  promptGuidelines: guidelines,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * [M3 工具适配层] session_read 工具的纯逻辑 handler(design §3.4 接口规格)。
3
3
  *
4
- * 分层约定(同 scheduler/cw-tool):本文件零 pi 依赖——agentDir 作参数注入,
4
+ * 分层约定(同 scheduler):本文件零 pi 依赖——agentDir 作参数注入,
5
5
  * 不调用 getAgentDir(),可完全单测;pi 注册与 getAgentDir() 调用在 index.ts。
6
6
  *
7
7
  * 按 action 分发到 11 条路径,串联基座解析(session-core parse)+ M1 core(tree/turns/render)+ M2 discovery