@mercury-fw/channel-http 0.3.0 → 0.4.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 +18 -0
- package/README.md +8 -8
- package/dist/http-server.d.ts +7 -4
- package/http-server.ts +39 -20
- package/index.ts +1 -1
- package/openapi.yaml +36 -17
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @mercury-fw/channel-http
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 521db42: Isolation between the people an agent talks to.
|
|
8
|
+
|
|
9
|
+
- The core keeps one identity per person, `<provider>:<id>`, so two providers issuing the same id are two people. Memory, the conversation archive, the tool log and pending confirmations are keyed on it.
|
|
10
|
+
- The wiki has a common area (`curated/`) everyone reads and an area per person (`users/<key>/`), which the model sees as `personal/`. Listing, reading and grepping reach the common area and the caller's own area, nothing else.
|
|
11
|
+
- Breaking: the model's `write_file` writes only under `personal/notes/`. The new `promote_note` tool copies one of the person's notes into `curated/`, and only once they confirm it with the token.
|
|
12
|
+
- A confirmation can only be confirmed by whoever staged it, in the same session. Its note is written once, in the person's area: a staged note no longer stays "pending" after it's resolved, for ids such as Google Chat's `users/<n>`.
|
|
13
|
+
- Breaking, `@mercury-fw/channel-types`: channel contract 3. `confirm` and `resolveConfirmation` take the caller's `Principal`, and so does every read getter except `manifest` and `health`. Channels written for contract 2 are refused.
|
|
14
|
+
- `@mercury-fw/confirm-engine`: the store's `stage`, `take` and `pending` take the owner's key.
|
|
15
|
+
- Breaking, `@mercury-fw/channel-http`: every read route returns only the caller's data. `/conversation?id=` takes one of the caller's `conversationId`s and `/conversations` lists them by `conversationId`. `/wiki/read` answers `404` outside what the caller can see, and `/memory/scroll` answers `400` for a collection that isn't kept per person.
|
|
16
|
+
- `@mercury-fw/channel-google-chat`: on channel contract 3.
|
|
17
|
+
- At startup, an instance's existing data moves to the new layout. Google Chat and terminal notes go into their areas, and Qdrant's ids go onto the new keys. Ids that don't say which provider they came from are left in place and logged.
|
|
18
|
+
- `mfw vault read` refuses a path outside the vault.
|
|
19
|
+
- The admin panel's wiki box edits the common area with the operator's tools (list, read, grep, `write_curated`), the same the nightly review uses.
|
|
20
|
+
|
|
3
21
|
## 0.3.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -76,20 +76,20 @@ Explicit alternative to re-sending a token as `/turn` `text`. Body `{ token, con
|
|
|
76
76
|
|
|
77
77
|
## Read-only introspection
|
|
78
78
|
|
|
79
|
-
All `GET`, all JSON, all reporting state already held in-process. They need an authenticated caller,
|
|
79
|
+
All `GET`, all JSON, all reporting state already held in-process. They need an authenticated caller and show only what belongs to them: their conversations, confirmations, tool calls and memory, and the wiki as they see it (the team's `curated/` plus their own notes as `personal/`). Someone else's data doesn't exist as far as a caller can tell; only `/manifest` and `/health` are the same for everyone.
|
|
80
80
|
|
|
81
81
|
| endpoint | `data` on success |
|
|
82
82
|
|---|---|
|
|
83
|
-
| `GET /conversation?id=<
|
|
84
|
-
| `GET /conversations?limit=<n>` | `{ conversations: [{
|
|
83
|
+
| `GET /conversation?id=<conversationId>&limit=<n>&offset=<cursor>` | `{ messages: [{ role, content, timestamp }], nextOffset }` — one of your conversations, its durable transcript in order (empty for an id that isn't yours); `400` if `id` is missing or has other characters than `/turn` accepts |
|
|
84
|
+
| `GET /conversations?limit=<n>` | `{ conversations: [{ conversationId, lastTimestamp, preview }] }` — your conversations, most-recently-active first |
|
|
85
85
|
| `GET /manifest` | `{ manifest: { coreApiVersion, plugins: [{ name, apiVersion, active, skills, hasBuild, customStatus }], activeClis, skills } }` |
|
|
86
|
-
| `GET /confirmations` | `{ pending: [{ sessionKey,
|
|
87
|
-
| `GET /tool-log` | `{ entries: [...] }` |
|
|
86
|
+
| `GET /confirmations` | `{ pending: [{ sessionKey, summary, expiresAt }] }` — your pending confirmations; tokens are deliberately never included |
|
|
87
|
+
| `GET /tool-log` | `{ entries: [...] }` — the tool calls of your recent turns |
|
|
88
88
|
| `GET /health` | `{ uptimeSeconds, memory, qdrantReachable, ollamaReachable }` |
|
|
89
|
-
| `GET /wiki/list` | `{ files: [...] }` |
|
|
90
|
-
| `GET /wiki/read?path=<
|
|
89
|
+
| `GET /wiki/list` | `{ files: [...] }` — `curated/...` and `personal/...` paths |
|
|
90
|
+
| `GET /wiki/read?path=<path>` | `{ content }` — a path as `/wiki/list` gives it; `400` if `path` is missing, `404` if it isn't among yours |
|
|
91
91
|
| `GET /wiki/grep?pattern=<regex>` | `{ matches: [...] }` — `400` if `pattern` is missing |
|
|
92
|
-
| `GET /memory/scroll?collection=<name>&limit=<n>&offset=<cursor>` | one page of
|
|
92
|
+
| `GET /memory/scroll?collection=<name>&limit=<n>&offset=<cursor>` | one page of your points in a collection kept per person (verbatim archive, episodic memory, semantic facts); `400` for any other collection |
|
|
93
93
|
| `GET /openapi.yaml` | the OpenAPI document for this surface (`text/yaml`) |
|
|
94
94
|
|
|
95
95
|
Conversation history and the conversation list are backed by the durable verbatim archive; they degrade to empty when the vector store is unreachable.
|
package/dist/http-server.d.ts
CHANGED
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
* Confirm resolution is injected too (`confirm`/`resolveConfirmation`), so this
|
|
20
20
|
* package never imports the app.
|
|
21
21
|
*/
|
|
22
|
-
import { type HandleTurn, type Authenticate, type ChannelHostReads, type ConfirmOutcome } from "@mercury-fw/channel-types";
|
|
22
|
+
import { type HandleTurn, type Authenticate, type ChannelHostReads, type ConfirmOutcome, type Principal } from "@mercury-fw/channel-types";
|
|
23
23
|
/** Resolves a bare confirmation token to a reply string, or `null` if the input isn't a token. Injected by the core (`ctx.confirm`). */
|
|
24
|
-
export type ConfirmFn = (token: string, sessionKey: string,
|
|
24
|
+
export type ConfirmFn = (token: string, sessionKey: string, principal: Principal) => Promise<string | null>;
|
|
25
25
|
/** The structured sibling of {@link ConfirmFn}, for the `/confirm` `resolved` flag. Injected by the core (`ctx.resolveConfirmation`). */
|
|
26
|
-
export type ResolveConfirmationFn = (token: string, sessionKey: string,
|
|
26
|
+
export type ResolveConfirmationFn = (token: string, sessionKey: string, principal: Principal) => Promise<ConfirmOutcome>;
|
|
27
27
|
export type TurnRequestDeps = {
|
|
28
28
|
handleTurn: HandleTurn;
|
|
29
29
|
/** Who is calling, injected from the app's auth provider; `null` = refused. */
|
|
@@ -81,7 +81,10 @@ type ReadRoute = {
|
|
|
81
81
|
* Builds the read-only routes, each carrying CORS headers on its `GET` and a
|
|
82
82
|
* shared `OPTIONS` preflight so a browser UI on `corsOrigin` (default `*`) can
|
|
83
83
|
* reach them. Every `GET` asks `authenticate` first and answers 401 without
|
|
84
|
-
* calling its getter when nobody is calling; the preflight doesn't ask.
|
|
84
|
+
* calling its getter when nobody is calling; the preflight doesn't ask. The
|
|
85
|
+
* caller's principal goes to every per-person getter, so the core answers with
|
|
86
|
+
* their data only; a conversation is named by its conversationId, as on
|
|
87
|
+
* `/turn`, and its session key is built from the caller.
|
|
85
88
|
* `json` JSON-encodes with the CORS headers merged in; `badRequest` does the
|
|
86
89
|
* same for a 400.
|
|
87
90
|
*/
|
package/http-server.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type Authenticate,
|
|
28
28
|
type ChannelHostReads,
|
|
29
29
|
type ConfirmOutcome,
|
|
30
|
+
type Principal,
|
|
30
31
|
} from "@mercury-fw/channel-types";
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -43,9 +44,9 @@ function forLog(text: string): string {
|
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/** Resolves a bare confirmation token to a reply string, or `null` if the input isn't a token. Injected by the core (`ctx.confirm`). */
|
|
46
|
-
export type ConfirmFn = (token: string, sessionKey: string,
|
|
47
|
+
export type ConfirmFn = (token: string, sessionKey: string, principal: Principal) => Promise<string | null>;
|
|
47
48
|
/** The structured sibling of {@link ConfirmFn}, for the `/confirm` `resolved` flag. Injected by the core (`ctx.resolveConfirmation`). */
|
|
48
|
-
export type ResolveConfirmationFn = (token: string, sessionKey: string,
|
|
49
|
+
export type ResolveConfirmationFn = (token: string, sessionKey: string, principal: Principal) => Promise<ConfirmOutcome>;
|
|
49
50
|
|
|
50
51
|
export type TurnRequestDeps = {
|
|
51
52
|
handleTurn: HandleTurn;
|
|
@@ -169,7 +170,7 @@ export async function handleTurnRequest(req: Request, deps: TurnRequestDeps): Pr
|
|
|
169
170
|
|
|
170
171
|
// Same deterministic interception as every other channel — a
|
|
171
172
|
// previously-approved mutation must never depend on the model.
|
|
172
|
-
const confirmReply = await deps.confirm(text, sessionKey, principal
|
|
173
|
+
const confirmReply = await deps.confirm(text, sessionKey, principal);
|
|
173
174
|
if (confirmReply !== null) {
|
|
174
175
|
send("final", { text: confirmReply });
|
|
175
176
|
close();
|
|
@@ -263,7 +264,7 @@ export async function handleConfirmRequest(req: Request, deps: ConfirmRequestDep
|
|
|
263
264
|
if (!CONVERSATION_ID.test(conversationId)) {
|
|
264
265
|
return Response.json({ ok: false, error: "invalid conversationId" }, { status: 400, headers: cors });
|
|
265
266
|
}
|
|
266
|
-
const outcome = await deps.resolveConfirmation(token, `${principal.id}:${conversationId}`, principal
|
|
267
|
+
const outcome = await deps.resolveConfirmation(token, `${principal.id}:${conversationId}`, principal);
|
|
267
268
|
switch (outcome.status) {
|
|
268
269
|
case "not-a-token":
|
|
269
270
|
case "not-found":
|
|
@@ -285,7 +286,10 @@ type ReadRoute = {
|
|
|
285
286
|
* Builds the read-only routes, each carrying CORS headers on its `GET` and a
|
|
286
287
|
* shared `OPTIONS` preflight so a browser UI on `corsOrigin` (default `*`) can
|
|
287
288
|
* reach them. Every `GET` asks `authenticate` first and answers 401 without
|
|
288
|
-
* calling its getter when nobody is calling; the preflight doesn't ask.
|
|
289
|
+
* calling its getter when nobody is calling; the preflight doesn't ask. The
|
|
290
|
+
* caller's principal goes to every per-person getter, so the core answers with
|
|
291
|
+
* their data only; a conversation is named by its conversationId, as on
|
|
292
|
+
* `/turn`, and its session key is built from the caller.
|
|
289
293
|
* `json` JSON-encodes with the CORS headers merged in; `badRequest` does the
|
|
290
294
|
* same for a 400.
|
|
291
295
|
*/
|
|
@@ -296,45 +300,60 @@ export function readRoutes(reads: ChannelHostReads, authenticate: Authenticate,
|
|
|
296
300
|
const badRequest = (message: string): Response => json({ ok: false, error: message }, 400);
|
|
297
301
|
const requireParam = (req: Request, name: string): string | null => new URL(req.url).searchParams.get(name);
|
|
298
302
|
const options = () => preflight(corsOrigin);
|
|
299
|
-
const route = (GET:
|
|
300
|
-
GET: async (req) =>
|
|
303
|
+
const route = (GET: (req: Request, principal: Principal) => Response | Promise<Response>): ReadRoute => ({
|
|
304
|
+
GET: async (req) => {
|
|
305
|
+
const principal = await authenticate(req);
|
|
306
|
+
return principal === null ? unauthorized(corsOrigin) : GET(req, principal);
|
|
307
|
+
},
|
|
301
308
|
OPTIONS: options,
|
|
302
309
|
});
|
|
303
310
|
return {
|
|
304
311
|
"/manifest": route(() => json({ ok: true, manifest: reads.manifest() })),
|
|
305
|
-
"/confirmations": route(() => json({ ok: true, pending: reads.pendingConfirmations() })),
|
|
306
|
-
"/conversation": route(async (req) => {
|
|
312
|
+
"/confirmations": route((_req, principal) => json({ ok: true, pending: reads.pendingConfirmations(principal) })),
|
|
313
|
+
"/conversation": route(async (req, principal) => {
|
|
307
314
|
const url = new URL(req.url);
|
|
308
315
|
const id = url.searchParams.get("id");
|
|
309
316
|
if (!id) return badRequest("missing ?id");
|
|
317
|
+
if (!CONVERSATION_ID.test(id)) return badRequest("invalid conversationId");
|
|
310
318
|
const limit = Number(url.searchParams.get("limit") ?? "200");
|
|
311
319
|
const offset = url.searchParams.get("offset") ?? undefined;
|
|
312
|
-
return json({ ok: true, ...(await reads.conversation(
|
|
320
|
+
return json({ ok: true, ...(await reads.conversation(principal, `${principal.id}:${id}`, limit, offset) as object) });
|
|
313
321
|
}),
|
|
314
|
-
"/conversations": route(async (req) => {
|
|
322
|
+
"/conversations": route(async (req, principal) => {
|
|
315
323
|
const limit = Number(new URL(req.url).searchParams.get("limit") ?? "50");
|
|
316
|
-
|
|
324
|
+
const { conversations } = (await reads.conversations(principal, limit)) as {
|
|
325
|
+
conversations: Array<{ sessionKey: string; lastTimestamp: string; preview: string }>;
|
|
326
|
+
};
|
|
327
|
+
const prefix = `${principal.id}:`;
|
|
328
|
+
return json({
|
|
329
|
+
ok: true,
|
|
330
|
+
conversations: conversations
|
|
331
|
+
.filter((c) => c.sessionKey.startsWith(prefix))
|
|
332
|
+
.map((c) => ({ conversationId: c.sessionKey.slice(prefix.length), lastTimestamp: c.lastTimestamp, preview: c.preview })),
|
|
333
|
+
});
|
|
317
334
|
}),
|
|
318
|
-
"/tool-log": route(() => json({ ok: true, entries: reads.toolLog() })),
|
|
335
|
+
"/tool-log": route((_req, principal) => json({ ok: true, entries: reads.toolLog(principal) })),
|
|
319
336
|
"/health": route(async () => json({ ok: true, ...(await reads.health() as object) })),
|
|
320
|
-
"/wiki/list": route(async () => json({ ok: true, files: await reads.wikiList() })),
|
|
321
|
-
"/wiki/read": route(async (req) => {
|
|
337
|
+
"/wiki/list": route(async (_req, principal) => json({ ok: true, files: await reads.wikiList(principal) })),
|
|
338
|
+
"/wiki/read": route(async (req, principal) => {
|
|
322
339
|
const path = requireParam(req, "path");
|
|
323
340
|
if (!path) return badRequest("missing ?path");
|
|
324
|
-
|
|
341
|
+
const content = await reads.wikiRead(principal, path);
|
|
342
|
+
return content === null ? json({ ok: false, error: "not found" }, 404) : json({ ok: true, content });
|
|
325
343
|
}),
|
|
326
|
-
"/wiki/grep": route(async (req) => {
|
|
344
|
+
"/wiki/grep": route(async (req, principal) => {
|
|
327
345
|
const pattern = requireParam(req, "pattern");
|
|
328
346
|
if (!pattern) return badRequest("missing ?pattern");
|
|
329
|
-
return json({ ok: true, matches: await reads.wikiGrep(pattern) });
|
|
347
|
+
return json({ ok: true, matches: await reads.wikiGrep(principal, pattern) });
|
|
330
348
|
}),
|
|
331
|
-
"/memory/scroll": route(async (req) => {
|
|
349
|
+
"/memory/scroll": route(async (req, principal) => {
|
|
332
350
|
const url = new URL(req.url);
|
|
333
351
|
const collection = url.searchParams.get("collection");
|
|
334
352
|
if (!collection) return badRequest("missing ?collection");
|
|
335
353
|
const limit = Number(url.searchParams.get("limit") ?? "50");
|
|
336
354
|
const offset = url.searchParams.get("offset") ?? undefined;
|
|
337
|
-
|
|
355
|
+
const page = await reads.memoryScroll(principal, collection, limit, offset);
|
|
356
|
+
return page === null ? badRequest(`unknown collection: ${collection}`) : json({ ok: true, ...(page as object) });
|
|
338
357
|
}),
|
|
339
358
|
};
|
|
340
359
|
}
|
package/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ export { createHttpProvider, type HttpProviderDeps } from "./http-provider.ts";
|
|
|
20
20
|
export const httpChannel: ChannelPlugin = {
|
|
21
21
|
// The contract this channel is written for, as a literal: importing
|
|
22
22
|
// CHANNEL_API_VERSION would report whichever contract is installed.
|
|
23
|
-
apiVersion:
|
|
23
|
+
apiVersion: 3,
|
|
24
24
|
name: "http",
|
|
25
25
|
build: (ctx) => {
|
|
26
26
|
if (!ctx.resolveConfirmation || !ctx.reads) {
|
package/openapi.yaml
CHANGED
|
@@ -21,6 +21,15 @@ info:
|
|
|
21
21
|
anything runs.
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
## What a caller sees
|
|
25
|
+
|
|
26
|
+
Every read is scoped to the caller: their own conversations, pending
|
|
27
|
+
confirmations, tool calls and memory, and the wiki as they see it, the
|
|
28
|
+
team's common area (`curated/`) plus their own notes (`personal/`).
|
|
29
|
+
Someone else's data doesn't exist as far as a caller can tell. Only
|
|
30
|
+
`/manifest` and `/health` are the same for everyone.
|
|
31
|
+
|
|
32
|
+
|
|
24
33
|
## Conversations
|
|
25
34
|
|
|
26
35
|
A conversation is identified by an opaque, client-owned `conversationId`
|
|
@@ -28,10 +37,11 @@ info:
|
|
|
28
37
|
`<caller id>:<conversationId>`, so the same id sent by someone else is a
|
|
29
38
|
conversation of their own, and a confirmation token staged in one person's
|
|
30
39
|
conversation is never found from another's. Omitting `conversationId` on
|
|
31
|
-
`POST /turn` starts a fresh ephemeral conversation. The durable transcript
|
|
32
|
-
readable via `GET /conversation`,
|
|
33
|
-
`GET /conversations` (both backed
|
|
34
|
-
empty when the vector store is
|
|
40
|
+
`POST /turn` starts a fresh ephemeral conversation. The durable transcript
|
|
41
|
+
of one of the caller's conversations is readable via `GET /conversation`,
|
|
42
|
+
and the list of their conversations via `GET /conversations` (both backed
|
|
43
|
+
by the verbatim archive; they degrade to empty when the vector store is
|
|
44
|
+
unreachable).
|
|
35
45
|
|
|
36
46
|
|
|
37
47
|
## Streaming and cancellation
|
|
@@ -127,9 +137,10 @@ paths:
|
|
|
127
137
|
- name: id
|
|
128
138
|
in: query
|
|
129
139
|
required: true
|
|
130
|
-
description: The
|
|
140
|
+
description: The caller's `conversationId`, as `/conversations` lists it.
|
|
131
141
|
schema:
|
|
132
142
|
type: string
|
|
143
|
+
pattern: "^[A-Za-z0-9_-]{1,128}$"
|
|
133
144
|
- name: limit
|
|
134
145
|
in: query
|
|
135
146
|
required: false
|
|
@@ -146,20 +157,20 @@ paths:
|
|
|
146
157
|
"401":
|
|
147
158
|
$ref: "#/components/responses/Unauthorized"
|
|
148
159
|
"200":
|
|
149
|
-
description: The conversation's messages in chronological order.
|
|
160
|
+
description: The conversation's messages in chronological order; none when it isn't one of the caller's.
|
|
150
161
|
content:
|
|
151
162
|
application/json:
|
|
152
163
|
schema:
|
|
153
164
|
$ref: "#/components/schemas/ConversationPage"
|
|
154
165
|
"400":
|
|
155
|
-
description: Missing `?id`.
|
|
166
|
+
description: Missing or malformed `?id`.
|
|
156
167
|
content:
|
|
157
168
|
application/json:
|
|
158
169
|
schema:
|
|
159
170
|
$ref: "#/components/schemas/Error"
|
|
160
171
|
/conversations:
|
|
161
172
|
get:
|
|
162
|
-
summary: List
|
|
173
|
+
summary: List the caller's conversations (most-recently-active first)
|
|
163
174
|
parameters:
|
|
164
175
|
- name: limit
|
|
165
176
|
in: query
|
|
@@ -202,7 +213,7 @@ paths:
|
|
|
202
213
|
type: object
|
|
203
214
|
/confirmations:
|
|
204
215
|
get:
|
|
205
|
-
summary:
|
|
216
|
+
summary: The caller's pending confirmations (tokens redacted)
|
|
206
217
|
responses:
|
|
207
218
|
"401":
|
|
208
219
|
$ref: "#/components/responses/Unauthorized"
|
|
@@ -214,7 +225,7 @@ paths:
|
|
|
214
225
|
type: object
|
|
215
226
|
/tool-log:
|
|
216
227
|
get:
|
|
217
|
-
summary:
|
|
228
|
+
summary: The tool calls made in the caller's recent turns
|
|
218
229
|
responses:
|
|
219
230
|
"401":
|
|
220
231
|
$ref: "#/components/responses/Unauthorized"
|
|
@@ -226,7 +237,7 @@ paths:
|
|
|
226
237
|
type: object
|
|
227
238
|
/wiki/list:
|
|
228
239
|
get:
|
|
229
|
-
summary: List the wiki
|
|
240
|
+
summary: List the wiki files the caller can see (`curated/` and `personal/`)
|
|
230
241
|
responses:
|
|
231
242
|
"401":
|
|
232
243
|
$ref: "#/components/responses/Unauthorized"
|
|
@@ -238,11 +249,12 @@ paths:
|
|
|
238
249
|
type: object
|
|
239
250
|
/wiki/read:
|
|
240
251
|
get:
|
|
241
|
-
summary: Read one wiki
|
|
252
|
+
summary: Read one wiki file the caller can see
|
|
242
253
|
parameters:
|
|
243
254
|
- name: path
|
|
244
255
|
in: query
|
|
245
256
|
required: true
|
|
257
|
+
description: A path as `/wiki/list` gives it, e.g. `curated/team.md` or `personal/notes/plan.md`.
|
|
246
258
|
schema:
|
|
247
259
|
type: string
|
|
248
260
|
responses:
|
|
@@ -260,9 +272,15 @@ paths:
|
|
|
260
272
|
application/json:
|
|
261
273
|
schema:
|
|
262
274
|
$ref: "#/components/schemas/Error"
|
|
275
|
+
"404":
|
|
276
|
+
description: No such file among those the caller can see.
|
|
277
|
+
content:
|
|
278
|
+
application/json:
|
|
279
|
+
schema:
|
|
280
|
+
$ref: "#/components/schemas/Error"
|
|
263
281
|
/wiki/grep:
|
|
264
282
|
get:
|
|
265
|
-
summary: Grep the wiki
|
|
283
|
+
summary: Grep the wiki files the caller can see
|
|
266
284
|
parameters:
|
|
267
285
|
- name: pattern
|
|
268
286
|
in: query
|
|
@@ -287,11 +305,12 @@ paths:
|
|
|
287
305
|
$ref: "#/components/schemas/Error"
|
|
288
306
|
/memory/scroll:
|
|
289
307
|
get:
|
|
290
|
-
summary: Scroll a
|
|
308
|
+
summary: Scroll the caller's points in a memory collection
|
|
291
309
|
parameters:
|
|
292
310
|
- name: collection
|
|
293
311
|
in: query
|
|
294
312
|
required: true
|
|
313
|
+
description: One of the collections kept per person (the verbatim archive, episodic memory, semantic facts), by its configured name.
|
|
295
314
|
schema:
|
|
296
315
|
type: string
|
|
297
316
|
- name: limit
|
|
@@ -315,7 +334,7 @@ paths:
|
|
|
315
334
|
schema:
|
|
316
335
|
type: object
|
|
317
336
|
"400":
|
|
318
|
-
description: Missing `?collection
|
|
337
|
+
description: Missing `?collection`, or one that isn't kept per person.
|
|
319
338
|
content:
|
|
320
339
|
application/json:
|
|
321
340
|
schema:
|
|
@@ -452,9 +471,9 @@ components:
|
|
|
452
471
|
- type: "null"
|
|
453
472
|
ConversationSummary:
|
|
454
473
|
type: object
|
|
455
|
-
required: [
|
|
474
|
+
required: [conversationId, lastTimestamp, preview]
|
|
456
475
|
properties:
|
|
457
|
-
|
|
476
|
+
conversationId:
|
|
458
477
|
type: string
|
|
459
478
|
lastTimestamp:
|
|
460
479
|
type: string
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mercury-fw/channel-http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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.36.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.36.0",
|
|
25
|
+
"@mercury-fw/plugin-types": "0.36.0",
|
|
26
26
|
"@mercury-fw/typescript-config": "*",
|
|
27
27
|
"@types/bun": "^1.4.2",
|
|
28
28
|
"typescript": "^6.0.3"
|