agentgui 1.0.551 → 1.0.553
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/database.js +2 -0
- package/lib/claude-runner.js +8 -0
- package/lib/ws-handlers-conv.js +23 -1
- package/package.json +1 -1
- package/server.js +10 -3
- package/static/index.html +25 -0
- package/static/js/client.js +30 -3
package/database.js
CHANGED
|
@@ -37,6 +37,7 @@ try {
|
|
|
37
37
|
db.run('PRAGMA foreign_keys = ON');
|
|
38
38
|
db.run('PRAGMA encoding = "UTF-8"');
|
|
39
39
|
db.run('PRAGMA synchronous = NORMAL');
|
|
40
|
+
db.run('PRAGMA busy_timeout = 5000');
|
|
40
41
|
db.run('PRAGMA cache_size = -64000');
|
|
41
42
|
db.run('PRAGMA mmap_size = 268435456');
|
|
42
43
|
db.run('PRAGMA temp_store = MEMORY');
|
|
@@ -48,6 +49,7 @@ try {
|
|
|
48
49
|
db.pragma('foreign_keys = ON');
|
|
49
50
|
db.pragma('encoding = "UTF-8"');
|
|
50
51
|
db.pragma('synchronous = NORMAL');
|
|
52
|
+
db.pragma('busy_timeout = 5000');
|
|
51
53
|
db.pragma('cache_size = -64000');
|
|
52
54
|
db.pragma('mmap_size = 268435456');
|
|
53
55
|
db.pragma('temp_store = MEMORY');
|
package/lib/claude-runner.js
CHANGED
|
@@ -95,6 +95,10 @@ class AgentRunner {
|
|
|
95
95
|
try { config.onPid(proc.pid); } catch (e) {}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
if (config.onProcess) {
|
|
99
|
+
try { config.onProcess(proc); } catch (e) {}
|
|
100
|
+
}
|
|
101
|
+
|
|
98
102
|
let jsonBuffer = '';
|
|
99
103
|
const outputs = [];
|
|
100
104
|
let timedOut = false;
|
|
@@ -274,6 +278,10 @@ class AgentRunner {
|
|
|
274
278
|
try { config.onPid(proc.pid); } catch (e) {}
|
|
275
279
|
}
|
|
276
280
|
|
|
281
|
+
if (config.onProcess) {
|
|
282
|
+
try { config.onProcess(proc); } catch (e) {}
|
|
283
|
+
}
|
|
284
|
+
|
|
277
285
|
const outputs = [];
|
|
278
286
|
let timedOut = false;
|
|
279
287
|
let sessionId = null;
|
package/lib/ws-handlers-conv.js
CHANGED
|
@@ -5,7 +5,7 @@ function notFound(msg = 'Not found') { fail(404, msg); }
|
|
|
5
5
|
|
|
6
6
|
export function register(router, deps) {
|
|
7
7
|
const { queries, activeExecutions, messageQueues, rateLimitState,
|
|
8
|
-
broadcastSync, processMessageWithStreaming } = deps;
|
|
8
|
+
broadcastSync, processMessageWithStreaming, activeProcessesByConvId } = deps;
|
|
9
9
|
|
|
10
10
|
router.handle('conv.ls', () => {
|
|
11
11
|
const conversations = queries.getConversationsList();
|
|
@@ -114,6 +114,28 @@ export function register(router, deps) {
|
|
|
114
114
|
return { ok: true, injected: true, conversationId: p.id, messageId: message.id };
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
router.handle('conv.steer', (p) => {
|
|
118
|
+
const conv = queries.getConversation(p.id);
|
|
119
|
+
if (!conv) notFound('Conversation not found');
|
|
120
|
+
if (!p.content) fail(400, 'Missing content');
|
|
121
|
+
const entry = activeExecutions.get(p.id);
|
|
122
|
+
if (!entry) fail(409, 'No active execution to steer');
|
|
123
|
+
const proc = activeProcessesByConvId.get(p.id);
|
|
124
|
+
if (!proc || !proc.stdin) fail(409, 'Process not available for steering');
|
|
125
|
+
|
|
126
|
+
const message = queries.createMessage(p.id, 'user', p.content);
|
|
127
|
+
queries.createEvent('message.created', { role: 'user', messageId: message.id }, p.id);
|
|
128
|
+
broadcastSync({ type: 'message_created', conversationId: p.id, message, timestamp: Date.now() });
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
// Write directly to stdin for steering
|
|
132
|
+
proc.stdin.write(p.content + '\n');
|
|
133
|
+
return { ok: true, steered: true, conversationId: p.id, messageId: message.id };
|
|
134
|
+
} catch (err) {
|
|
135
|
+
fail(500, `Failed to steer: ${err.message}`);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
117
139
|
router.handle('msg.ls', (p) => {
|
|
118
140
|
return queries.getPaginatedMessages(p.id, Math.min(p.limit || 50, 100), Math.max(p.offset || 0, 0));
|
|
119
141
|
});
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -292,6 +292,7 @@ const activeScripts = new Map();
|
|
|
292
292
|
const messageQueues = new Map();
|
|
293
293
|
const rateLimitState = new Map();
|
|
294
294
|
const activeProcessesByRunId = new Map();
|
|
295
|
+
const activeProcessesByConvId = new Map(); // Store process handles by conversationId for steering
|
|
295
296
|
const acpQueries = queries;
|
|
296
297
|
const STUCK_AGENT_THRESHOLD_MS = 600000;
|
|
297
298
|
const NO_PID_GRACE_PERIOD_MS = 60000;
|
|
@@ -3806,18 +3807,24 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
|
|
|
3806
3807
|
onPid: (pid) => {
|
|
3807
3808
|
const entry = activeExecutions.get(conversationId);
|
|
3808
3809
|
if (entry) entry.pid = pid;
|
|
3810
|
+
},
|
|
3811
|
+
onProcess: (proc) => {
|
|
3812
|
+
// Store process handle for steering
|
|
3813
|
+
activeProcessesByConvId.set(conversationId, proc);
|
|
3809
3814
|
}
|
|
3810
3815
|
};
|
|
3811
3816
|
|
|
3812
3817
|
const { outputs, sessionId: claudeSessionId } = await runClaudeWithStreaming(content, cwd, agentId || 'claude-code', config);
|
|
3813
|
-
|
|
3818
|
+
|
|
3814
3819
|
// Check if rate limit was already handled in stream detection
|
|
3815
3820
|
if (rateLimitState.get(conversationId)?.isStreamDetected) {
|
|
3816
3821
|
debugLog(`[rate-limit] Rate limit already handled in stream for conv ${conversationId}, skipping success handler`);
|
|
3822
|
+
activeProcessesByConvId.delete(conversationId);
|
|
3817
3823
|
return;
|
|
3818
3824
|
}
|
|
3819
|
-
|
|
3825
|
+
|
|
3820
3826
|
activeExecutions.delete(conversationId);
|
|
3827
|
+
activeProcessesByConvId.delete(conversationId);
|
|
3821
3828
|
batcher.drain();
|
|
3822
3829
|
debugLog(`[stream] Claude returned ${outputs.length} outputs, sessionId=${claudeSessionId}`);
|
|
3823
3830
|
|
|
@@ -4134,7 +4141,7 @@ const wsRouter = new WsRouter();
|
|
|
4134
4141
|
|
|
4135
4142
|
registerConvHandlers(wsRouter, {
|
|
4136
4143
|
queries, activeExecutions, messageQueues, rateLimitState,
|
|
4137
|
-
broadcastSync, processMessageWithStreaming
|
|
4144
|
+
broadcastSync, processMessageWithStreaming, activeProcessesByConvId
|
|
4138
4145
|
});
|
|
4139
4146
|
|
|
4140
4147
|
registerSessionHandlers(wsRouter, {
|
package/static/index.html
CHANGED
|
@@ -948,6 +948,26 @@
|
|
|
948
948
|
.inject-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
949
949
|
.inject-btn.visible { display: flex; }
|
|
950
950
|
|
|
951
|
+
.steer-btn {
|
|
952
|
+
display: none;
|
|
953
|
+
align-items: center;
|
|
954
|
+
justify-content: center;
|
|
955
|
+
width: 40px;
|
|
956
|
+
height: 40px;
|
|
957
|
+
background-color: #06b6d4;
|
|
958
|
+
color: white;
|
|
959
|
+
border: none;
|
|
960
|
+
border-radius: 0.5rem;
|
|
961
|
+
cursor: pointer;
|
|
962
|
+
flex-shrink: 0;
|
|
963
|
+
transition: background-color 0.15s;
|
|
964
|
+
margin-right: 0.25rem;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.steer-btn:hover:not(:disabled) { background-color: #0891b2; }
|
|
968
|
+
.steer-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
969
|
+
.steer-btn.visible { display: flex; }
|
|
970
|
+
|
|
951
971
|
/* ===== OVERLAY for mobile sidebar ===== */
|
|
952
972
|
.sidebar-overlay {
|
|
953
973
|
display: none;
|
|
@@ -3216,6 +3236,11 @@
|
|
|
3216
3236
|
<path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/>
|
|
3217
3237
|
</svg>
|
|
3218
3238
|
</button>
|
|
3239
|
+
<button class="steer-btn" id="steerBtn" title="Steer running agent with message" aria-label="Steer agent">
|
|
3240
|
+
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
3241
|
+
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/>
|
|
3242
|
+
</svg>
|
|
3243
|
+
</button>
|
|
3219
3244
|
<button class="stop-btn" id="stopBtn" title="Stop running agent (emergency)" aria-label="Stop agent">
|
|
3220
3245
|
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
3221
3246
|
<path d="M6 6h12v12H6z"/>
|
package/static/js/client.js
CHANGED
|
@@ -458,6 +458,7 @@ class AgentGUIClient {
|
|
|
458
458
|
|
|
459
459
|
this.ui.stopButton = document.getElementById('stopBtn');
|
|
460
460
|
this.ui.injectButton = document.getElementById('injectBtn');
|
|
461
|
+
this.ui.steerButton = document.getElementById('steerBtn');
|
|
461
462
|
|
|
462
463
|
if (this.ui.stopButton) {
|
|
463
464
|
this.ui.stopButton.addEventListener('click', async () => {
|
|
@@ -485,6 +486,28 @@ class AgentGUIClient {
|
|
|
485
486
|
});
|
|
486
487
|
}
|
|
487
488
|
|
|
489
|
+
if (this.ui.steerButton) {
|
|
490
|
+
this.ui.steerButton.addEventListener('click', async () => {
|
|
491
|
+
if (!this.state.currentConversation) return;
|
|
492
|
+
const message = this.ui.messageInput?.value || '';
|
|
493
|
+
if (!message.trim()) {
|
|
494
|
+
this.showError('Please enter a message to steer');
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
const data = await window.wsClient.rpc('conv.steer', { id: this.state.currentConversation.id, content: message });
|
|
499
|
+
console.log('Steer response:', data);
|
|
500
|
+
if (this.ui.messageInput) {
|
|
501
|
+
this.ui.messageInput.value = '';
|
|
502
|
+
this.ui.messageInput.style.height = 'auto';
|
|
503
|
+
}
|
|
504
|
+
} catch (err) {
|
|
505
|
+
console.error('Failed to steer:', err);
|
|
506
|
+
this.showError('Failed to steer: ' + err.message);
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
488
511
|
if (this.ui.messageInput) {
|
|
489
512
|
this.ui.messageInput.addEventListener('keydown', (e) => {
|
|
490
513
|
if (e.key === 'Enter' && e.ctrlKey) {
|
|
@@ -708,9 +731,10 @@ class AgentGUIClient {
|
|
|
708
731
|
return;
|
|
709
732
|
}
|
|
710
733
|
|
|
711
|
-
// Show stop and inject buttons when streaming starts for current conversation
|
|
734
|
+
// Show stop, steer, and inject buttons when streaming starts for current conversation
|
|
712
735
|
if (this.ui.stopButton) this.ui.stopButton.classList.add('visible');
|
|
713
736
|
if (this.ui.injectButton) this.ui.injectButton.classList.add('visible');
|
|
737
|
+
if (this.ui.steerButton) this.ui.steerButton.classList.add('visible');
|
|
714
738
|
if (this.ui.sendButton) this.ui.sendButton.style.display = 'none';
|
|
715
739
|
|
|
716
740
|
this.state.streamingConversations.set(data.conversationId, true);
|
|
@@ -937,9 +961,10 @@ class AgentGUIClient {
|
|
|
937
961
|
console.error('Streaming error:', data);
|
|
938
962
|
this._clearThinkingCountdown();
|
|
939
963
|
|
|
940
|
-
// Hide stop and inject buttons on error
|
|
964
|
+
// Hide stop, steer, and inject buttons on error
|
|
941
965
|
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
942
966
|
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
967
|
+
if (this.ui.steerButton) this.ui.steerButton.classList.remove('visible');
|
|
943
968
|
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
944
969
|
|
|
945
970
|
const conversationId = data.conversationId || this.state.currentSession?.conversationId;
|
|
@@ -988,9 +1013,10 @@ class AgentGUIClient {
|
|
|
988
1013
|
console.log('Streaming completed:', data);
|
|
989
1014
|
this._clearThinkingCountdown();
|
|
990
1015
|
|
|
991
|
-
// Hide stop and inject buttons when streaming completes
|
|
1016
|
+
// Hide stop, steer, and inject buttons when streaming completes
|
|
992
1017
|
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
993
1018
|
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
1019
|
+
if (this.ui.steerButton) this.ui.steerButton.classList.remove('visible');
|
|
994
1020
|
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
995
1021
|
|
|
996
1022
|
const conversationId = data.conversationId || this.state.currentSession?.conversationId;
|
|
@@ -2437,6 +2463,7 @@ class AgentGUIClient {
|
|
|
2437
2463
|
|
|
2438
2464
|
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
2439
2465
|
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
2466
|
+
if (this.ui.steerButton) this.ui.steerButton.classList.remove('visible');
|
|
2440
2467
|
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
2441
2468
|
|
|
2442
2469
|
var prevId = this.state.currentConversation?.id;
|