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.
Files changed (102) hide show
  1. package/README.md +326 -326
  2. package/dist/agent/llm.d.ts +4 -0
  3. package/dist/agent/llm.d.ts.map +1 -1
  4. package/dist/agent/prompt-builder.d.ts.map +1 -1
  5. package/dist/agent/prompt-builder.js +13 -9
  6. package/dist/agent/prompt-builder.js.map +1 -1
  7. package/dist/agent/providers/google.d.ts.map +1 -1
  8. package/dist/agent/providers/google.js.map +1 -1
  9. package/dist/agent/providers/openai.d.ts.map +1 -1
  10. package/dist/agent/providers/openai.js +43 -3
  11. package/dist/agent/providers/openai.js.map +1 -1
  12. package/dist/agent/session.d.ts +4 -0
  13. package/dist/agent/session.d.ts.map +1 -1
  14. package/dist/agent/session.js +45 -7
  15. package/dist/agent/session.js.map +1 -1
  16. package/dist/config/index.d.ts.map +1 -1
  17. package/dist/config/index.js +7 -1
  18. package/dist/config/index.js.map +1 -1
  19. package/dist/tools/__tests__/bash.test.js +1 -1
  20. package/dist/tools/__tests__/bash.test.js.map +1 -1
  21. package/dist/tools/__tests__/edit.test.js +875 -371
  22. package/dist/tools/__tests__/edit.test.js.map +1 -1
  23. package/dist/tools/__tests__/grep.test.js +32 -0
  24. package/dist/tools/__tests__/grep.test.js.map +1 -1
  25. package/dist/tools/__tests__/sed.test.d.ts +2 -0
  26. package/dist/tools/__tests__/sed.test.d.ts.map +1 -0
  27. package/dist/tools/__tests__/sed.test.js +228 -0
  28. package/dist/tools/__tests__/sed.test.js.map +1 -0
  29. package/dist/tools/bash.d.ts.map +1 -1
  30. package/dist/tools/bash.js +83 -10
  31. package/dist/tools/bash.js.map +1 -1
  32. package/dist/tools/edit.d.ts +50 -9
  33. package/dist/tools/edit.d.ts.map +1 -1
  34. package/dist/tools/edit.js +328 -158
  35. package/dist/tools/edit.js.map +1 -1
  36. package/dist/tools/grep.d.ts.map +1 -1
  37. package/dist/tools/grep.js +54 -30
  38. package/dist/tools/grep.js.map +1 -1
  39. package/dist/tools/registry.d.ts +0 -6
  40. package/dist/tools/registry.d.ts.map +1 -1
  41. package/dist/tools/registry.js +2 -29
  42. package/dist/tools/registry.js.map +1 -1
  43. package/dist/tools/sed.d.ts +4 -0
  44. package/dist/tools/sed.d.ts.map +1 -0
  45. package/dist/tools/sed.js +121 -0
  46. package/dist/tools/sed.js.map +1 -0
  47. package/dist/web/index.d.ts.map +1 -1
  48. package/dist/web/index.js +26 -3
  49. package/dist/web/index.js.map +1 -1
  50. package/dist/web/public/app.js +5 -855
  51. package/dist/web/public/confirm.js +70 -0
  52. package/dist/web/public/index.html +121 -120
  53. package/dist/web/public/input.js +107 -0
  54. package/dist/web/public/messages.js +395 -0
  55. package/dist/web/public/sidebar.js +82 -0
  56. package/dist/web/public/state.js +72 -0
  57. package/dist/web/public/style.css +996 -949
  58. package/dist/web/public/utils.js +37 -0
  59. package/dist/web/public/websocket.js +267 -0
  60. package/dist/web/public/ws.js +19 -0
  61. package/dist/web/session-bridge.d.ts.map +1 -1
  62. package/dist/web/session-bridge.js +3 -7
  63. package/dist/web/session-bridge.js.map +1 -1
  64. package/package.json +55 -54
  65. package/scripts/copy-native.mjs +24 -0
  66. package/scripts/copy-web.mjs +15 -0
  67. package/src/agent/index.ts +25 -25
  68. package/src/agent/llm.ts +4 -0
  69. package/src/agent/prompt-builder.ts +13 -9
  70. package/src/agent/providers/anthropic.ts +179 -179
  71. package/src/agent/providers/google.ts +210 -211
  72. package/src/agent/providers/ollama.ts +186 -186
  73. package/src/agent/providers/openai.ts +61 -5
  74. package/src/agent/session.ts +59 -3
  75. package/src/config/index.ts +342 -335
  76. package/src/tools/__tests__/bash.test.ts +1 -1
  77. package/src/tools/__tests__/edit.test.ts +981 -385
  78. package/src/tools/__tests__/grep.test.ts +35 -0
  79. package/src/tools/bash.ts +97 -11
  80. package/src/tools/edit.ts +362 -169
  81. package/src/tools/git.ts +76 -76
  82. package/src/tools/grep.ts +57 -30
  83. package/src/tools/registry.ts +2 -31
  84. package/src/tui/index.ts +786 -786
  85. package/src/web/index.ts +25 -3
  86. package/src/web/public/app.js +5 -855
  87. package/src/web/public/confirm.js +70 -0
  88. package/src/web/public/index.html +121 -120
  89. package/src/web/public/input.js +107 -0
  90. package/src/web/public/messages.js +395 -0
  91. package/src/web/public/sidebar.js +82 -0
  92. package/src/web/public/state.js +72 -0
  93. package/src/web/public/style.css +996 -949
  94. package/src/web/public/utils.js +37 -0
  95. package/src/web/public/websocket.js +267 -0
  96. package/src/web/public/ws.js +19 -0
  97. package/src/web/session-bridge.ts +193 -194
  98. package/.claude/settings.local.json +0 -14
  99. package/.kilo/plans/1780064105789-mighty-pixel.md +0 -171
  100. package/.kilo/plans/1780293725888-quick-wizard.md +0 -62
  101. package/.lonny/plan-web-cwd-status.md +0 -38
  102. package/src/tools/exec.ts +0 -348
@@ -0,0 +1,70 @@
1
+ /* ── Tool Confirm Dialog ── */
2
+
3
+ import { state } from './state.js'
4
+ import { escapeHtml } from './utils.js'
5
+ import { sendWsMsg } from './ws.js'
6
+
7
+ export function showConfirmDialog(toolCalls) {
8
+ const overlay = document.getElementById('confirm-overlay')
9
+ const list = document.getElementById('confirm-tool-list')
10
+ if (!overlay || !list) return
11
+ list.innerHTML = ''
12
+ for (const tc of toolCalls) {
13
+ const detail = tc.input?.file_path || tc.input?.command || tc.input?.package_name || ''
14
+ const item = document.createElement('div')
15
+ item.className = 'confirm-tool-item'
16
+ item.innerHTML =
17
+ `<span class="confirm-tool-name">${escapeHtml(tc.name)}</span>` +
18
+ (detail
19
+ ? ` <span class="confirm-tool-input">${escapeHtml(detail.length > 80 ? `${detail.slice(0, 80)}…` : detail)}</span>`
20
+ : '')
21
+ list.appendChild(item)
22
+ }
23
+ overlay.classList.remove('hidden')
24
+
25
+ state.pendingConfirmResolve = null
26
+
27
+ function cleanup(approved) {
28
+ overlay.classList.add('hidden')
29
+ allowBtn.removeEventListener('click', onAllow)
30
+ rejectBtn.removeEventListener('click', onReject)
31
+ if (state.pendingConfirmResolve) {
32
+ state.pendingConfirmResolve(approved)
33
+ state.pendingConfirmResolve = null
34
+ }
35
+ }
36
+
37
+ const allowBtn = document.getElementById('confirm-allow-btn')
38
+ const rejectBtn = document.getElementById('confirm-reject-btn')
39
+ const onAllow = () => {
40
+ sendWsMsg({ type: 'tool_confirm_response', approved: true })
41
+ cleanup(true)
42
+ }
43
+ const onReject = () => {
44
+ sendWsMsg({ type: 'tool_confirm_response', approved: false })
45
+ cleanup(false)
46
+ }
47
+ allowBtn.addEventListener('click', onAllow)
48
+ rejectBtn.addEventListener('click', onReject)
49
+ }
50
+
51
+ export function closeConfirmDialog() {
52
+ const overlay = document.getElementById('confirm-overlay')
53
+ if (overlay && !overlay.classList.contains('hidden')) {
54
+ overlay.classList.add('hidden')
55
+ if (state.pendingConfirmResolve) {
56
+ state.pendingConfirmResolve(false)
57
+ state.pendingConfirmResolve = null
58
+ }
59
+ }
60
+ }
61
+
62
+ // Click overlay background to close (added once)
63
+ const confirmOverlay = document.getElementById('confirm-overlay')
64
+ if (confirmOverlay) {
65
+ confirmOverlay.addEventListener('click', e => {
66
+ if (e.target === e.currentTarget) {
67
+ closeConfirmDialog()
68
+ }
69
+ })
70
+ }
@@ -1,120 +1,121 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Lonny - AI 编码代理</title>
7
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.css">
8
- <link rel="stylesheet" href="/style.css">
9
- </head>
10
- <body>
11
- <!-- Status Bar -->
12
- <header id="status-bar">
13
- <div class="status-left">
14
- <span class="logo">lonny</span>
15
- <span class="separator">|</span>
16
- <span id="status-indicator" class="status-idle">○ idle</span>
17
- <span class="separator">|</span>
18
- <span id="mode-display" class="mode-badge">code</span>
19
- <span class="separator">|</span>
20
- <span id="cwd-display" class="cwd-text"></span>
21
- </div>
22
- <div class="status-right">
23
- <span id="model-display" class="model-name">loading...</span>
24
- <span class="separator">|</span>
25
- <span class="token-stats">
26
- <span class="token-in" id="token-in">0</span>
27
- <span class="token-sep">/</span>
28
- <span class="token-out" id="token-out">0</span>
29
- <span class="token-calls" id="token-calls">(0)</span>
30
- </span>
31
- <span class="separator" id="balance-sep" style="display:none">|</span>
32
- <span id="balance-display" class="balance-display" style="display:none"></span>
33
- </div>
34
- </header>
35
-
36
- <!-- App Content (chat + sidebar) -->
37
- <div id="app-content">
38
- <!-- Chat Panel -->
39
- <div id="chat-panel">
40
- <!-- Chat Container -->
41
- <main id="chat-container">
42
- <div id="messages">
43
- <!-- Messages will be rendered here -->
44
- </div>
45
- </main>
46
-
47
- <!-- Input Area -->
48
- <footer id="input-area">
49
- <div class="input-wrapper">
50
- <div class="input-container">
51
- <div id="slash-hint" class="slash-hint hidden">
52
- <div class="slash-hint-header">Commands</div>
53
- <div class="slash-hint-item" data-cmd="mode code|plan|ask">/mode <span class="hint-arg">code|plan|ask</span> <span class="hint-desc">Switch mode</span></div>
54
- <div class="slash-hint-item" data-cmd="model ">/model <span class="hint-arg">&lt;name&gt;</span> <span class="hint-desc">Switch model</span></div>
55
- <div class="slash-hint-item" data-cmd="new">/new <span class="hint-desc">Start a new session</span></div>
56
- <div class="slash-hint-item" data-cmd="stop">/stop <span class="hint-desc">Stop the running agent</span></div>
57
- <div class="slash-hint-item" data-cmd="skills">/skills <span class="hint-desc">List active skills</span></div>
58
- <div class="slash-hint-item" data-cmd="prompts">/prompts <span class="hint-desc">List prompt templates</span></div>
59
- <div class="slash-hint-item" data-cmd="init">/init <span class="hint-desc">Create .lonny/skills/ &amp; prompts/</span></div>
60
- <div class="slash-hint-item" data-cmd="help">/help <span class="hint-desc">Show this help</span></div>
61
- </div>
62
- <textarea id="chat-input" rows="3" placeholder="Type a message... (type / for commands)"></textarea>
63
- </div>
64
- <button id="send-btn" title="Send">
65
- <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
66
- <path d="M22 2L11 13"/><path d="M22 2L15 22L11 13L2 9L22 2Z"/>
67
- </svg>
68
- </button>
69
- <button id="stop-btn" class="stop-btn hidden" title="Stop">
70
- <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
71
- <rect x="6" y="6" width="12" height="12" rx="2"/>
72
- </svg>
73
- </button>
74
- </div>
75
- </footer>
76
- </div>
77
-
78
- <!-- Right Sidebar -->
79
- <aside id="sidebar">
80
- <div class="sidebar-tabs">
81
- <button class="sidebar-tab active" data-tab="plans">░ Plans</button>
82
- <button class="sidebar-tab" data-tab="todos">☑ Todo</button>
83
- </div>
84
- <div class="sidebar-content">
85
- <div class="sidebar-pane active" id="plans-pane">
86
- <div id="plans-placeholder" class="sidebar-placeholder">Loading plans...</div>
87
- <div id="plans-list"></div>
88
- </div>
89
- <div class="sidebar-pane" id="todos-pane">
90
- <div id="todos-placeholder" class="sidebar-placeholder">Loading todos...</div>
91
- <div id="todos-list"></div>
92
- </div>
93
- </div>
94
- </aside>
95
- </div>
96
-
97
- <!-- Connection status overlay -->
98
- <div id="connection-overlay" class="overlay hidden">
99
- <div class="overlay-content">
100
- <div class="spinner"></div>
101
- <p>Connecting to Lonny...</p>
102
- </div>
103
- </div>
104
-
105
- <!-- Tool confirm dialog -->
106
- <div id="confirm-overlay" class="overlay hidden">
107
- <div class="confirm-dialog">
108
- <div class="confirm-header">Allow these tool calls?</div>
109
- <div id="confirm-tool-list" class="confirm-tool-list"></div>
110
- <div class="confirm-actions">
111
- <button id="confirm-reject-btn" class="confirm-btn confirm-btn-reject">Reject</button>
112
- <button id="confirm-allow-btn" class="confirm-btn confirm-btn-allow">Allow</button>
113
- </div>
114
- </div>
115
- </div>
116
-
117
- <script src="https://cdn.jsdelivr.net/npm/marked@15.0.4/marked.min.js"></script>
118
- <script src="/app.js"></script>
119
- </body>
120
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Lonny - AI 编码代理</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/normalize.css@8.0.1/normalize.css">
8
+ <link rel="stylesheet" href="/style.css">
9
+ </head>
10
+ <body>
11
+ <!-- Status Bar -->
12
+ <header id="status-bar">
13
+ <div class="status-left">
14
+ <span class="logo">lonny</span>
15
+ <span class="separator">|</span>
16
+ <span id="status-indicator" class="status-idle">○ idle</span>
17
+ <span class="separator">|</span>
18
+ <span id="mode-display" class="mode-badge">code</span>
19
+ <span class="separator">|</span>
20
+ <span id="cwd-display" class="cwd-text"></span>
21
+ </div>
22
+ <div class="status-right">
23
+ <span id="model-display" class="model-name">loading...</span>
24
+ <span class="separator">|</span>
25
+ <span class="token-stats">
26
+ <span class="token-in" id="token-in">0</span>
27
+ <span class="token-sep">/</span>
28
+ <span class="token-out" id="token-out">0</span>
29
+ <span class="token-calls" id="token-calls">(0)</span>
30
+ <span class="token-cache hidden" id="token-cache"></span>
31
+ </span>
32
+ <span class="separator" id="balance-sep" style="display:none">|</span>
33
+ <span id="balance-display" class="balance-display" style="display:none"></span>
34
+ </div>
35
+ </header>
36
+
37
+ <!-- App Content (chat + sidebar) -->
38
+ <div id="app-content">
39
+ <!-- Chat Panel -->
40
+ <div id="chat-panel">
41
+ <!-- Chat Container -->
42
+ <main id="chat-container">
43
+ <div id="messages">
44
+ <!-- Messages will be rendered here -->
45
+ </div>
46
+ </main>
47
+
48
+ <!-- Input Area -->
49
+ <footer id="input-area">
50
+ <div class="input-wrapper">
51
+ <div class="input-container">
52
+ <div id="slash-hint" class="slash-hint hidden">
53
+ <div class="slash-hint-header">Commands</div>
54
+ <div class="slash-hint-item" data-cmd="mode code|plan|ask">/mode <span class="hint-arg">code|plan|ask</span> <span class="hint-desc">Switch mode</span></div>
55
+ <div class="slash-hint-item" data-cmd="model ">/model <span class="hint-arg">&lt;name&gt;</span> <span class="hint-desc">Switch model</span></div>
56
+ <div class="slash-hint-item" data-cmd="new">/new <span class="hint-desc">Start a new session</span></div>
57
+ <div class="slash-hint-item" data-cmd="stop">/stop <span class="hint-desc">Stop the running agent</span></div>
58
+ <div class="slash-hint-item" data-cmd="skills">/skills <span class="hint-desc">List active skills</span></div>
59
+ <div class="slash-hint-item" data-cmd="prompts">/prompts <span class="hint-desc">List prompt templates</span></div>
60
+ <div class="slash-hint-item" data-cmd="init">/init <span class="hint-desc">Create .lonny/skills/ &amp; prompts/</span></div>
61
+ <div class="slash-hint-item" data-cmd="help">/help <span class="hint-desc">Show this help</span></div>
62
+ </div>
63
+ <textarea id="chat-input" rows="3" placeholder="Type a message... (type / for commands)"></textarea>
64
+ </div>
65
+ <button id="send-btn" title="Send">
66
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
67
+ <path d="M22 2L11 13"/><path d="M22 2L15 22L11 13L2 9L22 2Z"/>
68
+ </svg>
69
+ </button>
70
+ <button id="stop-btn" class="stop-btn hidden" title="Stop">
71
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
72
+ <rect x="6" y="6" width="12" height="12" rx="2"/>
73
+ </svg>
74
+ </button>
75
+ </div>
76
+ </footer>
77
+ </div>
78
+
79
+ <!-- Right Sidebar -->
80
+ <aside id="sidebar">
81
+ <div class="sidebar-tabs">
82
+ <button class="sidebar-tab active" data-tab="plans">░ Plans</button>
83
+ <button class="sidebar-tab" data-tab="todos">☑ Todo</button>
84
+ </div>
85
+ <div class="sidebar-content">
86
+ <div class="sidebar-pane active" id="plans-pane">
87
+ <div id="plans-placeholder" class="sidebar-placeholder">Loading plans...</div>
88
+ <div id="plans-list"></div>
89
+ </div>
90
+ <div class="sidebar-pane" id="todos-pane">
91
+ <div id="todos-placeholder" class="sidebar-placeholder">Loading todos...</div>
92
+ <div id="todos-list"></div>
93
+ </div>
94
+ </div>
95
+ </aside>
96
+ </div>
97
+
98
+ <!-- Connection status overlay -->
99
+ <div id="connection-overlay" class="overlay hidden">
100
+ <div class="overlay-content">
101
+ <div class="spinner"></div>
102
+ <p>Connecting to Lonny...</p>
103
+ </div>
104
+ </div>
105
+
106
+ <!-- Tool confirm dialog -->
107
+ <div id="confirm-overlay" class="overlay hidden">
108
+ <div class="confirm-dialog">
109
+ <div class="confirm-header">Allow these tool calls?</div>
110
+ <div id="confirm-tool-list" class="confirm-tool-list"></div>
111
+ <div class="confirm-actions">
112
+ <button id="confirm-reject-btn" class="confirm-btn confirm-btn-reject">Reject</button>
113
+ <button id="confirm-allow-btn" class="confirm-btn confirm-btn-allow">Allow</button>
114
+ </div>
115
+ </div>
116
+ </div>
117
+
118
+ <script src="https://cdn.jsdelivr.net/npm/marked@15.0.4/marked.min.js"></script>
119
+ <script type="module" src="/app.js"></script>
120
+ </body>
121
+ </html>
@@ -0,0 +1,107 @@
1
+ /* ── Input Handling, Event Listeners, Slash Hints ── */
2
+
3
+ import { addErrorMessage, addUserMessage } from './messages.js'
4
+ import { switchTab } from './sidebar.js'
5
+ import { chatInput, sendBtn, sidebarTabs, slashHint, state, stopBtn } from './state.js'
6
+ import { sendWsMsg } from './ws.js'
7
+
8
+ function sendMessage() {
9
+ const text = chatInput.value.trim()
10
+ if (!text || state.isRunning) return
11
+
12
+ addUserMessage(text)
13
+ chatInput.value = ''
14
+
15
+ if (!sendWsMsg({ type: 'message', text })) {
16
+ addErrorMessage('Not connected to server.')
17
+ }
18
+ }
19
+
20
+ function showSlashHint() {
21
+ slashHint.classList.remove('hidden')
22
+ const items = slashHint.querySelectorAll('.slash-hint-item')
23
+ items.forEach((item, i) => {
24
+ item.classList.toggle('selected', i === 0)
25
+ })
26
+ }
27
+
28
+ function hideSlashHint() {
29
+ slashHint.classList.add('hidden')
30
+ }
31
+
32
+ export function initInput() {
33
+ // Send button
34
+ sendBtn.addEventListener('click', sendMessage)
35
+
36
+ // Stop button
37
+ stopBtn.addEventListener('click', () => {
38
+ sendWsMsg({ type: 'stop' })
39
+ stopBtn.disabled = true
40
+ })
41
+
42
+ // Chat input keyboard
43
+ chatInput.addEventListener('keydown', e => {
44
+ if (e.key === 'Enter' && !e.shiftKey) {
45
+ e.preventDefault()
46
+ sendMessage()
47
+ }
48
+ if (e.key === 'Tab' && !slashHint.classList.contains('hidden')) {
49
+ e.preventDefault()
50
+ const selected = slashHint.querySelector('.slash-hint-item.selected')
51
+ if (selected) {
52
+ const cmd = selected.dataset.cmd
53
+ chatInput.value = `/${cmd}`
54
+ hideSlashHint()
55
+ chatInput.focus()
56
+ }
57
+ }
58
+ if (e.key === 'Escape') {
59
+ hideSlashHint()
60
+ }
61
+ })
62
+
63
+ // Slash hint: click to select
64
+ slashHint.addEventListener('click', e => {
65
+ const item = e.target.closest('.slash-hint-item')
66
+ if (item) {
67
+ const cmd = item.dataset.cmd
68
+ chatInput.value = `/${cmd}`
69
+ hideSlashHint()
70
+ chatInput.focus()
71
+ }
72
+ })
73
+
74
+ // Slash hint: hover to change selection
75
+ slashHint.addEventListener('mouseover', e => {
76
+ const item = e.target.closest('.slash-hint-item')
77
+ if (item) {
78
+ slashHint.querySelectorAll('.slash-hint-item').forEach(el => el.classList.remove('selected'))
79
+ item.classList.add('selected')
80
+ }
81
+ })
82
+
83
+ // Show/hide hint based on input content
84
+ chatInput.addEventListener('input', () => {
85
+ chatInput.style.height = 'auto'
86
+ chatInput.style.height = `${Math.min(chatInput.scrollHeight, 150)}px`
87
+
88
+ const text = chatInput.value
89
+ if (text === '/') {
90
+ showSlashHint()
91
+ } else if (!text.startsWith('/') || text.includes(' ')) {
92
+ hideSlashHint()
93
+ }
94
+ })
95
+
96
+ // Sidebar tab switching
97
+ sidebarTabs.forEach(tab => {
98
+ tab.addEventListener('click', () => {
99
+ switchTab(tab.dataset.tab)
100
+ })
101
+ })
102
+
103
+ // Heartbeat
104
+ setInterval(() => {
105
+ sendWsMsg({ type: 'ping' })
106
+ }, 30000)
107
+ }