lonny-agent 0.1.1 → 0.1.8

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 (225) hide show
  1. package/.kilo/plans/1780064105789-mighty-pixel.md +171 -0
  2. package/.lonny/fix-new-command-session-cleanup.md +65 -0
  3. package/.lonny/tui-autocomplete-above.md +62 -0
  4. package/dist/agent/__tests__/compaction.test.js +6 -4
  5. package/dist/agent/__tests__/compaction.test.js.map +1 -1
  6. package/dist/agent/compaction.d.ts.map +1 -1
  7. package/dist/agent/compaction.js +3 -2
  8. package/dist/agent/compaction.js.map +1 -1
  9. package/dist/agent/llm.d.ts +1 -1
  10. package/dist/agent/llm.d.ts.map +1 -1
  11. package/dist/agent/prompt-builder.d.ts.map +1 -1
  12. package/dist/agent/prompt-builder.js +26 -8
  13. package/dist/agent/prompt-builder.js.map +1 -1
  14. package/dist/agent/providers/anthropic.d.ts +1 -1
  15. package/dist/agent/providers/anthropic.d.ts.map +1 -1
  16. package/dist/agent/providers/anthropic.js +2 -2
  17. package/dist/agent/providers/anthropic.js.map +1 -1
  18. package/dist/agent/providers/google.d.ts +1 -1
  19. package/dist/agent/providers/google.d.ts.map +1 -1
  20. package/dist/agent/providers/google.js +2 -1
  21. package/dist/agent/providers/google.js.map +1 -1
  22. package/dist/agent/providers/ollama.d.ts +1 -1
  23. package/dist/agent/providers/ollama.d.ts.map +1 -1
  24. package/dist/agent/providers/ollama.js +2 -1
  25. package/dist/agent/providers/ollama.js.map +1 -1
  26. package/dist/agent/providers/openai.d.ts +1 -1
  27. package/dist/agent/providers/openai.d.ts.map +1 -1
  28. package/dist/agent/providers/openai.js +2 -2
  29. package/dist/agent/providers/openai.js.map +1 -1
  30. package/dist/agent/session.d.ts +5 -1
  31. package/dist/agent/session.d.ts.map +1 -1
  32. package/dist/agent/session.js +22 -3
  33. package/dist/agent/session.js.map +1 -1
  34. package/dist/pi-tui/autocomplete.d.ts +54 -0
  35. package/dist/pi-tui/autocomplete.d.ts.map +1 -0
  36. package/dist/pi-tui/autocomplete.js +636 -0
  37. package/dist/pi-tui/autocomplete.js.map +1 -0
  38. package/dist/pi-tui/components/box.d.ts +22 -0
  39. package/dist/pi-tui/components/box.d.ts.map +1 -0
  40. package/dist/pi-tui/components/box.js +104 -0
  41. package/dist/pi-tui/components/box.js.map +1 -0
  42. package/dist/pi-tui/components/cancellable-loader.d.ts +22 -0
  43. package/dist/pi-tui/components/cancellable-loader.d.ts.map +1 -0
  44. package/dist/pi-tui/components/cancellable-loader.js +35 -0
  45. package/dist/pi-tui/components/cancellable-loader.js.map +1 -0
  46. package/dist/pi-tui/components/editor.d.ts +249 -0
  47. package/dist/pi-tui/components/editor.d.ts.map +1 -0
  48. package/dist/pi-tui/components/editor.js +1883 -0
  49. package/dist/pi-tui/components/editor.js.map +1 -0
  50. package/dist/pi-tui/components/image.d.ts +28 -0
  51. package/dist/pi-tui/components/image.d.ts.map +1 -0
  52. package/dist/pi-tui/components/image.js +90 -0
  53. package/dist/pi-tui/components/image.js.map +1 -0
  54. package/dist/pi-tui/components/input.d.ts +37 -0
  55. package/dist/pi-tui/components/input.d.ts.map +1 -0
  56. package/dist/pi-tui/components/input.js +382 -0
  57. package/dist/pi-tui/components/input.js.map +1 -0
  58. package/dist/pi-tui/components/loader.d.ts +31 -0
  59. package/dist/pi-tui/components/loader.d.ts.map +1 -0
  60. package/dist/pi-tui/components/loader.js +70 -0
  61. package/dist/pi-tui/components/loader.js.map +1 -0
  62. package/dist/pi-tui/components/markdown.d.ts +96 -0
  63. package/dist/pi-tui/components/markdown.d.ts.map +1 -0
  64. package/dist/pi-tui/components/markdown.js +647 -0
  65. package/dist/pi-tui/components/markdown.js.map +1 -0
  66. package/dist/pi-tui/components/select-list.d.ts +50 -0
  67. package/dist/pi-tui/components/select-list.d.ts.map +1 -0
  68. package/dist/pi-tui/components/select-list.js +167 -0
  69. package/dist/pi-tui/components/select-list.js.map +1 -0
  70. package/dist/pi-tui/components/settings-list.d.ts +50 -0
  71. package/dist/pi-tui/components/settings-list.d.ts.map +1 -0
  72. package/dist/pi-tui/components/settings-list.js +189 -0
  73. package/dist/pi-tui/components/settings-list.js.map +1 -0
  74. package/dist/pi-tui/components/spacer.d.ts +12 -0
  75. package/dist/pi-tui/components/spacer.d.ts.map +1 -0
  76. package/dist/pi-tui/components/spacer.js +23 -0
  77. package/dist/pi-tui/components/spacer.js.map +1 -0
  78. package/dist/pi-tui/components/text.d.ts +19 -0
  79. package/dist/pi-tui/components/text.d.ts.map +1 -0
  80. package/dist/pi-tui/components/text.js +91 -0
  81. package/dist/pi-tui/components/text.js.map +1 -0
  82. package/dist/pi-tui/components/truncated-text.d.ts +13 -0
  83. package/dist/pi-tui/components/truncated-text.d.ts.map +1 -0
  84. package/dist/pi-tui/components/truncated-text.js +51 -0
  85. package/dist/pi-tui/components/truncated-text.js.map +1 -0
  86. package/dist/pi-tui/editor-component.d.ts +39 -0
  87. package/dist/pi-tui/editor-component.d.ts.map +1 -0
  88. package/dist/pi-tui/editor-component.js +2 -0
  89. package/dist/pi-tui/editor-component.js.map +1 -0
  90. package/dist/pi-tui/fuzzy.d.ts +16 -0
  91. package/dist/pi-tui/fuzzy.d.ts.map +1 -0
  92. package/dist/pi-tui/fuzzy.js +110 -0
  93. package/dist/pi-tui/fuzzy.js.map +1 -0
  94. package/dist/pi-tui/index.d.ts +23 -0
  95. package/dist/pi-tui/index.d.ts.map +1 -0
  96. package/dist/pi-tui/index.js +32 -0
  97. package/dist/pi-tui/index.js.map +1 -0
  98. package/dist/pi-tui/keybindings.d.ts +193 -0
  99. package/dist/pi-tui/keybindings.d.ts.map +1 -0
  100. package/dist/pi-tui/keybindings.js +174 -0
  101. package/dist/pi-tui/keybindings.js.map +1 -0
  102. package/dist/pi-tui/keys.d.ts +184 -0
  103. package/dist/pi-tui/keys.d.ts.map +1 -0
  104. package/dist/pi-tui/keys.js +1182 -0
  105. package/dist/pi-tui/keys.js.map +1 -0
  106. package/dist/pi-tui/kill-ring.d.ts +28 -0
  107. package/dist/pi-tui/kill-ring.d.ts.map +1 -0
  108. package/dist/pi-tui/kill-ring.js +44 -0
  109. package/dist/pi-tui/kill-ring.js.map +1 -0
  110. package/dist/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  111. package/dist/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  112. package/dist/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  113. package/dist/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  114. package/dist/pi-tui/native-modifiers.d.ts +3 -0
  115. package/dist/pi-tui/native-modifiers.d.ts.map +1 -0
  116. package/dist/pi-tui/native-modifiers.js +53 -0
  117. package/dist/pi-tui/native-modifiers.js.map +1 -0
  118. package/dist/pi-tui/stdin-buffer.d.ts +50 -0
  119. package/dist/pi-tui/stdin-buffer.d.ts.map +1 -0
  120. package/dist/pi-tui/stdin-buffer.js +361 -0
  121. package/dist/pi-tui/stdin-buffer.js.map +1 -0
  122. package/dist/pi-tui/terminal-image.d.ts +90 -0
  123. package/dist/pi-tui/terminal-image.d.ts.map +1 -0
  124. package/dist/pi-tui/terminal-image.js +343 -0
  125. package/dist/pi-tui/terminal-image.js.map +1 -0
  126. package/dist/pi-tui/terminal.d.ts +113 -0
  127. package/dist/pi-tui/terminal.d.ts.map +1 -0
  128. package/dist/pi-tui/terminal.js +478 -0
  129. package/dist/pi-tui/terminal.js.map +1 -0
  130. package/dist/pi-tui/tui.d.ts +227 -0
  131. package/dist/pi-tui/tui.d.ts.map +1 -0
  132. package/dist/pi-tui/tui.js +1091 -0
  133. package/dist/pi-tui/tui.js.map +1 -0
  134. package/dist/pi-tui/undo-stack.d.ts +17 -0
  135. package/dist/pi-tui/undo-stack.d.ts.map +1 -0
  136. package/dist/pi-tui/undo-stack.js +25 -0
  137. package/dist/pi-tui/undo-stack.js.map +1 -0
  138. package/dist/pi-tui/utils.d.ts +84 -0
  139. package/dist/pi-tui/utils.d.ts.map +1 -0
  140. package/dist/pi-tui/utils.js +1024 -0
  141. package/dist/pi-tui/utils.js.map +1 -0
  142. package/dist/pi-tui/word-navigation.d.ts +25 -0
  143. package/dist/pi-tui/word-navigation.d.ts.map +1 -0
  144. package/dist/pi-tui/word-navigation.js +98 -0
  145. package/dist/pi-tui/word-navigation.js.map +1 -0
  146. package/dist/tools/__tests__/edit.test.js +85 -0
  147. package/dist/tools/__tests__/edit.test.js.map +1 -1
  148. package/dist/tools/edit.d.ts.map +1 -1
  149. package/dist/tools/edit.js +23 -1
  150. package/dist/tools/edit.js.map +1 -1
  151. package/dist/tools/registry.d.ts +4 -0
  152. package/dist/tools/registry.d.ts.map +1 -1
  153. package/dist/tools/registry.js +15 -7
  154. package/dist/tools/registry.js.map +1 -1
  155. package/dist/tui/components.d.ts +1 -1
  156. package/dist/tui/components.d.ts.map +1 -1
  157. package/dist/tui/components.js +1 -1
  158. package/dist/tui/components.js.map +1 -1
  159. package/dist/tui/editor-patch.d.ts +13 -0
  160. package/dist/tui/editor-patch.d.ts.map +1 -0
  161. package/dist/tui/editor-patch.js +41 -0
  162. package/dist/tui/editor-patch.js.map +1 -0
  163. package/dist/tui/index.d.ts.map +1 -1
  164. package/dist/tui/index.js +15 -3
  165. package/dist/tui/index.js.map +1 -1
  166. package/dist/web/index.d.ts.map +1 -1
  167. package/dist/web/index.js +11 -2
  168. package/dist/web/index.js.map +1 -1
  169. package/dist/web/public/app.js +65 -4
  170. package/dist/web/public/style.css +49 -0
  171. package/dist/web/session-bridge.d.ts +0 -4
  172. package/dist/web/session-bridge.d.ts.map +1 -1
  173. package/dist/web/session-bridge.js +16 -2
  174. package/dist/web/session-bridge.js.map +1 -1
  175. package/package.json +5 -3
  176. package/src/agent/__tests__/compaction.test.ts +6 -4
  177. package/src/agent/compaction.ts +3 -2
  178. package/src/agent/llm.ts +5 -1
  179. package/src/agent/prompt-builder.ts +27 -8
  180. package/src/agent/providers/anthropic.ts +15 -8
  181. package/src/agent/providers/google.ts +6 -1
  182. package/src/agent/providers/ollama.ts +6 -1
  183. package/src/agent/providers/openai.ts +21 -12
  184. package/src/agent/session.ts +26 -3
  185. package/src/pi-tui/autocomplete.ts +808 -0
  186. package/src/pi-tui/components/box.ts +137 -0
  187. package/src/pi-tui/components/cancellable-loader.ts +40 -0
  188. package/src/pi-tui/components/editor.ts +2321 -0
  189. package/src/pi-tui/components/image.ts +130 -0
  190. package/src/pi-tui/components/input.ts +456 -0
  191. package/src/pi-tui/components/loader.ts +93 -0
  192. package/src/pi-tui/components/markdown.ts +839 -0
  193. package/src/pi-tui/components/select-list.ts +263 -0
  194. package/src/pi-tui/components/settings-list.ts +264 -0
  195. package/src/pi-tui/components/spacer.ts +28 -0
  196. package/src/pi-tui/components/text.ts +113 -0
  197. package/src/pi-tui/components/truncated-text.ts +65 -0
  198. package/src/pi-tui/editor-component.ts +74 -0
  199. package/src/pi-tui/fuzzy.ts +137 -0
  200. package/src/pi-tui/index.ts +115 -0
  201. package/src/pi-tui/keybindings.ts +245 -0
  202. package/src/pi-tui/keys.ts +1440 -0
  203. package/src/pi-tui/kill-ring.ts +46 -0
  204. package/src/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  205. package/src/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  206. package/src/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  207. package/src/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  208. package/src/pi-tui/native-modifiers.ts +65 -0
  209. package/src/pi-tui/stdin-buffer.ts +434 -0
  210. package/src/pi-tui/terminal-image.ts +469 -0
  211. package/src/pi-tui/terminal.ts +594 -0
  212. package/src/pi-tui/tui.ts +1376 -0
  213. package/src/pi-tui/undo-stack.ts +28 -0
  214. package/src/pi-tui/utils.ts +1176 -0
  215. package/src/pi-tui/word-navigation.ts +127 -0
  216. package/src/tools/__tests__/edit.test.ts +91 -0
  217. package/src/tools/edit.ts +27 -1
  218. package/src/tools/registry.ts +18 -8
  219. package/src/tui/components.ts +2 -2
  220. package/src/tui/index.ts +24 -10
  221. package/src/web/index.ts +11 -2
  222. package/src/web/public/app.js +65 -4
  223. package/src/web/public/style.css +49 -0
  224. package/src/web/session-bridge.ts +13 -2
  225. package/tsconfig.json +2 -2
@@ -1,4 +1,12 @@
1
1
  import { EventChannels, getGlobalEventBus } from '../agent/event-bus.js';
2
+ /**
3
+ * Bridge between Session/EventBus and WebSocket.
4
+ * Listens to EventBus events and forwards them as JSON messages.
5
+ */
6
+ /** Strip ANSI escape codes from a string (terminal colors are meaningless in the browser) */
7
+ function stripAnsi(text) {
8
+ return text.replace(/\x1b\[[0-9;]*m/g, '');
9
+ }
2
10
  const WS_PROTOCOL_VERSION = 1;
3
11
  export function startSessionBridge(session, config, send) {
4
12
  const bus = getGlobalEventBus();
@@ -24,11 +32,17 @@ export function startSessionBridge(session, config, send) {
24
32
  });
25
33
  const unsubToolResult = bus.on(EventChannels.TOOL_RESULT, data => {
26
34
  const d = data;
27
- send({ type: 'tool_result', name: d.name, id: d.id, success: true, output: d.output });
35
+ send({
36
+ type: 'tool_result',
37
+ name: d.name,
38
+ id: d.id,
39
+ success: true,
40
+ output: stripAnsi(d.output),
41
+ });
28
42
  });
29
43
  const unsubToolError = bus.on(EventChannels.TOOL_ERROR, data => {
30
44
  const d = data;
31
- send({ type: 'tool_result', name: d.name, id: d.id, success: false, error: d.error });
45
+ send({ type: 'tool_result', name: d.name, id: d.id, success: false, error: stripAnsi(d.error) });
32
46
  });
33
47
  const unsubTurnEnd = bus.on(EventChannels.TURN_END, data => {
34
48
  const d = data;
@@ -1 +1 @@
1
- {"version":3,"file":"session-bridge.js","sourceRoot":"","sources":["../../src/web/session-bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-bridge.js","sourceRoot":"","sources":["../../src/web/session-bridge.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lonny-agent",
3
- "version": "0.1.1",
3
+ "version": "0.1.8",
4
4
  "license": "MIT",
5
5
  "repository": "github:as3long/lonny-agent",
6
6
  "description": "AI coding agent that does more with fewer API calls, optimized for Coding Plan (5h 1200-call bundle)",
@@ -9,8 +9,9 @@
9
9
  "lonny": "./dist/index.js"
10
10
  },
11
11
  "scripts": {
12
- "build": "tsc && npm run copy:web",
12
+ "build": "tsc && npm run copy:web && npm run copy:native",
13
13
  "copy:web": "node -e \"const fs=require('fs'),p=require('path');const src=p.join('src','web','public');const dst=p.join('dist','web','public');if(fs.existsSync(src)){if(!fs.existsSync(dst))fs.mkdirSync(dst,{recursive:true});for(const f of fs.readdirSync(src)){fs.copyFileSync(p.join(src,f),p.join(dst,f))}}\"",
14
+ "copy:native": "node -e \"const fs=require('fs'),p=require('path');function cpDir(s,d){fs.mkdirSync(d,{recursive:true});for(const e of fs.readdirSync(s)){const sp=p.join(s,e),dp=p.join(d,e);fs.statSync(sp).isDirectory()?cpDir(sp,dp):fs.copyFileSync(sp,dp)}}const src=p.join('src','pi-tui','native');const dst=p.join('dist','pi-tui','native');if(fs.existsSync(src))cpDir(src,dst)\"",
14
15
  "dev": "tsx src/index.ts",
15
16
  "start": "node dist/index.js",
16
17
  "test": "vitest run",
@@ -32,8 +33,9 @@
32
33
  },
33
34
  "dependencies": {
34
35
  "@anthropic-ai/sdk": "^0.30.0",
35
- "@earendil-works/pi-tui": "^0.75.5",
36
36
  "adm-zip": "^0.5.17",
37
+ "get-east-asian-width": "1.6.0",
38
+ "marked": "15.0.12",
37
39
  "commander": "^12.0.0",
38
40
  "openai": "^4.0.0",
39
41
  "ws": "^8.21.0",
@@ -97,9 +97,10 @@ describe('compact', () => {
97
97
  // Should have: system + summary + 5 recent
98
98
  expect(result.newCount).toBeLessThan(result.originalCount)
99
99
  expect(result.messages[0].role).toBe('system')
100
- // Second message should be the summary
101
- expect(result.messages[1].role).toBe('system')
102
- expect(result.messages[1].content).toContain('Conversation History Summary')
100
+ // Last message should be the summary (appended at end for prompt cache stability)
101
+ const summaryMsg = result.messages[result.messages.length - 1]
102
+ expect(summaryMsg.role).toBe('system')
103
+ expect(summaryMsg.content).toContain('Conversation History Summary')
103
104
  })
104
105
 
105
106
  test('preserves tool-call cycles when cutting off', () => {
@@ -140,7 +141,8 @@ describe('compact', () => {
140
141
 
141
142
  const result = compact(messages, 100, 5)
142
143
  if (result.compressed) {
143
- const summary = result.messages[1].content || ''
144
+ // Summary is always the last message (appended at end for prompt cache stability)
145
+ const summary = result.messages[result.messages.length - 1].content || ''
144
146
  expect(summary).toContain('Total exchanges')
145
147
  }
146
148
  })
@@ -113,11 +113,12 @@ export function compact(
113
113
  // Build a summary of the older conversation
114
114
  const summary = buildSummary(toSummarize)
115
115
 
116
- // Reconstruct: system + summary + recent messages
116
+ // Reconstruct: system + recent messages + summary (appended at end so
117
+ // the sys+recent prefix stays stable for LLM prompt caching)
117
118
  const compacted: LLMMessage[] = [
118
119
  systemMsg,
119
- { role: 'system', content: `[Conversation History Summary]\n${summary}` },
120
120
  ...recentMessages,
121
+ { role: 'system', content: `[Conversation History Summary]\n${summary}` },
121
122
  ]
122
123
 
123
124
  return {
package/src/agent/llm.ts CHANGED
@@ -10,7 +10,11 @@ export interface LLMMessage {
10
10
  }
11
11
 
12
12
  export interface LLMProvider {
13
- chat(messages: LLMMessage[], tools: ToolDefinition[]): AsyncGenerator<LLMChunk>
13
+ chat(
14
+ messages: LLMMessage[],
15
+ tools: ToolDefinition[],
16
+ signal?: AbortSignal,
17
+ ): AsyncGenerator<LLMChunk>
14
18
  }
15
19
 
16
20
  export interface TokenUsage {
@@ -26,7 +26,19 @@ export function buildSystemPrompt(config: Config): string {
26
26
  - \`search\`: Search the web using Tavily (query: string, search_depth?: string, include_answer?: boolean, max_results?: number, topic?: string, days?: number)
27
27
  `
28
28
  }
29
- const planOnly = mode === 'plan'
29
+ if (mode === 'plan') {
30
+ return `Available tools (read-only investigation + write_plan):
31
+ - \`read\`: Read file contents (paths: string[])
32
+ - \`glob\`: Find files by glob pattern (pattern: string)
33
+ - \`grep\`: Search file content by regex (pattern: string, include?: string, path?: string)
34
+ - \`ls\`: List directory (path?: string)
35
+ - \`bash\`: Execute read-only shell commands for investigation (NEVER modify files)
36
+ - \`find\`: Find files by name pattern (pattern: string, path?: string, maxResults?: number)
37
+ - \`git\`: Run read-only git commands (command: string)
38
+ - \`search\`: Search the web using Tavily (query: string, search_depth?: string, include_answer?: boolean, max_results?: number, topic?: string, days?: number)
39
+ - \`write_plan\`: Save plan/todo markdown into .lonny/ folder
40
+ `
41
+ }
30
42
  return `Available tools:
31
43
  - \`read\`: Read file contents (paths: string[])
32
44
  - \`glob\`: Find files by glob pattern (pattern: string)
@@ -34,7 +46,7 @@ export function buildSystemPrompt(config: Config): string {
34
46
  - \`ls\`: List directory (path?: string)
35
47
  - \`bash\`: Execute a shell command
36
48
  - \`edit\`: Replace text in files — call with {"edits": [{"file_path", "old_string", "new_string"}]} (array required)
37
- ${planOnly ? '- `write_plan`: Save plan/todo markdown into .lonny/ folder\n' : ''}- \`install_skill\`: Install an npm package as a skill — fetches package info from npm, runs npm install, and creates a .lonny/skills/ file with usage instructions for the AI
49
+ - \`install_skill\`: Install an npm package as a skill — fetches package info from npm, runs npm install, and creates a .lonny/skills/ file with usage instructions for the AI
38
50
  - \`find\`: Find files by name pattern (pattern: string, path?: string, maxResults?: number)
39
51
  - \`git\`: Run read-only git commands (command: string)
40
52
  - \`search\`: Search the web using Tavily (query: string, search_depth?: string, include_answer?: boolean, max_results?: number, topic?: string, days?: number)
@@ -59,10 +71,12 @@ ${getToolListForMode(config.mode)}`
59
71
 
60
72
  RULES (plan-specific):
61
73
  1. Read first: Use read/grep/glob tools to gather all context you need before planning.
62
- 2. You CANNOT edit source files you have no code edit tools. Only read and analyze.
63
- 3. Use \`bash\` for read-only commands only.
64
- 4. You MUST persist the final plan AND todo list to a file in \`.lonny/\` using \`write_plan\`. The \`write_plan\` content MUST include both ## Plan and ## Todo List sections.
65
- 5. You MUST also include the todo list in your text response to the user (not just in the file).
74
+ 2. You NEVER edit source files. You ONLY use read-only tools (read/glob/grep/ls/find/git/search) and bash (read-only commands only). You do NOT have access to edit, install_skill, or exec.
75
+ 3. Use \`bash\` for investigation only NEVER to modify files, install packages, or run write operations.
76
+ 4. Your ONLY output is a plan file saved via \`write_plan\`. You CANNOT modify the codebase directly.
77
+ 5. You MUST persist the final plan AND todo list to a file in \`.lonny/\` using \`write_plan\`. The \`write_plan\` content MUST include both ## Plan and ## Todo List sections.
78
+ 6. You MUST also include the todo list in your text response to the user (not just in the file).
79
+ 7. If the user asks you to modify files, run write commands, or install packages — refuse and explain they need to switch to code mode (\`/mode code\`).
66
80
 
67
81
  OUTPUT FORMAT (you MUST include both in write_plan AND in your response text):
68
82
 
@@ -113,8 +127,13 @@ RULES (code-specific):
113
127
  ${isWindows ? ' - Use PowerShell. Do NOT use Unix commands like `cat`, `ls`, `grep`, `which`, `chmod`, `mv`, `cp`, `rm`, `touch`, `mkdir`, `uname`, etc.' : ''}
114
128
  ${isWindows ? ' - Use `type` instead of `cat`, `dir` instead of `ls`, `where` instead of `which`' : ''}`
115
129
 
130
+ // Plan mode uses its own standalone tool list inside modeInstructions — skip sharedRules
131
+ const rulesSection =
132
+ config.mode === 'plan'
133
+ ? ''
134
+ : `
135
+ ${sharedRules}`
116
136
  return `${modeInstructions}
117
137
 
118
- ${envSection}
119
- ${sharedRules}${skillsSection}`
138
+ ${envSection}${rulesSection}${skillsSection}`
120
139
  }
@@ -12,7 +12,11 @@ export class AnthropicProvider implements LLMProvider {
12
12
  this.model = model || 'claude-sonnet-4-20250514'
13
13
  }
14
14
 
15
- async *chat(messages: LLMMessage[], tools: ToolDefinition[]): AsyncGenerator<LLMChunk> {
15
+ async *chat(
16
+ messages: LLMMessage[],
17
+ tools: ToolDefinition[],
18
+ signal?: AbortSignal,
19
+ ): AsyncGenerator<LLMChunk> {
16
20
  const anthropicTools: Anthropic.Tool[] = tools.map(t => {
17
21
  const properties: Record<string, unknown> = {}
18
22
  for (const [key, param] of Object.entries(t.parameters)) {
@@ -71,13 +75,16 @@ export class AnthropicProvider implements LLMProvider {
71
75
  return { role: 'user' as const, content: '' }
72
76
  })
73
77
 
74
- const stream = this.client.messages.stream({
75
- model: this.model,
76
- system: systemMsg?.content || '',
77
- messages: anthropicMessages,
78
- tools: anthropicTools.length > 0 ? anthropicTools : undefined,
79
- max_tokens: 8192,
80
- })
78
+ const stream = this.client.messages.stream(
79
+ {
80
+ model: this.model,
81
+ system: systemMsg?.content || '',
82
+ messages: anthropicMessages,
83
+ tools: anthropicTools.length > 0 ? anthropicTools : undefined,
84
+ max_tokens: 8192,
85
+ },
86
+ signal ? { signal } : undefined,
87
+ )
81
88
 
82
89
  let currentToolUse: {
83
90
  id: string
@@ -48,7 +48,11 @@ export class GoogleProvider implements LLMProvider {
48
48
  this.baseUrl = baseURL || 'https://generativelanguage.googleapis.com/v1beta'
49
49
  }
50
50
 
51
- async *chat(messages: LLMMessage[], tools: ToolDefinition[]): AsyncGenerator<LLMChunk> {
51
+ async *chat(
52
+ messages: LLMMessage[],
53
+ tools: ToolDefinition[],
54
+ signal?: AbortSignal,
55
+ ): AsyncGenerator<LLMChunk> {
52
56
  const systemInstruction = messages.find(m => m.role === 'system')?.content || ''
53
57
  const nonSystemMessages = messages.filter(m => m.role !== 'system')
54
58
 
@@ -135,6 +139,7 @@ export class GoogleProvider implements LLMProvider {
135
139
  'Content-Type': 'application/json',
136
140
  'x-goog-api-key': this.apiKey,
137
141
  },
142
+ signal,
138
143
  body: JSON.stringify(body),
139
144
  })
140
145
 
@@ -40,7 +40,11 @@ export class OllamaProvider implements LLMProvider {
40
40
  this.model = model || 'llama3.2'
41
41
  }
42
42
 
43
- async *chat(messages: LLMMessage[], tools: ToolDefinition[]): AsyncGenerator<LLMChunk> {
43
+ async *chat(
44
+ messages: LLMMessage[],
45
+ tools: ToolDefinition[],
46
+ signal?: AbortSignal,
47
+ ): AsyncGenerator<LLMChunk> {
44
48
  // Build Ollama-format messages
45
49
  const ollamaMessages: OllamaMessage[] = messages.map(m => {
46
50
  if (m.role === 'system') {
@@ -108,6 +112,7 @@ export class OllamaProvider implements LLMProvider {
108
112
  const response = await fetch(`${this.baseUrl}/api/chat`, {
109
113
  method: 'POST',
110
114
  headers: { 'Content-Type': 'application/json' },
115
+ signal,
111
116
  body: JSON.stringify(body),
112
117
  })
113
118
 
@@ -1,4 +1,5 @@
1
1
  import OpenAI from 'openai'
2
+ import type { RequestOptions } from 'openai/core.js'
2
3
  import type {
3
4
  ChatCompletionChunk,
4
5
  ChatCompletionMessageParam,
@@ -47,7 +48,11 @@ export class OpenAIProvider implements LLMProvider {
47
48
  this.strictTools = strictTools ?? (baseURL ? /beta/i.test(baseURL) : false)
48
49
  }
49
50
 
50
- async *chat(messages: LLMMessage[], tools: ToolDefinition[]): AsyncGenerator<LLMChunk> {
51
+ async *chat(
52
+ messages: LLMMessage[],
53
+ tools: ToolDefinition[],
54
+ signal?: AbortSignal,
55
+ ): AsyncGenerator<LLMChunk> {
51
56
  const openAIFormattedTools: ChatCompletionTool[] = tools.map(t => {
52
57
  const properties: Record<string, unknown> = {}
53
58
  for (const [key, param] of Object.entries(t.parameters)) {
@@ -117,18 +122,22 @@ export class OpenAIProvider implements LLMProvider {
117
122
  const stream: Stream<ChatCompletionChunk> = await (
118
123
  this.client.chat.completions.create as (
119
124
  params: ExtendedCreateParams,
125
+ options?: RequestOptions,
120
126
  ) => Promise<Stream<ChatCompletionChunk>>
121
- )({
122
- model: this.model,
123
- messages: openAIMessages,
124
- tools: openAIFormattedTools.length > 0 ? openAIFormattedTools : undefined,
125
- stream: true,
126
- stream_options: { include_usage: true },
127
- ...(this.thinking
128
- ? { thinking: { type: 'enabled' }, reasoning_effort: this.reasoningEffort || 'high' }
129
- : {}),
130
- ...(this.enableCache ? { enable_cache: true } : {}),
131
- })
127
+ )(
128
+ {
129
+ model: this.model,
130
+ messages: openAIMessages,
131
+ tools: openAIFormattedTools.length > 0 ? openAIFormattedTools : undefined,
132
+ stream: true,
133
+ stream_options: { include_usage: true },
134
+ ...(this.thinking
135
+ ? { thinking: { type: 'enabled' }, reasoning_effort: this.reasoningEffort || 'high' }
136
+ : {}),
137
+ ...(this.enableCache ? { enable_cache: true } : {}),
138
+ },
139
+ signal ? { signal } : undefined,
140
+ )
132
141
 
133
142
  let currentToolCall: {
134
143
  id: string
@@ -155,7 +155,7 @@ function printToolResult(tc: ToolCall, result: ToolResult, output?: SessionOutpu
155
155
  writeOut(` ${GY}│${RS} ${GR}✔${RS} edit\n`, output)
156
156
  if (result.output) {
157
157
  for (const l of result.output.split('\n')) {
158
- if (l.trim()) writeOut(` ${GY}│${RS} ${GY}${l.trim()}${RS}\n`, output)
158
+ if (l.trim()) writeOut(` ${GY}│${RS} ${l.trim()}\n`, output)
159
159
  }
160
160
  }
161
161
  } else if (tc.name === 'write_plan') {
@@ -238,7 +238,20 @@ export class Session {
238
238
  applier: FileReadTracker
239
239
  config: Config
240
240
  output?: SessionOutput
241
- onPlanWritten?: (display: string) => void
241
+ private _onPlanWritten?: (display: string) => void
242
+ /** Set the plan-written callback and propagate to ToolRegistry */
243
+ set onPlanWritten(cb: ((display: string) => void) | undefined) {
244
+ this._onPlanWritten = cb
245
+ // Update context so setMode() picks it up too
246
+ this.registry.updateContext({ onPlanWritten: cb })
247
+ // Re-register write_plan tool with the new callback
248
+ if (this.registry.has('write_plan')) {
249
+ this.registry.reRegisterWritePlan(this.config.cwd, cb)
250
+ }
251
+ }
252
+ get onPlanWritten(): ((display: string) => void) | undefined {
253
+ return this._onPlanWritten
254
+ }
242
255
  totalInputTokens: number = 0
243
256
  totalOutputTokens: number = 0
244
257
  turnInputTokens: number = 0
@@ -246,6 +259,7 @@ export class Session {
246
259
  turnApiCalls: number = 0
247
260
  totalApiCalls: number = 0
248
261
  private stopped: boolean = false
262
+ private abortController: AbortController | null = null
249
263
 
250
264
  constructor(config: Config, output?: SessionOutput) {
251
265
  this.config = config
@@ -355,6 +369,8 @@ export class Session {
355
369
  /** Stop the current conversation gracefully */
356
370
  stop(): void {
357
371
  this.stopped = true
372
+ // Abort any in-flight LLM stream to stop token consumption immediately
373
+ this.abortController?.abort()
358
374
  }
359
375
 
360
376
  /** Check if the session was stopped */
@@ -383,6 +399,9 @@ export class Session {
383
399
 
384
400
  // Reset stopped flag for new conversation
385
401
  this.resetStopped()
402
+ // Create a new AbortController for this chat invocation
403
+ // (a new controller is needed each time because abort() is one-shot)
404
+ this.abortController = new AbortController()
386
405
 
387
406
  // Declare toolCalls outside the loop so we can reference it in stop check
388
407
  let toolCalls: ToolCall[] = []
@@ -406,7 +425,11 @@ export class Session {
406
425
  bus.emit(EventChannels.TURN_START, { prompt: userPrompt, iteration: iterations })
407
426
  bus.emit(EventChannels.LLM_STREAM_START, { iteration: iterations })
408
427
 
409
- const stream = this.provider.chat(this.messages, this.registry.getDefinitions())
428
+ const stream = this.provider.chat(
429
+ this.messages,
430
+ this.registry.getDefinitions(),
431
+ this.abortController.signal,
432
+ )
410
433
 
411
434
  for await (const chunk of stream) {
412
435
  if (chunk.reasoning_content) {