@mercury-fw/channel-http 0.8.3 → 0.9.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 +20 -0
- package/README.md +4 -2
- package/dist/http-provider.d.ts +2 -0
- package/dist/http-server.d.ts +10 -1
- package/http-provider.ts +3 -0
- package/http-server.ts +47 -13
- package/index.ts +1 -0
- package/openapi.yaml +37 -4
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @mercury-fw/channel-http
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
- 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.
|
|
9
|
+
- `ChannelHostReads.me` says how the caller asked to be called, or `null`.
|
|
10
|
+
- 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.
|
|
11
|
+
|
|
12
|
+
## 0.8.4
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- 62bb3a3: - `persona.name` is the assistant's name, Mercury when left out. A name that's blank, longer than 40 characters or on more than one line stops the app at startup.
|
|
17
|
+
- Every text the model reads refers to the name as `{{assistant}}`, and the core puts the persona's name there: the persona's identity and tone, the system prompt, plugin fragments, skills and tool descriptions. The model no longer takes "Mercury" for its own name when the app calls its assistant something else.
|
|
18
|
+
- Where the one acting is the system around the model, the texts say "the system" or "this instance" instead of Mercury: the wiki tools, the cli-engine's errors, the jira skill and its issue lists.
|
|
19
|
+
- Plugins get `assistantName` in their `build()` context, and channels in their runtime context, for texts they build while running. The login messages, the linking reply and the HTTP login page name the assistant that way.
|
|
20
|
+
- The tools a person runs as the assistant itself are `<tool>AsSelf`, previously `<tool>AsMercury`. The `mercury.act-as-self` roles are unchanged.
|
|
21
|
+
- `mfw create` writes the assistant's name as `persona.name`, with `persona/identity.md` and `persona/tone.md` referring to it. An existing app keeps working as it is: to rename its assistant everywhere, add `name` to its persona and write `{{assistant}}` in its persona files.
|
|
22
|
+
|
|
3
23
|
## 0.8.3
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -45,10 +45,11 @@ 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` | The user message. A bare confirmation token here confirms a staged action (see the `pending` event) without invoking the model. |
|
|
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. |
|
|
49
50
|
| `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
51
|
|
|
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.
|
|
52
|
+
**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
53
|
|
|
53
54
|
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
55
|
|
|
@@ -97,6 +98,7 @@ All `GET`, all JSON, all reporting state already held in-process. They need an a
|
|
|
97
98
|
| `GET /conversations?limit=<n>` | `{ conversations: [{ conversationId, lastTimestamp, preview }] }` — your conversations, most-recently-active first |
|
|
98
99
|
| `GET /manifest` | `{ manifest: { coreApiVersion, plugins: [{ name, apiVersion, active, skills, hasBuild, customStatus }], activeClis, skills } }` |
|
|
99
100
|
| `GET /confirmations` | `{ pending: [{ sessionKey, summary, expiresAt }] }` — your pending confirmations; tokens are deliberately never included |
|
|
101
|
+
| `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
102
|
| `GET /tool-log` | `{ entries: [...] }` — the tool calls of your recent turns |
|
|
101
103
|
| `GET /health` | `{ uptimeSeconds, memory, qdrantReachable, ollamaReachable }` |
|
|
102
104
|
| `GET /wiki/list` | `{ files: [...] }` — `curated/...` and `personal/...` paths |
|
package/dist/http-provider.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export type HttpProviderDeps = {
|
|
|
36
36
|
linking?: ChannelLinking;
|
|
37
37
|
/** The surface's budget for a reopened conversation (`HTTP_SURFACE_RESUME_MAX_CHARS`). */
|
|
38
38
|
resumeMaxChars?: number;
|
|
39
|
+
/** What the pages people read call the assistant. */
|
|
40
|
+
assistantName?: string;
|
|
39
41
|
};
|
|
40
42
|
export declare function createHttpProvider(deps: HttpProviderDeps): Provider & {
|
|
41
43
|
stop(): Promise<void>;
|
package/dist/http-server.d.ts
CHANGED
|
@@ -60,6 +60,11 @@ 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.
|
|
63
68
|
*/
|
|
64
69
|
export declare function handleTurnRequest(req: Request, deps: TurnRequestDeps): Promise<Response>;
|
|
65
70
|
export type ConfirmRequestDeps = {
|
|
@@ -102,10 +107,12 @@ export type CompleteLoginFn = (state: string, code: string) => Promise<LoginOutc
|
|
|
102
107
|
* in, with `code` and `state` (or `error`, when they refused). Public: the
|
|
103
108
|
* person's browser carries no token here, and the single-use `state` the core
|
|
104
109
|
* issued for them is what ties the request to their login. Answers with a
|
|
105
|
-
* plain page telling them how it went
|
|
110
|
+
* plain page telling them how it went, naming the assistant as the app does
|
|
111
|
+
* (`Mercury` from an older core that doesn't say).
|
|
106
112
|
*/
|
|
107
113
|
export declare function handleLoginCallback(req: Request, deps: {
|
|
108
114
|
complete: CompleteLoginFn;
|
|
115
|
+
assistantName?: string;
|
|
109
116
|
}): Promise<Response>;
|
|
110
117
|
/** A single read route: its `GET` handler plus the shared `OPTIONS` preflight. */
|
|
111
118
|
type ReadRoute = {
|
|
@@ -133,6 +140,8 @@ export type HttpServerDeps = TurnRequestDeps & {
|
|
|
133
140
|
completeLogin?: CompleteLoginFn;
|
|
134
141
|
/** Linking another account to the caller; `/link` is mounted only with it. */
|
|
135
142
|
linking?: ChannelLinking;
|
|
143
|
+
/** What the pages people read call the assistant. */
|
|
144
|
+
assistantName?: string;
|
|
136
145
|
};
|
|
137
146
|
/** Starts the HTTP surface: `POST /turn` (4a) plus the read-only routes (4b)
|
|
138
147
|
* when `reads` is supplied. */
|
package/http-provider.ts
CHANGED
|
@@ -33,6 +33,8 @@ export type HttpProviderDeps = {
|
|
|
33
33
|
linking?: ChannelLinking;
|
|
34
34
|
/** The surface's budget for a reopened conversation (`HTTP_SURFACE_RESUME_MAX_CHARS`). */
|
|
35
35
|
resumeMaxChars?: number;
|
|
36
|
+
/** What the pages people read call the assistant. */
|
|
37
|
+
assistantName?: string;
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
export function createHttpProvider(deps: HttpProviderDeps): Provider & { stop(): Promise<void> } {
|
|
@@ -51,6 +53,7 @@ export function createHttpProvider(deps: HttpProviderDeps): Provider & { stop():
|
|
|
51
53
|
completeLogin: deps.logins?.complete,
|
|
52
54
|
...(deps.linking === undefined ? {} : { linking: deps.linking }),
|
|
53
55
|
...(deps.resumeMaxChars === undefined ? {} : { resumeMaxChars: deps.resumeMaxChars }),
|
|
56
|
+
...(deps.assistantName === undefined ? {} : { assistantName: deps.assistantName }),
|
|
54
57
|
});
|
|
55
58
|
deps.logins?.accept(deps.logins.callbackUrl);
|
|
56
59
|
},
|
package/http-server.ts
CHANGED
|
@@ -147,24 +147,33 @@ export function openApiResponse(corsOrigin = "*"): Response {
|
|
|
147
147
|
* one-on-one (`multiUser: false`). No `conversationId` means a fresh ephemeral
|
|
148
148
|
* session. Never throws: a refused caller is a 401, a bad body a 400, a
|
|
149
149
|
* mid-turn failure an `error` event on the stream.
|
|
150
|
+
*
|
|
151
|
+
* `{ opening: true, conversationId }`, with no `text`, asks the assistant to
|
|
152
|
+
* speak first in that conversation (right after someone signs in): the core
|
|
153
|
+
* decides whether it has anything to say, and a `final` with an empty text
|
|
154
|
+
* means it hasn't.
|
|
150
155
|
*/
|
|
151
156
|
export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Promise<Response> {
|
|
152
157
|
const origin = deps.corsOrigin ?? "*";
|
|
153
158
|
const cors = corsHeaders(origin);
|
|
154
159
|
const principal = await caller(req, deps, origin);
|
|
155
160
|
if (principal instanceof Response) return principal;
|
|
156
|
-
let body: { text?: unknown; conversationId?: unknown };
|
|
161
|
+
let body: { text?: unknown; conversationId?: unknown; opening?: unknown };
|
|
157
162
|
try {
|
|
158
163
|
body = (await req.json()) as typeof body;
|
|
159
164
|
} catch {
|
|
160
165
|
return Response.json({ ok: false, error: "body must be JSON" }, { status: 400, headers: cors });
|
|
161
166
|
}
|
|
162
|
-
|
|
163
|
-
return Response.json({ ok: false, error: "missing text" }, { status: 400, headers: cors });
|
|
164
|
-
}
|
|
165
|
-
const text = body.text;
|
|
167
|
+
const opening = body.opening === true;
|
|
166
168
|
// A conversation the client names is one it reopens; an ephemeral one never comes back.
|
|
167
169
|
const named = typeof body.conversationId === "string" && body.conversationId.trim().length > 0;
|
|
170
|
+
if (opening && !named) {
|
|
171
|
+
return Response.json({ ok: false, error: "an opening needs a conversationId" }, { status: 400, headers: cors });
|
|
172
|
+
}
|
|
173
|
+
if (!opening && (typeof body.text !== "string" || body.text.trim().length === 0)) {
|
|
174
|
+
return Response.json({ ok: false, error: "missing text" }, { status: 400, headers: cors });
|
|
175
|
+
}
|
|
176
|
+
const text = opening ? "" : (body.text as string);
|
|
168
177
|
const conversationId = named ? (body.conversationId as string).trim() : (deps.newSessionKey ?? (() => crypto.randomUUID()))();
|
|
169
178
|
if (!CONVERSATION_ID.test(conversationId)) {
|
|
170
179
|
return Response.json({ ok: false, error: "invalid conversationId" }, { status: 400, headers: cors });
|
|
@@ -198,7 +207,7 @@ export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Pr
|
|
|
198
207
|
|
|
199
208
|
// Same deterministic interception as every other channel — a
|
|
200
209
|
// previously-approved mutation must never depend on the model.
|
|
201
|
-
const confirmReply = await deps.confirm(text, sessionKey, principal);
|
|
210
|
+
const confirmReply = opening ? null : await deps.confirm(text, sessionKey, principal);
|
|
202
211
|
if (confirmReply !== null) {
|
|
203
212
|
send("final", { text: confirmReply });
|
|
204
213
|
close();
|
|
@@ -236,6 +245,7 @@ export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Pr
|
|
|
236
245
|
logPrefix: `[http:${forLog(sessionKey)}] `,
|
|
237
246
|
abortSignal: abort.signal,
|
|
238
247
|
...(named ? { resume: deps.resumeMaxChars === undefined ? {} : { maxChars: deps.resumeMaxChars } } : {}),
|
|
248
|
+
...(opening ? { opening: true as const } : {}),
|
|
239
249
|
},
|
|
240
250
|
sink,
|
|
241
251
|
);
|
|
@@ -352,18 +362,23 @@ function loginPage(text: string, status: number): Response {
|
|
|
352
362
|
* in, with `code` and `state` (or `error`, when they refused). Public: the
|
|
353
363
|
* person's browser carries no token here, and the single-use `state` the core
|
|
354
364
|
* issued for them is what ties the request to their login. Answers with a
|
|
355
|
-
* plain page telling them how it went
|
|
365
|
+
* plain page telling them how it went, naming the assistant as the app does
|
|
366
|
+
* (`Mercury` from an older core that doesn't say).
|
|
356
367
|
*/
|
|
357
|
-
export async function handleLoginCallback(
|
|
368
|
+
export async function handleLoginCallback(
|
|
369
|
+
req: Request,
|
|
370
|
+
deps: { complete: CompleteLoginFn; assistantName?: string },
|
|
371
|
+
): Promise<Response> {
|
|
372
|
+
const assistant = deps.assistantName ?? "Mercury";
|
|
358
373
|
const params = new URL(req.url).searchParams;
|
|
359
374
|
const error = params.get("error");
|
|
360
375
|
if (error !== null) {
|
|
361
376
|
const reason = error.replace(/[^A-Za-z0-9_.-]/g, "").slice(0, 64);
|
|
362
|
-
return loginPage(`The login didn't happen (${reason}). Ask
|
|
377
|
+
return loginPage(`The login didn't happen (${reason}). Ask ${assistant} again for a new link.`, 400);
|
|
363
378
|
}
|
|
364
379
|
const code = params.get("code");
|
|
365
380
|
const state = params.get("state");
|
|
366
|
-
if (!code || !state) return loginPage(
|
|
381
|
+
if (!code || !state) return loginPage(`The login link came back without a code or a state. Ask ${assistant} again for a new link.`, 400);
|
|
367
382
|
const outcome = await deps.complete(state, code);
|
|
368
383
|
return outcome.ok
|
|
369
384
|
? loginPage(`You're logged in to ${outcome.service}. Go back to the conversation and ask again.`, 200)
|
|
@@ -371,8 +386,15 @@ export async function handleLoginCallback(req: Request, deps: { complete: Comple
|
|
|
371
386
|
}
|
|
372
387
|
|
|
373
388
|
/** The login callback route, mounted when the server can complete logins. */
|
|
374
|
-
function loginRoutes(
|
|
375
|
-
|
|
389
|
+
function loginRoutes(
|
|
390
|
+
complete: CompleteLoginFn,
|
|
391
|
+
assistantName: string | undefined,
|
|
392
|
+
): Record<string, { GET: (req: Request) => Promise<Response> }> {
|
|
393
|
+
return {
|
|
394
|
+
"/login/callback": {
|
|
395
|
+
GET: (req) => handleLoginCallback(req, { complete, ...(assistantName === undefined ? {} : { assistantName }) }),
|
|
396
|
+
},
|
|
397
|
+
};
|
|
376
398
|
}
|
|
377
399
|
|
|
378
400
|
/** A single read route: its `GET` handler plus the shared `OPTIONS` preflight. */
|
|
@@ -437,6 +459,16 @@ export function readRoutes(
|
|
|
437
459
|
});
|
|
438
460
|
}),
|
|
439
461
|
"/tool-log": route(async (_req, principal) => json({ ok: true, entries: await reads.toolLog(principal) })),
|
|
462
|
+
// Who the caller is to the core, on a core that can say.
|
|
463
|
+
...(reads.me === undefined
|
|
464
|
+
? {}
|
|
465
|
+
: {
|
|
466
|
+
"/me": route(async (_req, principal) => {
|
|
467
|
+
// Null when the core no longer admits the caller: still a name, null.
|
|
468
|
+
const me = (await reads.me!(principal)) as { name: string | null } | null;
|
|
469
|
+
return json({ ok: true, name: me?.name ?? null });
|
|
470
|
+
}),
|
|
471
|
+
}),
|
|
440
472
|
"/health": route(async () => json({ ok: true, ...(await reads.health() as object) })),
|
|
441
473
|
"/wiki/list": route(async (_req, principal) => json({ ok: true, files: await reads.wikiList(principal) })),
|
|
442
474
|
"/wiki/read": route(async (req, principal) => {
|
|
@@ -471,6 +503,8 @@ export type HttpServerDeps = TurnRequestDeps & {
|
|
|
471
503
|
completeLogin?: CompleteLoginFn;
|
|
472
504
|
/** Linking another account to the caller; `/link` is mounted only with it. */
|
|
473
505
|
linking?: ChannelLinking;
|
|
506
|
+
/** What the pages people read call the assistant. */
|
|
507
|
+
assistantName?: string;
|
|
474
508
|
};
|
|
475
509
|
|
|
476
510
|
/** Starts the HTTP surface: `POST /turn` (4a) plus the read-only routes (4b)
|
|
@@ -502,7 +536,7 @@ export function startHttpServer(deps: HttpServerDeps): ReturnType<typeof Bun.ser
|
|
|
502
536
|
},
|
|
503
537
|
"/openapi.yaml": { GET: () => openApiResponse(origin), OPTIONS: () => preflight(origin) },
|
|
504
538
|
...(deps.reads ? readRoutes(deps.reads, deps.authenticate, origin, deps.admit) : {}),
|
|
505
|
-
...(deps.completeLogin ? loginRoutes(deps.completeLogin) : {}),
|
|
539
|
+
...(deps.completeLogin ? loginRoutes(deps.completeLogin, deps.assistantName) : {}),
|
|
506
540
|
...(deps.linking ? linkRoutes({ ...deps, start: deps.linking.start, corsOrigin: origin }) : {}),
|
|
507
541
|
},
|
|
508
542
|
error: (err) =>
|
package/index.ts
CHANGED
|
@@ -46,6 +46,7 @@ export const httpChannel: ChannelPlugin = {
|
|
|
46
46
|
admit: ctx.admit,
|
|
47
47
|
...(ctx.linking === undefined ? {} : { linking: ctx.linking }),
|
|
48
48
|
...(resumeMaxChars === undefined ? {} : { resumeMaxChars }),
|
|
49
|
+
...(ctx.assistantName === undefined ? {} : { assistantName: ctx.assistantName }),
|
|
49
50
|
...(logins ? { logins: { callbackUrl: `${publicUrl}/login/callback`, accept: logins.accept, complete: logins.complete } } : {}),
|
|
50
51
|
});
|
|
51
52
|
},
|
package/openapi.yaml
CHANGED
|
@@ -85,7 +85,11 @@ paths:
|
|
|
85
85
|
Runs one model turn and streams the result as `text/event-stream`. If
|
|
86
86
|
`text` is a pending confirmation token it is resolved deterministically
|
|
87
87
|
without ever consulting the model. Close the connection to cancel an
|
|
88
|
-
in-flight turn.
|
|
88
|
+
in-flight turn. With `opening: true` and no `text`, the assistant
|
|
89
|
+
speaks first in the conversation named (right after someone signs
|
|
90
|
+
in): it does only for someone it doesn't know the name of, asking how
|
|
91
|
+
to call them, and otherwise the stream carries a single `final` with
|
|
92
|
+
an empty text.
|
|
89
93
|
requestBody:
|
|
90
94
|
required: true
|
|
91
95
|
content:
|
|
@@ -113,7 +117,7 @@ paths:
|
|
|
113
117
|
schema:
|
|
114
118
|
$ref: "#/components/schemas/TurnEvent"
|
|
115
119
|
"400":
|
|
116
|
-
description: Missing/invalid body, or a conversationId with characters outside the allowed ones.
|
|
120
|
+
description: Missing/invalid body, an opening without a conversationId, or a conversationId with characters outside the allowed ones.
|
|
117
121
|
content:
|
|
118
122
|
application/json:
|
|
119
123
|
schema:
|
|
@@ -305,6 +309,32 @@ paths:
|
|
|
305
309
|
application/json:
|
|
306
310
|
schema:
|
|
307
311
|
type: object
|
|
312
|
+
/me:
|
|
313
|
+
get:
|
|
314
|
+
summary: Who the caller is to the assistant
|
|
315
|
+
description: >
|
|
316
|
+
How the caller asked to be called, `null` when they never said: a UI
|
|
317
|
+
asks for an opening turn after a sign-in only then. Not there on an
|
|
318
|
+
older core, which can't say.
|
|
319
|
+
responses:
|
|
320
|
+
"401":
|
|
321
|
+
$ref: "#/components/responses/Unauthorized"
|
|
322
|
+
"403":
|
|
323
|
+
$ref: "#/components/responses/NotAdmitted"
|
|
324
|
+
"503":
|
|
325
|
+
$ref: "#/components/responses/NotAdmitted"
|
|
326
|
+
"200":
|
|
327
|
+
description: The caller's name, or null.
|
|
328
|
+
content:
|
|
329
|
+
application/json:
|
|
330
|
+
schema:
|
|
331
|
+
type: object
|
|
332
|
+
required: [ok, name]
|
|
333
|
+
properties:
|
|
334
|
+
ok:
|
|
335
|
+
type: boolean
|
|
336
|
+
name:
|
|
337
|
+
type: [string, "null"]
|
|
308
338
|
/tool-log:
|
|
309
339
|
get:
|
|
310
340
|
summary: The tool calls made in the caller's recent turns
|
|
@@ -519,11 +549,14 @@ components:
|
|
|
519
549
|
schemas:
|
|
520
550
|
TurnRequest:
|
|
521
551
|
type: object
|
|
522
|
-
required: [text]
|
|
523
552
|
properties:
|
|
524
553
|
text:
|
|
525
554
|
type: string
|
|
526
|
-
description: The user's message, or a bare confirmation token.
|
|
555
|
+
description: The user's message, or a bare confirmation token. Required, unless `opening` is true.
|
|
556
|
+
opening:
|
|
557
|
+
type: boolean
|
|
558
|
+
enum: [true]
|
|
559
|
+
description: The assistant speaks first in `conversationId`, which is then required; `text` is ignored.
|
|
527
560
|
conversationId:
|
|
528
561
|
type: string
|
|
529
562
|
pattern: "^[A-Za-z0-9_-]{1,128}$"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mercury-fw/channel-http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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.
|
|
21
|
+
"@mercury-fw/channel-types": ">=0.49.0 <1.0.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@mercury-fw/channel-types": "0.
|
|
25
|
-
"@mercury-fw/plugin-types": "0.
|
|
24
|
+
"@mercury-fw/channel-types": "0.49.0",
|
|
25
|
+
"@mercury-fw/plugin-types": "0.49.0",
|
|
26
26
|
"@mercury-fw/typescript-config": "*",
|
|
27
27
|
"@types/bun": "^1.4.2",
|
|
28
28
|
"typescript": "^6.0.3"
|