agentgui 1.0.939 → 1.0.941

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 (46) hide show
  1. package/AGENTS.md +12 -7
  2. package/lib/claude-runner-agents.js +25 -0
  3. package/lib/ws-handlers-util.js +27 -1
  4. package/package.json +1 -1
  5. package/server.js +10 -1
  6. package/site/app/index.html +14 -37
  7. package/site/app/js/app.js +506 -104
  8. package/site/app/js/backend.js +44 -32
  9. package/site/app/vendor/anentrypoint-design/247420.css +274 -86
  10. package/site/app/vendor/anentrypoint-design/247420.js +12 -12
  11. package/site/app/vendor/cdn/dompurify.js +9 -0
  12. package/site/app/vendor/cdn/fonts/1291de6d401a.woff2 +0 -0
  13. package/site/app/vendor/cdn/fonts/1ba89a87e0b8.woff2 +0 -0
  14. package/site/app/vendor/cdn/fonts/3644d51c507b.woff2 +0 -0
  15. package/site/app/vendor/cdn/fonts/4b91d2650dc2.woff2 +0 -0
  16. package/site/app/vendor/cdn/fonts/530d036ba64a.woff2 +0 -0
  17. package/site/app/vendor/cdn/fonts/570a2bdd8f8b.woff2 +0 -0
  18. package/site/app/vendor/cdn/fonts/5dd6d880fee9.woff2 +0 -0
  19. package/site/app/vendor/cdn/fonts/62de9143afe3.woff2 +0 -0
  20. package/site/app/vendor/cdn/fonts/64884efa2f11.woff2 +0 -0
  21. package/site/app/vendor/cdn/fonts/68cd7063be2e.woff2 +0 -0
  22. package/site/app/vendor/cdn/fonts/6c252abcf99b.woff2 +0 -0
  23. package/site/app/vendor/cdn/fonts/71e69e06516a.woff2 +0 -0
  24. package/site/app/vendor/cdn/fonts/9ea68c62083f.woff2 +0 -0
  25. package/site/app/vendor/cdn/fonts/c010f9b7d6b2.woff2 +0 -0
  26. package/site/app/vendor/cdn/fonts/d69723fc74be.woff2 +0 -0
  27. package/site/app/vendor/cdn/fonts/fonts.css +459 -0
  28. package/site/app/vendor/cdn/marked.js +8 -0
  29. package/site/app/vendor/cdn/prismjs/components/prism-bash.min.js +1 -0
  30. package/site/app/vendor/cdn/prismjs/components/prism-clike.min.js +1 -0
  31. package/site/app/vendor/cdn/prismjs/components/prism-core.min.js +1 -0
  32. package/site/app/vendor/cdn/prismjs/components/prism-css.min.js +1 -0
  33. package/site/app/vendor/cdn/prismjs/components/prism-diff.min.js +1 -0
  34. package/site/app/vendor/cdn/prismjs/components/prism-go.min.js +1 -0
  35. package/site/app/vendor/cdn/prismjs/components/prism-javascript.min.js +1 -0
  36. package/site/app/vendor/cdn/prismjs/components/prism-json.min.js +1 -0
  37. package/site/app/vendor/cdn/prismjs/components/prism-jsx.min.js +1 -0
  38. package/site/app/vendor/cdn/prismjs/components/prism-markdown.min.js +1 -0
  39. package/site/app/vendor/cdn/prismjs/components/prism-markup.min.js +1 -0
  40. package/site/app/vendor/cdn/prismjs/components/prism-python.min.js +1 -0
  41. package/site/app/vendor/cdn/prismjs/components/prism-rust.min.js +1 -0
  42. package/site/app/vendor/cdn/prismjs/components/prism-sql.min.js +1 -0
  43. package/site/app/vendor/cdn/prismjs/components/prism-toml.min.js +1 -0
  44. package/site/app/vendor/cdn/prismjs/components/prism-tsx.min.js +1 -0
  45. package/site/app/vendor/cdn/prismjs/components/prism-typescript.min.js +1 -0
  46. package/site/app/vendor/cdn/prismjs/components/prism-yaml.min.js +1 -0
@@ -27,14 +27,17 @@ function withToken(url) {
27
27
  return url + (url.includes('?') ? '&' : '?') + 'token=' + encodeURIComponent(tok);
28
28
  }
29
29
 
30
+ function lsGet(k) { try { return localStorage.getItem(k); } catch { return null; } }
31
+ function lsSet(k, v) { try { localStorage.setItem(k, v); } catch {} }
32
+
30
33
  export function getBackend() {
31
34
  const u = new URL(location.href);
32
35
  const fromQs = u.searchParams.get('backend');
33
- if (fromQs) { localStorage.setItem(KEY, fromQs); return fromQs; }
34
- return localStorage.getItem(KEY) || DEFAULT_BACKEND;
36
+ if (fromQs) { lsSet(KEY, fromQs); return fromQs; }
37
+ return lsGet(KEY) || DEFAULT_BACKEND;
35
38
  }
36
39
 
37
- export function setBackend(url) { localStorage.setItem(KEY, url); }
40
+ export function setBackend(url) { lsSet(KEY, url); }
38
41
 
39
42
  export async function probeBackend(base) {
40
43
  try {
@@ -203,12 +206,28 @@ function addSessionListener(sessionId, fn) {
203
206
 
204
207
  // ---------- Agents / models (WS) ----------
205
208
 
206
- export async function listModels(base) {
209
+ export async function listAgents(base) {
207
210
  const { agents } = await wsCall(base, 'agents.list', {});
208
- // Compatibility shape: app.js expects an array of {id, name?, ...}
209
211
  return agents || [];
210
212
  }
211
213
 
214
+ export async function listActiveChats(base) {
215
+ try { const { sessions } = await wsCall(base, 'chat.active', {}); return sessions || []; }
216
+ catch { return []; }
217
+ }
218
+
219
+ export async function cancelChat(base, sessionId) {
220
+ return wsCall(base, 'chat.cancel', { sessionId });
221
+ }
222
+
223
+ export async function listAgentModels(base, agentId) {
224
+ if (!agentId) return [];
225
+ try {
226
+ const { models } = await wsCall(base, 'agents.models', { id: agentId });
227
+ return models || [];
228
+ } catch { return []; }
229
+ }
230
+
212
231
  // ---------- Streaming chat (WS) ----------
213
232
  //
214
233
  // Yields events of shape:
@@ -218,29 +237,15 @@ export async function listModels(base) {
218
237
  // { type: 'error', error: '...' }
219
238
  //
220
239
  // Caller signature kept compatible with the previous HTTP/SSE impl.
221
- export async function* streamChat(base, { model, messages, signal, agentId, resumeSid }) {
222
- // The last user message is the prompt; agentgui's claude-runner doesn't
223
- // accept a full message list it spawns the agent for a single prompt.
224
- // For multi-turn, the agent's own session/resume handles continuity.
240
+ export async function* streamChat(base, { model, messages, signal, agentId, resumeSid, cwd }) {
241
+ // The last user message is the prompt; agentgui's claude-runner spawns the
242
+ // agent for a single prompt. Multi-turn continuity is the agent's own resume.
225
243
  const last = messages[messages.length - 1];
226
244
  const content = last?.content || '';
227
245
  if (!content) return;
228
246
 
229
- // app.js treats the "model" picker as the agent picker (selects from
230
- // agents.list ids). If no explicit agentId is given, model IS the agent.
231
- // If `model` looks like a real model id (has a slash), keep it as model
232
- // and default agent to claude-code.
233
- let resolvedAgentId = agentId;
234
- let resolvedModel = model;
235
- if (!resolvedAgentId) {
236
- if (!model || /^[a-z][a-z0-9-]*$/.test(model)) {
237
- // Bare slug — treat as agentId.
238
- resolvedAgentId = model || 'claude-code';
239
- resolvedModel = undefined;
240
- } else {
241
- resolvedAgentId = 'claude-code';
242
- }
243
- }
247
+ const resolvedAgentId = agentId || 'claude-code';
248
+ const resolvedModel = model || undefined;
244
249
 
245
250
  // Queue events here; the async iterator pulls from it.
246
251
  const queue = [];
@@ -252,7 +257,7 @@ export async function* streamChat(base, { model, messages, signal, agentId, resu
252
257
  // Kick off the chat on the server.
253
258
  let started;
254
259
  try {
255
- started = await wsCall(base, 'chat.sendMessage', { content, agentId: resolvedAgentId, model: resolvedModel, resumeSid });
260
+ started = await wsCall(base, 'chat.sendMessage', { content, agentId: resolvedAgentId, model: resolvedModel, resumeSid, cwd });
256
261
  } catch (e) {
257
262
  yield { type: 'error', error: e.message };
258
263
  return;
@@ -260,25 +265,31 @@ export async function* streamChat(base, { model, messages, signal, agentId, resu
260
265
  const sessionId = started?.sessionId;
261
266
  if (!sessionId) { yield { type: 'error', error: 'no sessionId from server' }; return; }
262
267
 
268
+ const finish = () => { done = true; if (resolveWait) { resolveWait(); resolveWait = null; } };
269
+
263
270
  const unsub = addSessionListener(sessionId, (ev) => {
264
271
  if (ev.type === 'streaming_progress') {
265
272
  const block = ev.block;
266
273
  if (block?.type === 'text' && block.text) push({ type: 'text', text: block.text });
267
274
  else if (block?.type === 'tool_use') push({ type: 'tool', block });
268
- else if (block?.type === 'tool_result') push({ type: 'tool', block });
275
+ else if (block?.type === 'tool_result') push({ type: 'tool_result', block });
269
276
  else if (block?.type === 'result') push({ type: 'result', block });
270
277
  } else if (ev.type === 'streaming_complete') {
271
- done = true;
272
- if (resolveWait) { resolveWait(); resolveWait = null; }
278
+ finish();
273
279
  } else if (ev.type === 'streaming_error') {
274
280
  errored = ev.error || 'streaming error';
275
- done = true;
276
- if (resolveWait) { resolveWait(); resolveWait = null; }
281
+ finish();
277
282
  }
278
283
  });
279
284
 
280
- // Wire AbortSignal to chat.cancel.
281
- const onAbort = () => { wsCall(base, 'chat.cancel', { sessionId }).catch(() => {}); };
285
+ // If the websocket drops mid-stream, streaming_complete will never arrive —
286
+ // surface an error and end the iterator instead of hanging forever.
287
+ const onWs = (s) => { if ((s === 'closed' || s === 'error') && !done) { errored = errored || 'connection lost during stream'; finish(); } };
288
+ const unsubWs = onWsStatus ? onWsStatus(onWs) : null;
289
+
290
+ // Wire AbortSignal to chat.cancel — and end the iterator immediately so the
291
+ // caller's busy state clears even if the server never emits a final event.
292
+ const onAbort = () => { wsCall(base, 'chat.cancel', { sessionId }).catch(() => {}); finish(); };
282
293
  if (signal) {
283
294
  if (signal.aborted) onAbort();
284
295
  else signal.addEventListener('abort', onAbort, { once: true });
@@ -295,6 +306,7 @@ export async function* streamChat(base, { model, messages, signal, agentId, resu
295
306
  if (errored) yield { type: 'error', error: errored };
296
307
  } finally {
297
308
  unsub();
309
+ if (typeof unsubWs === 'function') unsubWs();
298
310
  if (signal) signal.removeEventListener?.('abort', onAbort);
299
311
  }
300
312
  }