@mercury-fw/channel-http 0.8.4 → 0.10.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @mercury-fw/channel-http
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b2f8cfb: - A confirmation can offer several choices (for example "only this time" / "always for this host"): a plugin tool stages them with the new `stageChoices` of `SessionToolContext`, and gets one token per choice. The person takes one; the others die with it, and they all expire together.
8
+ - The paper-trail note of a group of choices is one, under its first token, and says which choice was taken.
9
+ - `detectPendingConfirmation` returns the `choices` a staging offers; a plain staging is one choice labelled `Conferma`.
10
+ - A confirmed action can hand its result back to the model (`resume` on a choice): instead of showing the person the raw result, the channel resumes the turn and the model answers on it, the result reaching it as the tool result of the call that staged it, never stored in the history.
11
+ - A resumed turn answers the person who confirmed even in a shared space, and the line standing for the confirmation isn't archived as something they said. A result nobody resumes within five minutes (the life of a confirmation token) is forgotten, and a restart loses it.
12
+ - `InboundTurn.resumeAfterConfirm` and `RESUME_AFTER_CONFIRM` (channel contract) carry the resumed turn; `ConfirmOutcome` has a `resume` status.
13
+ - Terminal: every choice is printed on its own line with its token; a confirmation that resumes runs the turn again.
14
+ - Google Chat: the confirmation card has one button per choice; a typed token or a clicked choice that resumes runs the turn again, in the session's queue. The channel now needs channel-types 0.51.0 or later.
15
+ - HTTP: the `pending` event carries `choices`; a token sent to `POST /turn` that resumes streams the model's answer on the same request; `POST /confirm` answers `resume: true`, and `POST /turn { resume: true, conversationId }` runs that turn. The channel now needs channel-types 0.51.0 or later.
16
+
17
+ ## 0.9.0
18
+
19
+ ### Minor Changes
20
+
21
+ - b7438a6: - The assistant remembers how each person wants to be called: they say it in conversation and the model saves it with the new `set_preferred_name` tool. It's a setting kept in `.mercury/profiles.json` on the vault's volume, not a wiki note, and every one-to-one turn tells the model the name.
22
+ - A turn can be an opening (`opening: true`, with no text): the assistant speaks first, but only to someone whose name it doesn't know, introducing itself and asking how to call them. For anyone else, and in a shared space, it says nothing and keeps nothing.
23
+ - `ChannelHostReads.me` says how the caller asked to be called, or `null`.
24
+ - HTTP: `POST /turn` takes `{ opening: true, conversationId }`, and `GET /me` answers `{ name }`. A UI asks for an opening right after a sign-in, when the name is `null`. The channel now needs channel-types 0.49.0 or later.
25
+
3
26
  ## 0.8.4
4
27
 
5
28
  ### Patch Changes
package/README.md CHANGED
@@ -45,10 +45,12 @@ Runs one conversational turn; the reply streams back as Server-Sent Events.
45
45
 
46
46
  | field | type | required | description |
47
47
  |---|---|---|---|
48
- | `text` | string | yes | The user message. A bare confirmation token here confirms a staged action (see the `pending` event) without invoking the model. |
48
+ | `text` | string | yes, unless `opening` or `resume` | The user message. A bare confirmation token here confirms a staged action (see the `pending` event) without invoking the model; when the action hands its result back to the model, the model's answer streams on this same request. |
49
+ | `opening` | `true` | no | The assistant speaks first, before the person writes anything: what a UI asks right after someone signs in, when `GET /me` says the assistant doesn't know their name. It introduces itself and asks how to call them, and saves the answer once they give it. For someone it already knows, the stream is a single `final` with an empty text. Needs `conversationId`; `text` is ignored. |
50
+ | `resume` | `true` | no | The turn a `POST /confirm` answered `resume: true` for: the model answers on what the confirmed action handed back. Needs `conversationId`; `text` is ignored. With nothing to resume, the stream is a single `final` with an empty text. |
49
51
  | `conversationId` | string | no | Opaque, client-owned id that continues one of your conversations: letters, digits, `-` and `_`, up to 128 characters (a UUID fits) once surrounding whitespace is trimmed, `400` otherwise. Sent again, however long after, it continues that conversation, rebuilt from the archive if Mercury had freed it (see `HTTP_SURFACE_RESUME_MAX_CHARS`). Omitted ⇒ a fresh one-off session that can't be continued. |
50
52
 
51
- **Responses**: `200 text/event-stream` (the events below); `400` if `text` is missing, the body isn't JSON, or `conversationId` has other characters than the ones above.
53
+ **Responses**: `200 text/event-stream` (the events below); `400` if `text` is missing, an opening has no `conversationId`, the body isn't JSON, or `conversationId` has other characters than the ones above.
52
54
 
53
55
  Reasoning and answer text arrive as **incremental deltas** — never one finished block — so a UI renders them live; treat the `final` event as the authoritative text. **To cancel** a turn (e.g. a generation that's diverging), close the connection: Mercury aborts the in-flight generation.
54
56
 
@@ -61,7 +63,7 @@ Reasoning and answer text arrive as **incremental deltas** — never one finishe
61
63
  | `tool` | `{ label, detail, toolCallId, name }` | a tool call starts; `name` is the tool the model called, absent when Mercury is saving something to memory |
62
64
  | `tool_finish` | `{ toolCallId, outcome }` | a tool call settles (`outcome`: `success` \| `failed` \| `pending`) |
63
65
  | `text` | `{ chunk }` | an answer-text delta |
64
- | `pending` | `{ command, token }` | a confirm-required action was staged; send `token` back to confirm it — as a later `/turn` `text`, or via `POST /confirm` |
66
+ | `pending` | `{ command, token, choices }` | a confirm-required action was staged; send a token back to confirm it — as a later `/turn` `text`, or via `POST /confirm`. `choices` is every way to answer (`{ token, label }`, in order; one, labelled `Conferma`, for a plain staging): the others die with the one sent |
65
67
  | `login` | `{ service, url }` | the caller isn't logged in to `service` yet, which a plugin acts on as them: show them `url` to open (the model never gets it) |
66
68
  | `final` | `{ text }` | the complete answer (also emitted for a token confirmation, with no model turn) |
67
69
  | `error` | `{ message }` | the turn failed mid-stream |
@@ -85,7 +87,7 @@ A code for linking another of the caller's accounts to them, for the UI to show:
85
87
 
86
88
  ## `POST /confirm`
87
89
 
88
- Explicit alternative to re-sending a token as `/turn` `text`. Body `{ token, conversationId }`; returns `{ ok: true, resolved: true, text }` when the token was a pending confirmation, `{ ok: true, resolved: false }` otherwise. `400` if `token` or `conversationId` is missing, or the id has other characters than `/turn` accepts. Never invokes the model.
90
+ Explicit alternative to re-sending a token as `/turn` `text`. Body `{ token, conversationId }`; returns `{ ok: true, resolved: true, text }` when the token was a pending confirmation, `{ ok: true, resolved: false }` otherwise. When the confirmed action hands its result back to the model, it returns `{ ok: true, resolved: true, resume: true }` instead: open `POST /turn { resume: true, conversationId }` to hear the model's answer. `400` if `token` or `conversationId` is missing, or the id has other characters than `/turn` accepts. Never invokes the model.
89
91
 
90
92
  ## Read-only introspection
91
93
 
@@ -97,6 +99,7 @@ All `GET`, all JSON, all reporting state already held in-process. They need an a
97
99
  | `GET /conversations?limit=<n>` | `{ conversations: [{ conversationId, lastTimestamp, preview }] }` — your conversations, most-recently-active first |
98
100
  | `GET /manifest` | `{ manifest: { coreApiVersion, plugins: [{ name, apiVersion, active, skills, hasBuild, customStatus }], activeClis, skills } }` |
99
101
  | `GET /confirmations` | `{ pending: [{ sessionKey, summary, expiresAt }] }` — your pending confirmations; tokens are deliberately never included |
102
+ | `GET /me` | `{ name }` — how you asked the assistant to call you, `null` when you never said; not there on a core older than this channel |
100
103
  | `GET /tool-log` | `{ entries: [...] }` — the tool calls of your recent turns |
101
104
  | `GET /health` | `{ uptimeSeconds, memory, qdrantReachable, ollamaReachable }` |
102
105
  | `GET /wiki/list` | `{ files: [...] }` — `curated/...` and `personal/...` paths |
@@ -60,6 +60,13 @@ export declare function openApiResponse(corsOrigin?: string): Response;
60
60
  * one-on-one (`multiUser: false`). No `conversationId` means a fresh ephemeral
61
61
  * session. Never throws: a refused caller is a 401, a bad body a 400, a
62
62
  * mid-turn failure an `error` event on the stream.
63
+ *
64
+ * `{ opening: true, conversationId }`, with no `text`, asks the assistant to
65
+ * speak first in that conversation (right after someone signs in): the core
66
+ * decides whether it has anything to say, and a `final` with an empty text
67
+ * means it hasn't. `{ resume: true, conversationId }` runs the turn a
68
+ * `/confirm` answered `resume: true` for; a bare token whose confirmation
69
+ * resumes runs that turn on the same stream.
63
70
  */
64
71
  export declare function handleTurnRequest(req: Request, deps: TurnRequestDeps): Promise<Response>;
65
72
  export type ConfirmRequestDeps = {
@@ -78,7 +85,9 @@ export type ConfirmRequestDeps = {
78
85
  * pending confirmation and its staged action was consumed and run; the `text`
79
86
  * then reports whether that execution succeeded. `resolved: false` means the
80
87
  * token was not a pending confirmation (unknown, expired, already used, or not
81
- * even token-shaped). Never touches the model.
88
+ * even token-shaped). `resume: true` (with no `text`) means the action handed
89
+ * its result back to the model: the client hears its answer by opening
90
+ * `/turn { resume: true, conversationId }`. Never touches the model.
82
91
  */
83
92
  export declare function handleConfirmRequest(req: Request, deps: ConfirmRequestDeps): Promise<Response>;
84
93
  export type LinkRequestDeps = {
package/http-server.ts CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  detectLoginRequired,
27
27
  detectPendingConfirmation,
28
28
  PENDING_CONFIRMATION_NOTE,
29
+ RESUME_AFTER_CONFIRM,
29
30
  type HandleTurn,
30
31
  type TurnSink,
31
32
  type Authenticate,
@@ -147,24 +148,40 @@ export function openApiResponse(corsOrigin = "*"): Response {
147
148
  * one-on-one (`multiUser: false`). No `conversationId` means a fresh ephemeral
148
149
  * session. Never throws: a refused caller is a 401, a bad body a 400, a
149
150
  * mid-turn failure an `error` event on the stream.
151
+ *
152
+ * `{ opening: true, conversationId }`, with no `text`, asks the assistant to
153
+ * speak first in that conversation (right after someone signs in): the core
154
+ * decides whether it has anything to say, and a `final` with an empty text
155
+ * means it hasn't. `{ resume: true, conversationId }` runs the turn a
156
+ * `/confirm` answered `resume: true` for; a bare token whose confirmation
157
+ * resumes runs that turn on the same stream.
150
158
  */
151
159
  export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Promise<Response> {
152
160
  const origin = deps.corsOrigin ?? "*";
153
161
  const cors = corsHeaders(origin);
154
162
  const principal = await caller(req, deps, origin);
155
163
  if (principal instanceof Response) return principal;
156
- let body: { text?: unknown; conversationId?: unknown };
164
+ let body: { text?: unknown; conversationId?: unknown; opening?: unknown; resume?: unknown };
157
165
  try {
158
166
  body = (await req.json()) as typeof body;
159
167
  } catch {
160
168
  return Response.json({ ok: false, error: "body must be JSON" }, { status: 400, headers: cors });
161
169
  }
162
- if (typeof body.text !== "string" || body.text.trim().length === 0) {
163
- return Response.json({ ok: false, error: "missing text" }, { status: 400, headers: cors });
164
- }
165
- const text = body.text;
170
+ const opening = body.opening === true;
166
171
  // A conversation the client names is one it reopens; an ephemeral one never comes back.
167
172
  const named = typeof body.conversationId === "string" && body.conversationId.trim().length > 0;
173
+ // The turn a `/confirm` answered `resume: true` for.
174
+ const resume = body.resume === true;
175
+ if (opening && !named) {
176
+ return Response.json({ ok: false, error: "an opening needs a conversationId" }, { status: 400, headers: cors });
177
+ }
178
+ if (resume && !named) {
179
+ return Response.json({ ok: false, error: "a resumed turn needs a conversationId" }, { status: 400, headers: cors });
180
+ }
181
+ if (!opening && !resume && (typeof body.text !== "string" || body.text.trim().length === 0)) {
182
+ return Response.json({ ok: false, error: "missing text" }, { status: 400, headers: cors });
183
+ }
184
+ const text = opening || resume ? "" : (body.text as string);
168
185
  const conversationId = named ? (body.conversationId as string).trim() : (deps.newSessionKey ?? (() => crypto.randomUUID()))();
169
186
  if (!CONVERSATION_ID.test(conversationId)) {
170
187
  return Response.json({ ok: false, error: "invalid conversationId" }, { status: 400, headers: cors });
@@ -198,12 +215,14 @@ export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Pr
198
215
 
199
216
  // Same deterministic interception as every other channel — a
200
217
  // previously-approved mutation must never depend on the model.
201
- const confirmReply = await deps.confirm(text, sessionKey, principal);
202
- if (confirmReply !== null) {
218
+ const confirmReply = opening || resume ? null : await deps.confirm(text, sessionKey, principal);
219
+ if (confirmReply !== null && confirmReply !== RESUME_AFTER_CONFIRM) {
203
220
  send("final", { text: confirmReply });
204
221
  close();
205
222
  return;
206
223
  }
224
+ // The confirmed action's result goes back to the model: the turn runs again, on this stream.
225
+ const resuming = resume || confirmReply === RESUME_AFTER_CONFIRM;
207
226
 
208
227
  const sink: TurnSink = {
209
228
  onToolStart: (label, detail, toolCallId, toolName) => send("tool", { label, detail, toolCallId, name: toolName }),
@@ -215,7 +234,7 @@ export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Pr
215
234
  onReasoningEnd: (id, failed) => send("reasoning_end", { id, failed }),
216
235
  onStep: (step) => {
217
236
  const pending = detectPendingConfirmation(step);
218
- if (pending) send("pending", { command: pending.summary, token: pending.token });
237
+ if (pending) send("pending", { command: pending.summary, token: pending.token, choices: pending.choices });
219
238
  const login = detectLoginRequired(step);
220
239
  if (login) send("login", { service: login.service, url: login.url });
221
240
  },
@@ -229,13 +248,15 @@ export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Pr
229
248
  {
230
249
  channel: "http",
231
250
  multiUser: false,
232
- text,
251
+ text: resuming ? "" : text,
233
252
  sessionKey,
234
253
  principal,
235
254
  // The caller's id comes from the auth provider, unchecked here.
236
255
  logPrefix: `[http:${forLog(sessionKey)}] `,
237
256
  abortSignal: abort.signal,
238
257
  ...(named ? { resume: deps.resumeMaxChars === undefined ? {} : { maxChars: deps.resumeMaxChars } } : {}),
258
+ ...(opening ? { opening: true as const } : {}),
259
+ ...(resuming ? { resumeAfterConfirm: true as const } : {}),
239
260
  },
240
261
  sink,
241
262
  );
@@ -275,7 +296,9 @@ export type ConfirmRequestDeps = {
275
296
  * pending confirmation and its staged action was consumed and run; the `text`
276
297
  * then reports whether that execution succeeded. `resolved: false` means the
277
298
  * token was not a pending confirmation (unknown, expired, already used, or not
278
- * even token-shaped). Never touches the model.
299
+ * even token-shaped). `resume: true` (with no `text`) means the action handed
300
+ * its result back to the model: the client hears its answer by opening
301
+ * `/turn { resume: true, conversationId }`. Never touches the model.
279
302
  */
280
303
  export async function handleConfirmRequest(req: Request, deps: ConfirmRequestDeps): Promise<Response> {
281
304
  const origin = deps.corsOrigin ?? "*";
@@ -305,6 +328,9 @@ export async function handleConfirmRequest(req: Request, deps: ConfirmRequestDep
305
328
  return Response.json({ ok: true, resolved: true, text: `Confermato ed eseguito: ${JSON.stringify(outcome.data)}` }, { headers: cors });
306
329
  case "failed":
307
330
  return Response.json({ ok: true, resolved: true, text: `Confermato, ma l'esecuzione è fallita: ${outcome.error}` }, { headers: cors });
331
+ case "resume":
332
+ // The result went back to the model: the client opens `/turn { resume: true }` to hear its answer.
333
+ return Response.json({ ok: true, resolved: true, resume: true }, { headers: cors });
308
334
  }
309
335
  }
310
336
 
@@ -449,6 +475,16 @@ export function readRoutes(
449
475
  });
450
476
  }),
451
477
  "/tool-log": route(async (_req, principal) => json({ ok: true, entries: await reads.toolLog(principal) })),
478
+ // Who the caller is to the core, on a core that can say.
479
+ ...(reads.me === undefined
480
+ ? {}
481
+ : {
482
+ "/me": route(async (_req, principal) => {
483
+ // Null when the core no longer admits the caller: still a name, null.
484
+ const me = (await reads.me!(principal)) as { name: string | null } | null;
485
+ return json({ ok: true, name: me?.name ?? null });
486
+ }),
487
+ }),
452
488
  "/health": route(async () => json({ ok: true, ...(await reads.health() as object) })),
453
489
  "/wiki/list": route(async (_req, principal) => json({ ok: true, files: await reads.wikiList(principal) })),
454
490
  "/wiki/read": route(async (req, principal) => {
package/openapi.yaml CHANGED
@@ -71,7 +71,16 @@ info:
71
71
  An irreversible action is never executed on the model's say-so: the turn
72
72
  emits a `pending` event carrying a one-time `token`. The client confirms by
73
73
  sending that token back — either as the `text` of a follow-up `POST /turn`,
74
- or via `POST /confirm`.
74
+ or via `POST /confirm`. An action can offer several `choices` (e.g. "only
75
+ this time" / "always"), each with its own token: the caller sends back one,
76
+ and the others die with it.
77
+
78
+
79
+ Some actions hand their result back to the model instead of to the caller
80
+ (a page read after the caller allowed it, for example). Confirming one by
81
+ `POST /turn` streams the model's answer on that same stream; `POST /confirm`
82
+ answers `resume: true`, and the client then opens
83
+ `POST /turn { resume: true, conversationId }` to hear it.
75
84
  security:
76
85
  - bearerAuth: []
77
86
  servers:
@@ -85,7 +94,14 @@ paths:
85
94
  Runs one model turn and streams the result as `text/event-stream`. If
86
95
  `text` is a pending confirmation token it is resolved deterministically
87
96
  without ever consulting the model. Close the connection to cancel an
88
- in-flight turn.
97
+ in-flight turn. When the confirmation hands its result back to the
98
+ model, the model's answer to it streams instead. With `resume: true`
99
+ and no `text`, the turn is the one a `/confirm` answered `resume: true`
100
+ for. With `opening: true` and no `text`, the assistant
101
+ speaks first in the conversation named (right after someone signs
102
+ in): it does only for someone it doesn't know the name of, asking how
103
+ to call them, and otherwise the stream carries a single `final` with
104
+ an empty text.
89
105
  requestBody:
90
106
  required: true
91
107
  content:
@@ -104,7 +120,8 @@ paths:
104
120
  An SSE stream. Each event is one of the types in `TurnEvent`:
105
121
  `reasoning`/`reasoning_end`, `text` (incremental deltas), `tool`/
106
122
  `tool_finish` (status of a tool call), `pending` (a staged
107
- confirm-required action, with its command and token), `login` (a
123
+ confirm-required action, with its command, its token and the
124
+ choices it offers), `login` (a
108
125
  service the caller has to log in to first, with the link to open),
109
126
  `final` (the
110
127
  authoritative complete answer), and `error`.
@@ -113,7 +130,7 @@ paths:
113
130
  schema:
114
131
  $ref: "#/components/schemas/TurnEvent"
115
132
  "400":
116
- description: Missing/invalid body, or a conversationId with characters outside the allowed ones.
133
+ description: Missing/invalid body, an opening or a resumed turn without a conversationId, or a conversationId with characters outside the allowed ones.
117
134
  content:
118
135
  application/json:
119
136
  schema:
@@ -142,7 +159,9 @@ paths:
142
159
  description: >
143
160
  `resolved: true` (with the reply `text`) when the token was a pending
144
161
  confirmation, `resolved: false` when it was not (unknown, expired, or
145
- already used).
162
+ already used). `resolved: true` with `resume: true` and no `text`
163
+ when the action handed its result back to the model: the answer
164
+ comes from `POST /turn { resume: true, conversationId }`.
146
165
  content:
147
166
  application/json:
148
167
  schema:
@@ -305,6 +324,32 @@ paths:
305
324
  application/json:
306
325
  schema:
307
326
  type: object
327
+ /me:
328
+ get:
329
+ summary: Who the caller is to the assistant
330
+ description: >
331
+ How the caller asked to be called, `null` when they never said: a UI
332
+ asks for an opening turn after a sign-in only then. Not there on an
333
+ older core, which can't say.
334
+ responses:
335
+ "401":
336
+ $ref: "#/components/responses/Unauthorized"
337
+ "403":
338
+ $ref: "#/components/responses/NotAdmitted"
339
+ "503":
340
+ $ref: "#/components/responses/NotAdmitted"
341
+ "200":
342
+ description: The caller's name, or null.
343
+ content:
344
+ application/json:
345
+ schema:
346
+ type: object
347
+ required: [ok, name]
348
+ properties:
349
+ ok:
350
+ type: boolean
351
+ name:
352
+ type: [string, "null"]
308
353
  /tool-log:
309
354
  get:
310
355
  summary: The tool calls made in the caller's recent turns
@@ -519,11 +564,18 @@ components:
519
564
  schemas:
520
565
  TurnRequest:
521
566
  type: object
522
- required: [text]
523
567
  properties:
524
568
  text:
525
569
  type: string
526
- description: The user's message, or a bare confirmation token.
570
+ description: The user's message, or a bare confirmation token. Required, unless `opening` or `resume` is true.
571
+ opening:
572
+ type: boolean
573
+ enum: [true]
574
+ description: The assistant speaks first in `conversationId`, which is then required; `text` is ignored.
575
+ resume:
576
+ type: boolean
577
+ enum: [true]
578
+ description: The turn a `/confirm` answered `resume: true` for, in `conversationId`, which is then required; `text` is ignored.
527
579
  conversationId:
528
580
  type: string
529
581
  pattern: "^[A-Za-z0-9_-]{1,128}$"
@@ -561,7 +613,18 @@ components:
561
613
  description: On `pending` — the staged action's summary.
562
614
  token:
563
615
  type: string
564
- description: On `pending` — the one-time confirmation token.
616
+ description: On `pending` — the one-time confirmation token (the first choice's).
617
+ choices:
618
+ type: array
619
+ description: On `pending` — every way to answer, in order; a plain staging has one, labelled `Conferma`.
620
+ items:
621
+ type: object
622
+ required: [token, label]
623
+ properties:
624
+ token:
625
+ type: string
626
+ label:
627
+ type: string
565
628
  service:
566
629
  type: string
567
630
  description: On `login` — the service the caller has to log in to.
@@ -593,7 +656,11 @@ components:
593
656
  type: boolean
594
657
  text:
595
658
  type: string
596
- description: The confirmation reply, present when `resolved` is true.
659
+ description: The confirmation reply, present when `resolved` is true and `resume` isn't.
660
+ resume:
661
+ type: boolean
662
+ enum: [true]
663
+ description: The action handed its result back to the model; its answer comes from `POST /turn { resume: true, conversationId }`.
597
664
  Message:
598
665
  type: object
599
666
  required: [role, content, timestamp]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercury-fw/channel-http",
3
- "version": "0.8.4",
3
+ "version": "0.10.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,11 +18,11 @@
18
18
  "access": "public"
19
19
  },
20
20
  "peerDependencies": {
21
- "@mercury-fw/channel-types": ">=0.45.0 <1.0.0"
21
+ "@mercury-fw/channel-types": ">=0.51.0 <1.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@mercury-fw/channel-types": "0.48.0",
25
- "@mercury-fw/plugin-types": "0.48.0",
24
+ "@mercury-fw/channel-types": "0.51.0",
25
+ "@mercury-fw/plugin-types": "0.51.0",
26
26
  "@mercury-fw/typescript-config": "*",
27
27
  "@types/bun": "^1.4.2",
28
28
  "typescript": "^6.0.3"