@mono-agent/web 0.13.0 → 0.14.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 (41) hide show
  1. package/README.md +104 -4
  2. package/dist/contracts.d.ts +5 -0
  3. package/dist/contracts.d.ts.map +1 -1
  4. package/dist/index.d.ts +3 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/notification-client.d.ts +19 -0
  9. package/dist/notification-client.d.ts.map +1 -0
  10. package/dist/notification-client.js +136 -0
  11. package/dist/notification-client.js.map +1 -0
  12. package/dist/notification-ingress.d.ts +17 -0
  13. package/dist/notification-ingress.d.ts.map +1 -0
  14. package/dist/notification-ingress.js +170 -0
  15. package/dist/notification-ingress.js.map +1 -0
  16. package/dist/operator-client.d.ts +6 -1
  17. package/dist/operator-client.d.ts.map +1 -1
  18. package/dist/operator-client.js +30 -0
  19. package/dist/operator-client.js.map +1 -1
  20. package/dist/server.d.ts.map +1 -1
  21. package/dist/server.js +28 -0
  22. package/dist/server.js.map +1 -1
  23. package/dist/service.d.ts +17 -1
  24. package/dist/service.d.ts.map +1 -1
  25. package/dist/service.js +61 -0
  26. package/dist/service.js.map +1 -1
  27. package/dist/state-paths.d.ts +1 -0
  28. package/dist/state-paths.d.ts.map +1 -1
  29. package/dist/state-paths.js +12 -0
  30. package/dist/state-paths.js.map +1 -1
  31. package/dist/store.d.ts +18 -1
  32. package/dist/store.d.ts.map +1 -1
  33. package/dist/store.js +146 -8
  34. package/dist/store.js.map +1 -1
  35. package/package.json +4 -4
  36. package/webapp/dist/assets/index-BpHUwRCC.css +1 -0
  37. package/webapp/dist/assets/index-DpEe0YWc.js +47 -0
  38. package/webapp/dist/index.html +2 -2
  39. package/webapp/dist/sw.js +1 -1
  40. package/webapp/dist/assets/index-1UedhLBk.css +0 -1
  41. package/webapp/dist/assets/index-D-X2B4kU.js +0 -47
package/README.md CHANGED
@@ -6,7 +6,14 @@ and serves the assistant-ui PWA from one always-on process.
6
6
 
7
7
  ## Category
8
8
 
9
+ <!-- package-metadata:start -->
10
+ <!-- Generated by scripts/generate-package-docs.mjs. Do not edit by hand. -->
11
+
9
12
  Category: `operator-surface`
13
+ Tier: `core`
14
+ Catalog responsibility: Serves the always-on browser operator console for persistent multi-agent conversations, streamed turns, and local-device attachments.
15
+
16
+ <!-- package-metadata:end -->
10
17
 
11
18
  ## Responsibility
12
19
 
@@ -17,14 +24,35 @@ Category: `operator-surface`
17
24
  `~/.mono-agent/web`.
18
25
  - Keep an upstream turn running when a browser reloads or disconnects, and expose
19
26
  state invalidations over SSE so any connected browser can catch up.
27
+ - Render a running agent's structured `AskUser` interaction as one complete web
28
+ form and proxy validated answer submission back to that same model run.
20
29
  - Accept browser-selected files through bounded staged uploads and forward the
21
30
  exact transport-neutral `AgentAttachment` contract used by Telegram.
22
31
  - Serve the assistant-ui PWA and its versioned JSON/SSE API.
32
+ - Accept explicit cron/webhook `web:new` notification delivery through an
33
+ owner-private, bearer-authenticated loopback ingress; persist one marked,
34
+ assistant-only conversation per distinct result only after agent history is
35
+ durably appended.
23
36
 
24
37
  ## Install / Usage
25
38
 
26
- The `mono-agent web` command owns normal lifecycle management. The embeddable
27
- server API is also available:
39
+ `@mono-agent/agent-app` provides the normal managed-service lifecycle:
40
+
41
+ ```bash
42
+ mono-agent web start
43
+ mono-agent web status
44
+ mono-agent web
45
+ ```
46
+
47
+ Bare `mono-agent web` reports status and usable URLs; it does not implicitly
48
+ start or mutate the service. Use `mono-agent web run` for a foreground process
49
+ or `--loopback` to bind only `127.0.0.1`.
50
+
51
+ Install this package directly only when embedding the server in another host:
52
+
53
+ ```bash
54
+ pnpm add @mono-agent/web
55
+ ```
28
56
 
29
57
  ```ts
30
58
  import { startWebServer } from "@mono-agent/web";
@@ -76,10 +104,66 @@ responses that arrive while the console is hidden or unfocused. Notifications
76
104
  include a short response preview and open the exact conversation. Permission
77
105
  is requested only from the bell, the preference is browser-origin-local, and
78
106
  the page/PWA must remain alive: this is not a Web Push subscription and does
79
- not notify after the application is fully closed.
107
+ not notify after the application is fully closed. Cron/webhook notification
108
+ threads use the same bell and are marked `CRON` / `WEBHOOK` in the sidebar,
109
+ header, and browser notification title.
110
+
111
+ An app-managed cron job or webhook endpoint can set `notify: true` with the
112
+ exact destination `notifyConversationId: "web:new"`. Each distinct result gets
113
+ a new thread without changing the selected thread. Delivery is idempotent,
114
+ best-effort, attempted once with a five-second bound, and has no outbox when the
115
+ web service is unavailable. Other `web:*` destinations are not accepted.
116
+
117
+ When the selected agent advertises `capabilities.askUser`, a running `AskUser`
118
+ tool call appears as one form containing all remaining questions. Each question
119
+ has two or three described choices plus an **Other** custom-reply field;
120
+ multi-select questions accept several choices and custom text. The browser
121
+ submits the form atomically and the agent resumes the existing run. Cancelling
122
+ the turn also cancels its pending form.
123
+
124
+ ## Architecture
125
+
126
+ ### Data flow
127
+
128
+ 1. `server.ts` accepts the versioned browser API, staged uploads, and SSE
129
+ subscriptions, then delegates stateful work to `WebConsoleService`.
130
+ 2. The service discovers agents from the trace-source registry, persists agent,
131
+ thread, message, part, turn, upload, and preference records through the
132
+ SQLite store, and drives each agent over its loopback operator endpoint.
133
+ 3. Service mutations publish invalidations. Browsers consume `/api/v1/events`
134
+ and refetch authoritative projections, so reloads and concurrent tabs do not
135
+ own or interrupt upstream turns.
136
+ 4. The bundled assistant-ui webapp maps those DTOs into its external store,
137
+ thread list, messages, composer, attachments, activity, and notification UI.
138
+ 5. `deliverWebNotification` reads the owner-private live ingress record and
139
+ performs one bearer-authenticated loopback delivery. The service first
140
+ appends the result to agent history, then atomically exposes an idempotent
141
+ assistant-only cron or webhook thread.
142
+
143
+ ### Package structure
144
+
145
+ | Source area | Responsibility |
146
+ | --- | --- |
147
+ | [`server.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/server.ts) | HTTP service, `/api/v1` routes, uploads, SSE invalidations, host/origin checks, and static webapp serving. |
148
+ | [`service.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/service.ts) | Application lifecycle for discovery, threads, turns, attachments, `AskUser` snapshots/submission, cancellation, notifications, and invalidation. |
149
+ | [`store.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/store.ts) | Owner-private SQLite schema and transactional persistence. |
150
+ | [`operator-client.ts`](https://github.com/robertsreberski/mono-agent/blob/main/packages/web/src/operator-client.ts) | Structured turn streaming, info, pending/submitted `AskUser`, cancellation, and durable history append over the operator protocol. |
151
+ | [`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 notification delivery. |
152
+ | [`webapp/`](https://github.com/robertsreberski/mono-agent/tree/main/packages/web/webapp) | Isolated assistant-ui PWA, including atomic `AskUser` forms, tests, and its own dependency lockfile. |
80
153
 
81
154
  ## Public API
82
155
 
156
+ ### Start here
157
+
158
+ | API | Use it for |
159
+ | --- | --- |
160
+ | `startWebServer` | Start the persistent browser service and receive the actual bound URL plus idempotent stop methods. |
161
+ | `prepareWebState` / `prepareWebStatePaths` | Create and validate the owner-private state layout before starting a custom service. |
162
+ | `resetWebState` | Perform the explicit whole-store reset used by host lifecycle commands. |
163
+ | `deliverWebNotification` | Deliver one idempotent cron/webhook result through the private loopback ingress. |
164
+ | `discoverOperatorAgents` | Read trusted operator endpoints from trace-source manifests. |
165
+ | `WebBootstrap`, `WebThreadDetail`, `WebEvent`, and related `Web*` DTOs | Build another client against the versioned browser API. |
166
+
83
167
  <!-- public-api-inventory:start -->
84
168
  <!-- Generated by scripts/generate-public-api-docs.mjs. Do not edit by hand. -->
85
169
 
@@ -92,6 +176,9 @@ CreateWebThreadInput
92
176
  CreateWebUploadInput
93
177
  DEFAULT_WEB_HOST
94
178
  DEFAULT_WEB_PORT
179
+ DeliverWebNotificationInput
180
+ DeliverWebNotificationOptions
181
+ DeliverWebNotificationResult
95
182
  DiscoverOperatorAgentsOptions
96
183
  DiscoveredOperatorAgent
97
184
  PatchWebAgentInput
@@ -118,6 +205,7 @@ WebMessage
118
205
  WebMessagePart
119
206
  WebMessageStatus
120
207
  WebModelOption
208
+ WebNotificationTriggerKind
121
209
  WebQuote
122
210
  WebRunState
123
211
  WebRunStatus
@@ -126,8 +214,10 @@ WebStatePathOptions
126
214
  WebStatePaths
127
215
  WebThread
128
216
  WebThreadDetail
217
+ WebThreadTrigger
129
218
  defaultTraceRegistryDir
130
219
  defaultWebStateDir
220
+ deliverWebNotification
131
221
  discoverOperatorAgents
132
222
  isTrustedOperatorBaseUrl
133
223
  operatorBaseUrlFromMetadata
@@ -149,6 +239,8 @@ The browser API is rooted at `/api/v1`:
149
239
 
150
240
  - `GET /bootstrap`, `PATCH /agents/:id`, and `GET/PATCH /threads/:id`
151
241
  - `POST /threads`, `/threads/:id/turns`, and `/threads/:id/cancel`
242
+ - `GET /threads/:id/ask` and `POST /threads/:id/ask` for the current structured
243
+ `AskUser` snapshot and atomic answer submission
152
244
  - `POST /uploads`, `PUT/GET /uploads/:id/content`, and `DELETE /uploads/:id`
153
245
  - `GET /events` (SSE)
154
246
 
@@ -173,11 +265,19 @@ import a communication adapter or another operator surface.
173
265
  - Agent runtime/provider execution or conversation history inside an agent.
174
266
  - The operator-adapter HTTP server published by each agent.
175
267
  - CLI background-process, launchd, or conflict-safe Tailscale Serve lifecycle.
176
- - Session Recorder history/replay, which remains in `@mono-agent/session-web`.
268
+ - Recorded-run replay, which belongs to `@mono-agent/tui`; the legacy read-only
269
+ Session Recorder remains a separate package pending retirement.
177
270
  - Authentication. Network reachability is the intentional security boundary.
178
271
  - Host filesystem browsing: attachments come only from the browser device's
179
272
  native file picker.
180
273
 
274
+ ## Related Documentation
275
+
276
+ - [Always-on web console guide](https://mono-agent-docs.vercel.app/observability/web-console/)
277
+ - [Operator stream endpoint](https://mono-agent-docs.vercel.app/channels/tui/)
278
+ - [Sessions and concurrency](https://mono-agent-docs.vercel.app/runtime/sessions-concurrency/)
279
+ - [Artifacts and traces](https://mono-agent-docs.vercel.app/observability/artifacts-and-traces/)
280
+
181
281
  ## Verification
182
282
 
183
283
  ```sh
@@ -10,6 +10,10 @@ export declare const WEB_MAX_ACTIVE_ATTACHMENT_TURN_BYTES: number;
10
10
  export declare const WEB_MAX_QUEUED_ATTACHMENT_TURNS = 32;
11
11
  export declare const WEB_MAX_TURN_TEXT_CHARACTERS = 200000;
12
12
  export type WebAgentStatus = "online" | "offline" | "degraded";
13
+ export type WebNotificationTriggerKind = "cron" | "webhook";
14
+ export interface WebThreadTrigger {
15
+ readonly kind: WebNotificationTriggerKind;
16
+ }
13
17
  export interface WebModelOption {
14
18
  readonly effortLevels?: readonly string[];
15
19
  readonly reasoning?: boolean;
@@ -52,6 +56,7 @@ export interface WebThread {
52
56
  readonly createdAt: string;
53
57
  readonly updatedAt: string;
54
58
  readonly revision: number;
59
+ readonly trigger?: WebThreadTrigger;
55
60
  readonly lastMessagePreview?: string;
56
61
  readonly messageCount: number;
57
62
  readonly runState: WebRunState;
@@ -1 +1 @@
1
- {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,9 +6,11 @@ export { DEFAULT_WEB_HOST, DEFAULT_WEB_PORT, startWebServer, } from "./server.js
6
6
  export type { StartWebServerOptions, WebServerHandle } from "./server.js";
7
7
  export { defaultWebStateDir, prepareWebState, prepareWebStatePaths, resetWebState, resolveWebStatePaths, } from "./state-paths.js";
8
8
  export type { WebStatePathOptions, WebStatePaths } from "./state-paths.js";
9
+ export { deliverWebNotification } from "./notification-client.js";
10
+ export type { DeliverWebNotificationInput, DeliverWebNotificationOptions, DeliverWebNotificationResult, } from "./notification-client.js";
9
11
  export { defaultTraceRegistryDir, discoverOperatorAgents, isTrustedOperatorBaseUrl, operatorBaseUrlFromMetadata, } from "./discovery.js";
10
12
  export type { DiscoverOperatorAgentsOptions, DiscoveredOperatorAgent, } from "./discovery.js";
11
13
  export { WEB_API_VERSION, WEB_MAX_CONCURRENT_UPLOADS, WEB_MAX_ACTIVE_ATTACHMENT_TURN_BYTES, WEB_MAX_FILES_PER_TURN, WEB_MAX_STAGED_UPLOAD_BYTES, WEB_MAX_STAGED_UPLOADS, WEB_MAX_QUEUED_ATTACHMENT_TURNS, WEB_MAX_TURN_ATTACHMENT_BYTES, WEB_STAGED_UPLOAD_TTL_MS, } from "./contracts.js";
12
- export type { CreateWebThreadInput, CreateWebUploadInput, PatchWebAgentInput, PatchWebThreadInput, StartWebTurnInput, WebAgentStatus, WebAgentSummary, WebAttachment, WebBootstrap, WebEvent, WebEventType, WebMessage, WebMessagePart, WebMessageStatus, WebModelOption, WebQuote, WebRunState, WebRunStatus, WebThread, WebThreadDetail, } from "./contracts.js";
14
+ export type { CreateWebThreadInput, CreateWebUploadInput, PatchWebAgentInput, PatchWebThreadInput, StartWebTurnInput, WebAgentStatus, WebAgentSummary, WebAttachment, WebBootstrap, WebEvent, WebEventType, WebMessage, WebMessagePart, WebMessageStatus, WebModelOption, WebNotificationTriggerKind, WebQuote, WebRunState, WebRunStatus, WebThread, WebThreadDetail, WebThreadTrigger, } from "./contracts.js";
13
15
  export { WebConsoleError } from "./errors.js";
14
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE3E,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,oCAAoC,EACpC,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,+BAA+B,EAC/B,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,SAAS,EACT,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EACV,2BAA2B,EAC3B,6BAA6B,EAC7B,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,6BAA6B,EAC7B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,oCAAoC,EACpC,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,+BAA+B,EAC/B,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,0BAA0B,EAC1B,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,SAAS,EACT,eAAe,EACf,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { DEFAULT_WEB_HOST, DEFAULT_WEB_PORT, startWebServer, } from "./server.js";
6
6
  export { defaultWebStateDir, prepareWebState, prepareWebStatePaths, resetWebState, resolveWebStatePaths, } from "./state-paths.js";
7
+ export { deliverWebNotification } from "./notification-client.js";
7
8
  export { defaultTraceRegistryDir, discoverOperatorAgents, isTrustedOperatorBaseUrl, operatorBaseUrlFromMetadata, } from "./discovery.js";
8
9
  export { WEB_API_VERSION, WEB_MAX_CONCURRENT_UPLOADS, WEB_MAX_ACTIVE_ATTACHMENT_TURN_BYTES, WEB_MAX_FILES_PER_TURN, WEB_MAX_STAGED_UPLOAD_BYTES, WEB_MAX_STAGED_UPLOADS, WEB_MAX_QUEUED_ATTACHMENT_TURNS, WEB_MAX_TURN_ATTACHMENT_BYTES, WEB_STAGED_UPLOAD_TTL_MS, } from "./contracts.js";
9
10
  export { WebConsoleError } from "./errors.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,GACf,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AAMxB,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,oCAAoC,EACpC,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,+BAA+B,EAC/B,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AAwBxB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,GACf,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,aAAa,EACb,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAOlE,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AAMxB,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,oCAAoC,EACpC,sBAAsB,EACtB,2BAA2B,EAC3B,sBAAsB,EACtB,+BAA+B,EAC/B,6BAA6B,EAC7B,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AA0BxB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { WebNotificationTriggerKind } from "./contracts.js";
2
+ import { type WebStatePathOptions } from "./state-paths.js";
3
+ export interface DeliverWebNotificationInput {
4
+ readonly sourceId: string;
5
+ readonly triggerKind: WebNotificationTriggerKind;
6
+ readonly deliveryKey: string;
7
+ readonly text: string;
8
+ }
9
+ export interface DeliverWebNotificationOptions extends WebStatePathOptions {
10
+ readonly fetchImpl?: typeof fetch;
11
+ readonly timeoutMs?: number;
12
+ }
13
+ export interface DeliverWebNotificationResult {
14
+ readonly threadId: string;
15
+ readonly duplicate: boolean;
16
+ }
17
+ /** Deliver once to the active local web console. There is intentionally no retry or outbox. */
18
+ export declare function deliverWebNotification(input: DeliverWebNotificationInput, options?: DeliverWebNotificationOptions): Promise<DeliverWebNotificationResult>;
19
+ //# sourceMappingURL=notification-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-client.d.ts","sourceRoot":"","sources":["../src/notification-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gBAAgB,CAAC;AAEjE,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAQlF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;IACjD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,6BAA8B,SAAQ,mBAAmB;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAWD,+FAA+F;AAC/F,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,2BAA2B,EAClC,OAAO,GAAE,6BAAkC,GAC1C,OAAO,CAAC,4BAA4B,CAAC,CA0CvC"}
@@ -0,0 +1,136 @@
1
+ import { constants as fsConstants } from "node:fs";
2
+ import { lstat, open } from "node:fs/promises";
3
+ import { errorMessage, WebConsoleError } from "./errors.js";
4
+ import { resolveWebStatePaths } from "./state-paths.js";
5
+ const NOTIFICATION_INGRESS_SCHEMA = 1;
6
+ const NOTIFICATION_INGRESS_PATH = "/internal/v1/notifications";
7
+ const MAX_INGRESS_RECORD_BYTES = 64 * 1024;
8
+ const MAX_RESPONSE_BYTES = 64 * 1024;
9
+ const DEFAULT_TIMEOUT_MS = 5_000;
10
+ /** Deliver once to the active local web console. There is intentionally no retry or outbox. */
11
+ export async function deliverWebNotification(input, options = {}) {
12
+ const path = resolveWebStatePaths(options).notificationIngress;
13
+ const ingress = await readIngressRecord(path);
14
+ const fetchImpl = options.fetchImpl ?? fetch;
15
+ let response;
16
+ try {
17
+ response = await fetchImpl(ingress.url, {
18
+ method: "POST",
19
+ redirect: "error",
20
+ signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
21
+ headers: {
22
+ authorization: `Bearer ${ingress.token}`,
23
+ "content-type": "application/json",
24
+ },
25
+ body: JSON.stringify(input),
26
+ });
27
+ }
28
+ catch (error) {
29
+ throw new WebConsoleError("notification_ingress_unavailable", `The web notification ingress is unavailable (${errorMessage(error)}).`, 503);
30
+ }
31
+ const bodyText = await readBoundedResponse(response);
32
+ if (!response.ok) {
33
+ throw new WebConsoleError("notification_delivery_failed", `The web notification ingress responded ${String(response.status)}${bodyText.length === 0 ? "." : `: ${bodyText.slice(0, 300)}`}`, 502);
34
+ }
35
+ let parsed;
36
+ try {
37
+ parsed = JSON.parse(bodyText);
38
+ }
39
+ catch {
40
+ throw new WebConsoleError("invalid_notification_response", "The web notification ingress returned invalid JSON.", 502);
41
+ }
42
+ const result = asRecord(parsed);
43
+ if (result === undefined || typeof result.threadId !== "string" || typeof result.duplicate !== "boolean") {
44
+ throw new WebConsoleError("invalid_notification_response", "The web notification ingress returned an invalid result.", 502);
45
+ }
46
+ return { threadId: result.threadId, duplicate: result.duplicate };
47
+ }
48
+ async function readIngressRecord(path) {
49
+ const info = await lstat(path).catch(() => undefined);
50
+ const currentUid = typeof process.getuid === "function" ? process.getuid() : undefined;
51
+ if (info === undefined || !info.isFile() || info.isSymbolicLink()
52
+ || info.size <= 0 || info.size > MAX_INGRESS_RECORD_BYTES
53
+ || (currentUid !== undefined && info.uid !== currentUid)
54
+ || (info.mode & 0o077) !== 0) {
55
+ throw new WebConsoleError("notification_ingress_unavailable", "The owner-private web notification ingress record is unavailable.", 503);
56
+ }
57
+ const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
58
+ let contents;
59
+ try {
60
+ const opened = await handle.stat();
61
+ if (!opened.isFile() || opened.dev !== info.dev || opened.ino !== info.ino || opened.size !== info.size) {
62
+ throw new WebConsoleError("notification_ingress_unavailable", "The web notification ingress record changed while opening.", 503);
63
+ }
64
+ contents = await handle.readFile("utf8");
65
+ }
66
+ finally {
67
+ await handle.close();
68
+ }
69
+ let parsed;
70
+ try {
71
+ parsed = JSON.parse(contents);
72
+ }
73
+ catch {
74
+ throw new WebConsoleError("notification_ingress_unavailable", "The web notification ingress record is invalid.", 503);
75
+ }
76
+ const record = asRecord(parsed);
77
+ if (record === undefined
78
+ || record.schema !== NOTIFICATION_INGRESS_SCHEMA
79
+ || !Number.isSafeInteger(record.pid) || record.pid <= 0
80
+ || typeof record.instanceId !== "string" || record.instanceId.length === 0 || record.instanceId.length > 128
81
+ || typeof record.url !== "string"
82
+ || typeof record.token !== "string" || record.token.length < 32 || record.token.length > 256
83
+ || typeof record.updatedAt !== "string" || !Number.isFinite(Date.parse(record.updatedAt))) {
84
+ throw new WebConsoleError("notification_ingress_unavailable", "The web notification ingress record is invalid.", 503);
85
+ }
86
+ assertTrustedIngressUrl(record.url);
87
+ return record;
88
+ }
89
+ function assertTrustedIngressUrl(value) {
90
+ let url;
91
+ try {
92
+ url = new URL(value);
93
+ }
94
+ catch {
95
+ throw new WebConsoleError("notification_ingress_unavailable", "The web notification ingress URL is invalid.", 503);
96
+ }
97
+ if (url.protocol !== "http:"
98
+ || url.hostname !== "127.0.0.1"
99
+ || url.port.length === 0
100
+ || url.pathname !== NOTIFICATION_INGRESS_PATH
101
+ || url.search.length > 0
102
+ || url.hash.length > 0
103
+ || url.username.length > 0
104
+ || url.password.length > 0) {
105
+ throw new WebConsoleError("notification_ingress_unavailable", "The web notification ingress URL is not trusted.", 503);
106
+ }
107
+ }
108
+ async function readBoundedResponse(response) {
109
+ if (response.body === null)
110
+ return "";
111
+ const reader = response.body.getReader();
112
+ const chunks = [];
113
+ let total = 0;
114
+ try {
115
+ for (;;) {
116
+ const { done, value } = await reader.read();
117
+ if (done)
118
+ break;
119
+ total += value.byteLength;
120
+ if (total > MAX_RESPONSE_BYTES) {
121
+ throw new WebConsoleError("invalid_notification_response", "The web notification response is too large.", 502);
122
+ }
123
+ chunks.push(value);
124
+ }
125
+ }
126
+ finally {
127
+ await reader.cancel().catch(() => undefined);
128
+ }
129
+ return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)), total).toString("utf8");
130
+ }
131
+ function asRecord(value) {
132
+ return typeof value === "object" && value !== null && !Array.isArray(value)
133
+ ? value
134
+ : undefined;
135
+ }
136
+ //# sourceMappingURL=notification-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-client.js","sourceRoot":"","sources":["../src/notification-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { WebService, WebServiceLogger } from "./service.js";
2
+ declare const INGRESS_SCHEMA = 1;
3
+ export interface WebNotificationIngressRecord {
4
+ readonly schema: typeof INGRESS_SCHEMA;
5
+ readonly pid: number;
6
+ readonly instanceId: string;
7
+ readonly url: string;
8
+ readonly token: string;
9
+ readonly updatedAt: string;
10
+ }
11
+ export interface WebNotificationIngressHandle {
12
+ readonly url: string;
13
+ stop(): Promise<void>;
14
+ }
15
+ export declare function startWebNotificationIngress(service: WebService, logger?: WebServiceLogger): Promise<WebNotificationIngressHandle>;
16
+ export {};
17
+ //# sourceMappingURL=notification-ingress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-ingress.d.ts","sourceRoot":"","sources":["../src/notification-ingress.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEjE,QAAA,MAAM,cAAc,IAAI,CAAC;AAMzB,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE,UAAU,EACnB,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,CAAC,4BAA4B,CAAC,CAmGvC"}
@@ -0,0 +1,170 @@
1
+ import { randomBytes, randomUUID } from "node:crypto";
2
+ import { chmod, lstat, open, readFile, rename, unlink } from "node:fs/promises";
3
+ import { createServer } from "node:http";
4
+ import { bearerTokensEqual, close, listen, readAuthorizationBearer, } from "@mono-agent/agent-contracts";
5
+ import express, {} from "express";
6
+ import { WEB_MAX_TURN_TEXT_CHARACTERS } from "./contracts.js";
7
+ import { errorMessage, WebConsoleError } from "./errors.js";
8
+ const INGRESS_SCHEMA = 1;
9
+ const INGRESS_HOST = "127.0.0.1";
10
+ const INGRESS_PATH = "/internal/v1/notifications";
11
+ const MAX_INGRESS_BODY_BYTES = 2 * 1024 * 1024;
12
+ const MAX_INGRESS_RECORD_BYTES = 64 * 1024;
13
+ export async function startWebNotificationIngress(service, logger) {
14
+ const token = randomBytes(32).toString("base64url");
15
+ const instanceId = randomUUID();
16
+ const app = express();
17
+ const server = createServer(app);
18
+ const active = new Set();
19
+ let stopPromise;
20
+ server.headersTimeout = 10_000;
21
+ server.requestTimeout = 10_000;
22
+ server.keepAliveTimeout = 1_000;
23
+ app.disable("x-powered-by");
24
+ app.post(INGRESS_PATH, express.json({ limit: MAX_INGRESS_BODY_BYTES, strict: true }), (req, res, next) => {
25
+ const presented = readAuthorizationBearer(req.header("authorization"));
26
+ if (presented === undefined || !bearerTokensEqual(presented, token)) {
27
+ res.status(401).json({ error: { code: "unauthorized", message: "Unauthorized." } });
28
+ return;
29
+ }
30
+ let input;
31
+ try {
32
+ input = parseNotificationRequest(req.body);
33
+ }
34
+ catch (error) {
35
+ next(error);
36
+ return;
37
+ }
38
+ const delivery = service.deliverNotification(input);
39
+ active.add(delivery);
40
+ void delivery.finally(() => active.delete(delivery)).catch(() => undefined);
41
+ void delivery.then((result) => {
42
+ res.status(result.duplicate ? 200 : 201).json({
43
+ threadId: result.thread.id,
44
+ duplicate: result.duplicate,
45
+ });
46
+ }).catch(next);
47
+ });
48
+ app.use((error, _req, res, next) => {
49
+ if (res.headersSent) {
50
+ next(error);
51
+ return;
52
+ }
53
+ const known = error instanceof WebConsoleError;
54
+ const syntax = error instanceof SyntaxError && error.status === 400;
55
+ const tooLarge = typeof error === "object" && error !== null
56
+ && (error.status === 413 || error.type === "entity.too.large");
57
+ const status = known ? error.status : tooLarge ? 413 : syntax ? 400 : 500;
58
+ const code = known ? error.code : tooLarge ? "request_too_large" : syntax ? "invalid_json" : "internal_error";
59
+ if (status >= 500)
60
+ logger?.error?.("Web notification ingress failed.", { error: errorMessage(error) });
61
+ res.status(status).json({
62
+ error: {
63
+ code,
64
+ message: known || syntax ? errorMessage(error) : "Internal server error.",
65
+ },
66
+ });
67
+ });
68
+ const address = await listen(server, 0, INGRESS_HOST, {
69
+ listenFailed: (reason) => new WebConsoleError("notification_ingress_start_failed", `Web notification ingress failed to listen: ${reason}`, 500),
70
+ noAddress: () => new WebConsoleError("notification_ingress_start_failed", "Web notification ingress did not receive a TCP address.", 500),
71
+ });
72
+ const url = `http://${INGRESS_HOST}:${address.port}${INGRESS_PATH}`;
73
+ const record = {
74
+ schema: INGRESS_SCHEMA,
75
+ pid: process.pid,
76
+ instanceId,
77
+ url,
78
+ token,
79
+ updatedAt: new Date().toISOString(),
80
+ };
81
+ try {
82
+ await publishIngressRecord(service.store.paths.notificationIngress, record);
83
+ }
84
+ catch (error) {
85
+ await close(server);
86
+ throw error;
87
+ }
88
+ return {
89
+ url,
90
+ stop() {
91
+ stopPromise ??= (async () => {
92
+ try {
93
+ await close(server);
94
+ await Promise.allSettled([...active]);
95
+ }
96
+ finally {
97
+ await removeOwnIngressRecord(service.store.paths.notificationIngress, instanceId);
98
+ }
99
+ })();
100
+ return stopPromise;
101
+ },
102
+ };
103
+ }
104
+ function parseNotificationRequest(body) {
105
+ if (typeof body !== "object" || body === null || Array.isArray(body)) {
106
+ throw new WebConsoleError("invalid_notification", "Notification body must be a JSON object.", 400);
107
+ }
108
+ const record = body;
109
+ const allowed = new Set(["sourceId", "triggerKind", "deliveryKey", "text"]);
110
+ if (Object.keys(record).some((key) => !allowed.has(key))) {
111
+ throw new WebConsoleError("invalid_notification", "Notification body contains unsupported fields.", 400);
112
+ }
113
+ const sourceId = normalizedField(record.sourceId, "sourceId", 512);
114
+ const deliveryKey = normalizedField(record.deliveryKey, "deliveryKey", 1_024);
115
+ if (record.triggerKind !== "cron" && record.triggerKind !== "webhook") {
116
+ throw new WebConsoleError("invalid_notification", "triggerKind must be 'cron' or 'webhook'.", 400);
117
+ }
118
+ if (typeof record.text !== "string" || record.text.trim().length === 0) {
119
+ throw new WebConsoleError("invalid_notification", "text is required.", 400);
120
+ }
121
+ if (record.text.length > WEB_MAX_TURN_TEXT_CHARACTERS) {
122
+ throw new WebConsoleError("invalid_notification", "Notification text exceeds the web text limit.", 413);
123
+ }
124
+ return { sourceId, triggerKind: record.triggerKind, deliveryKey, text: record.text };
125
+ }
126
+ function normalizedField(value, name, maxLength) {
127
+ if (typeof value !== "string") {
128
+ throw new WebConsoleError("invalid_notification", `${name} is required.`, 400);
129
+ }
130
+ const normalized = value.trim();
131
+ if (normalized.length === 0 || normalized.length > maxLength) {
132
+ throw new WebConsoleError("invalid_notification", `${name} must contain 1 to ${String(maxLength)} characters.`, 400);
133
+ }
134
+ return normalized;
135
+ }
136
+ async function publishIngressRecord(path, record) {
137
+ const temporary = `${path}.${record.instanceId}.tmp`;
138
+ const handle = await open(temporary, "wx", 0o600);
139
+ try {
140
+ await handle.writeFile(`${JSON.stringify(record)}\n`, "utf8");
141
+ await handle.sync();
142
+ }
143
+ finally {
144
+ await handle.close();
145
+ }
146
+ try {
147
+ await rename(temporary, path);
148
+ await chmod(path, 0o600);
149
+ }
150
+ catch (error) {
151
+ await unlink(temporary).catch(() => undefined);
152
+ throw error;
153
+ }
154
+ }
155
+ async function removeOwnIngressRecord(path, instanceId) {
156
+ const info = await lstat(path).catch(() => undefined);
157
+ if (info === undefined || !info.isFile() || info.isSymbolicLink() || info.size > MAX_INGRESS_RECORD_BYTES)
158
+ return;
159
+ let parsed;
160
+ try {
161
+ parsed = JSON.parse(await readFile(path, "utf8"));
162
+ }
163
+ catch {
164
+ return;
165
+ }
166
+ const record = typeof parsed === "object" && parsed !== null ? parsed : undefined;
167
+ if (record?.instanceId === instanceId)
168
+ await unlink(path).catch(() => undefined);
169
+ }
170
+ //# sourceMappingURL=notification-ingress.js.map