agentgui 1.0.957 → 1.0.959
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/.claude/workflows/gui-completion.js +88 -0
- package/AGENTS.md +36 -4
- package/CHANGELOG.md +7 -0
- package/PUNCHLIST-COMPLETION.md +266 -0
- package/lib/http-handler.js +196 -16
- package/package.json +1 -1
- package/scripts/validate-mutations.mjs +40 -0
- package/site/app/js/app.js +771 -104
- package/site/app/js/backend.js +61 -3
- package/site/app/vendor/anentrypoint-design/247420.css +110 -0
- package/site/app/vendor/anentrypoint-design/247420.js +12 -12
package/site/app/js/backend.js
CHANGED
|
@@ -93,6 +93,45 @@ export function downloadUrl(base, filePath) {
|
|
|
93
93
|
return withToken(base + '/api/download/' + encodeURIComponent(filePath));
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// ---------- File mutations (confined server endpoints) ----------
|
|
97
|
+
|
|
98
|
+
// Shared shape: each throws an Error carrying .status so the app maps
|
|
99
|
+
// 403/404/409/413 to plain human copy instead of the raw server string.
|
|
100
|
+
async function mutateJSON(base, route, body) {
|
|
101
|
+
const r = await authedFetch(base + route, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: { 'Content-Type': 'application/json' },
|
|
104
|
+
body: JSON.stringify(body),
|
|
105
|
+
});
|
|
106
|
+
const j = await r.json().catch(() => ({}));
|
|
107
|
+
if (!r.ok) { const e = new Error(j.error || (route + ': ' + r.status)); e.status = r.status; throw e; }
|
|
108
|
+
return j;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Stat a path (GET /api/stat/<encoded>) for cwd validation. Returns {ok, dir}.
|
|
112
|
+
// Throws an Error carrying .status (403 outside roots, 404 missing).
|
|
113
|
+
export async function statPath(base, p) {
|
|
114
|
+
const r = await authedFetch(base + '/api/stat/' + encodeURIComponent(p));
|
|
115
|
+
const j = await r.json().catch(() => ({}));
|
|
116
|
+
if (!r.ok) { const e = new Error(j.error || ('stat: ' + r.status)); e.status = r.status; throw e; }
|
|
117
|
+
return j;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function renameEntry(base, filePath, newName) { return mutateJSON(base, '/api/rename', { path: filePath, newName }); }
|
|
121
|
+
export function deleteEntry(base, filePath, recursive) { return mutateJSON(base, '/api/delete', { path: filePath, recursive: !!recursive }); }
|
|
122
|
+
export function makeDir(base, dirPath, name) { return mutateJSON(base, '/api/mkdir', { dir: dirPath, name }); }
|
|
123
|
+
|
|
124
|
+
// Upload a File/Blob as raw bytes (PUT /api/upload-file). onProgress is not
|
|
125
|
+
// available via fetch streaming everywhere, so progress is per-file (done or
|
|
126
|
+
// not) - the kit UploadProgress shows per-file rows, which this satisfies.
|
|
127
|
+
export async function uploadFile(base, dirPath, file, overwrite) {
|
|
128
|
+
const qs = '?dir=' + encodeURIComponent(dirPath) + '&name=' + encodeURIComponent(file.name) + (overwrite ? '&overwrite=1' : '');
|
|
129
|
+
const r = await authedFetch(base + '/api/upload-file' + qs, { method: 'PUT', body: file });
|
|
130
|
+
const j = await r.json().catch(() => ({}));
|
|
131
|
+
if (!r.ok) { const e = new Error(j.error || ('upload: ' + r.status)); e.status = r.status; throw e; }
|
|
132
|
+
return j;
|
|
133
|
+
}
|
|
134
|
+
|
|
96
135
|
// ---------- History (HTTP, served by ccsniff) ----------
|
|
97
136
|
|
|
98
137
|
export async function listSessions(base) {
|
|
@@ -330,9 +369,27 @@ export async function* streamChat(base, { model, messages, signal, agentId, resu
|
|
|
330
369
|
}
|
|
331
370
|
});
|
|
332
371
|
|
|
333
|
-
// If the websocket drops mid-stream, streaming_complete will never arrive
|
|
334
|
-
//
|
|
335
|
-
|
|
372
|
+
// If the websocket drops mid-stream, streaming_complete will never arrive.
|
|
373
|
+
// Don't fail immediately: the client auto-reconnects (and the open handler
|
|
374
|
+
// re-subscribes this session's listeners), so give it a ~12s grace window.
|
|
375
|
+
// Only if the socket is still down when the timer expires do we surface the
|
|
376
|
+
// error and end the iterator instead of hanging forever.
|
|
377
|
+
const WS_GRACE_MS = 12000;
|
|
378
|
+
let graceTimer = null;
|
|
379
|
+
const onWs = (s) => {
|
|
380
|
+
if (done) return;
|
|
381
|
+
if (s === 'open') {
|
|
382
|
+
// Reconnected in time - the open handler already re-subscribed us.
|
|
383
|
+
if (graceTimer) { clearTimeout(graceTimer); graceTimer = null; }
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if ((s === 'closed' || s === 'error') && !graceTimer) {
|
|
387
|
+
graceTimer = setTimeout(() => {
|
|
388
|
+
graceTimer = null;
|
|
389
|
+
if (!done) { errored = errored || 'connection lost during stream'; finish(); }
|
|
390
|
+
}, WS_GRACE_MS);
|
|
391
|
+
}
|
|
392
|
+
};
|
|
336
393
|
const unsubWs = onWsStatus ? onWsStatus(onWs) : null;
|
|
337
394
|
|
|
338
395
|
// Wire AbortSignal to chat.cancel - and end the iterator immediately so the
|
|
@@ -354,6 +411,7 @@ export async function* streamChat(base, { model, messages, signal, agentId, resu
|
|
|
354
411
|
if (errored) yield { type: 'error', error: errored };
|
|
355
412
|
} finally {
|
|
356
413
|
unsub();
|
|
414
|
+
if (graceTimer) { clearTimeout(graceTimer); graceTimer = null; }
|
|
357
415
|
if (typeof unsubWs === 'function') unsubWs();
|
|
358
416
|
if (signal) signal.removeEventListener?.('abort', onAbort);
|
|
359
417
|
}
|
|
@@ -3300,6 +3300,48 @@
|
|
|
3300
3300
|
.ds-247420 .ws-drawer-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
3301
3301
|
.ds-247420 .ws-scrim { display: none; }
|
|
3302
3302
|
|
|
3303
|
+
/* ============================================================
|
|
3304
|
+
Row title highlight, expanded-row actions, filter pills.
|
|
3305
|
+
============================================================ */
|
|
3306
|
+
|
|
3307
|
+
/* Highlighted match inside a Row title (Row `highlight` prop). */
|
|
3308
|
+
.ds-247420 .ds-hl { background: color-mix(in oklab, var(--accent) 25%, transparent); color: inherit; border-radius: 2px; }
|
|
3309
|
+
|
|
3310
|
+
/* Action strip rendered inside an EXPANDED Row (Row `actions` prop). The
|
|
3311
|
+
buttons stop propagation so they never fire the row onClick. */
|
|
3312
|
+
.ds-247420 .row-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); width: 100%; padding-top: var(--space-2); }
|
|
3313
|
+
.ds-247420 .row-act {
|
|
3314
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
3315
|
+
padding: 4px 10px; min-height: 28px;
|
|
3316
|
+
background: var(--bg-2); border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-1);
|
|
3317
|
+
color: var(--fg-2); font-family: var(--ff-body); font-size: var(--fs-tiny); cursor: pointer;
|
|
3318
|
+
transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
|
|
3319
|
+
}
|
|
3320
|
+
.ds-247420 .row-act:hover { background: var(--bg-3); color: var(--fg); }
|
|
3321
|
+
.ds-247420 .row-act:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
3322
|
+
|
|
3323
|
+
/* FilterPills - a group of pill toggle buttons (aria-pressed). */
|
|
3324
|
+
.ds-247420 .ds-filter-pills { display: flex; flex-wrap: wrap; gap: var(--space-2); }
|
|
3325
|
+
.ds-247420 .ds-filter-pill {
|
|
3326
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
3327
|
+
padding: 4px 12px; min-height: 28px;
|
|
3328
|
+
background: var(--bg-2); border: var(--bw-hair) solid var(--bg-3); border-radius: 999px;
|
|
3329
|
+
color: var(--fg-2); font-family: var(--ff-body); font-size: var(--fs-tiny); cursor: pointer;
|
|
3330
|
+
transition: background var(--dur-snap) var(--ease), color var(--dur-snap) var(--ease);
|
|
3331
|
+
}
|
|
3332
|
+
.ds-247420 .ds-filter-pill:hover { background: var(--bg-3); color: var(--fg); }
|
|
3333
|
+
.ds-247420 .ds-filter-pill.active { background: var(--accent-tint); color: var(--accent); border-color: var(--accent); }
|
|
3334
|
+
.ds-247420 .ds-filter-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
3335
|
+
|
|
3336
|
+
/* Touch floor for the new small controls. */
|
|
3337
|
+
@media (pointer: coarse) {
|
|
3338
|
+
.ds-247420 .row-act, .ds-247420 .ds-filter-pill { min-height: 44px; }
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
/* Disabled file actions (read-only row / in-flight mutation) stay visible but inert. */
|
|
3342
|
+
.ds-247420 .ds-file-act:disabled { opacity: .45; cursor: default; }
|
|
3343
|
+
.ds-247420 .ds-file-act:disabled:hover { background: transparent; color: var(--fg-3); }
|
|
3344
|
+
|
|
3303
3345
|
/* community.css */
|
|
3304
3346
|
/* ============================================================
|
|
3305
3347
|
247420 design system — community surface (Discord-style chat)
|
|
@@ -5021,6 +5063,50 @@
|
|
|
5021
5063
|
.ds-247420 .agentchat-empty-suggestion:hover { background: color-mix(in srgb, var(--accent, var(--fg)) 12%, transparent); }
|
|
5022
5064
|
.ds-247420 .agentchat-empty-suggestion:focus-visible { outline: 2px solid var(--accent, var(--fg)); outline-offset: 2px; }
|
|
5023
5065
|
|
|
5066
|
+
/* Guided install path in the empty state: copy line + monospaced command rows
|
|
5067
|
+
(each with its own copy button) + a recheck button. No animation. */
|
|
5068
|
+
.ds-247420 .agentchat-install {
|
|
5069
|
+
margin-top: var(--space-4, 16px);
|
|
5070
|
+
display: flex;
|
|
5071
|
+
flex-direction: column;
|
|
5072
|
+
gap: var(--space-2, 8px);
|
|
5073
|
+
text-align: left;
|
|
5074
|
+
width: 100%;
|
|
5075
|
+
}
|
|
5076
|
+
.ds-247420 .agentchat-install-text { margin: 0; font-size: .9em; color: var(--fg-3); }
|
|
5077
|
+
.ds-247420 .agentchat-install-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--space-1, 4px); }
|
|
5078
|
+
.ds-247420 .agentchat-install-row {
|
|
5079
|
+
display: flex; align-items: center; gap: var(--space-2, 8px);
|
|
5080
|
+
padding: var(--space-1, 4px) var(--space-2, 8px);
|
|
5081
|
+
border: var(--bw-hair) solid var(--rule);
|
|
5082
|
+
border-radius: var(--r-1, 4px);
|
|
5083
|
+
background: var(--bg-2);
|
|
5084
|
+
}
|
|
5085
|
+
.ds-247420 .agentchat-install-agent { flex: 0 0 auto; font-size: var(--fs-tiny); color: var(--fg-3); min-width: 8ch; }
|
|
5086
|
+
.ds-247420 .agentchat-install-cmd {
|
|
5087
|
+
flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
5088
|
+
font-family: var(--ff-mono); font-size: var(--fs-sm); color: var(--fg-2, var(--fg));
|
|
5089
|
+
}
|
|
5090
|
+
.ds-247420 .agentchat-install-copy {
|
|
5091
|
+
flex: 0 0 auto; cursor: pointer; padding: 2px 8px; min-height: 24px;
|
|
5092
|
+
border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-1, 4px);
|
|
5093
|
+
background: none; color: var(--fg-3);
|
|
5094
|
+
font-family: var(--ff-body); font-size: var(--fs-tiny);
|
|
5095
|
+
}
|
|
5096
|
+
.ds-247420 .agentchat-install-copy:hover { background: var(--bg-3); color: var(--fg); }
|
|
5097
|
+
.ds-247420 .agentchat-install-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
5098
|
+
.ds-247420 .agentchat-install-actions { display: flex; justify-content: flex-start; }
|
|
5099
|
+
|
|
5100
|
+
/* Host-supplied transcript export actions riding the controls row. */
|
|
5101
|
+
.ds-247420 .agentchat-export-act {
|
|
5102
|
+
cursor: pointer; padding: 4px 10px; min-height: 32px;
|
|
5103
|
+
border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-1, 4px);
|
|
5104
|
+
background: none; color: var(--fg-3);
|
|
5105
|
+
font-family: var(--ff-body); font-size: var(--fs-tiny);
|
|
5106
|
+
}
|
|
5107
|
+
.ds-247420 .agentchat-export-act:hover { background: var(--bg-2); color: var(--fg); }
|
|
5108
|
+
.ds-247420 .agentchat-export-act:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
5109
|
+
|
|
5024
5110
|
/* "working" tail: a long silent tool call shouldn't read as frozen. */
|
|
5025
5111
|
.ds-247420 .agentchat-working {
|
|
5026
5112
|
display: flex;
|
|
@@ -5303,6 +5389,30 @@
|
|
|
5303
5389
|
.ds-247420 .chat-msg-action { min-width: 44px; height: 44px; }
|
|
5304
5390
|
.ds-247420 .ds-dash-select { min-width: 44px; min-height: 44px; }
|
|
5305
5391
|
.ds-247420 .ds-file-more-btn, .ds-247420 .ds-roots-tab, .ds-247420 .ds-dash-errors-toggle { min-height: 44px; }
|
|
5392
|
+
.ds-247420 .agentchat-export-act { min-height: 44px; }
|
|
5393
|
+
.ds-247420 .agentchat-install-copy { min-height: 44px; min-width: 44px; }
|
|
5394
|
+
}
|
|
5395
|
+
|
|
5396
|
+
/* --- SessionMeta: middot-separated session metadata strip. Class is
|
|
5397
|
+
.ds-session-meta-strip (.ds-session-meta is taken by ConversationList row
|
|
5398
|
+
meta above). Wraps at narrow widths. --- */
|
|
5399
|
+
.ds-247420 .ds-session-meta-strip {
|
|
5400
|
+
display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2);
|
|
5401
|
+
font-size: var(--fs-tiny); color: var(--fg-3);
|
|
5402
|
+
}
|
|
5403
|
+
.ds-247420 .ds-session-meta-item { display: inline-flex; align-items: center; gap: var(--space-1); min-width: 0; }
|
|
5404
|
+
.ds-247420 .ds-session-meta-item + .ds-session-meta-item::before { content: '\00B7'; color: var(--fg-3); margin-right: var(--space-1); }
|
|
5405
|
+
.ds-247420 .ds-session-meta-label { color: var(--fg-3); }
|
|
5406
|
+
.ds-247420 .ds-session-meta-value { font-family: var(--ff-mono); color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5407
|
+
.ds-247420 .ds-session-meta-copy {
|
|
5408
|
+
padding: 2px 8px; min-height: 24px; cursor: pointer;
|
|
5409
|
+
background: var(--bg-2); border: var(--bw-hair) solid var(--bg-3); border-radius: var(--r-1);
|
|
5410
|
+
color: var(--fg-3); font-family: var(--ff-body); font-size: var(--fs-micro);
|
|
5411
|
+
}
|
|
5412
|
+
.ds-247420 .ds-session-meta-copy:hover { background: var(--bg-3); color: var(--fg); }
|
|
5413
|
+
.ds-247420 .ds-session-meta-copy:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
5414
|
+
@media (pointer: coarse), (max-width: 640px) {
|
|
5415
|
+
.ds-247420 .ds-session-meta-copy { min-height: 44px; min-width: 44px; }
|
|
5306
5416
|
}
|
|
5307
5417
|
|
|
5308
5418
|
/* editor-primitives.css */
|