@mono-agent/web 0.20.14 → 0.21.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 (65) hide show
  1. package/README.md +388 -70
  2. package/dist/contracts.d.ts +364 -8
  3. package/dist/contracts.d.ts.map +1 -1
  4. package/dist/contracts.js +2 -0
  5. package/dist/contracts.js.map +1 -1
  6. package/dist/cron-reply-context.d.ts +27 -0
  7. package/dist/cron-reply-context.d.ts.map +1 -0
  8. package/dist/cron-reply-context.js +241 -0
  9. package/dist/cron-reply-context.js.map +1 -0
  10. package/dist/discovery.d.ts +1 -0
  11. package/dist/discovery.d.ts.map +1 -1
  12. package/dist/discovery.js +8 -6
  13. package/dist/discovery.js.map +1 -1
  14. package/dist/effort-ladder.d.ts +14 -0
  15. package/dist/effort-ladder.d.ts.map +1 -1
  16. package/dist/effort-ladder.js +41 -0
  17. package/dist/effort-ladder.js.map +1 -1
  18. package/dist/index.d.ts +2 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/long-lived-fetch.d.ts +5 -0
  23. package/dist/long-lived-fetch.d.ts.map +1 -0
  24. package/dist/long-lived-fetch.js +22 -0
  25. package/dist/long-lived-fetch.js.map +1 -0
  26. package/dist/monitor-reply.d.ts +10 -0
  27. package/dist/monitor-reply.d.ts.map +1 -0
  28. package/dist/monitor-reply.js +67 -0
  29. package/dist/monitor-reply.js.map +1 -0
  30. package/dist/notification-client.d.ts.map +1 -1
  31. package/dist/notification-client.js +5 -4
  32. package/dist/notification-client.js.map +1 -1
  33. package/dist/operator-client.d.ts +25 -1
  34. package/dist/operator-client.d.ts.map +1 -1
  35. package/dist/operator-client.js +167 -8
  36. package/dist/operator-client.js.map +1 -1
  37. package/dist/server.d.ts +42 -1
  38. package/dist/server.d.ts.map +1 -1
  39. package/dist/server.js +627 -49
  40. package/dist/server.js.map +1 -1
  41. package/dist/service.d.ts +201 -8
  42. package/dist/service.d.ts.map +1 -1
  43. package/dist/service.js +1425 -136
  44. package/dist/service.js.map +1 -1
  45. package/dist/store-migrations.d.ts +23 -0
  46. package/dist/store-migrations.d.ts.map +1 -0
  47. package/dist/store-migrations.js +212 -0
  48. package/dist/store-migrations.js.map +1 -0
  49. package/dist/store.d.ts +294 -17
  50. package/dist/store.d.ts.map +1 -1
  51. package/dist/store.js +1637 -263
  52. package/dist/store.js.map +1 -1
  53. package/package.json +8 -5
  54. package/webapp/dist/assets/{assistant-ui-BzN2E6n6.js → assistant-ui-pZmGxIp2.js} +16 -16
  55. package/webapp/dist/assets/index-6uQ7TVQe.css +1 -0
  56. package/webapp/dist/assets/index-CsSMjSgW.js +156 -0
  57. package/webapp/dist/assets/{markdown-Vq23xgh7.js → markdown-Du5t10ja.js} +1 -1
  58. package/webapp/dist/badge-96.png +0 -0
  59. package/webapp/dist/index.html +26 -6
  60. package/webapp/dist/manifest.webmanifest +1 -1
  61. package/webapp/dist/notification-sw.js +3 -1
  62. package/webapp/dist/sw.js +1 -1
  63. package/webapp/dist/{workbox-9c191d2f.js → workbox-2fbc6a65.js} +1 -1
  64. package/webapp/dist/assets/index-C4a2Dv1W.js +0 -155
  65. package/webapp/dist/assets/index-mhMBLGB0.css +0 -1
package/README.md CHANGED
@@ -24,6 +24,15 @@ Catalog responsibility: Serves the always-on browser operator console for persis
24
24
  - Persist agents, threads, messages, structured reasoning/tool/telemetry parts,
25
25
  revisions, turns, attachments, and agent pin preferences under
26
26
  `~/.mono-agent/web`.
27
+ - Persist optional per-agent model/effort defaults for new interactive web
28
+ threads, copied at creation and clearable back to resolved config.
29
+ - Preserve each turn's requested, attempted, and answering route plus the Pi
30
+ wrapper's effective thinking level, and project bounded fallback/retry
31
+ attribution without exposing raw provider diagnostics.
32
+ - Proxy ephemeral, exact-origin provider-auth status and login sessions to the
33
+ currently connected capability-advertising agent without storing credentials,
34
+ prompt input, or session projections. The client omits authorization for an
35
+ agent with no operator API key and sends the discovered bearer when one exists.
27
36
  - Let an MCP-capable agent replace the interim first-message title with a short
28
37
  semantic title and evolve it after a material topic shift, while treating any
29
38
  user rename as a permanent lock.
@@ -31,7 +40,20 @@ Catalog responsibility: Serves the always-on browser operator console for persis
31
40
  opaque-artifact metadata carried by structured agent stream events; web
32
41
  SQLite remains a client cache, not the canonical lifecycle store.
33
42
  - Keep an upstream turn running when a browser reloads or disconnects, and expose
34
- state invalidations over SSE so any connected browser can catch up.
43
+ content deltas plus rate-limited change hints over SSE so any connected browser
44
+ can catch up without reloading what it already holds.
45
+ - Project foreground outcomes and retained background-job activity into conversation
46
+ rows. Current work and failures take priority over reply previews, including for
47
+ closed conversations and job cards outside the loaded message page.
48
+ Silent assistant-only host wakes retain the previous meaningful outcome for
49
+ this priority, without changing their actual run status or completion time.
50
+ The optional `runState.lastOutcome` is derived from retained user-message
51
+ provenance and visible reply content; `null` means no prior outcome, while
52
+ absent metadata preserves compatibility with older cached summaries.
53
+ - Shape projected transcripts at the service boundary — drop non-allowlisted
54
+ telemetry payloads, and preview oversized tool arguments/results with their
55
+ byte count and a digest — while serving the whole part from message-bound
56
+ routes and honoring `?full=1` as the unshaped escape hatch.
35
57
  - Offer plain-text follow-ups to a capable active provider run, persist their
36
58
  pending/applied/queued state, and promote safe fallbacks into normal turns.
37
59
  - Render a running agent's structured `AskUser` interaction by exact
@@ -52,17 +74,43 @@ Catalog responsibility: Serves the always-on browser operator console for persis
52
74
  channel per `(sourceId, jobId)` and reconcile all run states from the agent.
53
75
  - Accept exact-source/thread process-job notifications through the same private
54
76
  ingress, update one durable job card in place without a verbatim-history
55
- append, and proxy thread job refreshes through the agent's independent owner
56
- operator capability.
77
+ append, and poll its bounded redacted live output tail through the agent's
78
+ independent owner operator capability without persisting each output chunk.
79
+ The browser collects cards from the loaded message window in one
80
+ conversation-level stack after the transcript. Queued, starting, and running
81
+ cards stay visible by default; terminal cards remain mounted behind
82
+ expandable history so status counts and live transitions stay current.
83
+ When a loaded launching `Exec`/`Bash` call has its exact persisted machine
84
+ receipt, that response's Activity also shows its actual start row. The
85
+ terminal row appears where the exact wake was consumed: at the steered point
86
+ in an active response or first in a follow-up response. If no retained wake
87
+ marker exists, the terminal row falls back beside the launch. These rows
88
+ never poll or duplicate card output; legacy or paginated-out launch receipts
89
+ remain stack-only.
57
90
  - Accept exact-source/thread Monitor wakes through that private ingress, steer
58
91
  them into the active run or serialize one assistant-only follow-up, retain
59
92
  delivery identity plus a payload hash for fail-closed duplicate handling, and
60
- group their secret-free projections into one compact Monitor activity row per
61
- assistant run. Raw event text and delivery keys never reach the rendered row;
62
- Monitor wakes have no browser-side stop control.
93
+ group their secret-free projections into compact Monitor activity. The
94
+ browser presents adjacent activity-only follow-ups for the same watch as one
95
+ continuous Activity block; a visible reply or unrelated message ends that
96
+ block. Raw event text and delivery keys never reach the rendered row; Monitor
97
+ wakes have no browser-side stop control. For a verified Monitor wake, a
98
+ terminal no-op assistant message is suppressed while an earlier meaningful
99
+ answer and rich content remain visible. Push delivery waits for an outstanding
100
+ steering receipt and uses the normalized reply. Monitor callbacks use the
101
+ independent owner credential derived from the advertised private state directory.
63
102
 
64
103
  ## Install / Usage
65
104
 
105
+ Process-job completion replies suppress only an exact sentinel-only terminal
106
+ message with a verified wake association; narration and rich replies remain
107
+ visible. Silent replies create no response push. A web wake receipt confirms
108
+ that steering was applied or that the agent accepted the exact follow-up turn
109
+ request, which is also when the wake's host-owned chain depth and background
110
+ starts bind to it; the turn's later model outcome remains separate. Job cards
111
+ distinguish an unknown pre-confirmation receipt from failure and explain that
112
+ replay was suppressed.
113
+
66
114
  `@mono-agent/agent-app` provides the normal managed-service lifecycle:
67
115
 
68
116
  ```bash
@@ -71,12 +119,21 @@ mono-agent web status
71
119
  mono-agent web
72
120
  ```
73
121
 
122
+ The server and the webapp ship as one artifact — this package serves the bundle
123
+ built beside it — so `mono-agent web restart` re-stages both and they always
124
+ upgrade together. A console build older than the bootstrap and delta protocol
125
+ below cannot read this server, and an installed PWA picks up a new build only
126
+ when its staged service worker is applied.
127
+
74
128
  Bare `mono-agent web` reports status and usable URLs; it does not implicitly
75
129
  start or mutate the service. Use `mono-agent web run` for a foreground process
76
130
  or `--loopback` to bind only `127.0.0.1`. The curated `--theme` values are
77
- `evergreen` (default), `ocean`, `plum`, and `terracotta`. Managed starts persist
78
- the selection, restarts retain it unless explicitly replaced, and `web status`
79
- reports the effective theme.
131
+ `evergreen` (default), `ocean`, `plum`, and `terracotta`. `--name <label>` sets
132
+ the console label used for the installed PWA name/short name, browser title, and
133
+ rail brand, defaulting to the machine hostname. Managed starts persist both
134
+ selections, restarts retain them unless explicitly replaced, `--name -` clears
135
+ the stored label back to the hostname default, and `web status` reports the
136
+ effective values.
80
137
 
81
138
  Install this package directly only when embedding the server in another host:
82
139
 
@@ -97,13 +154,14 @@ who can reach port 5050 can inspect conversations and operate discovered agents;
97
154
  use host firewall/LAN policy and Tailscale ACLs as the access boundary. The
98
155
  server emits no CORS permission and rejects cross-origin mutations.
99
156
 
100
- The service derives its console identity from the operating-system hostname.
101
- That hostname replaces the generic mono-agent brand in the desktop/mobile
102
- header, prefixes the browser title, and becomes the installed PWA name. The
103
- selected theme changes shell and accent colors while keeping content, status,
104
- warning, and danger semantics consistent across hosts. The generated PWA
105
- manifest remains network-fetched rather than service-worker-cached so its host
106
- identity cannot be masked by a generic build-time manifest.
157
+ The service uses the operating-system hostname as its machine identity and as
158
+ the default display name. An operator-selected `name` replaces that default in
159
+ the desktop/mobile header, browser title, and installed PWA name while the
160
+ hostname continues to key browser cache ownership. The selected theme changes
161
+ shell and accent colors while keeping content, status, warning, and danger
162
+ semantics consistent across hosts. The generated PWA manifest remains
163
+ network-fetched rather than service-worker-cached so its console identity cannot
164
+ be masked by a generic build-time manifest.
107
165
 
108
166
  Private IP literals, localhost, the machine hostname, and its exact `.local`
109
167
  name are accepted as browser hosts. Set `MONO_AGENT_WEB_ALLOWED_HOSTS` to a
@@ -114,18 +172,47 @@ managed agent protects its loopback operator endpoint, discovery reads only
114
172
 
115
173
  On desktop, the agent rail has fixed compact and expanded layouts selected by
116
174
  an explicit expand/collapse control. That choice is remembered by the browser.
117
- Offline agents are hidden behind a subtle count by default; pinned agents and
118
- the currently selected agent remain visible even while offline. The same
119
- filter applies to the desktop rail, mobile picker, and command palette. Pin or
120
- unpin with the star control; pins live in the web service so favorites stay
121
- consistent over localhost, LAN, and Tailscale.
122
-
123
- The assistant-ui run-settings popover combines searchable model selection with
124
- the selected model's supported reasoning-effort choices and becomes a
125
- viewport-safe bottom sheet on narrow screens. Exact choices come from the
126
- running agent's startup capability snapshot; when a model does not advertise
127
- exact levels, the console offers provider/config default only instead of the
128
- global effort ladder. Usage telemetry remains internal
175
+ On narrow touch screens, a deliberate right swipe across the unoccupied chat
176
+ surface or ordinary unselected transcript text opens the conversation drawer.
177
+ A left swipe across either open navigation drawer closes it. Controls, active
178
+ text selections, inputs, and any native horizontal scroller keep their normal
179
+ touch behavior; short drags and vertically dominant scrolling do not trigger
180
+ navigation. The two header navigation controls remain available as 44-pixel
181
+ touch targets.
182
+ Offline agents that remain in the current discovery result are hidden behind a
183
+ subtle count by default; pinned agents and the currently selected agent remain
184
+ visible even while offline. An agent omitted by a successful discovery refresh
185
+ is removed from every picker and from that count regardless of its prior pin or
186
+ selection. Its rows, conversations, and pin stay retained in SQLite and return
187
+ if the same source id is discovered again. The same filter applies to the
188
+ desktop rail, mobile picker, and command palette. Pin or unpin with the star
189
+ control; pins live in the web service so favorites stay consistent over
190
+ localhost, LAN, and Tailscale.
191
+
192
+ The single-row mobile header keeps navigation, title, notifications, and a
193
+ conversation actions menu together. Run settings sit beside **Send** in the
194
+ composer, where the compact trigger shows only the resolved model and effort.
195
+ Its popover combines searchable model selection with the selected model's
196
+ supported reasoning-effort choices and becomes a viewport-safe bottom sheet on
197
+ narrow screens. Model rows use the configured
198
+ display name when present and otherwise use the running agent's catalog name.
199
+ Choosing a model applies it immediately while keeping the popover open for an
200
+ effort choice; the explicit **Close** action finishes the interaction.
201
+ Pointer/touch opening and model or effort changes do not focus the search input;
202
+ keyboard opening retains search navigation. Local LM Studio and Ollama models
203
+ confirmed as embedding-only by native metadata are omitted from chat choices.
204
+ Unknown metadata preserves compatibility; model names are not used as a heuristic.
205
+ A persisted catalog-only selection stays visible by its canonical reference when
206
+ lazy provider metadata is absent, fails, or omits that model. While no metadata
207
+ describes that model at all, its current effort and the shared compatibility
208
+ ladder the agent accepts remain controllable, without reverting to the agent
209
+ default; once exact metadata loads it supplies the catalog display name and the
210
+ advertised ladder, which may narrow, keep, or remove those choices. Exact choices
211
+ come from the running agent's startup capability snapshot. Silence about a model
212
+ is not a claim and keeps the compatibility ladder, but metadata that marks a
213
+ model as reasoning-capable without enumerating its levels hides the effort
214
+ control entirely, default row included, rather than guessing cloud grades.
215
+ Usage telemetry remains internal
129
216
  and is summarized through a context display that keeps exact Pi, Codex,
130
217
  OpenCode, and ACP provider-request snapshots separate from last-turn processed
131
218
  tokens and conversation cost. Running turns are labeled `Updating`; failed
@@ -133,9 +220,68 @@ turns and model changes are `Last measured`. A running or successful compaction
133
220
  suppresses the older number until the next exact snapshot, while legacy and
134
221
  unsupported-runtime threads show `Context —` instead of deriving a percentage
135
222
  from aggregate work.
136
- Structured reasoning, routine tools, and one update-in-place row per compaction
137
- share the stream-aware Activity disclosure, which collapses at every terminal
138
- message state without reordering answer parts.
223
+ Structured reasoning, routine tools, process-job lifecycle evidence, and one
224
+ update-in-place row per compaction share the stream-aware Activity disclosure,
225
+ which collapses at every terminal message state without reordering answer
226
+ parts. Receipt-bearing job launches keep their start row beside the exact
227
+ launch call. Their terminal row follows the consumed wake chronologically, with
228
+ launch-adjacent placement only as a fallback when no wake marker was retained;
229
+ ordinary adjacent same-tool calls retain their existing clustering.
230
+
231
+ The picker is labelled **Next turn**. The conversation header carries no run
232
+ attribution. Below an assistant message, a normal route marker appears only when
233
+ the model that ran differs from the conversation's current selection. A fallback
234
+ warning always appears there, even when its answering model matches the current
235
+ selection, and names requested and answering models plus the classified reason
236
+ when the runtime supplied one. Expanding the marker shows the bounded route
237
+ chain, same-model retries, route-level effort, and Pi's actual effective thinking
238
+ level. Configured subagents keep independent attribution in their own Activity
239
+ row. The browser never infers a fallback from selector state, and the
240
+ route-attribution payload never carries raw provider errors or request identifiers.
241
+
242
+ The agent rail's settings action opens a separate **Agent settings** dialog.
243
+ Its model and effort choices become the defaults for subsequently created web
244
+ console conversations for that agent. Either field may inherit resolved config,
245
+ and **Revert to config** clears both overrides in one action. These settings live
246
+ in the web service's SQLite database, survive restart, and do not edit
247
+ `mono-agent.config.json`; existing conversations and Telegram, Slack, cron,
248
+ webhook, API, and TUI requests remain unchanged.
249
+
250
+ The same dialog shows **Provider authentication** when the current agent
251
+ advertises provider-auth v1. It renders only the host-returned providers used by
252
+ effective routes, distinguishing detected credentials from live-request
253
+ verification: **OK** requires a retained real success, while static presence is
254
+ **Not verified**. A normal-size neutral Authenticate/Re-authenticate button stays
255
+ available for every supported login method regardless of the row state. Agents
256
+ advertising the additive checks capability also show one section-level **Run
257
+ check** button; it checks only the displayed providers, reports partial fixed
258
+ outcomes inline, and never runs on settings reads or polling. GitHub Copilot and
259
+ OpenAI Codex expose Pi device-code flows;
260
+ Anthropic accepts the final redirect URL/code; API-key providers use masked
261
+ provider-owned prompts. Re-authentication remains available during an active
262
+ login: a valid choice replaces it, while the compact flow stays visible until
263
+ the fresh session arrives. Stale start, poll, input, and cancel responses cannot
264
+ restore the old session. Polling continues through identical active snapshots;
265
+ an expired retained session releases the local running control, while transient
266
+ read failures remain retryable. A replacement whose prior credential transaction
267
+ cannot drain safely within two seconds reports `replacement_timeout`. Secret
268
+ input is cleared before submission. The web
269
+ server requires exact origin, proxies through the agent's ordinary operator
270
+ connection, marks responses no-store, and never reads the Pi auth path. The
271
+ operator request is keyless when that agent has no API key and uses its bearer
272
+ when it does. Auth state is component/agent memory only—not SQLite, thread
273
+ history, browser storage, or run artifacts. The console remains a
274
+ trusted-network single-user surface, not a per-human authenticated application.
275
+
276
+ Live checks are explicit side effects: each selected provider receives one tiny
277
+ no-fallback request, which may consume quota, trigger minimum billing, or refresh
278
+ OAuth. Check sessions and observations are process-local, expire from memory,
279
+ and are not persisted by the web service. They must be cancelled explicitly
280
+ before starting authentication.
281
+
282
+ Standalone process-job and Monitor revival turns in an existing web conversation
283
+ read that conversation's model/effort snapshot immediately before admission. A
284
+ wake steered into an active run stays on that run's already selected route.
139
285
 
140
286
  New interactive threads initially use the first user message as their fallback
141
287
  title. On compatible routes, an allowlisted app-owned `SetConversationTitle`
@@ -161,6 +307,8 @@ unavailable entries disabled with their status. Keyboard, pointer, and touch
161
307
  selection only inserts the canonical `$skill-name` token at the caret; it never
162
308
  sends the draft. Loading, stale, empty, unsupported, offline, and registry-error
163
309
  states leave normal composition available.
310
+ The browse control uses a library icon, and touch opening leaves the search
311
+ unfocused so the software keyboard does not cover the viewport-safe sheet.
164
312
 
165
313
  Select rendered message text to quote it into the composer. One quote is kept
166
314
  with the authored user message and supplied to the operator as Markdown
@@ -168,18 +316,30 @@ blockquote context; it does not rewrite the visible message text. The public
168
316
  turn DTO exposes this as `quote: { text, messageId }`, and the source message
169
317
  must belong to the same thread.
170
318
 
171
- The composer remains sendable while a response is running. A text-only send is
172
- persisted immediately and offered to the active provider as live guidance. Its
173
- message shows `pending`, `applied`, `queued`, or `cancelled`; an unsupported
174
- provider, delivery failure, end-of-turn race, or web-service restart queues it
175
- as the next normal turn instead of dropping it. Attachments keep the ordinary
176
- turn path, and cancelling the active turn also cancels its pending or queued
177
- live follow-ups. Live follow-ups are capped at 8,000 characters and 100
178
- unsettled messages per thread.
179
-
180
- Once the provider applies a follow-up, the assistant's Activity disclosure also
319
+ The composer has one **Send** action whether the thread looks idle or running.
320
+ Button Send, desktop Enter, and **Control/Command + Shift + Enter** all submit
321
+ one immutable UUID-bearing payload; Shift+Enter and touch Enter remain newline.
322
+ The server, not browser run state, admits it as either a normal turn or targeted
323
+ live guidance for the exact active Web operation. There is no secondary Steer
324
+ button. With no active turn, the service starts exactly one normal turn using
325
+ the conversation route captured at admission.
326
+
327
+ The message shows `pending`, `applied`, `queued`, `cancelled`, or `uncertain`.
328
+ `applied` means exact owned-operation transcript consumption, not provider
329
+ receipt or answer adherence. Unsupported targeting or proved-safe removal
330
+ visibly queues one normal turn; a post-dispatch failure, end-of-turn race,
331
+ cancellation, or restart is permanently uncertain and is not retried
332
+ automatically. Active-turn attachments are rejected with
333
+ `active_attachments_unsupported` before upload ownership changes; the browser
334
+ restores the full text, structured quote, and staged files. The same payload may
335
+ be deliberately sent after the response finishes with a new submission id.
336
+ Cancelling the active turn also cancels its pending or queued live follow-ups.
337
+ Live follow-ups are capped at 8,000 characters and 100 unsettled messages per
338
+ thread.
339
+
340
+ Once exact consumption is host-confirmed, the assistant's Activity disclosure also
181
341
  receives one completed `↪️ Steered: “<safe preview>”` tool row with result
182
- `Applied to current run`. The original follow-up remains the full human message;
342
+ `Consumed by current run`. The original follow-up remains the full human message;
183
343
  the synthetic activity carries only a one-line, redacted, 40-code-point preview.
184
344
 
185
345
  The header bell explicitly enables standards-based Web Push. Permission and
@@ -227,12 +387,40 @@ late or replayed delivery cannot recreate the channel. Bootstrap and paging are
227
387
  bounded per source and archive state, with redirect-resolving thread fetches for selections
228
388
  or mutations outside the current window.
229
389
 
230
- Run-now and runtime enable/disable controls appear only when the agent advertises
231
- them. They require same-origin browser mutation, source-qualified routing, an
232
- operator API key, explicit agent-side opt-in, an idempotency key, and an
233
- agent-issued confirmation. The console neither edits config nor computes a
234
- schedule. Runtime enable state, run history, action audit, and idempotency are
235
- agent-owned; the web SQLite database is a reconnect/refresh projection.
390
+ The cron header is read-only: a quiet line shows human-language cadence and the
391
+ agent-authored next run in the viewer's local date/time, with the viewer timezone
392
+ available on the time label. Wall-clock cadence includes the scheduler timezone
393
+ (UTC by default); unsupported expressions retain normalized cron text and timezone.
394
+ Disabled or removed jobs say so. Missing, invalid, past, or offline/stale next-run
395
+ state says **Next run unavailable**. Configuration stays in files/config JSON;
396
+ the browser neither edits it nor computes a schedule.
397
+
398
+ Each visible terminal run keeps **Reply**, status, and local time in its compact
399
+ footer; secondary artifact, originating-session, activity, and truncation
400
+ diagnostics live under an accessible **Details** disclosure. Reply snapshots the
401
+ exact persisted result at activation—compact summary unless activity detail is
402
+ already loaded—and imports at most 32 KiB of explicitly marked untrusted
403
+ provenance/result into a separate normal conversation for the same agent. It
404
+ does not fetch hidden detail, rerun cron, invoke a provider, submit the empty
405
+ composer, include tools/files/config/neighbouring history, or continue the cron
406
+ session. The agent must positively advertise context-import v1 with a sufficient
407
+ byte limit. Unknown transport outcomes keep one operation for explicit Retry;
408
+ they are never replayed on startup. The page keeps unresolved identity in
409
+ memory even when bounded session persistence is unavailable, and temporary
410
+ offline/unsupported retry preflight does not discard it. A later deliberate
411
+ Reply after a definitive failure creates another operation and conversation.
412
+ The imported conversation renders that immutable snapshot as a compact card:
413
+ run provenance and status, Markdown result, optional failure/truncation notices,
414
+ and an accessible **Details** disclosure with exact raw JSON. This is read-time
415
+ presentation only. The two host-seeded rows, snapshot text and digest remain
416
+ unchanged in SQLite and in the agent's canonical history; unparseable or
417
+ non-v1 text remains ordinary text.
418
+
419
+ The web HTTP config-view, run-now, and effective-enabled proxies remain available
420
+ for operator clients. Mutations still require same-origin requests, source-qualified
421
+ routing, an operator API key, explicit agent-side opt-in, an idempotency key, and
422
+ agent-issued confirmation. Runtime enable state, run history, action audit, and
423
+ idempotency are agent-owned; web SQLite is a reconnect/refresh projection.
236
424
 
237
425
  When the selected agent advertises `capabilities.askUser`, a running `AskUser`
238
426
  tool call appears as one form containing all remaining questions. Each question
@@ -258,7 +446,21 @@ artifact availability, and explicit persistence failure. The browser never
258
446
  reconstructs those outcomes from display text, run JSONL, or its own SQLite.
259
447
  Historical content and artifact references remain untrusted; references expose
260
448
  no host path and may later become unavailable when their independently retained
261
- artifact is removed.
449
+ artifact is removed. An oversized argument or result reaches the card as a
450
+ preview carrying its full byte count and a digest of the body it was cut from;
451
+ expanding fetches the whole part from the message-bound tool-call route, and a
452
+ device-restored repair is accepted only when that digest still matches.
453
+
454
+ The console runs in a browser-local data mode — Auto, Lean, or Full — cycled
455
+ from the sidebar-footer indicator or the command palette, alongside a session
456
+ byte total and per-minute rate marked estimated whenever any component is not a
457
+ browser measurement. `Auto` reads the Network Information API and resolves to
458
+ Full where there is none (Safari, and so iOS), which is why an installed PWA is
459
+ offered Lean once. Lean loads pictures and MCP App documents on request, batches
460
+ delta paint to one second, halves page sizes and poll rates, and retains fewer
461
+ image blobs; polling pauses while the document is hidden. The preference keys
462
+ `mono-agent.web.data-mode` and `mono-agent.web.data-mode-suggested` are browser
463
+ storage, not configuration.
262
464
  ### Reply files and MCP Apps
263
465
 
264
466
  Agents that advertise reply attachments expose message-bound downloads in the
@@ -276,7 +478,11 @@ message write and rejected if found in durable rich-part records.
276
478
 
277
479
  The service mints exact-thread/message/part capabilities only while projecting
278
480
  a browser DTO, with the existing ten-minute access TTL bounded by the part's
279
- retention deadline. An authentic expired capability returns
481
+ retention deadline and quantised down to a five-minute bucket, so a repeated
482
+ projection of the same message yields the same URL and its transcript stays
483
+ conditionally revalidatable. Content is served
484
+ `private, max-age=<remaining key life>, no-transform`; those bytes may therefore
485
+ survive in the browser's own private disk cache after the key expires. An authentic expired capability returns
280
486
  `reply_access_expired`; forged, cross-thread, and unknown references keep the
281
487
  generic not-found response. The PWA then asks the exact-origin access route to
282
488
  re-project the authoritative retained part and retries an attachment, app
@@ -307,6 +513,33 @@ cross-resource requests fail. See
307
513
 
308
514
  ## Architecture
309
515
 
516
+ ### Silent cron history
517
+
518
+ Successful empty answers and answers suppressed by `NOTHING_TO_REPORT` add no
519
+ visible conversation row. Failures, real output and completed notification
520
+ content remain visible. The console retains compact silent run history separately
521
+ from visible messages; each job keeps up to 500 visible and 500 suppressed run
522
+ projections. Paging, search, previews and message counts exclude suppressed rows.
523
+
524
+ Schema 19 marks existing definitely-silent projections without deleting their
525
+ messages, turns or delivery receipts. Ambiguous text truncated by an older agent
526
+ stays visible until authoritative evidence arrives. Cron transcript caches reset
527
+ when their authoritative revision advances, so previously loaded older pages may
528
+ need to be loaded again. Offline hydration also hides old synthetic-only silent
529
+ rows while preserving delivered text and rich replies.
530
+
531
+ ### Storage migrations
532
+
533
+ `src/store-migrations.ts` owns the ordered, named migration registry; its last
534
+ step determines the supported SQLite schema version. Append a guarded step and
535
+ historical-layout preservation tests when changing storage. Never relabel a
536
+ shipped step: rebase before choosing a version, and append a repair if released
537
+ layouts collided. Bootstrap DDL and the upgrade transaction stay in `store.ts`.
538
+ Registry validation runs before bootstrap writes, and schema postconditions run
539
+ before the upgrade commits and on current-version opens. Step failures report
540
+ only the version/name, not stored content. A version stamp is not an execution
541
+ ledger; postconditions check the required effects.
542
+
310
543
  ### Data flow
311
544
 
312
545
  1. `server.ts` accepts the versioned browser API, staged uploads, and SSE
@@ -316,35 +549,53 @@ cross-resource requests fail. See
316
549
  Web Push subscription/event/delivery, notification, and cron projection
317
550
  records through the SQLite store, and
318
551
  drives each agent over its loopback operator endpoint.
319
- 3. Service mutations publish invalidations. Browsers consume `/api/v1/events`
320
- and refetch authoritative projections, including the selected agent's
321
- memory-only live skill registry, so reloads and concurrent tabs do not own or
322
- interrupt upstream turns.
552
+ 3. Service mutations publish content deltas and change hints. A browser names
553
+ one conversation on `/api/v1/events` and receives that conversation's
554
+ `message.delta` frames in full; every other change arrives as a hint,
555
+ rate-limited to one per conversation per second, that it answers with an
556
+ `If-None-Match` read of exactly the projection it names — including the
557
+ selected agent's memory-only live skill registry. Responses are compressed
558
+ and carry per-route cache policy: immutable hashed assets, revalidated shell
559
+ and worker scripts, `private, no-cache` API reads, and immutable
560
+ content-addressed upload bytes. Reloads and concurrent tabs still do not own
561
+ or interrupt upstream turns.
323
562
  4. The bundled assistant-ui webapp maps those DTOs—including canonical
324
563
  lifecycle metadata—into its external store, thread list, messages, compact
325
564
  Monitor activity, tool cards, composer, attachments, and push-subscription
326
- UI; its service worker handles background delivery and same-origin clicks.
565
+ UI, keeping a per-conversation cache that is also written to the device
566
+ (IndexedDB `mono-agent-web`, version 2, swept per writer on hydration) so a
567
+ cold start draws before the first response. Its service worker precaches the
568
+ shell, handles background push delivery and same-origin clicks, and is
569
+ registered in `prompt` mode: a new build is staged and applied on the next
570
+ idle foreground moment or an explicit reload, never over a running turn or an
571
+ unsent draft.
327
572
  5. `deliverWebNotification` reads the owner-private live ingress record and
328
573
  performs one bearer-authenticated loopback delivery. Cron/webhook delivery
329
574
  first appends the result to agent history, then atomically exposes an
330
575
  idempotent assistant-only thread. A process-job delivery instead updates one
331
576
  source/thread-bound durable card; its normal wake turn owns the single agent
332
- history entry. A Monitor delivery is steered into an active run or becomes an
333
- assistant-only follow-up in the exact existing web thread; exact host-owned
334
- receipts update one compact, secret-free activity row rather than creating
335
- repeated steering cards. The browser may be closed, but the web service must
336
- remain running.
577
+ history entry. Its receipt returns once that exact follow-up is durably
578
+ admitted rather than waiting for model completion. A Monitor delivery is
579
+ steered into an active run or becomes an assistant-only follow-up in the exact
580
+ existing web thread; exact host-owned receipts update one compact, secret-free
581
+ activity row rather than creating repeated steering cards. The browser may be
582
+ closed, but the web service must remain running.
583
+
584
+ Monitor activity shows suppressed lines/batches and follow-up, steered, or
585
+ unknown wake dispositions. These are host delivery counts, not model-turn or
586
+ cost estimates. Historical v1 Monitor projections in SQLite and browser caches
587
+ remain readable alongside v2 projections.
337
588
 
338
589
  ### Package structure
339
590
 
340
591
  | Source area | Responsibility |
341
592
  | --- | --- |
342
- | [`server.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/server.ts) | HTTP service, `/api/v1` routes, host/theme bootstrap identity, per-host PWA manifest, uploads, SSE invalidations, host/origin checks, and static webapp serving. |
343
- | [`service.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/service.ts) | Application lifecycle for discovery, threads, turns, agent-authored automatic titles, live-input delivery/fallback, attachments, `AskUser` snapshots/submission, cancellation, notifications, and invalidation. |
593
+ | [`server.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/server.ts) | HTTP service, `/api/v1` routes, hostname/display-name/theme bootstrap identity, per-console PWA manifest, uploads, SSE invalidations, host/origin checks, provider-auth no-store proxy routes, and static webapp serving. |
594
+ | [`service.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/service.ts) | Application lifecycle for discovery, threads, turns, agent-authored automatic titles, live-input delivery/fallback, attachments, `AskUser` snapshots/submission, provider-auth connection-generation guarding, cancellation, notifications, and invalidation. |
344
595
  | [`store.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/store.ts) | Owner-private SQLite schema and transactional persistence, including race-safe automatic-title updates that never overwrite a user rename. |
345
- | [`operator-client.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/operator-client.ts) | Structured turn streaming, info/capabilities, live-input settlement, pending/submitted `AskUser`, cancellation, durable history append, and owner-authenticated process-job reads/cancel over the operator protocol. |
596
+ | [`operator-client.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/operator-client.ts) | Structured turn streaming, info/capabilities, live-input settlement, pending/submitted `AskUser`, cancellation, durable history append, conditionally bearer-authenticated provider-auth, and owner-authenticated process-job requests over the operator protocol. |
346
597
  | [`notification-client.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/notification-client.ts) and [`notification-ingress.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/notification-ingress.ts) | Bounded, authenticated cron/webhook delivery, source/thread-bound process-job cards, and Monitor wake turns. |
347
- | [`webapp/`](https://github.com/robertsreberski/mono-agent/tree/main/packages/web/webapp) | Isolated assistant-ui PWA, including compact Monitor activity, atomic `AskUser` forms, tests, and its own dependency lockfile. |
598
+ | [`webapp/`](https://github.com/robertsreberski/mono-agent/tree/main/packages/web/webapp) | Isolated assistant-ui PWA, including compact Monitor activity, the loaded conversation-level process-job stack and live tails, atomic `AskUser` forms, tests, and its own dependency lockfile. |
348
599
 
349
600
  ## Public API
350
601
 
@@ -359,7 +610,7 @@ cross-resource requests fail. See
359
610
  | `discoverAcpBridgeAgents` | Discover Worklab-importable ACP sources through a credential-free, versioned ownership contract. |
360
611
  | `discoverOperatorAgents` | Read trusted operator endpoints from trace-source manifests. |
361
612
  | `WebBootstrap`, `WebThreadDetail`, `WebEvent`, and related `Web*` DTOs | Build another client against the versioned browser API. |
362
- | `WEB_THEMES`, `DEFAULT_WEB_THEME`, `WebTheme`, and `WebConsoleIdentity` | Select a curated theme and consume the hostname/theme identity returned to browsers. |
613
+ | `WEB_THEMES`, `DEFAULT_WEB_THEME`, `WEB_CONSOLE_NAME_MAX_CHARACTERS`, `WebTheme`, and `WebConsoleIdentity` | Select a curated theme/name and consume the hostname/display-name/theme identity returned to browsers. |
363
614
 
364
615
  <!-- public-api-inventory:start -->
365
616
  <!-- Generated by scripts/generate-public-api-docs.mjs. Do not edit by hand. -->
@@ -376,6 +627,7 @@ ACP_PROTOCOL_VERSION
376
627
  AcpBridgeDiscovery
377
628
  AcpBridgeSourceDescriptor
378
629
  AcpBridgeSourceHealth
630
+ CreateWebCronReplyInput
379
631
  CreateWebThreadInput
380
632
  CreateWebUploadInput
381
633
  DEFAULT_WEB_HOST
@@ -398,11 +650,14 @@ OperatorTurnInput
398
650
  OperatorTurnResult
399
651
  PatchWebAgentInput
400
652
  PatchWebThreadInput
653
+ PutWebAgentRunSettingsInput
401
654
  SearchWebThreadsInput
402
655
  StartWebLiveInputInput
403
656
  StartWebServerOptions
657
+ StartWebSubmissionInput
404
658
  StartWebTurnInput
405
659
  WEB_API_VERSION
660
+ WEB_CONSOLE_NAME_MAX_CHARACTERS
406
661
  WEB_MAX_ACTIVE_ATTACHMENT_TURN_BYTES
407
662
  WEB_MAX_CONCURRENT_UPLOADS
408
663
  WEB_MAX_FILES_PER_TURN
@@ -417,16 +672,25 @@ WEB_STAGED_UPLOAD_TTL_MS
417
672
  WEB_THEMES
418
673
  WEB_THREAD_SEARCH_MAX
419
674
  WEB_THREAD_SEARCH_MIN_QUERY
675
+ WebAgentRunSettings
420
676
  WebAgentStatus
421
677
  WebAgentSummary
678
+ WebAgentsChangedPayload
422
679
  WebAttachment
423
680
  WebBootstrap
681
+ WebBootstrapScope
424
682
  WebConsoleError
425
683
  WebConsoleIdentity
684
+ WebCronReplyContextPart
685
+ WebCronReplyReceipt
686
+ WebCronReplySnapshotKind
426
687
  WebEvent
427
688
  WebEventType
689
+ WebJobActivity
428
690
  WebLiveInputReceipt
429
691
  WebMessage
692
+ WebMessageDelta
693
+ WebMessageDeltaOp
430
694
  WebMessagePart
431
695
  WebMessageStatus
432
696
  WebModelOption
@@ -435,8 +699,14 @@ WebPushBootstrap
435
699
  WebPushSubscriptionState
436
700
  WebPushSubscriptionStatus
437
701
  WebQuote
702
+ WebRunAttribution
703
+ WebRunExecution
704
+ WebRunRetry
705
+ WebRunSelection
706
+ WebRunSettingSource
438
707
  WebRunState
439
708
  WebRunStatus
709
+ WebRunTransition
440
710
  WebServerHandle
441
711
  WebSkillAvailability
442
712
  WebSkillInfo
@@ -444,8 +714,10 @@ WebSkillRegistry
444
714
  WebSkillUnavailableReason
445
715
  WebStatePathOptions
446
716
  WebStatePaths
717
+ WebSubmissionReceipt
447
718
  WebTheme
448
719
  WebThread
720
+ WebThreadChangedPayload
449
721
  WebThreadDetail
450
722
  WebThreadNotificationTriggerKind
451
723
  WebThreadSearchHit
@@ -476,9 +748,16 @@ the actual bound address/port plus idempotent `stop()` and `close()` methods.
476
748
 
477
749
  The browser API is rooted at `/api/v1`:
478
750
 
479
- - `GET /bootstrap`, `PATCH /agents/:id`, and `GET/PATCH/DELETE /threads/:id`
751
+ - `GET /bootstrap` (one `?sourceId`/`?archived` thread bucket, `?limit` capped),
752
+ `PATCH /agents/:id`, and `GET/PATCH/DELETE /threads/:id`
480
753
  - `POST /threads`, `/threads/:id/turns`, `/threads/:id/live-input`, and
481
- `/threads/:id/cancel`
754
+ `/threads/:id/cancel`; the browser composer uses
755
+ `POST /threads/:id/submissions` plus recovery reads at
756
+ `GET /threads/:id/submissions/:submissionId`
757
+ - `GET /threads/:id/messages/:messageId` for one message (delta gap recovery)
758
+ and `GET /threads/:id/messages/:messageId/tool-calls/:toolCallId` for the
759
+ unshaped body behind a truncated tool-call preview; `?full=1` on a transcript
760
+ read returns it unshaped
482
761
  - `GET /threads/:id/ask` and `POST /threads/:id/ask` for the current structured
483
762
  `AskUser` snapshot and atomic answer submission
484
763
  - `POST /uploads`, `PUT/GET /uploads/:id/content`, and `DELETE /uploads/:id`
@@ -491,7 +770,13 @@ The browser API is rooted at `/api/v1`:
491
770
  and `POST /push/events/:eventId/ack`; the status read carries the exact
492
771
  `X-Mono-Agent-Web-Origin` browser-origin claim and returns no endpoint or key
493
772
  material
494
- - `GET /events` (SSE)
773
+ - `GET /events` (SSE); the optional `?thread=<id>` subscribes that connection to
774
+ one conversation's `message.delta` frames, resolving a redirected id to the
775
+ canonical one. `Last-Event-ID` is ignored — `ready` means resync
776
+ - `POST /agents/:sourceId/cron/jobs/:jobId/runs/:runId/reply-threads` snapshots
777
+ one visible terminal run and imports it into one normal same-agent thread;
778
+ the exact-origin request carries a browser-owned operation UUID and summary
779
+ or already-loaded-detail selection
495
780
 
496
781
  `GET /healthz` is intentionally outside the versioned API for service probes.
497
782
  Its compatibility-stable `status` remains `ok` while the additive `push` field
@@ -503,16 +788,49 @@ The additive `WebBootstrap.console` object carries the server-derived
503
788
  `POST /threads/:id/live-input` accepts `{ text }` and returns a persisted message
504
789
  with `disposition: "pending" | "queued"`; SSE invalidation exposes its later
505
790
  `liveInputStatus` settlement.
791
+
792
+ `POST /threads/:id/submissions` accepts a canonical UUID plus the ordinary turn
793
+ payload. The thread-scoped immutable payload is recorded in `web_submissions`
794
+ with its turn/live-input/rejection associations. Same-id same-payload replay
795
+ returns the existing current receipt without dispatch; conflicting reuse is
796
+ `409`. `GET /threads/:id/submissions/:submissionId` is side-effect-free and both
797
+ receipt routes are `private, no-store`. Browser recovery persists only thread
798
+ and submission ids in session storage—never draft text or file bytes—and checks
799
+ the receipt without automatically posting again.
800
+
801
+ Schema 22 adds a nullable `live_inputs.dispatch_started_at` marker committed
802
+ before the operator request. On restart, an unmarked offered row is safe to
803
+ queue once; a marked row becomes `uncertain` and non-promotable. Existing
804
+ schema-21 rows migrate with a null marker, so their earlier dispatch history is
805
+ not reconstructible. Schema 23 adds the durable `web_submissions` ledger and
806
+ keeps it for the lifetime of its thread, including archive; permanent deletion
807
+ cascades it. Schema 24 adds the read indexes used by conversation run-state
808
+ lookups. Schema 25 adds `cron_reply_operations`, which owns the immutable
809
+ snapshot, canonical-import identity, pending/terminal settlement, and deletion
810
+ tombstone. Pending rows survive restart for explicit same-operation retry;
811
+ failed or deleted rows cannot expose or resurrect a conversation. Stop the Web
812
+ service and make a compatible database backup before migration. A schema-24
813
+ binary refuses a schema-25 database; rollback requires
814
+ restoring that compatible pre-upgrade backup and loses writes made afterward.
815
+ Upgrade the operator-adapter before the Web producer so `liveInputTargeting`
816
+ is available; an older operator is not guessed through and the receipt visibly
817
+ queues `unsupported_targeting`. Deployment remains a separate operation.
506
818
  Permanent deletion is limited to archived, inactive conversations. It removes
507
819
  database descendants transactionally and deletes committed attachment files;
508
820
  startup and scheduled cleanup remove any file orphaned by a crash or transient
509
- filesystem failure after the database commit.
821
+ filesystem failure after the database commit. Archiving a truly empty manual
822
+ conversation conditionally removes it instead; any authoritative trigger,
823
+ message, turn, attachment, live input, submission receipt, or delivery evidence makes the server
824
+ preserve it in Archived, including activity that arrives during the archive
825
+ race.
510
826
 
511
827
  ## Dependency Boundary
512
828
 
513
829
  The server depends only on the `core` `@mono-agent/agent-contracts` and
514
830
  `@mono-agent/config` packages, the `observability` trace-source registry, and
515
- Express. Its compiled browser bundle additionally contains the production graph
831
+ Express. Its Node-side operator clients use Undici to keep deliberately
832
+ long-lived turn and host-wake streams under their explicit lifecycle owners.
833
+ Its compiled browser bundle additionally contains the production graph
516
834
  from the isolated `webapp` lockfile: assistant-ui, Base UI, cmdk, React, and
517
835
  Workbox plus their transitive dependencies. The repository advisory and license
518
836
  gates audit that nested production graph separately because it ships inside this