thatgfsj-code 3.0.4 → 3.0.6

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 (205) hide show
  1. package/README.md +64 -21
  2. package/dist/app/index.d.ts +93 -22
  3. package/dist/app/index.d.ts.map +1 -1
  4. package/dist/app/index.js +232 -40
  5. package/dist/app/index.js.map +1 -1
  6. package/dist/cmd/index.d.ts +12 -20
  7. package/dist/cmd/index.d.ts.map +1 -1
  8. package/dist/cmd/index.js +192 -146
  9. package/dist/cmd/index.js.map +1 -1
  10. package/dist/config/index.d.ts +4 -0
  11. package/dist/config/index.d.ts.map +1 -1
  12. package/dist/config/index.js +5 -0
  13. package/dist/config/index.js.map +1 -1
  14. package/dist/config/types.d.ts +5 -0
  15. package/dist/config/types.d.ts.map +1 -1
  16. package/dist/llm/anthropic.d.ts +1 -1
  17. package/dist/llm/anthropic.d.ts.map +1 -1
  18. package/dist/llm/anthropic.js +116 -84
  19. package/dist/llm/anthropic.js.map +1 -1
  20. package/dist/llm/gemini.d.ts +27 -3
  21. package/dist/llm/gemini.d.ts.map +1 -1
  22. package/dist/llm/gemini.js +196 -73
  23. package/dist/llm/gemini.js.map +1 -1
  24. package/dist/llm/index.d.ts +20 -1
  25. package/dist/llm/index.d.ts.map +1 -1
  26. package/dist/llm/index.js +66 -6
  27. package/dist/llm/index.js.map +1 -1
  28. package/dist/llm/openai.d.ts +5 -1
  29. package/dist/llm/openai.d.ts.map +1 -1
  30. package/dist/llm/openai.js +96 -56
  31. package/dist/llm/openai.js.map +1 -1
  32. package/dist/mcp/client.d.ts +84 -34
  33. package/dist/mcp/client.d.ts.map +1 -1
  34. package/dist/mcp/client.js +291 -126
  35. package/dist/mcp/client.js.map +1 -1
  36. package/dist/session/compactor.d.ts +27 -7
  37. package/dist/session/compactor.d.ts.map +1 -1
  38. package/dist/session/compactor.js +78 -23
  39. package/dist/session/compactor.js.map +1 -1
  40. package/dist/session/index.d.ts +96 -33
  41. package/dist/session/index.d.ts.map +1 -1
  42. package/dist/session/index.js +278 -60
  43. package/dist/session/index.js.map +1 -1
  44. package/dist/tools/file.d.ts +10 -2
  45. package/dist/tools/file.d.ts.map +1 -1
  46. package/dist/tools/file.js +54 -5
  47. package/dist/tools/file.js.map +1 -1
  48. package/dist/tools/git.d.ts +16 -2
  49. package/dist/tools/git.d.ts.map +1 -1
  50. package/dist/tools/git.js +61 -18
  51. package/dist/tools/git.js.map +1 -1
  52. package/dist/tools/nwt.d.ts.map +1 -1
  53. package/dist/tools/nwt.js +8 -1
  54. package/dist/tools/nwt.js.map +1 -1
  55. package/dist/tools/search.d.ts +8 -1
  56. package/dist/tools/search.d.ts.map +1 -1
  57. package/dist/tools/search.js +105 -34
  58. package/dist/tools/search.js.map +1 -1
  59. package/dist/tools/shell.d.ts +0 -4
  60. package/dist/tools/shell.d.ts.map +1 -1
  61. package/dist/tools/shell.js +11 -26
  62. package/dist/tools/shell.js.map +1 -1
  63. package/dist/tools/types.d.ts +7 -0
  64. package/dist/tools/types.d.ts.map +1 -1
  65. package/dist/tools/types.js.map +1 -1
  66. package/dist/tui/app.d.ts.map +1 -1
  67. package/dist/tui/app.js +90 -18
  68. package/dist/tui/app.js.map +1 -1
  69. package/dist/tui/components/ChatMessage.d.ts.map +1 -1
  70. package/dist/tui/components/ChatMessage.js +9 -2
  71. package/dist/tui/components/ChatMessage.js.map +1 -1
  72. package/dist/tui/components/ConfirmPrompt.d.ts +21 -0
  73. package/dist/tui/components/ConfirmPrompt.d.ts.map +1 -0
  74. package/dist/tui/components/ConfirmPrompt.js +45 -0
  75. package/dist/tui/components/ConfirmPrompt.js.map +1 -0
  76. package/dist/tui/components/Header.d.ts +10 -5
  77. package/dist/tui/components/Header.d.ts.map +1 -1
  78. package/dist/tui/components/Header.js +13 -5
  79. package/dist/tui/components/Header.js.map +1 -1
  80. package/dist/tui/components/StatusBar.d.ts +7 -0
  81. package/dist/tui/components/StatusBar.d.ts.map +1 -1
  82. package/dist/tui/components/StatusBar.js +8 -3
  83. package/dist/tui/components/StatusBar.js.map +1 -1
  84. package/dist/tui/components/Thinking.d.ts +5 -0
  85. package/dist/tui/components/Thinking.d.ts.map +1 -1
  86. package/dist/tui/components/Thinking.js +24 -2
  87. package/dist/tui/components/Thinking.js.map +1 -1
  88. package/dist/tui/components/ToolCall.d.ts +6 -1
  89. package/dist/tui/components/ToolCall.d.ts.map +1 -1
  90. package/dist/tui/components/ToolCall.js +50 -22
  91. package/dist/tui/components/ToolCall.js.map +1 -1
  92. package/dist/tui/components/UserInput.d.ts +5 -0
  93. package/dist/tui/components/UserInput.d.ts.map +1 -1
  94. package/dist/tui/components/UserInput.js +9 -13
  95. package/dist/tui/components/UserInput.js.map +1 -1
  96. package/dist/tui/hooks/useChat.d.ts +1 -0
  97. package/dist/tui/hooks/useChat.d.ts.map +1 -1
  98. package/dist/tui/hooks/useChat.js +36 -16
  99. package/dist/tui/hooks/useChat.js.map +1 -1
  100. package/dist/tui/hooks/useCommands.d.ts +7 -1
  101. package/dist/tui/hooks/useCommands.d.ts.map +1 -1
  102. package/dist/tui/hooks/useCommands.js +77 -41
  103. package/dist/tui/hooks/useCommands.js.map +1 -1
  104. package/dist/tui/theme.d.ts +34 -0
  105. package/dist/tui/theme.d.ts.map +1 -0
  106. package/dist/tui/theme.js +34 -0
  107. package/dist/tui/theme.js.map +1 -0
  108. package/dist/tui/welcome.d.ts.map +1 -1
  109. package/dist/tui/welcome.js +9 -7
  110. package/dist/tui/welcome.js.map +1 -1
  111. package/dist/types.d.ts +16 -1
  112. package/dist/types.d.ts.map +1 -1
  113. package/dist/utils/diff.d.ts +5 -0
  114. package/dist/utils/diff.d.ts.map +1 -1
  115. package/dist/utils/diff.js +15 -0
  116. package/dist/utils/diff.js.map +1 -1
  117. package/dist/version.d.ts +12 -0
  118. package/dist/version.d.ts.map +1 -0
  119. package/dist/version.js +29 -0
  120. package/dist/version.js.map +1 -0
  121. package/package.json +19 -7
  122. package/CHANGELOG.md +0 -192
  123. package/DEVELOPMENT.md +0 -286
  124. package/ROADMAP.md +0 -91
  125. package/docs/API_KEY_GUIDE.md +0 -236
  126. package/docs/FAQ.md +0 -182
  127. package/install.bat +0 -63
  128. package/install.ps1 +0 -238
  129. package/install.sh +0 -113
  130. package/src/app/index.ts +0 -197
  131. package/src/cache/fingerprint.ts +0 -101
  132. package/src/cache/index.ts +0 -21
  133. package/src/cache/smartModel.ts +0 -133
  134. package/src/cache/stats.ts +0 -199
  135. package/src/cache/volatile.ts +0 -47
  136. package/src/cmd/index.tsx +0 -288
  137. package/src/config/index.ts +0 -156
  138. package/src/config/providers.ts +0 -234
  139. package/src/config/types.ts +0 -71
  140. package/src/hooks/index.ts +0 -111
  141. package/src/llm/anthropic.ts +0 -413
  142. package/src/llm/gemini.ts +0 -169
  143. package/src/llm/index.ts +0 -303
  144. package/src/llm/openai.ts +0 -243
  145. package/src/llm/provider.ts +0 -71
  146. package/src/mcp/client.ts +0 -330
  147. package/src/prompts/index.ts +0 -260
  148. package/src/session/compactor.ts +0 -103
  149. package/src/session/index.ts +0 -181
  150. package/src/session/message.ts +0 -42
  151. package/src/skills/brainstorming.ts +0 -43
  152. package/src/skills/code-review.ts +0 -45
  153. package/src/skills/executing-plans.ts +0 -27
  154. package/src/skills/frontend-design.ts +0 -35
  155. package/src/skills/git-workflow.ts +0 -36
  156. package/src/skills/improve-architecture.ts +0 -38
  157. package/src/skills/index.ts +0 -136
  158. package/src/skills/neuroweave.ts +0 -47
  159. package/src/skills/playwright.ts +0 -72
  160. package/src/skills/prototype.ts +0 -30
  161. package/src/skills/subagent.ts +0 -28
  162. package/src/skills/supabase.ts +0 -44
  163. package/src/skills/systematic-debugging.ts +0 -44
  164. package/src/skills/tdd.ts +0 -39
  165. package/src/skills/triage.ts +0 -35
  166. package/src/skills/verification.ts +0 -31
  167. package/src/skills/writing-plans.ts +0 -42
  168. package/src/tools/file.ts +0 -143
  169. package/src/tools/git.ts +0 -132
  170. package/src/tools/index.ts +0 -111
  171. package/src/tools/nwt.ts +0 -598
  172. package/src/tools/search.ts +0 -263
  173. package/src/tools/shell.ts +0 -136
  174. package/src/tools/types.ts +0 -122
  175. package/src/tui/app.tsx +0 -159
  176. package/src/tui/components/ChatList.tsx +0 -41
  177. package/src/tui/components/ChatMessage.tsx +0 -54
  178. package/src/tui/components/Header.tsx +0 -66
  179. package/src/tui/components/InitWizard.tsx +0 -247
  180. package/src/tui/components/Markdown.tsx +0 -35
  181. package/src/tui/components/ModelSelector.tsx +0 -107
  182. package/src/tui/components/StatusBar.tsx +0 -30
  183. package/src/tui/components/Thinking.tsx +0 -17
  184. package/src/tui/components/ToolCall.tsx +0 -102
  185. package/src/tui/components/UserInput.tsx +0 -151
  186. package/src/tui/hooks/useChat.ts +0 -287
  187. package/src/tui/hooks/useCommands.ts +0 -271
  188. package/src/tui/index.ts +0 -6
  189. package/src/tui/welcome.ts +0 -177
  190. package/src/types.ts +0 -104
  191. package/src/utils/diff.ts +0 -71
  192. package/src/utils/project.ts +0 -99
  193. package/src/utils/stableStringify.ts +0 -47
  194. package/src/utils/thinking.ts +0 -119
  195. package/tests/cache/fingerprint.test.ts +0 -75
  196. package/tests/cache/providerCatalog.test.ts +0 -31
  197. package/tests/cache/smartModel.test.ts +0 -100
  198. package/tests/cache/stableStringify.test.ts +0 -43
  199. package/tests/cache/stats.test.ts +0 -146
  200. package/tests/cache/volatile.test.ts +0 -75
  201. package/tests/smoke-pollution.mjs +0 -78
  202. package/tests/smoke-thinking.mjs +0 -110
  203. package/tests/smoke-tool-stream.mjs +0 -69
  204. package/tests/smoke-tool.mjs +0 -117
  205. package/tsconfig.json +0 -21
@@ -1,6 +1,20 @@
1
1
  /**
2
2
  * Session Manager - Manages conversation history with auto-compaction
3
+ *
4
+ * v3.0.5 additions:
5
+ * - Disk persistence: every completed round can be persisted to
6
+ * ~/.thatgfsj/sessions/<id>.json, and /resume restores a previous
7
+ * session. Files are validated on load (dangling tool_calls are
8
+ * stripped) so a restored history can never produce a provider 400.
9
+ * - reset(): starts a fresh conversation while KEEPING the system prompt
10
+ * (the old clear() dropped it, leaving every later request unprompted).
11
+ * - autoCompact: when history exceeds maxMessages it is compacted once
12
+ * (atomic tool groups, see compactor.ts) instead of being silently
13
+ * truncated every round. The TUI is notified via onAutoCompact.
3
14
  */
15
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, unlinkSync, statSync } from 'fs';
16
+ import { join } from 'path';
17
+ import { homedir } from 'os';
4
18
  import { ContextCompactor } from './compactor.js';
5
19
  /**
6
20
  * v2.2.4 (port from v2.1.0): patterns that, if found in an assistant
@@ -9,19 +23,10 @@ import { ContextCompactor } from './compactor.js';
9
23
  * turn's LLM sees the marker and starts echoing it back, creating a
10
24
  * self-reinforcing "[已中断]" hallucination loop.
11
25
  *
12
- * v2.2.6.1 tightening (smoke-test driven): the design here is a
13
- * two-tier check:
14
- * 1. STRONG markers — unambiguous pollution, always drop:
15
- * - `[已中断]` bracketed (the model emits this exact form)
16
- * - `[interrupted]` bracketed English equivalent
17
- * 2. WEAK markers — only drop if the message is also short (<200
18
- * chars). Long messages mentioning "response truncated" are
19
- * legitimate conversation (e.g. user complaints about past
20
- * behavior), not pollution.
21
- *
22
- * Earlier drafts matched bare `已中断` substrings and "response
23
- * truncated" anywhere in the message — both were too greedy and
24
- * dropped legitimate Chinese/English conversation.
26
+ * Two-tier check:
27
+ * 1. STRONG markers — unambiguous pollution, always drop.
28
+ * 2. WEAK markers — only drop if the message is also short (<200 chars),
29
+ * doesn't end with '?' and has no temporal references.
25
30
  */
26
31
  const POLLUTION_STRONG = [
27
32
  /\[已中断\]/,
@@ -38,13 +43,6 @@ function looksPolluted(content) {
38
43
  return false;
39
44
  if (POLLUTION_STRONG.some(p => p.test(content)))
40
45
  return true;
41
- // WEAK pattern gate:
42
- // - Message must be short (model got cut off mid-stream, so it
43
- // can't be long)
44
- // - Message must NOT end with '?' (questions are user complaints
45
- // about past behavior, not pollution)
46
- // - Message must NOT contain "last time" / "earlier" / "before"
47
- // (temporal references indicate user discussing past behavior)
48
46
  if (content.length >= 200)
49
47
  return false;
50
48
  if (/\?\s*$/.test(content.trim()))
@@ -55,49 +53,100 @@ function looksPolluted(content) {
55
53
  return true;
56
54
  return false;
57
55
  }
56
+ export function sessionsDir() {
57
+ return join(homedir(), '.thatgfsj', 'sessions');
58
+ }
59
+ /**
60
+ * v3.0.5: shared load-time sanitizer. A restored history must never carry a
61
+ * tool_calls block without its results, or the next provider request 400s:
62
+ * - drop orphaned 'tool' messages (no preceding assistant tool_calls)
63
+ * - strip tool_calls from an assistant message whose results were lost
64
+ */
65
+ export function sanitizeLoadedMessages(messages) {
66
+ const out = [];
67
+ let pendingCalls = 0;
68
+ for (const m of messages) {
69
+ if (m.role === 'tool') {
70
+ if (pendingCalls > 0) {
71
+ out.push(m);
72
+ pendingCalls--;
73
+ }
74
+ // else: orphaned tool result — drop
75
+ continue;
76
+ }
77
+ if (pendingCalls > 0) {
78
+ // A new non-tool message arrived before results completed: the
79
+ // previous assistant's tool_calls have no results — strip them.
80
+ for (let i = out.length - 1; i >= 0; i--) {
81
+ if (out[i].role === 'assistant' && out[i].tool_calls) {
82
+ const fixed = { ...out[i] };
83
+ delete fixed.tool_calls;
84
+ fixed.content = fixed.content || '(tool calls dropped: results missing)';
85
+ out[i] = fixed;
86
+ break;
87
+ }
88
+ }
89
+ pendingCalls = 0;
90
+ }
91
+ if (m.role === 'assistant' && m.tool_calls && m.tool_calls.length > 0) {
92
+ pendingCalls = m.tool_calls.length;
93
+ }
94
+ out.push(m);
95
+ }
96
+ // Trailing dangling calls (crash mid-loop) — strip them too.
97
+ if (pendingCalls > 0) {
98
+ for (let i = out.length - 1; i >= 0; i--) {
99
+ if (out[i].role === 'assistant' && out[i].tool_calls) {
100
+ const fixed = { ...out[i] };
101
+ delete fixed.tool_calls;
102
+ fixed.content = fixed.content || '(tool calls dropped: results missing)';
103
+ out[i] = fixed;
104
+ break;
105
+ }
106
+ }
107
+ }
108
+ return out;
109
+ }
58
110
  export class SessionManager {
59
111
  messages = [];
60
112
  sessionId;
61
113
  createdAt;
62
114
  compactor;
63
115
  maxMessages;
64
- /** v2.2.4: counter for messages dropped by the pollution filter. */
65
116
  droppedCount = 0;
117
+ /** Whether history was compacted and the new-context toast not yet surfaced. */
118
+ lastCompaction = null;
119
+ /** Extra metadata recorded in the session file. */
120
+ meta;
66
121
  /**
67
- * v3.0.0: callback invoked when the message count exceeds maxMessages.
68
- * Reasonix principle: do not mutate messages to compact; instead, signal
69
- * the TUI to suggest the user call /new (start a fresh session) so that
70
- * the upstream prompt cache keeps a stable prefix. NWT auto-logs every
71
- * meaningful event, so no history is lost — it just moves out of the
72
- * live cache window.
122
+ * Called after history exceeded maxMessages and was compacted once.
123
+ * The TUI surfaces a toast; nobody is required to act.
73
124
  */
74
- onSuggestNewSession;
125
+ onAutoCompact;
75
126
  constructor(maxMessages = 50, options) {
76
127
  this.maxMessages = maxMessages;
77
128
  this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
78
129
  this.createdAt = new Date();
79
130
  this.compactor = new ContextCompactor({ maxMessages });
80
- this.onSuggestNewSession = options?.onSuggestNewSession;
131
+ this.onAutoCompact = options?.onAutoCompact;
81
132
  }
82
133
  addMessage(role, content, extras) {
83
134
  this.messages.push({ role, content, ...extras });
84
135
  this.autoCompact();
85
136
  }
86
137
  /**
87
- * v2.2.4 (port from v2.1.0): same as addMessage but returns false
88
- * (and skips the push) if the message content matches a known
89
- * truncation/abort pollution pattern. Use this for assistant
90
- * messages whose stream might have been aborted.
138
+ * Same as addMessage but returns false (and skips the push) if the message
139
+ * content matches a known truncation/abort pollution pattern.
91
140
  */
92
141
  addMessageSafe(role, content, extras) {
93
- if (role === 'assistant' && looksPolluted(content)) {
142
+ if (role === 'assistant' && looksPolluted(typeof content === 'string' ? content : JSON.stringify(content))) {
94
143
  this.droppedCount++;
95
144
  return false;
96
145
  }
97
146
  this.addMessage(role, content, extras);
98
147
  return true;
99
148
  }
100
- /** v2.2.4: total messages dropped by addMessageSafe since session start. */
149
+ /** Total messages dropped by addMessageSafe since session start. */
101
150
  getDroppedCount() {
102
151
  return this.droppedCount;
103
152
  }
@@ -107,44 +156,86 @@ export class SessionManager {
107
156
  getMessageCount() {
108
157
  return this.messages.length;
109
158
  }
159
+ /**
160
+ * v3.0.5: fresh conversation that KEEPS the system prompt. The previous
161
+ * clear() removed it, so every later request ran without the identity /
162
+ * tool / skills instructions until the process restarted.
163
+ */
164
+ reset() {
165
+ const system = this.messages.find(m => m.role === 'system');
166
+ this.messages = system ? [system] : [];
167
+ this.lastCompaction = null;
168
+ // v3.0.5 fix: fresh id so the next persist() writes a NEW session file
169
+ // instead of overwriting the old conversation (which would make it
170
+ // unrestorable via /resume).
171
+ this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
172
+ this.createdAt = new Date();
173
+ }
174
+ /**
175
+ * v3.0.5: replace in-memory history (used by /resume). Input is sanitized
176
+ * so a malformed file can not poison the next request.
177
+ */
178
+ restore(messages, meta) {
179
+ this.messages = sanitizeLoadedMessages(messages);
180
+ if (meta?.id)
181
+ this.sessionId = meta.id;
182
+ if (meta?.createdAt) {
183
+ const d = new Date(meta.createdAt);
184
+ if (!isNaN(d.getTime()))
185
+ this.createdAt = d;
186
+ }
187
+ if (meta?.provider || meta?.model) {
188
+ this.meta = { provider: meta.provider, model: meta.model };
189
+ }
190
+ this.lastCompaction = null;
191
+ }
110
192
  clear() {
111
193
  this.messages = [];
112
194
  }
113
195
  /**
114
- * v3.0.0: "auto-compact" is now a no-op on `messages`. We instead invoke
115
- * the registered `onSuggestNewSession` callback (if any) so the TUI can
116
- * surface a "context is getting long, consider /new" hint. This keeps
117
- * the messages array byte-stable across rounds, which is critical for
118
- * prompt-cache hit-rate: any reordering or summary insertion would
119
- * invalidate every cache breakpoint the upstream provider has set.
120
- *
121
- * Rationale (Reasonix-style): when context exceeds the model window or
122
- * the configured maxMessages threshold, the right action is to start a
123
- * NEW session, not to silently truncate or summarize. NWT auto-logs
124
- * every meaningful event, so the user does not lose context — it just
125
- * moves out of the live cache window. If the user wants a longer
126
- * session, they can bump maxMessages in `~/.thatgfsj/config.json`.
127
- *
128
- * The original "fake compact" logic (insert `[Earlier conversation...]`
129
- * summary) is preserved as `truncate()` for callers that explicitly
130
- * want to compact (e.g. /new command hard-reset).
196
+ * v3.0.5: replace the (first) system prompt in place used when tools or
197
+ * the permission mode change and the prompt must be rebuilt without
198
+ * resetting the conversation.
199
+ */
200
+ replaceSystemMessage(content) {
201
+ const idx = this.messages.findIndex(m => m.role === 'system');
202
+ if (idx >= 0) {
203
+ this.messages[idx] = { ...this.messages[idx], content };
204
+ }
205
+ else {
206
+ this.messages.unshift({ role: 'system', content });
207
+ }
208
+ }
209
+ /**
210
+ * Compact when over the limit once per crossing, atomic groups (v3.0.5).
211
+ * The previous design only suggested /new and never touched history, which
212
+ * let sessions grow unbounded (and the /compact path silently rewrote
213
+ * history every round). Now: compact at the threshold, notify once.
131
214
  */
132
215
  autoCompact() {
133
216
  if (this.messages.length <= this.maxMessages)
134
217
  return;
135
- // Notify the TUI. The default TUI behavior is to render a toast:
136
- // ⚠️ 上下文超长({currentLength}/{max}),建议调 /new 开新会话
137
- // (NWT 已自动归档历史)
218
+ const { compacted } = this.compactor.compact(this.messages);
219
+ if (compacted.length >= this.messages.length)
220
+ return; // nothing removable
221
+ const before = this.messages.length;
222
+ this.messages = compacted;
223
+ this.lastCompaction = { before, after: compacted.length };
138
224
  try {
139
- this.onSuggestNewSession?.({
140
- currentLength: this.messages.length,
141
- max: this.maxMessages,
142
- });
225
+ this.onAutoCompact?.(this.lastCompaction);
143
226
  }
144
227
  catch {
145
228
  // Callback failure must not break the chat loop.
146
229
  }
147
- // Intentionally do NOT mutate this.messages. Prefix stability matters more.
230
+ }
231
+ /** v3.0.5: explicit compaction (the /compact command). */
232
+ compactNow() {
233
+ const { compacted, result } = this.compactor.compact(this.messages);
234
+ if (result.removedCount <= 0)
235
+ return null;
236
+ const before = this.messages.length;
237
+ this.messages = compacted;
238
+ return { before, after: compacted.length };
148
239
  }
149
240
  truncate(maxMessages) {
150
241
  if (maxMessages) {
@@ -164,6 +255,133 @@ export class SessionManager {
164
255
  createdAt: this.createdAt,
165
256
  };
166
257
  }
258
+ // ==================== Persistence (v3.0.5) ====================
259
+ /**
260
+ * Write the current history to ~/.thatgfsj/sessions/<id>.json and prune
261
+ * old sessions. Fire-and-forget friendly: errors are swallowed by the
262
+ * caller — a full disk must never kill a chat round.
263
+ */
264
+ persist() {
265
+ try {
266
+ const dir = sessionsDir();
267
+ if (!existsSync(dir))
268
+ mkdirSync(dir, { recursive: true });
269
+ const file = {
270
+ version: 1,
271
+ id: this.sessionId,
272
+ createdAt: this.createdAt.toISOString(),
273
+ updatedAt: new Date().toISOString(),
274
+ provider: this.meta?.provider,
275
+ model: this.meta?.model,
276
+ messages: this.messages,
277
+ };
278
+ writeFileSync(join(dir, `${this.sessionId}.json`), JSON.stringify(file, null, 2), 'utf-8');
279
+ pruneSessions(20);
280
+ }
281
+ catch {
282
+ // best-effort
283
+ }
284
+ }
285
+ /**
286
+ * Restore a previously persisted session into THIS manager (in place).
287
+ */
288
+ loadFrom(file) {
289
+ this.restore(file.messages || [], {
290
+ id: file.id,
291
+ createdAt: file.createdAt,
292
+ provider: file.provider,
293
+ model: file.model,
294
+ });
295
+ }
296
+ /** List the most recent persisted sessions, newest first. */
297
+ static list(limit = 10) {
298
+ const dir = sessionsDir();
299
+ if (!existsSync(dir))
300
+ return [];
301
+ try {
302
+ const files = readdirSync(dir)
303
+ .filter(f => f.endsWith('.json'))
304
+ .map(f => {
305
+ const full = join(dir, f);
306
+ try {
307
+ return { full, mtime: statSync(full).mtimeMs };
308
+ }
309
+ catch {
310
+ return null;
311
+ }
312
+ })
313
+ .filter((x) => x !== null)
314
+ .sort((a, b) => b.mtime - a.mtime)
315
+ .slice(0, limit);
316
+ const out = [];
317
+ for (const f of files) {
318
+ try {
319
+ const data = JSON.parse(readFileSync(f.full, 'utf-8'));
320
+ const firstUser = (data.messages || []).find(m => m.role === 'user' && typeof m.content === 'string');
321
+ out.push({
322
+ id: data.id,
323
+ updatedAt: new Date(data.updatedAt || data.createdAt || Date.now()),
324
+ messageCount: (data.messages || []).length,
325
+ preview: firstUser ? firstUser.content.slice(0, 60).replace(/\s+/g, ' ') : '(no user messages)',
326
+ });
327
+ }
328
+ catch {
329
+ // skip corrupted file
330
+ }
331
+ }
332
+ return out;
333
+ }
334
+ catch {
335
+ return [];
336
+ }
337
+ }
338
+ /** Load a persisted session file by id (or filename). */
339
+ static load(id) {
340
+ const dir = sessionsDir();
341
+ const filename = id.endsWith('.json') ? id : `${id}.json`;
342
+ const full = join(dir, filename);
343
+ if (!existsSync(full))
344
+ return null;
345
+ try {
346
+ const data = JSON.parse(readFileSync(full, 'utf-8'));
347
+ if (!data || !Array.isArray(data.messages))
348
+ return null;
349
+ return data;
350
+ }
351
+ catch {
352
+ return null;
353
+ }
354
+ }
355
+ }
356
+ /** Keep only the newest `keep` session files. */
357
+ function pruneSessions(keep) {
358
+ const dir = sessionsDir();
359
+ if (!existsSync(dir))
360
+ return;
361
+ try {
362
+ const files = readdirSync(dir)
363
+ .filter(f => f.endsWith('.json'))
364
+ .map(f => {
365
+ const full = join(dir, f);
366
+ try {
367
+ return { full, mtime: statSync(full).mtimeMs };
368
+ }
369
+ catch {
370
+ return null;
371
+ }
372
+ })
373
+ .filter((x) => x !== null)
374
+ .sort((a, b) => b.mtime - a.mtime);
375
+ for (const f of files.slice(keep)) {
376
+ try {
377
+ unlinkSync(f.full);
378
+ }
379
+ catch { /* best-effort */ }
380
+ }
381
+ }
382
+ catch {
383
+ // best-effort
384
+ }
167
385
  }
168
386
  export { ContextCompactor } from './compactor.js';
169
387
  export * from './message.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/session/index.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,15 @@
1
1
  /**
2
2
  * File Tool - File operations
3
+ *
4
+ * v3.0.5: write and delete now route through the permission pipeline.
5
+ * - write shows a line diff (utils/diff.ts, previously dead code) via
6
+ * ctx.confirmEdit before touching the file
7
+ * - delete asks via ctx.confirmAction
8
+ * - read/list/exists/mkdir stay silent (read-class operations)
9
+ * With no context wired (should not happen — App always installs one) we
10
+ * fail closed for destructive actions instead of silently executing.
3
11
  */
4
- import type { Tool, ToolResult } from './types.js';
12
+ import type { Tool, ToolResult, ToolContext } from './types.js';
5
13
  export declare class FileTool implements Tool {
6
14
  name: string;
7
15
  description: string;
@@ -35,7 +43,7 @@ export declare class FileTool implements Tool {
35
43
  description: string;
36
44
  required: boolean;
37
45
  }[];
38
- execute(params: Record<string, any>): Promise<ToolResult>;
46
+ execute(params: Record<string, any>, ctx?: ToolContext): Promise<ToolResult>;
39
47
  private readFile;
40
48
  private writeFile;
41
49
  private listDir;
@@ -1 +1 @@
1
- {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/tools/file.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAInD,qBAAa,QAAS,YAAW,IAAI;IACnC,IAAI,SAAU;IACd,WAAW,SAA8D;IAEzE,WAAW;;;;;;;;;;;;;;;;;MAQT;IAEF,QAAQ;qBAC4B,CAAC,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,EAAE;;;;MAI9E;IAEF,UAAU;;;;;QAIR;IAEI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAyB/D,OAAO,CAAC,QAAQ;IAmChB,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,OAAO;IAoBf,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,KAAK;CAId"}
1
+ {"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/tools/file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAKhE,qBAAa,QAAS,YAAW,IAAI;IACnC,IAAI,SAAU;IACd,WAAW,SAA8D;IAEzE,WAAW;;;;;;;;;;;;;;;;;MAQT;IAEF,QAAQ;qBAC4B,CAAC,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,EAAE;;;;MAI9E;IAEF,UAAU;;;;;QAIR;IAEI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAyBlF,OAAO,CAAC,QAAQ;YAmCF,SAAS;IAmCvB,OAAO,CAAC,OAAO;YAoBD,UAAU;IAmBxB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,KAAK;CAId"}
@@ -1,8 +1,17 @@
1
1
  /**
2
2
  * File Tool - File operations
3
+ *
4
+ * v3.0.5: write and delete now route through the permission pipeline.
5
+ * - write shows a line diff (utils/diff.ts, previously dead code) via
6
+ * ctx.confirmEdit before touching the file
7
+ * - delete asks via ctx.confirmAction
8
+ * - read/list/exists/mkdir stay silent (read-class operations)
9
+ * With no context wired (should not happen — App always installs one) we
10
+ * fail closed for destructive actions instead of silently executing.
3
11
  */
4
12
  import { readFileSync, writeFileSync, existsSync, readdirSync, statSync, mkdirSync, unlinkSync } from 'fs';
5
13
  import { join, dirname } from 'path';
14
+ import { DiffPreview } from '../utils/diff.js';
6
15
  export class FileTool {
7
16
  name = 'file';
8
17
  description = 'Perform file operations: read, write, list, delete, etc.';
@@ -26,18 +35,18 @@ export class FileTool {
26
35
  { name: 'path', type: 'string', description: 'File or directory path', required: true },
27
36
  { name: 'content', type: 'string', description: 'Content to write (for write action)', required: false }
28
37
  ];
29
- async execute(params) {
38
+ async execute(params, ctx) {
30
39
  const { action, path, content } = params;
31
40
  try {
32
41
  switch (action) {
33
42
  case 'read':
34
43
  return this.readFile(path);
35
44
  case 'write':
36
- return this.writeFile(path, content || '');
45
+ return await this.writeFile(path, content || '', ctx);
37
46
  case 'list':
38
47
  return this.listDir(path);
39
48
  case 'delete':
40
- return this.deleteFile(path);
49
+ return await this.deleteFile(path, ctx);
41
50
  case 'exists':
42
51
  return this.checkExists(path);
43
52
  case 'mkdir':
@@ -81,7 +90,37 @@ export class FileTool {
81
90
  }
82
91
  return { success: true, output: content };
83
92
  }
84
- writeFile(path, content) {
93
+ async writeFile(path, content, ctx) {
94
+ // v3.0.5: permission gate with diff preview. Fail closed when no
95
+ // confirmation channel is available at all.
96
+ if (ctx?.confirmEdit) {
97
+ const original = existsSync(path) ? (() => {
98
+ try {
99
+ return readFileSync(path, 'utf-8');
100
+ }
101
+ catch {
102
+ return '';
103
+ }
104
+ })() : '';
105
+ const diff = DiffPreview.diff(original, content);
106
+ const status = existsSync(path)
107
+ ? `修改文件 (−${diff.removed} / +${diff.added} 行)`
108
+ : `新建文件 (+${diff.added} 行)`;
109
+ const preview = diff.added + diff.removed > 400
110
+ ? DiffPreview.truncate(diff)
111
+ : DiffPreview.format(diff);
112
+ const ok = await ctx.confirmEdit({
113
+ message: `${status}: ${path}\n\n${preview}`,
114
+ });
115
+ if (!ok) {
116
+ return { success: false, error: 'File write cancelled by user' };
117
+ }
118
+ }
119
+ else {
120
+ // v3.0.5 fix: symmetric fail-closed — no confirmation channel at all
121
+ // (not even a context) means no writes, same as deleteFile.
122
+ return { success: false, error: 'File write requires confirmation, but no confirmation channel is available' };
123
+ }
85
124
  const dir = dirname(path);
86
125
  if (!existsSync(dir)) {
87
126
  mkdirSync(dir, { recursive: true });
@@ -106,10 +145,20 @@ export class FileTool {
106
145
  });
107
146
  return { success: true, output: JSON.stringify(items, null, 2) };
108
147
  }
109
- deleteFile(path) {
148
+ async deleteFile(path, ctx) {
110
149
  if (!existsSync(path)) {
111
150
  return { success: false, error: `Path not found: ${path}` };
112
151
  }
152
+ // v3.0.5: delete is destructive — always ask, fail closed without a channel.
153
+ if (ctx?.confirmAction) {
154
+ const ok = await ctx.confirmAction(`删除文件: ${path}`);
155
+ if (!ok) {
156
+ return { success: false, error: 'File deletion cancelled by user' };
157
+ }
158
+ }
159
+ else {
160
+ return { success: false, error: 'File deletion requires confirmation, but no confirmation channel is available' };
161
+ }
113
162
  unlinkSync(path);
114
163
  return { success: true, output: `Deleted: ${path}` };
115
164
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/tools/file.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/tools/file.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,17 @@
1
1
  /**
2
2
  * Git Tool - Git operations
3
+ *
4
+ * v3.0.5:
5
+ * - Command injection fixed: user/model-controlled values (commit
6
+ * messages, branch names, extra args) were interpolated into a shell
7
+ * string executed via cmd.exe. All calls now go through execFile with
8
+ * an argument array, so values are passed verbatim and never parsed
9
+ * by a shell.
10
+ * - Write actions (commit/push/pull/checkout/add) require confirmation
11
+ * via ctx.confirmAction; read-only actions (status/log/diff/branch)
12
+ * run silently. App.requestConfirmation owns the mode decision.
3
13
  */
4
- import type { Tool, ToolResult } from './types.js';
14
+ import type { Tool, ToolResult, ToolContext } from './types.js';
5
15
  export declare class GitTool implements Tool {
6
16
  name: string;
7
17
  description: string;
@@ -38,7 +48,11 @@ export declare class GitTool implements Tool {
38
48
  description: string;
39
49
  required: boolean;
40
50
  }[];
41
- execute(params: Record<string, any>): Promise<ToolResult>;
51
+ /**
52
+ * Run git with an argument array (no shell interpolation).
53
+ */
54
+ private git;
55
+ execute(params: Record<string, any>, ctx?: ToolContext): Promise<ToolResult>;
42
56
  private status;
43
57
  private log;
44
58
  private diff;
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/tools/git.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAOnD,qBAAa,OAAQ,YAAW,IAAI;IAClC,IAAI,SAAS;IACb,WAAW,SAA6D;IAExE,WAAW;;;;;;;;;;;;;;;;;;;;;MAST;IAEF,QAAQ;qBACsB,CAAC,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,EAAE;;;MAGxE;IAEF,UAAU;;;;;QAKR;IAEI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;YAqCjD,MAAM;YAKN,GAAG;YAKH,IAAI;YAKJ,MAAM;YAYN,MAAM;YAKN,QAAQ;YASR,IAAI;YAKJ,IAAI;YAKJ,GAAG;CAIlB"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/tools/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAWhE,qBAAa,OAAQ,YAAW,IAAI;IAClC,IAAI,SAAS;IACb,WAAW,SAA6D;IAExE,WAAW;;;;;;;;;;;;;;;;;;;;;MAST;IAEF,QAAQ;qBACsB,CAAC,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,EAAE;;;MAGxE;IAEF,UAAU;;;;;QAKR;IAEF;;OAEG;YACW,GAAG;IASX,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;YAoDpE,MAAM;YAKN,GAAG;YAQH,IAAI;YAMJ,MAAM;YAYN,MAAM;YAKN,QAAQ;YASR,IAAI;YAKJ,IAAI;YAKJ,GAAG;CAIlB"}