lonny-agent 0.2.4 → 0.2.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.
- package/README.md +326 -326
- package/dist/agent/llm.d.ts +4 -0
- package/dist/agent/llm.d.ts.map +1 -1
- package/dist/agent/prompt-builder.d.ts.map +1 -1
- package/dist/agent/prompt-builder.js +13 -9
- package/dist/agent/prompt-builder.js.map +1 -1
- package/dist/agent/providers/google.d.ts.map +1 -1
- package/dist/agent/providers/google.js.map +1 -1
- package/dist/agent/providers/openai.d.ts.map +1 -1
- package/dist/agent/providers/openai.js +43 -3
- package/dist/agent/providers/openai.js.map +1 -1
- package/dist/agent/session.d.ts +4 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +45 -7
- package/dist/agent/session.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +7 -1
- package/dist/config/index.js.map +1 -1
- package/dist/tools/__tests__/bash.test.js +1 -1
- package/dist/tools/__tests__/bash.test.js.map +1 -1
- package/dist/tools/__tests__/edit.test.js +875 -371
- package/dist/tools/__tests__/edit.test.js.map +1 -1
- package/dist/tools/__tests__/grep.test.js +32 -0
- package/dist/tools/__tests__/grep.test.js.map +1 -1
- package/dist/tools/__tests__/sed.test.d.ts +2 -0
- package/dist/tools/__tests__/sed.test.d.ts.map +1 -0
- package/dist/tools/__tests__/sed.test.js +228 -0
- package/dist/tools/__tests__/sed.test.js.map +1 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +83 -10
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +50 -9
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +328 -158
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +54 -30
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/registry.d.ts +0 -6
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +2 -29
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/sed.d.ts +4 -0
- package/dist/tools/sed.d.ts.map +1 -0
- package/dist/tools/sed.js +121 -0
- package/dist/tools/sed.js.map +1 -0
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +26 -3
- package/dist/web/index.js.map +1 -1
- package/dist/web/public/app.js +5 -855
- package/dist/web/public/confirm.js +70 -0
- package/dist/web/public/index.html +121 -120
- package/dist/web/public/input.js +107 -0
- package/dist/web/public/messages.js +395 -0
- package/dist/web/public/sidebar.js +82 -0
- package/dist/web/public/state.js +72 -0
- package/dist/web/public/style.css +996 -949
- package/dist/web/public/utils.js +37 -0
- package/dist/web/public/websocket.js +267 -0
- package/dist/web/public/ws.js +19 -0
- package/dist/web/session-bridge.d.ts.map +1 -1
- package/dist/web/session-bridge.js +3 -7
- package/dist/web/session-bridge.js.map +1 -1
- package/package.json +55 -54
- package/scripts/copy-native.mjs +24 -0
- package/scripts/copy-web.mjs +15 -0
- package/src/agent/index.ts +25 -25
- package/src/agent/llm.ts +4 -0
- package/src/agent/prompt-builder.ts +13 -9
- package/src/agent/providers/anthropic.ts +179 -179
- package/src/agent/providers/google.ts +210 -211
- package/src/agent/providers/ollama.ts +186 -186
- package/src/agent/providers/openai.ts +61 -5
- package/src/agent/session.ts +59 -3
- package/src/config/index.ts +342 -335
- package/src/tools/__tests__/bash.test.ts +1 -1
- package/src/tools/__tests__/edit.test.ts +981 -385
- package/src/tools/__tests__/grep.test.ts +35 -0
- package/src/tools/bash.ts +97 -11
- package/src/tools/edit.ts +362 -169
- package/src/tools/git.ts +76 -76
- package/src/tools/grep.ts +57 -30
- package/src/tools/registry.ts +2 -31
- package/src/tui/index.ts +786 -786
- package/src/web/index.ts +25 -3
- package/src/web/public/app.js +5 -855
- package/src/web/public/confirm.js +70 -0
- package/src/web/public/index.html +121 -120
- package/src/web/public/input.js +107 -0
- package/src/web/public/messages.js +395 -0
- package/src/web/public/sidebar.js +82 -0
- package/src/web/public/state.js +72 -0
- package/src/web/public/style.css +996 -949
- package/src/web/public/utils.js +37 -0
- package/src/web/public/websocket.js +267 -0
- package/src/web/public/ws.js +19 -0
- package/src/web/session-bridge.ts +193 -194
- package/.claude/settings.local.json +0 -14
- package/.kilo/plans/1780064105789-mighty-pixel.md +0 -171
- package/.kilo/plans/1780293725888-quick-wizard.md +0 -62
- package/.lonny/plan-web-cwd-status.md +0 -38
- package/src/tools/exec.ts +0 -348
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* ── Utility Functions ── */
|
|
2
|
+
|
|
3
|
+
import { chatContainer } from './state.js'
|
|
4
|
+
|
|
5
|
+
export function getWsUrl() {
|
|
6
|
+
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
7
|
+
return `${proto}//${location.host}`
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function scrollToBottom() {
|
|
11
|
+
requestAnimationFrame(() => {
|
|
12
|
+
chatContainer.scrollTop = chatContainer.scrollHeight
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function escapeHtml(text) {
|
|
17
|
+
const div = document.createElement('div')
|
|
18
|
+
div.textContent = text
|
|
19
|
+
return div.innerHTML
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function formatTimestamp() {
|
|
23
|
+
return new Date().toLocaleTimeString()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function formatTokenCount(n) {
|
|
27
|
+
if (n >= 1000000) return `${(n / 1000000).toFixed(n % 1000000 === 0 ? 0 : 1)}M`
|
|
28
|
+
if (n >= 1000) return `${(n / 1000).toFixed(n % 1000 === 0 ? 0 : 1)}K`
|
|
29
|
+
return String(n)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function renderMarkdown(text) {
|
|
33
|
+
if (typeof marked !== 'undefined' && marked.parse) {
|
|
34
|
+
return marked.parse(text, { breaks: true, gfm: true })
|
|
35
|
+
}
|
|
36
|
+
return `<pre>${escapeHtml(text)}</pre>`
|
|
37
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/* ── WebSocket Connection & Message Handler ── */
|
|
2
|
+
|
|
3
|
+
import { closeConfirmDialog, showConfirmDialog } from './confirm.js'
|
|
4
|
+
import {
|
|
5
|
+
addErrorMessage,
|
|
6
|
+
addSystemMessage,
|
|
7
|
+
addTokenStats,
|
|
8
|
+
addToolCall,
|
|
9
|
+
addToolResult,
|
|
10
|
+
appendChunk,
|
|
11
|
+
finalizeAssistantMessage,
|
|
12
|
+
hideThinking,
|
|
13
|
+
renderSessionHistory,
|
|
14
|
+
showThinking,
|
|
15
|
+
startAssistantMessage,
|
|
16
|
+
} from './messages.js'
|
|
17
|
+
import { updatePlansAndTodos } from './sidebar.js'
|
|
18
|
+
import {
|
|
19
|
+
balanceDisplay,
|
|
20
|
+
balanceSep,
|
|
21
|
+
connectionOverlay,
|
|
22
|
+
cwdDisplay,
|
|
23
|
+
MAX_RECONNECT_ATTEMPTS,
|
|
24
|
+
messagesEl,
|
|
25
|
+
modeDisplay,
|
|
26
|
+
modelDisplay,
|
|
27
|
+
pendingToolCalls,
|
|
28
|
+
RECONNECT_DELAY,
|
|
29
|
+
setStatus,
|
|
30
|
+
state,
|
|
31
|
+
tokenCache,
|
|
32
|
+
tokenCalls,
|
|
33
|
+
tokenIn,
|
|
34
|
+
tokenOut,
|
|
35
|
+
} from './state.js'
|
|
36
|
+
import { formatTokenCount, getWsUrl, scrollToBottom } from './utils.js'
|
|
37
|
+
import { setWs } from './ws.js'
|
|
38
|
+
|
|
39
|
+
function handleMessage(msg) {
|
|
40
|
+
switch (msg.type) {
|
|
41
|
+
case 'hello':
|
|
42
|
+
state.currentMode = msg.mode || 'code'
|
|
43
|
+
state.currentModel = msg.model || ''
|
|
44
|
+
state.currentProvider = msg.provider || ''
|
|
45
|
+
modeDisplay.textContent = state.currentMode
|
|
46
|
+
modelDisplay.textContent = `${state.currentProvider}/${state.currentModel}`
|
|
47
|
+
if (msg.totalIn !== undefined) {
|
|
48
|
+
tokenIn.textContent = formatTokenCount(msg.totalIn || 0)
|
|
49
|
+
tokenOut.textContent = formatTokenCount(msg.totalOut || 0)
|
|
50
|
+
tokenCalls.textContent = `(${msg.totalApi || 0})`
|
|
51
|
+
const cacheHit = msg.totalCacheHit ?? 0
|
|
52
|
+
const cacheMiss = msg.totalCacheMiss ?? 0
|
|
53
|
+
const cacheTotal = cacheHit + cacheMiss
|
|
54
|
+
if (cacheTotal > 0) {
|
|
55
|
+
const pct = Math.round((cacheHit / cacheTotal) * 100)
|
|
56
|
+
tokenCache.textContent = `| 缓存 ${pct}%`
|
|
57
|
+
tokenCache.classList.remove('hidden')
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (msg.webBalance) {
|
|
61
|
+
balanceDisplay.textContent = `余额:${msg.webBalance}`
|
|
62
|
+
balanceDisplay.style.display = ''
|
|
63
|
+
balanceSep.style.display = ''
|
|
64
|
+
} else if (msg.balance) {
|
|
65
|
+
balanceDisplay.textContent = `余额:${msg.balance}`
|
|
66
|
+
balanceDisplay.style.display = ''
|
|
67
|
+
balanceSep.style.display = ''
|
|
68
|
+
} else {
|
|
69
|
+
balanceDisplay.style.display = 'none'
|
|
70
|
+
balanceSep.style.display = 'none'
|
|
71
|
+
}
|
|
72
|
+
if (msg.cwd) {
|
|
73
|
+
const maxLen = 35
|
|
74
|
+
cwdDisplay.textContent = msg.cwd.length > maxLen ? `...${msg.cwd.slice(-maxLen)}` : msg.cwd
|
|
75
|
+
}
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
case 'chunk':
|
|
79
|
+
appendChunk(msg.text || '')
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
case 'thinking':
|
|
83
|
+
showThinking(msg.text || '')
|
|
84
|
+
break
|
|
85
|
+
|
|
86
|
+
case 'thinking_end':
|
|
87
|
+
hideThinking()
|
|
88
|
+
break
|
|
89
|
+
|
|
90
|
+
case 'tool_call':
|
|
91
|
+
addToolCall(msg.name, msg.input, msg.id)
|
|
92
|
+
break
|
|
93
|
+
|
|
94
|
+
case 'tool_result':
|
|
95
|
+
if (msg.success) {
|
|
96
|
+
addToolResult(msg.name, true, msg.output || '', msg.id)
|
|
97
|
+
} else {
|
|
98
|
+
addToolResult(msg.name, false, msg.error || 'Unknown error', msg.id)
|
|
99
|
+
}
|
|
100
|
+
break
|
|
101
|
+
|
|
102
|
+
case 'turn_start':
|
|
103
|
+
setStatus(true)
|
|
104
|
+
startAssistantMessage()
|
|
105
|
+
break
|
|
106
|
+
|
|
107
|
+
case 'turn_end':
|
|
108
|
+
hideThinking()
|
|
109
|
+
for (const [, el] of pendingToolCalls) el.classList.remove('executing')
|
|
110
|
+
pendingToolCalls.clear()
|
|
111
|
+
finalizeAssistantMessage()
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
case 'done':
|
|
115
|
+
setStatus(false)
|
|
116
|
+
hideThinking()
|
|
117
|
+
for (const [, el] of pendingToolCalls) el.classList.remove('executing')
|
|
118
|
+
pendingToolCalls.clear()
|
|
119
|
+
finalizeAssistantMessage()
|
|
120
|
+
if (msg.reason === 'error') {
|
|
121
|
+
addErrorMessage('An error occurred during processing.')
|
|
122
|
+
}
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
case 'mode_changed':
|
|
126
|
+
state.currentMode = msg.mode
|
|
127
|
+
modeDisplay.textContent = state.currentMode
|
|
128
|
+
addSystemMessage(`Switched to ${state.currentMode} mode`)
|
|
129
|
+
break
|
|
130
|
+
|
|
131
|
+
case 'model_changed':
|
|
132
|
+
state.currentModel = msg.model
|
|
133
|
+
modelDisplay.textContent = `${state.currentProvider}/${state.currentModel}`
|
|
134
|
+
addSystemMessage(`Model switched to ${state.currentModel}`)
|
|
135
|
+
break
|
|
136
|
+
|
|
137
|
+
case 'session_cleared':
|
|
138
|
+
messagesEl.innerHTML = ''
|
|
139
|
+
addSystemMessage('Session cleared. Starting fresh.')
|
|
140
|
+
break
|
|
141
|
+
|
|
142
|
+
case 'help':
|
|
143
|
+
if (Array.isArray(msg.commands)) {
|
|
144
|
+
addSystemMessage('Available commands:')
|
|
145
|
+
msg.commands.forEach(cmd => addSystemMessage(cmd))
|
|
146
|
+
}
|
|
147
|
+
break
|
|
148
|
+
|
|
149
|
+
case 'compaction':
|
|
150
|
+
addSystemMessage(`📦 Compressed context: ${msg.before} → ${msg.after} messages`)
|
|
151
|
+
break
|
|
152
|
+
|
|
153
|
+
case 'plan_written':
|
|
154
|
+
addSystemMessage(`📝 Plan written: ${msg.display || ''}`)
|
|
155
|
+
break
|
|
156
|
+
|
|
157
|
+
case 'session_history':
|
|
158
|
+
renderSessionHistory(msg.messages)
|
|
159
|
+
setTimeout(scrollToBottom, 50)
|
|
160
|
+
break
|
|
161
|
+
|
|
162
|
+
case 'plan_data':
|
|
163
|
+
state.plans = msg.plans || []
|
|
164
|
+
state.currentPlanName = msg.currentPlanName || ''
|
|
165
|
+
state.todos = msg.todos || []
|
|
166
|
+
updatePlansAndTodos()
|
|
167
|
+
break
|
|
168
|
+
|
|
169
|
+
case 'balance_update':
|
|
170
|
+
if (msg.webBalance) {
|
|
171
|
+
balanceDisplay.textContent = `余额:${msg.webBalance}`
|
|
172
|
+
balanceDisplay.style.display = ''
|
|
173
|
+
balanceSep.style.display = ''
|
|
174
|
+
} else if (msg.balance) {
|
|
175
|
+
balanceDisplay.textContent = `余额:${msg.balance}`
|
|
176
|
+
balanceDisplay.style.display = ''
|
|
177
|
+
balanceSep.style.display = ''
|
|
178
|
+
}
|
|
179
|
+
break
|
|
180
|
+
|
|
181
|
+
case 'token_stats':
|
|
182
|
+
addTokenStats(
|
|
183
|
+
msg.turnIn,
|
|
184
|
+
msg.turnOut,
|
|
185
|
+
msg.totalIn,
|
|
186
|
+
msg.totalOut,
|
|
187
|
+
msg.turnApi,
|
|
188
|
+
msg.totalApi,
|
|
189
|
+
msg.totalCacheHit,
|
|
190
|
+
msg.totalCacheMiss,
|
|
191
|
+
)
|
|
192
|
+
break
|
|
193
|
+
|
|
194
|
+
case 'tool_confirm_request': {
|
|
195
|
+
if (!msg.toolCalls || msg.toolCalls.length === 0) break
|
|
196
|
+
showConfirmDialog(msg.toolCalls)
|
|
197
|
+
break
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
case 'error':
|
|
201
|
+
closeConfirmDialog()
|
|
202
|
+
addErrorMessage(msg.message || 'Unknown error')
|
|
203
|
+
break
|
|
204
|
+
|
|
205
|
+
case 'pong':
|
|
206
|
+
break
|
|
207
|
+
|
|
208
|
+
default:
|
|
209
|
+
console.log('Unknown message type:', msg.type)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function connect() {
|
|
214
|
+
connectionOverlay.classList.remove('hidden')
|
|
215
|
+
|
|
216
|
+
let ws
|
|
217
|
+
try {
|
|
218
|
+
ws = new WebSocket(getWsUrl())
|
|
219
|
+
setWs(ws)
|
|
220
|
+
} catch (err) {
|
|
221
|
+
connectionOverlay.classList.add('hidden')
|
|
222
|
+
addErrorMessage(`Connection failed: ${err.message}`)
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
ws.onopen = () => {
|
|
227
|
+
connectionOverlay.classList.add('hidden')
|
|
228
|
+
state.reconnectAttempts = 0
|
|
229
|
+
addSystemMessage('Connected to Lonny')
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
ws.onmessage = event => {
|
|
233
|
+
try {
|
|
234
|
+
const msg = JSON.parse(event.data)
|
|
235
|
+
handleMessage(msg)
|
|
236
|
+
} catch (err) {
|
|
237
|
+
console.error('Failed to parse message:', err)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
ws.onclose = () => {
|
|
242
|
+
setStatus(false)
|
|
243
|
+
hideThinking()
|
|
244
|
+
for (const [, el] of pendingToolCalls) el.classList.remove('executing')
|
|
245
|
+
pendingToolCalls.clear()
|
|
246
|
+
if (state.streamingMsgEl) {
|
|
247
|
+
finalizeAssistantMessage()
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (state.reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
|
251
|
+
state.reconnectAttempts++
|
|
252
|
+
const delay = RECONNECT_DELAY * Math.min(state.reconnectAttempts, 5)
|
|
253
|
+
connectionOverlay.querySelector('p').textContent =
|
|
254
|
+
`Reconnecting in ${delay / 1000}s... (attempt ${state.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`
|
|
255
|
+
connectionOverlay.classList.remove('hidden')
|
|
256
|
+
setTimeout(connect, delay)
|
|
257
|
+
} else {
|
|
258
|
+
connectionOverlay.querySelector('p').textContent = 'Connection lost. Please refresh the page.'
|
|
259
|
+
connectionOverlay.classList.remove('hidden')
|
|
260
|
+
addErrorMessage('Connection lost. Refresh the page to reconnect.')
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
ws.onerror = () => {
|
|
265
|
+
// onclose will fire after this
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* ── WebSocket Reference & Send ── */
|
|
2
|
+
|
|
3
|
+
let ws = null
|
|
4
|
+
|
|
5
|
+
export function getWs() {
|
|
6
|
+
return ws
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function setWs(newWs) {
|
|
10
|
+
ws = newWs
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function sendWsMsg(data) {
|
|
14
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
15
|
+
ws.send(JSON.stringify(data))
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-bridge.d.ts","sourceRoot":"","sources":["../../src/web/session-bridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAYhD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAA;AAI7C,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"session-bridge.d.ts","sourceRoot":"","sources":["../../src/web/session-bridge.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAYhD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAA;AAI7C,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAqKrE"}
|
|
@@ -20,6 +20,8 @@ export function startSessionBridge(session, config, send) {
|
|
|
20
20
|
totalIn: session.totalInputTokens,
|
|
21
21
|
totalOut: session.totalOutputTokens,
|
|
22
22
|
totalApi: session.totalApiCalls,
|
|
23
|
+
totalCacheHit: session.totalCacheHitTokens || undefined,
|
|
24
|
+
totalCacheMiss: session.totalCacheMissTokens || undefined,
|
|
23
25
|
});
|
|
24
26
|
// ── Subscribe to EventBus ──
|
|
25
27
|
const unsubTurnStart = bus.on(EventChannels.TURN_START, data => {
|
|
@@ -37,7 +39,7 @@ export function startSessionBridge(session, config, send) {
|
|
|
37
39
|
name: d.name,
|
|
38
40
|
id: d.id,
|
|
39
41
|
success: true,
|
|
40
|
-
output: stripAnsi(d.output),
|
|
42
|
+
output: d.name === 'edit' ? d.output : stripAnsi(d.output),
|
|
41
43
|
});
|
|
42
44
|
});
|
|
43
45
|
const unsubToolError = bus.on(EventChannels.TOOL_ERROR, data => {
|
|
@@ -86,12 +88,6 @@ export function startSessionBridge(session, config, send) {
|
|
|
86
88
|
send({ type: 'model_changed', model: arg });
|
|
87
89
|
return;
|
|
88
90
|
}
|
|
89
|
-
if (cmd === 'new') {
|
|
90
|
-
const { Session } = await import('../agent/session.js');
|
|
91
|
-
Session.clearSavedSession(config.cwd);
|
|
92
|
-
send({ type: 'session_cleared' });
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
91
|
if (cmd === 'help') {
|
|
96
92
|
send({
|
|
97
93
|
type: 'help',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-bridge.js","sourceRoot":"","sources":["../../src/web/session-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAIxE;;;GAGG;AAEH,6FAA6F;AAC7F,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAA;AAC5C,CAAC;AASD,MAAM,mBAAmB,GAAG,CAAC,CAAA;AAE7B,MAAM,UAAU,kBAAkB,CAChC,OAAgB,EAChB,MAAc,EACd,IAAY;IAEZ,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAA;IAE/B,kDAAkD;IAClD,IAAI,CAAC;QACH,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,mBAAmB;QAC5B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,OAAO,EAAE,OAAO,CAAC,gBAAgB;QACjC,QAAQ,EAAE,OAAO,CAAC,iBAAiB;QACnC,QAAQ,EAAE,OAAO,CAAC,aAAa;
|
|
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,54 +1,55 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "lonny-agent",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"repository": "github:as3long/lonny-agent",
|
|
6
|
-
"description": "AI coding agent that does more with fewer API calls, optimized for Coding Plan (5h 1200-call bundle)",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"bin": {
|
|
9
|
-
"lonny": "./dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc && npm run copy:web && npm run copy:native",
|
|
13
|
-
"copy:web": "node -
|
|
14
|
-
"copy:native": "node -
|
|
15
|
-
"dev": "tsx src/index.ts",
|
|
16
|
-
"start": "node dist/index.js",
|
|
17
|
-
"test": "vitest run",
|
|
18
|
-
"test:watch": "vitest",
|
|
19
|
-
"lint": "biome lint .",
|
|
20
|
-
"lint:fix": "biome lint --fix .",
|
|
21
|
-
"format": "biome format --write .",
|
|
22
|
-
"check": "biome check .",
|
|
23
|
-
"check:fix": "biome check --fix .",
|
|
24
|
-
"prepare": "husky"
|
|
25
|
-
},
|
|
26
|
-
"lint-staged": {
|
|
27
|
-
"*.{ts,js,mjs}": [
|
|
28
|
-
"biome check --fix --no-errors-on-unmatched"
|
|
29
|
-
],
|
|
30
|
-
"*.{json,jsonc}": [
|
|
31
|
-
"biome format --write --no-errors-on-unmatched"
|
|
32
|
-
]
|
|
33
|
-
},
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"@anthropic-ai/sdk": "^0.30.0",
|
|
36
|
-
"adm-zip": "^0.5.17",
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"@
|
|
47
|
-
"@types/
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
|
|
54
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "lonny-agent",
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"repository": "github:as3long/lonny-agent",
|
|
6
|
+
"description": "AI coding agent that does more with fewer API calls, optimized for Coding Plan (5h 1200-call bundle)",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"lonny": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && npm run copy:web && npm run copy:native",
|
|
13
|
+
"copy:web": "node scripts/copy-web.mjs",
|
|
14
|
+
"copy:native": "node scripts/copy-native.mjs",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"lint": "biome lint .",
|
|
20
|
+
"lint:fix": "biome lint --fix .",
|
|
21
|
+
"format": "biome format --write .",
|
|
22
|
+
"check": "biome check .",
|
|
23
|
+
"check:fix": "biome check --fix .",
|
|
24
|
+
"prepare": "husky"
|
|
25
|
+
},
|
|
26
|
+
"lint-staged": {
|
|
27
|
+
"*.{ts,js,mjs}": [
|
|
28
|
+
"biome check --fix --no-errors-on-unmatched"
|
|
29
|
+
],
|
|
30
|
+
"*.{json,jsonc}": [
|
|
31
|
+
"biome format --write --no-errors-on-unmatched"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@anthropic-ai/sdk": "^0.30.0",
|
|
36
|
+
"adm-zip": "^0.5.17",
|
|
37
|
+
"commander": "^12.0.0",
|
|
38
|
+
"get-east-asian-width": "1.6.0",
|
|
39
|
+
"jest-diff": "^30.4.1",
|
|
40
|
+
"marked": "15.0.12",
|
|
41
|
+
"openai": "^4.0.0",
|
|
42
|
+
"ws": "^8.21.0",
|
|
43
|
+
"zod": "^3.23.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@biomejs/biome": "^2.4.15",
|
|
47
|
+
"@types/node": "^22.19.19",
|
|
48
|
+
"@types/ws": "^8.18.1",
|
|
49
|
+
"husky": "^9.1.7",
|
|
50
|
+
"lint-staged": "^17.0.5",
|
|
51
|
+
"tsx": "^4.19.0",
|
|
52
|
+
"typescript": "^5.6.0",
|
|
53
|
+
"vitest": "^2.1.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// copy:native — recursively copies src/pi-tui/native/ to dist/pi-tui/native/
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const __dirname = fileURLToPath(new URL('..', import.meta.url))
|
|
7
|
+
|
|
8
|
+
function cpDir(s, d) {
|
|
9
|
+
mkdirSync(d, { recursive: true })
|
|
10
|
+
for (const e of readdirSync(s)) {
|
|
11
|
+
const sp = join(s, e)
|
|
12
|
+
const dp = join(d, e)
|
|
13
|
+
if (statSync(sp).isDirectory()) {
|
|
14
|
+
cpDir(sp, dp)
|
|
15
|
+
} else {
|
|
16
|
+
copyFileSync(sp, dp)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const src = join(__dirname, 'src', 'pi-tui', 'native')
|
|
22
|
+
const dst = join(__dirname, 'dist', 'pi-tui', 'native')
|
|
23
|
+
|
|
24
|
+
if (existsSync(src)) cpDir(src, dst)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// copy:web — copies src/web/public/ to dist/web/public/
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const __dirname = fileURLToPath(new URL('..', import.meta.url))
|
|
7
|
+
const src = join(__dirname, 'src', 'web', 'public')
|
|
8
|
+
const dst = join(__dirname, 'dist', 'web', 'public')
|
|
9
|
+
|
|
10
|
+
if (existsSync(src)) {
|
|
11
|
+
if (!existsSync(dst)) mkdirSync(dst, { recursive: true })
|
|
12
|
+
for (const f of readdirSync(src)) {
|
|
13
|
+
copyFileSync(join(src, f), join(dst, f))
|
|
14
|
+
}
|
|
15
|
+
}
|
package/src/agent/index.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import * as readline from 'node:readline'
|
|
2
|
-
import type { Config } from '../config/index.js'
|
|
3
|
-
import { Session, type SessionOutput } from './session.js'
|
|
4
|
-
|
|
5
|
-
export async function runAgent(prompt: string, config: Config): Promise<void> {
|
|
6
|
-
const output: SessionOutput = {
|
|
7
|
-
write: text => process.stdout.write(text),
|
|
8
|
-
confirmTool: async toolCalls => {
|
|
9
|
-
console.log('\nAllow these tool calls?')
|
|
10
|
-
for (const tc of toolCalls) {
|
|
11
|
-
const input = JSON.stringify(tc.input)
|
|
12
|
-
console.log(` \u2022 ${tc.name}: ${input.slice(0, 120)}`)
|
|
13
|
-
}
|
|
14
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
15
|
-
return new Promise(resolve => {
|
|
16
|
-
rl.question(' (y/N) ', answer => {
|
|
17
|
-
rl.close()
|
|
18
|
-
resolve(answer.trim().toLowerCase() === 'y')
|
|
19
|
-
})
|
|
20
|
-
})
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
const session = (await Session.load(config, output)) || new Session(config, output)
|
|
24
|
-
await session.chat(prompt)
|
|
25
|
-
}
|
|
1
|
+
import * as readline from 'node:readline'
|
|
2
|
+
import type { Config } from '../config/index.js'
|
|
3
|
+
import { Session, type SessionOutput } from './session.js'
|
|
4
|
+
|
|
5
|
+
export async function runAgent(prompt: string, config: Config): Promise<void> {
|
|
6
|
+
const output: SessionOutput = {
|
|
7
|
+
write: text => process.stdout.write(text),
|
|
8
|
+
confirmTool: async toolCalls => {
|
|
9
|
+
console.log('\nAllow these tool calls?')
|
|
10
|
+
for (const tc of toolCalls) {
|
|
11
|
+
const input = JSON.stringify(tc.input)
|
|
12
|
+
console.log(` \u2022 ${tc.name}: ${input.slice(0, 120)}`)
|
|
13
|
+
}
|
|
14
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
|
|
15
|
+
return new Promise(resolve => {
|
|
16
|
+
rl.question(' (y/N) ', answer => {
|
|
17
|
+
rl.close()
|
|
18
|
+
resolve(answer.trim().toLowerCase() === 'y')
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
const session = (await Session.load(config, output)) || new Session(config, output)
|
|
24
|
+
await session.chat(prompt)
|
|
25
|
+
}
|
package/src/agent/llm.ts
CHANGED
|
@@ -20,6 +20,10 @@ export interface LLMProvider {
|
|
|
20
20
|
export interface TokenUsage {
|
|
21
21
|
input_tokens: number
|
|
22
22
|
output_tokens: number
|
|
23
|
+
/** DeepSeek-specific: tokens read from cache */
|
|
24
|
+
prompt_cache_hit_tokens?: number
|
|
25
|
+
/** DeepSeek-specific: tokens not in cache */
|
|
26
|
+
prompt_cache_miss_tokens?: number
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
export interface LLMChunk {
|
|
@@ -49,13 +49,12 @@ export async function buildSystemPrompt(config: Config): Promise<string> {
|
|
|
49
49
|
- \`glob\`: Find files by glob pattern (pattern: string)
|
|
50
50
|
- \`grep\`: Search file content by regex (pattern: string, include?: string, path?: string)
|
|
51
51
|
- \`ls\`: List directory (path?: string)
|
|
52
|
-
- \`bash\`: Execute a shell command
|
|
53
|
-
- \`edit\`: Replace text in files
|
|
52
|
+
- \`bash\`: Execute a shell command — for running commands (NOT for creating or modifying files — use \`edit\` for that)
|
|
53
|
+
- \`edit\`: Replace text in files using markdown code block format. Use: \`edit({ content: "\`\`\`edit\\nfile: path\\nold: |\\ntext\\nnew: |\\ntext\\n\`\`\`" })\`
|
|
54
54
|
- \`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
|
|
55
55
|
- \`find\`: Find files by name pattern (pattern: string, path?: string, maxResults?: number)
|
|
56
56
|
- \`git\`: Run read-only git commands (command: string)
|
|
57
57
|
- \`search\`: Search the web using Tavily (query: string, search_depth?: string, include_answer?: boolean, max_results?: number, topic?: string, days?: number)
|
|
58
|
-
- \`exec\`: Run JavaScript in a sandbox to orchestrate multiple tool calls — all tools are available as \`await tools.xxx(args)\` inside exec (code mode only)
|
|
59
58
|
`
|
|
60
59
|
}
|
|
61
60
|
|
|
@@ -64,8 +63,7 @@ export async function buildSystemPrompt(config: Config): Promise<string> {
|
|
|
64
63
|
RULES:
|
|
65
64
|
1. Read first: Use read/grep/glob tools to gather all context you need before making any edits.
|
|
66
65
|
2. Be thorough: Explore the relevant parts of the codebase.
|
|
67
|
-
3. COST OPTIMIZATION (CRITICAL): Each API call costs money. You MUST maximize work per call. Use \`read(paths: [...])\` to read multiple files at once. Use \`edit({
|
|
68
|
-
4. When using \`edit\`, the \`edits\` value MUST be an array — even for a single change. Do NOT pass empty objects.
|
|
66
|
+
3. COST OPTIMIZATION (CRITICAL): Each API call costs money. You MUST maximize work per call. Use \`read(paths: [...])\` to read multiple files at once. Use \`edit({ content: "..." })\` with multiple \`\`\`edit blocks to edit multiple files at once.
|
|
69
67
|
|
|
70
68
|
${getToolListForMode(config.mode)}`
|
|
71
69
|
|
|
@@ -74,7 +72,7 @@ ${getToolListForMode(config.mode)}`
|
|
|
74
72
|
config.mode === 'plan'
|
|
75
73
|
? `You are a planning agent. Your ONLY job is to investigate the codebase and produce an actionable implementation plan with a todo list.
|
|
76
74
|
|
|
77
|
-
You CANNOT edit files. You do NOT have access to edit, bash (write mode),
|
|
75
|
+
You CANNOT edit files. You do NOT have access to edit, bash (write mode), or install_skill.
|
|
78
76
|
Any attempt to call these tools will FAIL — they are simply unavailable in this mode.
|
|
79
77
|
|
|
80
78
|
RULES (plan-specific):
|
|
@@ -116,9 +114,15 @@ RULES (ask-specific):
|
|
|
116
114
|
|
|
117
115
|
RULES (code-specific):
|
|
118
116
|
1. Read first: Use read/grep/glob tools to gather all context you need BEFORE making any edits. The \`read\` output prefixes each line with "<lineNumber>: " for easy reference. Do NOT include the "N: " prefix when copying text into \`edit\`.
|
|
119
|
-
2. edit CALL FORMAT —
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
2. edit CALL FORMAT — use markdown code block format:
|
|
118
|
+
\`\`\`edit
|
|
119
|
+
file: src/file.ts
|
|
120
|
+
old: |
|
|
121
|
+
text to replace
|
|
122
|
+
new: |
|
|
123
|
+
replacement text
|
|
124
|
+
\`\`\`
|
|
125
|
+
Use separate \`\`\`edit blocks for multiple files.
|
|
122
126
|
3. After making edits to a file, if you need to make ANOTHER edit to the SAME file, you MUST re-read it first to get the updated content.
|
|
123
127
|
4. If \`edit\` reports \`old_string not found\`, do NOT retry with the same old_string — re-read the file immediately to see its actual current content, then retry with correctly-copied text.
|
|
124
128
|
5. When copying old_string from \`read\` output, include 2-3 lines of context BEFORE and AFTER the target change to make the string unique in the file.
|