@happyvertical/smrt-chat 0.55.7 → 0.55.9
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/AGENTS.md +38 -7
- package/README.md +8 -0
- package/agents/recipes.md +38 -0
- package/agents/token-streaming.md +12 -0
- package/dist/chunks/{chat-stream-BM3-maAM.js → chat-stream-BBk_5_Ph.js} +455 -4
- package/dist/chunks/chat-stream-BBk_5_Ph.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -4
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +92 -1
- package/dist/recipes.d.ts +48 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/runtime-report-tools.d.ts +132 -0
- package/dist/runtime-report-tools.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +111 -5
- package/dist/sveltekit.js +1 -1
- package/dist/tool-loop.d.ts.map +1 -1
- package/package.json +17 -15
- package/dist/chunks/chat-stream-BM3-maAM.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -97,6 +97,31 @@ fail-closed `allowedTools` offer/execution gates; RBAC, tenant, redaction,
|
|
|
97
97
|
bounded query results, and audit authority remain in the authenticated
|
|
98
98
|
`PrincipalRun` supplied by `@happyvertical/smrt-agents`.
|
|
99
99
|
|
|
100
|
+
## Runtime report tools (#3711)
|
|
101
|
+
|
|
102
|
+
`createRuntimeReportTools()` (`runtime-report-tools.ts`) returns four
|
|
103
|
+
`PrincipalTool`s for the persona `extraTools` seam: `reports.runtime.sources`,
|
|
104
|
+
`reports.runtime.define` (`preview` / `apply`), `reports.runtime.list`, and
|
|
105
|
+
`reports.runtime.run`. The model supplies only a declarative
|
|
106
|
+
`RuntimeReportSpec` from `@happyvertical/smrt-reports`; sources are a
|
|
107
|
+
server-owned allow-list gated by `run.assertOperation(collection, 'read')`, and
|
|
108
|
+
field policy, `readPermission`, and tenancy are enforced by the compiler against
|
|
109
|
+
the live run on every compile and every stored-report run. `apply` needs the
|
|
110
|
+
`specHash` of the previewed spec plus an app-owned `confirmation` host that
|
|
111
|
+
resolves only after a human approved that exact spec; with no host the tools are
|
|
112
|
+
propose-only and a model-supplied `confirmed` flag is ignored. Saving also needs
|
|
113
|
+
`create` on the `RuntimeReport` collection, and `list`/`run` need `read`; the
|
|
114
|
+
default `reportsCollection` is the `RuntimeReport` class itself, which resolves
|
|
115
|
+
to the catalog slug. A string `collection`/`reportsCollection` is used verbatim
|
|
116
|
+
as a catalog slug (never a class name); tests must run the tools under the real
|
|
117
|
+
`assertOperationPermission` guard so a wrong slug fails. A stored report is
|
|
118
|
+
written only by this confirmed `apply`: the model layer refuses every other
|
|
119
|
+
insert/spec change, and the generic manifest tools (`buildManifestToolCatalog`,
|
|
120
|
+
`invokeManifestTool`) never offer or run a `RuntimeReport` write. Stored-report
|
|
121
|
+
reads/saves run under the principal's tenant (`withPrincipalTenant`), refuse a
|
|
122
|
+
mismatching ambient tenant, and filter by the principal tenant explicitly. Contract and authority model:
|
|
123
|
+
[`packages/reports/agents/runtime-reports.md`](../reports/agents/runtime-reports.md).
|
|
124
|
+
|
|
100
125
|
## Conversational Harness (L3, #1891)
|
|
101
126
|
|
|
102
127
|
The `AgentSession` runtime depends on personas, agents, and users. Keep those
|
|
@@ -115,13 +140,12 @@ The gateway bearer token proves only "this request came from the gateway"; it ne
|
|
|
115
140
|
|
|
116
141
|
## Token Streaming (SSE, #1936)
|
|
117
142
|
|
|
118
|
-
`chat-stream.ts` is the SSE seam for embeddable conversational UIs
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
- **Custom tools stream via `binding.extraTools`** — the persona binding threads an optional `extraTools?: PrincipalTool[]` down to `runPersonaConversationTurn`, so a *streamed* persona chat can offer non-manifest, service-backed tools (the persona messaging tool `messages.send`, or an assistance-request/lead-ticket tool wrapping a `@smrt({ api:false, mcp:false })` service) and thus *act*, not only answer — matching the non-streaming persona path. It is resolved server-side by `authorize` (trusted), never from request input, and stays fully gated: each tool is filtered by the persona's `allowedTools` (offer gate) and re-asserts the bound principal's authority in `execute` (execution gate). Offering a tool is not authorizing it.
|
|
143
|
+
`chat-stream.ts` is the SSE seam for embeddable conversational UIs:
|
|
144
|
+
`runChatConversationStream()` (engine), `createChatStreamHandler({ authorize })`
|
|
145
|
+
(Fetch handler; `authorize` is the sole trust boundary), and the browser-safe
|
|
146
|
+
`SmrtChatBackend` (`./client`). Persona-bound streams reuse the harness gates
|
|
147
|
+
unchanged, and `binding.extraTools` is resolved server-side. Full contract:
|
|
148
|
+
[agents/token-streaming.md](agents/token-streaming.md).
|
|
125
149
|
|
|
126
150
|
## Streamed assistant turns with browser tools (#2908)
|
|
127
151
|
|
|
@@ -160,6 +184,13 @@ the config `ai` block. Pass `runtime`: a turn never keeps the RLS request tx
|
|
|
160
184
|
(`clientRequestMessageId`): the PK is the retry reservation. See
|
|
161
185
|
[docs](../../docs/assistant-dock.md).
|
|
162
186
|
|
|
187
|
+
## Recipes (#3719)
|
|
188
|
+
|
|
189
|
+
`src/recipes.ts` declares `chat.assistant` (the AssistantDock as a `header.end`
|
|
190
|
+
shell widget, an `llm` provider, runtime `both`; help in
|
|
191
|
+
`src/assistant.recipe.md`). Keep each recipe class self-contained. Details:
|
|
192
|
+
[agents/recipes.md](agents/recipes.md).
|
|
193
|
+
|
|
163
194
|
## Gotchas
|
|
164
195
|
|
|
165
196
|
- **sessionContext, not context**: `context` is reserved for slug scoping. Use `getSessionContext()`/`updateSessionContext()` for agent memory.
|
package/README.md
CHANGED
|
@@ -287,6 +287,14 @@ In the page, `createAssistantHttpTransport({ endpoint: '/api/assistant' })`
|
|
|
287
287
|
is the dock's `transport`, and `createAssistantHttpActionClient` its
|
|
288
288
|
`actionClient` when the route is given `actions: { adapter }`.
|
|
289
289
|
|
|
290
|
+
### Recipes
|
|
291
|
+
|
|
292
|
+
`AssistantRecipe` (`chat.assistant`) declares the assistant dock for app and
|
|
293
|
+
agent catalogs: a shell widget in `header.end` that mounts `AssistantDock`
|
|
294
|
+
from `@happyvertical/smrt-chat/svelte`, an `llm` provider (hosted keys
|
|
295
|
+
`OPENAI_API_KEY`, `ANTHROPIC_API_KEY` or `GEMINI_API_KEY`, or a browser model
|
|
296
|
+
with no key), and user-facing help. See `agents/recipes.md`.
|
|
297
|
+
|
|
290
298
|
## API
|
|
291
299
|
|
|
292
300
|
### Models
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Chat recipes (#3719)
|
|
2
|
+
|
|
3
|
+
Recipes are declared in `src/recipes.ts` (see
|
|
4
|
+
[`packages/core/agents/recipes.md`](../../core/agents/recipes.md)) and are
|
|
5
|
+
read statically by the scanner. Each class stays self-contained: rooms and the
|
|
6
|
+
assistant are separate recipes that add their own class to the same file.
|
|
7
|
+
|
|
8
|
+
## `chat.assistant` (`AssistantRecipe`)
|
|
9
|
+
|
|
10
|
+
The AI assistant dock as a shell widget. Help: `src/assistant.recipe.md`.
|
|
11
|
+
|
|
12
|
+
- **Surface.** One `shell-widget` in `header.end` exporting
|
|
13
|
+
`@happyvertical/smrt-chat/svelte#AssistantDock`. `header.end` is where
|
|
14
|
+
`AppShell` puts `dockToggles` by default; the toggle and the
|
|
15
|
+
`ShellDockTool` come from the host shell, not from this package. The host
|
|
16
|
+
supplies the dock's `transport` and the shell's `DataSurfaceRegistry`
|
|
17
|
+
([`docs/assistant-dock.md`](../../../docs/assistant-dock.md)).
|
|
18
|
+
- **Models.** `AgentSession`, `ChatRoom`, `ChatParticipant`, `ChatThread`,
|
|
19
|
+
`ChatMessage`: the records the assistant routes persist into. No `nav`: the
|
|
20
|
+
generated list routes are tenant-scoped, not member-scoped, so the dock never
|
|
21
|
+
reads them and no admin screen should.
|
|
22
|
+
- **Provider.** One `llm` provider. Hosted options (`openai`, `anthropic`,
|
|
23
|
+
`gemini`) each need their own key (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`,
|
|
24
|
+
`GEMINI_API_KEY`, the names `@happyvertical/smrt-config` resolves); the
|
|
25
|
+
browser options (`webllm`, `bitgpu`) need none. Names only, never values.
|
|
26
|
+
- **Runtime.** `both`: the dock, the in-memory transport and the browser
|
|
27
|
+
inference path run in the browser; saved conversations and tool turns go
|
|
28
|
+
through `mountAssistantRoutes()` on the server.
|
|
29
|
+
- **No `demoSeed`.** The package ships no assistant fixture; the in-memory
|
|
30
|
+
transport starts empty. Add one with `demoSeed` once a fixture exists.
|
|
31
|
+
- **Report tools (#3711).** The help describes `createRuntimeReportTools()` as
|
|
32
|
+
an opt-in capability a host passes through `extraTools`. The recipe does not
|
|
33
|
+
wire them.
|
|
34
|
+
|
|
35
|
+
Tests: `src/__tests__/recipes.test.ts` scans `src` with the real scanner
|
|
36
|
+
(declaration validity, qualified model names, emitted surfaces, providers and
|
|
37
|
+
runtime), runs `ManifestGenerator.assertRecipeOptions`/`assertRecipeHelp`, and
|
|
38
|
+
renders the help.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Token Streaming (SSE, #1936)
|
|
2
|
+
|
|
3
|
+
Moved verbatim from `packages/chat/AGENTS.md` to keep the instruction chain under
|
|
4
|
+
the 32 KB cap (`pnpm check:agents-chain`). The summary stays in AGENTS.md.
|
|
5
|
+
|
|
6
|
+
`chat-stream.ts` is the SSE seam for embeddable conversational UIs (first consumer: the Happy chat widget, `animation#5`): a client POSTs the conversation so far and receives a `text/event-stream` of `data: <json>` frames — `token` deltas as the model generates, then a final `done` frame with the message. The wire `ChatStreamEvent` union also declares `emotion` and `control` (#1921 host-page control commands) lanes for forward compatibility; the v1 engine emits `token`/`done`/`error`.
|
|
7
|
+
|
|
8
|
+
- **`runChatConversationStream({ context, messages })`** — the transport-agnostic engine (an `AsyncGenerator<ChatStreamEvent>`). Dispatches on `context.binding`: **persona-bound** runs the full `runPersonaConversationTurn` with a token sink wired through the tool loop (`onToken` → `ai.chat({ stream: true, onProgress })`), then persists via `ChatService` and emits the persisted message as `done`; **plain/unbound** streams `ai.stream()` directly and emits a synthesized (unpersisted) `done`. Streamed tokens are a live PREVIEW (a tool-call round may narrate before acting); the `done` message is authoritative. Failures surface as an in-band `error` event, never a throw (the 200 has already committed once streaming starts).
|
|
9
|
+
- **`createChatStreamHandler({ authorize, allowedOrigins?, allowCredentials? })`** — a Fetch-compatible handler returning `text/event-stream` (mirrors `createVoiceGatewayTurnHandler`). `authorize(request, body)` is the SOLE trust boundary and works exactly like the voice gateway: this module NEVER authorizes from the request's `session` metadata — the app validates the caller (bearer session id / cookie / same-origin) and the claimed ids against the authenticated principal, and returns an already-authorized `ChatStreamContext`. Generation caps (`model`/`maxTokens`/`maxSteps`) live on the context (server-resolved), never on the request. Cross-origin embedding uses the same fail-closed CORS posture as core `_events` (#1861): the `Origin` is echoed only when allow-listed (never `*`), credentials only when opted in.
|
|
10
|
+
- **`SmrtChatBackend` (`@happyvertical/smrt-chat/client`)** — the consume side of the same contract: a browser SSE client (`src/client.ts`) that POSTs the conversation and dispatches `token`/`emotion`/`done`/`error` frames to streaming handlers, tolerating heartbeat comments and frames split across chunks. The subpath is BROWSER-SAFE and dependency-free (no server runtime, no workspace imports — keep it that way), and its widget-facing types are structurally identical to `@happyvertical/animation`'s `ChatBackend` contract so an instance plugs straight into the floating chat widget. `src/client.contract.ts` carries the compile-time locks pinning it to `chat-stream.ts`'s `ChatStreamEvent`/`ChatStreamSession` — a NON-test module precisely so `pnpm typecheck` actually enforces them (`tsconfig.typecheck.json` excludes `.test.ts` files, and Vitest transpiles without typechecking). A clean close without a `done` frame surfaces as an error (never an empty reply), and a settled turn cancels the reader so the connection is released promptly.
|
|
11
|
+
- **Persona path reuses the harness's own gates unchanged** — persona principal, fail-closed `allowedTools` offer+execution gates, tenant binding. `onToken` is best-effort telemetry threaded through `runToolLoop`; it never changes what the loop persists or authorizes.
|
|
12
|
+
- **Custom tools stream via `binding.extraTools`** — the persona binding threads an optional `extraTools?: PrincipalTool[]` down to `runPersonaConversationTurn`, so a *streamed* persona chat can offer non-manifest, service-backed tools (the persona messaging tool `messages.send`, or an assistance-request/lead-ticket tool wrapping a `@smrt({ api:false, mcp:false })` service) and thus *act*, not only answer — matching the non-streaming persona path. It is resolved server-side by `authorize` (trusted), never from request input, and stays fully gated: each tool is filtered by the persona's `allowedTools` (offer gate) and re-asserts the bound principal's authority in `execute` (execution gate). Offering a tool is not authorizing it.
|