agent-afk 5.100.4 → 5.101.0

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 (44) hide show
  1. package/dist/agent/awareness/types.d.ts +1 -1
  2. package/dist/agent/facets/schema.d.ts +2 -0
  3. package/dist/agent/permissions-store.d.ts +1 -1
  4. package/dist/agent/session/session-identity.d.ts +1 -1
  5. package/dist/agent/session/session-registry.d.ts +1 -1
  6. package/dist/agent/tools/hooks/path-approval-hook.d.ts +1 -1
  7. package/dist/agent/trace/events.d.ts +3 -0
  8. package/dist/agent/trace/types.d.ts +1 -1
  9. package/dist/cli/commands/web.d.ts +2 -0
  10. package/dist/cli/json-date-replacer.d.ts +1 -0
  11. package/dist/cli/session-store.d.ts +2 -2
  12. package/dist/cli/web-options.d.ts +8 -0
  13. package/dist/cli.mjs +617 -612
  14. package/dist/config/env.d.ts +3 -0
  15. package/dist/index.mjs +73 -73
  16. package/dist/paths.d.ts +1 -0
  17. package/dist/telegram.mjs +10 -10
  18. package/dist/web-server/auth.d.ts +14 -0
  19. package/dist/web-server/elicitation-web.d.ts +22 -0
  20. package/dist/web-server/frontend/ansi-strip.d.ts +1 -0
  21. package/dist/web-server/frontend/app.d.ts +1 -0
  22. package/dist/web-server/frontend/chrome.d.ts +1 -0
  23. package/dist/web-server/frontend/ledger-adapter.d.ts +14 -0
  24. package/dist/web-server/frontend/markdown-dom.d.ts +1 -0
  25. package/dist/web-server/frontend/queue-panel.d.ts +21 -0
  26. package/dist/web-server/frontend/queue-reorder.d.ts +5 -0
  27. package/dist/web-server/frontend/render.d.ts +35 -0
  28. package/dist/web-server/frontend/scroll-pin.d.ts +7 -0
  29. package/dist/web-server/frontend/sse-client.d.ts +20 -0
  30. package/dist/web-server/frontend/view-model.d.ts +48 -0
  31. package/dist/web-server/frontend/web-token.d.ts +3 -0
  32. package/dist/web-server/handoff.d.ts +9 -0
  33. package/dist/web-server/routes.d.ts +22 -0
  34. package/dist/web-server/server.d.ts +24 -0
  35. package/dist/web-server/session-owner.d.ts +33 -0
  36. package/dist/web-server/session-source.d.ts +11 -0
  37. package/dist/web-server/sse-protocol.d.ts +20 -0
  38. package/dist/web-server/static-assets.d.ts +10 -0
  39. package/dist/web-server/stream-route.d.ts +2 -0
  40. package/dist/web-ui-assets/app.js +64 -0
  41. package/dist/web-ui-assets/chrome.js +1 -0
  42. package/dist/web-ui-assets/index.html +64 -0
  43. package/dist/web-ui-assets/styles.css +333 -0
  44. package/package.json +4 -3
@@ -0,0 +1,64 @@
1
+ <!doctype html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
+ <meta name="color-scheme" content="dark" />
7
+ <!-- The page holds a live bearer token; keep it out of referrers entirely. -->
8
+ <meta name="referrer" content="no-referrer" />
9
+ <!-- Server-templated on the document response only. The bearer token is
10
+ delivered HERE rather than in a JS-readable cookie: cookies are scoped
11
+ by host and not by port, so any page on another 127.0.0.1:<port> origin
12
+ could read a non-HttpOnly cookie and drive the agent. A <meta> tag (not
13
+ an inline <script>) because the CSP sets script-src 'self'. -->
14
+ <meta name="afk-token" content="__AFK_WEB_TOKEN__" />
15
+ <title>afk web</title>
16
+ <!-- Inline data-URI favicon: browsers request /favicon.ico unconditionally,
17
+ and a 404 there logs a console error on every load. Inlining answers it
18
+ without a round trip or an extra asset in the bundle. -->
19
+ <link
20
+ rel="icon"
21
+ href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='10' fill='%23f9854b'/%3E%3C/svg%3E"
22
+ />
23
+ <link rel="stylesheet" href="/styles.css" />
24
+ </head>
25
+ <body>
26
+ <header class="topbar">
27
+ <div class="brand">
28
+ <span class="brand-mark"></span>
29
+ <span class="brand-name">afk</span>
30
+ </div>
31
+ <div id="meter" class="meter"></div>
32
+ <div id="status" class="status">idle</div>
33
+ <button id="menu-toggle" class="menu-toggle" aria-label="Toggle sessions">☰</button>
34
+ </header>
35
+
36
+ <main class="layout">
37
+ <aside id="sidebar" class="sidebar">
38
+ <div class="sidebar-head">
39
+ <span>Sessions</span>
40
+ <button id="new-session" class="new-session" title="Start a new session">+ New</button>
41
+ </div>
42
+ <div id="sessions" class="sessions"></div>
43
+ </aside>
44
+
45
+ <section class="main">
46
+ <div id="transcript" class="transcript"></div>
47
+ <div id="approvals" class="approvals"></div>
48
+ <div id="composer" class="composer">
49
+ <div id="queue" class="queue"></div>
50
+ <div class="composer-row">
51
+ <textarea id="prompt" rows="2" placeholder="Message the agent…"></textarea>
52
+ <button id="stop" class="stop" hidden>Stop</button>
53
+ <button id="send" class="send">Send</button>
54
+ </div>
55
+ </div>
56
+ </section>
57
+ </main>
58
+
59
+ <script type="module" src="/app.js"></script>
60
+ <!-- Was an inline <script>; moved to a file because the document is served
61
+ under `script-src 'self'`, which blocks inline execution. -->
62
+ <script type="module" src="/chrome.js"></script>
63
+ </body>
64
+ </html>
@@ -0,0 +1,333 @@
1
+ /*
2
+ * Brand tokens hand-ported from website/app/globals.css. The docs site is a
3
+ * separate Next.js package, so these are copied deliberately rather than
4
+ * imported — keep the hexes in sync when the brand moves.
5
+ */
6
+ :root {
7
+ --bg: #07070b;
8
+ --bg-raised: #0e0e14;
9
+ --bg-sunken: #050508;
10
+ --border: #1e1e28;
11
+ --text: #e8e8ef;
12
+ --text-dim: #9a9aab;
13
+ --text-faint: #6a6a7b;
14
+ --accent: #f9854b;
15
+ --success: #4ade80;
16
+ --error: #f87171;
17
+ --warning: #fbbf24;
18
+ --radius: 8px;
19
+ --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
20
+ --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
21
+ }
22
+
23
+ * { box-sizing: border-box; }
24
+
25
+ html, body {
26
+ margin: 0;
27
+ height: 100%;
28
+ background: var(--bg);
29
+ color: var(--text);
30
+ font-family: var(--sans);
31
+ font-size: 14px;
32
+ -webkit-font-smoothing: antialiased;
33
+ }
34
+
35
+ /* ── top bar ─────────────────────────────────────────────────────────────── */
36
+ .topbar {
37
+ display: flex;
38
+ align-items: center;
39
+ gap: 16px;
40
+ height: 48px;
41
+ padding: 0 16px;
42
+ border-bottom: 1px solid var(--border);
43
+ background: var(--bg-raised);
44
+ position: sticky;
45
+ top: 0;
46
+ z-index: 10;
47
+ }
48
+ .brand { display: flex; align-items: center; gap: 8px; font-weight: 600; }
49
+ .brand-mark {
50
+ width: 10px; height: 10px; border-radius: 50%;
51
+ background: var(--accent);
52
+ box-shadow: 0 0 12px var(--accent);
53
+ }
54
+ .brand-name { letter-spacing: 0.04em; }
55
+ .meter { margin-left: auto; color: var(--text-dim); font-family: var(--mono); font-size: 12px; }
56
+ .status {
57
+ font-family: var(--mono); font-size: 11px; text-transform: uppercase;
58
+ letter-spacing: 0.06em; color: var(--text-faint);
59
+ padding: 3px 8px; border: 1px solid var(--border); border-radius: 999px;
60
+ }
61
+ .status-open { color: var(--success); border-color: color-mix(in srgb, var(--success) 40%, transparent); }
62
+ .status-reconnecting, .status-connecting { color: var(--warning); border-color: color-mix(in srgb, var(--warning) 40%, transparent); }
63
+ .status-closed { color: var(--text-faint); }
64
+ .menu-toggle {
65
+ display: none; background: none; border: 1px solid var(--border);
66
+ color: var(--text); border-radius: var(--radius); padding: 4px 10px; cursor: pointer;
67
+ }
68
+
69
+ /* ── layout ──────────────────────────────────────────────────────────────── */
70
+ .layout { display: flex; height: calc(100% - 48px); }
71
+ .sidebar {
72
+ width: 280px; flex: 0 0 280px; border-right: 1px solid var(--border);
73
+ background: var(--bg-sunken); overflow-y: auto;
74
+ }
75
+ .sidebar-head {
76
+ padding: 12px 16px 8px; font-size: 11px; text-transform: uppercase;
77
+ letter-spacing: 0.08em; color: var(--text-faint);
78
+ display: flex; align-items: center; justify-content: space-between; gap: 8px;
79
+ }
80
+ .new-session {
81
+ font: inherit; letter-spacing: inherit; text-transform: inherit;
82
+ color: var(--accent); background: transparent;
83
+ border: 1px solid var(--border); border-radius: 6px;
84
+ padding: 3px 9px; cursor: pointer; white-space: nowrap;
85
+ }
86
+ .new-session:hover:not(:disabled) { border-color: var(--accent); background: var(--bg-raised); }
87
+ .new-session:disabled { opacity: 0.5; cursor: default; }
88
+ .main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
89
+
90
+ /* ── sessions ────────────────────────────────────────────────────────────── */
91
+ .session-row {
92
+ display: flex; flex-direction: column; gap: 5px; width: 100%;
93
+ padding: 10px 16px; background: none; border: none; border-left: 2px solid transparent;
94
+ color: var(--text); text-align: left; cursor: pointer; font: inherit;
95
+ }
96
+ .session-row:hover { background: var(--bg-raised); }
97
+ .session-row.is-active { background: var(--bg-raised); border-left-color: var(--accent); }
98
+ .session-title {
99
+ font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
100
+ }
101
+ .session-meta { display: flex; align-items: center; gap: 6px; }
102
+ .badge {
103
+ font-size: 10px; padding: 1px 6px; border-radius: 999px;
104
+ font-family: var(--mono); letter-spacing: 0.03em;
105
+ }
106
+ .badge-live { color: var(--success); background: color-mix(in srgb, var(--success) 14%, transparent); }
107
+ .badge-readonly { color: var(--text-faint); background: color-mix(in srgb, var(--text-faint) 14%, transparent); }
108
+ .badge-alive { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
109
+ .session-time { font-size: 10px; color: var(--text-faint); margin-left: auto; }
110
+ .session-cwd {
111
+ font-family: var(--mono); font-size: 10px; color: var(--text-dim);
112
+ background: var(--bg-sunken); border: 1px solid var(--border);
113
+ border-radius: 4px; padding: 1px 5px;
114
+ max-width: 96px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
115
+ }
116
+ .empty { padding: 16px; color: var(--text-faint); }
117
+
118
+ /* ── transcript ──────────────────────────────────────────────────────────── */
119
+ .transcript { flex: 1; overflow-y: auto; padding: 20px; scroll-behavior: smooth; }
120
+ .msg { margin-bottom: 18px; max-width: 900px; }
121
+ .msg-role {
122
+ font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
123
+ color: var(--text-faint); margin-bottom: 5px;
124
+ }
125
+ .msg-body { white-space: pre-wrap; word-break: break-word; line-height: 1.6; }
126
+ .msg-user .msg-body {
127
+ background: var(--bg-raised); border-left: 2px solid var(--accent);
128
+ padding: 10px 14px; border-radius: 0 var(--radius) var(--radius) 0;
129
+ }
130
+ .msg-thinking { opacity: 0.75; font-style: italic; }
131
+ .msg-thinking summary { cursor: pointer; }
132
+ .msg-error .msg-body { color: var(--error); }
133
+
134
+ /*
135
+ * Markdown blocks (markdown-dom.ts).
136
+ *
137
+ * Invariant: `.md-body` must cancel the `pre-wrap` inherited from `.msg-body`.
138
+ * That rule exists so UNPARSED text keeps its newlines; once the same text is
139
+ * parsed into real block elements, preserving source newlines double-spaces
140
+ * every paragraph and list. Whitespace is re-preserved only inside `pre`/`code`,
141
+ * where it is semantic.
142
+ */
143
+ .md-body { white-space: normal; }
144
+ .md > :first-child { margin-top: 0; }
145
+ .md > :last-child { margin-bottom: 0; }
146
+ .md-p { margin: 0 0 10px; }
147
+ .md-heading {
148
+ margin: 18px 0 8px; line-height: 1.3; font-weight: 600;
149
+ color: var(--text); letter-spacing: -0.01em;
150
+ }
151
+ .md h1.md-heading { font-size: 19px; }
152
+ .md h2.md-heading {
153
+ font-size: 16px; padding-bottom: 5px; border-bottom: 1px solid var(--border);
154
+ }
155
+ .md h3.md-heading { font-size: 14px; }
156
+ .md h4.md-heading, .md h5.md-heading, .md h6.md-heading {
157
+ font-size: 13px; color: var(--text-dim);
158
+ }
159
+ .md-list { margin: 0 0 10px; padding-left: 22px; }
160
+ .md-list li { margin: 3px 0; }
161
+ .md-list .md-p { margin: 0; }
162
+ .md-task { margin-right: 7px; vertical-align: middle; accent-color: var(--accent); }
163
+ .md-code-inline {
164
+ font-family: var(--mono); font-size: 0.88em;
165
+ background: var(--bg-sunken); border: 1px solid var(--border);
166
+ border-radius: 4px; padding: 1px 5px; color: var(--accent);
167
+ white-space: pre-wrap; word-break: break-word;
168
+ }
169
+ .md-pre {
170
+ margin: 0 0 12px; padding: 11px 13px; overflow-x: auto;
171
+ background: var(--bg-sunken); border: 1px solid var(--border);
172
+ border-radius: var(--radius); white-space: pre;
173
+ }
174
+ .md-pre code {
175
+ font-family: var(--mono); font-size: 12px; line-height: 1.55;
176
+ color: var(--text); background: none; border: 0; padding: 0; white-space: pre;
177
+ }
178
+ .md-quote {
179
+ margin: 0 0 10px; padding: 2px 0 2px 12px;
180
+ border-left: 2px solid var(--border); color: var(--text-dim);
181
+ }
182
+ .md-hr { border: 0; border-top: 1px solid var(--border); margin: 16px 0; }
183
+ .md-link { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
184
+ .md-link:hover { text-decoration-thickness: 2px; }
185
+ .md-image { color: var(--text-faint); font-style: italic; }
186
+ .md-table {
187
+ border-collapse: collapse; margin: 0 0 12px; font-size: 12px; display: block;
188
+ overflow-x: auto; max-width: 100%;
189
+ }
190
+ .md-table th, .md-table td {
191
+ border: 1px solid var(--border); padding: 5px 9px; text-align: left;
192
+ }
193
+ .md-table th { background: var(--bg-raised); font-weight: 600; }
194
+ .notice {
195
+ font-family: var(--mono); font-size: 11px; color: var(--text-faint);
196
+ padding: 5px 0; border-top: 1px dashed var(--border); margin: 14px 0;
197
+ }
198
+
199
+ /* ── tool calls ──────────────────────────────────────────────────────────── */
200
+ .tool {
201
+ border: 1px solid var(--border); border-radius: var(--radius);
202
+ margin-bottom: 10px; background: var(--bg-raised); overflow: hidden;
203
+ }
204
+ .tool-summary {
205
+ display: flex; align-items: center; gap: 8px; padding: 8px 12px;
206
+ cursor: pointer; font-family: var(--mono); font-size: 12px; list-style: none;
207
+ }
208
+ .tool-summary::-webkit-details-marker { display: none; }
209
+ .tool-dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 6px; }
210
+ .tool-dot-running { background: var(--warning); }
211
+ .tool-dot-ok { background: var(--success); }
212
+ .tool-dot-error { background: var(--error); }
213
+ .tool-name { color: var(--accent); }
214
+ .tool-target { color: var(--text-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
215
+ .tool-body { padding: 0 12px 12px; border-top: 1px solid var(--border); }
216
+ .tool-label {
217
+ font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em;
218
+ color: var(--text-faint); margin: 10px 0 4px;
219
+ }
220
+ .tool-pre {
221
+ margin: 0; padding: 10px; background: var(--bg-sunken); border-radius: 6px;
222
+ font-family: var(--mono); font-size: 12px; line-height: 1.5;
223
+ overflow-x: auto; white-space: pre-wrap; word-break: break-word;
224
+ max-height: 420px; overflow-y: auto;
225
+ }
226
+ /* The "we genuinely do not have this" state — visually distinct from empty
227
+ output, because they mean different things. */
228
+ .tool-unavailable {
229
+ margin-top: 10px; padding: 8px 10px; border-radius: 6px;
230
+ background: color-mix(in srgb, var(--warning) 8%, transparent);
231
+ border: 1px dashed color-mix(in srgb, var(--warning) 35%, transparent);
232
+ color: var(--warning); font-size: 12px; line-height: 1.5;
233
+ }
234
+ .tool-more {
235
+ margin-top: 8px; background: none; border: 1px solid var(--border);
236
+ color: var(--text-dim); border-radius: 6px; padding: 4px 10px;
237
+ cursor: pointer; font-family: var(--mono); font-size: 11px;
238
+ }
239
+ .tool-more:hover { color: var(--text); border-color: var(--accent); }
240
+
241
+ /* ── composer ────────────────────────────────────────────────────────────── */
242
+ .composer { border-top: 1px solid var(--border); background: var(--bg-raised); padding: 12px 20px; }
243
+ .composer.is-readonly { opacity: 0.6; }
244
+ .composer-row { display: flex; gap: 10px; align-items: flex-end; }
245
+ #prompt {
246
+ flex: 1; resize: vertical; background: var(--bg-sunken); color: var(--text);
247
+ border: 1px solid var(--border); border-radius: var(--radius);
248
+ padding: 10px 12px; font-family: var(--sans); font-size: 14px; line-height: 1.5;
249
+ }
250
+ #prompt:focus { outline: none; border-color: var(--accent); }
251
+ #prompt:disabled { cursor: not-allowed; }
252
+ .send {
253
+ background: var(--accent); color: #1a0d05; border: none; border-radius: var(--radius);
254
+ padding: 10px 18px; font-weight: 600; cursor: pointer;
255
+ }
256
+ .send:disabled { background: var(--border); color: var(--text-faint); cursor: not-allowed; }
257
+ .queue { display: flex; flex-direction: column; gap: 5px; margin-bottom: 8px; }
258
+ .queue-row {
259
+ display: flex; align-items: center; gap: 8px; padding: 5px 10px;
260
+ background: var(--bg-sunken); border: 1px dashed var(--border); border-radius: 6px;
261
+ font-size: 12px; color: var(--text-dim);
262
+ }
263
+ .queue-text { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
264
+ .queue-btn {
265
+ background: none; border: none; color: var(--text-faint);
266
+ cursor: pointer; padding: 0 4px; font-size: 12px;
267
+ }
268
+ .queue-btn:hover { color: var(--accent); }
269
+
270
+ /* ── mobile ──────────────────────────────────────────────────────────────── */
271
+ @media (max-width: 720px) {
272
+ .menu-toggle { display: block; }
273
+ .meter { display: none; }
274
+ .sidebar {
275
+ position: fixed; top: 48px; bottom: 0; left: 0; z-index: 9;
276
+ transform: translateX(-100%); transition: transform 0.18s ease;
277
+ }
278
+ .sidebar.is-open { transform: translateX(0); }
279
+ .transcript { padding: 14px; }
280
+ .composer { padding: 10px 14px; }
281
+ }
282
+
283
+ /*
284
+ * Approvals.
285
+ *
286
+ * Invariant: a pending approval BLOCKS the agent's turn, so these cards must be
287
+ * impossible to miss — they sit directly above the composer (the region the
288
+ * operator is already looking at) rather than in the transcript, where new
289
+ * output would scroll them out of view while the turn stays stuck.
290
+ */
291
+ .approvals:empty { display: none; }
292
+ .approvals {
293
+ border-top: 1px solid var(--border); background: var(--bg-sunken);
294
+ padding: 10px 16px; display: flex; flex-direction: column; gap: 8px;
295
+ max-height: 42vh; overflow-y: auto;
296
+ }
297
+ .approval-card {
298
+ border: 1px solid var(--warning); border-radius: var(--radius);
299
+ background: var(--bg-raised); padding: 11px 13px;
300
+ }
301
+ .approval-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
302
+ .approval-badge {
303
+ font-family: var(--mono); font-size: 10px; text-transform: uppercase;
304
+ letter-spacing: 0.06em; color: var(--warning);
305
+ border: 1px solid var(--warning); border-radius: 4px; padding: 1px 6px;
306
+ }
307
+ .approval-source { font-family: var(--mono); font-size: 10px; color: var(--text-faint); }
308
+ .approval-title { font-size: 13px; line-height: 1.5; color: var(--text); }
309
+ .approval-desc { font-size: 12px; line-height: 1.5; color: var(--text-dim); margin-top: 4px; }
310
+ .approval-input {
311
+ width: 100%; margin-top: 8px; padding: 7px 9px;
312
+ font-family: var(--mono); font-size: 12px;
313
+ color: var(--text); background: var(--bg-sunken);
314
+ border: 1px solid var(--border); border-radius: 6px;
315
+ }
316
+ .approval-input:focus { outline: none; border-color: var(--accent); }
317
+ .approval-actions { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }
318
+ .approval-btn {
319
+ font: inherit; font-size: 12px; cursor: pointer;
320
+ color: var(--text); background: var(--bg-sunken);
321
+ border: 1px solid var(--border); border-radius: 6px; padding: 5px 12px;
322
+ }
323
+ .approval-btn:hover { border-color: var(--text-dim); }
324
+ .approval-primary { color: var(--bg); background: var(--success); border-color: var(--success); }
325
+ .approval-primary:hover { filter: brightness(1.08); }
326
+ .approval-danger { color: var(--error); border-color: var(--error); background: transparent; }
327
+ .approval-danger:hover { background: rgba(248, 113, 113, 0.12); }
328
+ .stop {
329
+ font: inherit; font-size: 12px; cursor: pointer; white-space: nowrap;
330
+ color: var(--error); background: transparent;
331
+ border: 1px solid var(--error); border-radius: 6px; padding: 0 14px;
332
+ }
333
+ .stop:hover { background: rgba(248, 113, 113, 0.12); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-afk",
3
- "version": "5.100.4",
3
+ "version": "5.101.0",
4
4
  "description": "Open-source coding-agent harness you can actually change — own the loop (prompts, gates, routing, skills, terminal states), use any model, run long tasks while you're away.",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -102,7 +102,7 @@
102
102
  },
103
103
  "scripts": {
104
104
  "postinstall": "node dist/postinstall.mjs || node scripts/postinstall.mjs || true",
105
- "build": "tsc && node scripts/copy-prompts.js",
105
+ "build": "tsc && node scripts/build-web-ui.mjs && node scripts/copy-prompts.js",
106
106
  "dev": "tsx watch src/cli/index.ts",
107
107
  "start": "node dist/cli/index.js",
108
108
  "start:chat": "node dist/cli/index.js chat",
@@ -120,7 +120,7 @@
120
120
  "test:coverage": "vitest run --coverage",
121
121
  "test:watch": "vitest watch",
122
122
  "test:pty": "vitest run --config vitest.pty.config.ts",
123
- "lint": "tsc --noEmit",
123
+ "lint": "tsc --noEmit && tsc --noEmit -p tsconfig.web.json",
124
124
  "audit:sdk": "tsx scripts/audit-sdk-dependency.ts",
125
125
  "audit:sdk:check": "tsx scripts/audit-sdk-dependency.ts --check",
126
126
  "audit:sdk:update-lock": "tsx scripts/audit-sdk-dependency.ts --update-lock",
@@ -137,6 +137,7 @@
137
137
  "fix:pins:check": "tsx scripts/update-hash-pins.ts --check",
138
138
  "stats:tools": "tsx scripts/count-tool-calls.ts",
139
139
  "clean": "rm -rf dist",
140
+ "build:web-ui": "node scripts/build-web-ui.mjs",
140
141
  "build:dist": "node scripts/build-dist.mjs",
141
142
  "release": "node scripts/release.mjs",
142
143
  "release:dry": "node scripts/release.mjs --dry-run"