@happyvertical/smrt-chat 0.55.9 → 0.55.10
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 +25 -14
- package/README.md +12 -0
- package/agents/dev-workbench.md +69 -0
- package/agents/helper-preferences.md +65 -0
- package/agents/overview-tools.md +172 -0
- package/agents/token-streaming.md +3 -2
- package/dist/chunks/{ChatService-C9UxuJaw.js → ChatService-CFaaDHyU.js} +2 -2
- package/dist/chunks/{ChatService-C9UxuJaw.js.map → ChatService-CFaaDHyU.js.map} +1 -1
- package/dist/chunks/chat-stream-DN6Tw-Xu.js +2027 -0
- package/dist/chunks/chat-stream-DN6Tw-Xu.js.map +1 -0
- package/dist/dev-assistant-transport.d.ts +5 -0
- package/dist/dev-assistant-transport.d.ts.map +1 -0
- package/dist/dev-character-persistence-client.d.ts +24 -0
- package/dist/dev-character-persistence-client.d.ts.map +1 -0
- package/dist/dev-helper-client.d.ts +4 -0
- package/dist/dev-helper-client.d.ts.map +1 -0
- package/dist/dev-helper-server.d.ts +18 -0
- package/dist/dev-helper-server.d.ts.map +1 -0
- package/dist/helper-preferences-service.d.ts +22 -0
- package/dist/helper-preferences-service.d.ts.map +1 -0
- package/dist/helper-preferences.d.ts +84 -0
- package/dist/helper-preferences.d.ts.map +1 -0
- package/dist/helper-preferences.js +32 -0
- package/dist/helper-preferences.js.map +1 -0
- package/dist/helper-profile-store.d.ts +27 -0
- package/dist/helper-profile-store.d.ts.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +683 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/agent-runtime.js +1 -1
- package/dist/manifest.json +122 -3
- package/dist/overview-tools.d.ts +221 -0
- package/dist/overview-tools.d.ts.map +1 -0
- package/dist/playground.d.ts +1 -1
- package/dist/playground.d.ts.map +1 -1
- package/dist/playground.js +99 -88
- package/dist/playground.js.map +1 -1
- package/dist/recipes.d.ts +81 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/routes/api/dev-character-conversation/protocol.d.ts +33 -0
- package/dist/routes/api/dev-character-conversation/protocol.d.ts.map +1 -0
- package/dist/routes/api/dev-character-persistence/config.d.ts +24 -0
- package/dist/routes/api/dev-character-persistence/config.d.ts.map +1 -0
- package/dist/scripts/provision-dev-character-persistence.d.ts +3 -0
- package/dist/scripts/provision-dev-character-persistence.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +144 -11
- package/dist/svelte/components/assistant/AssistantDock.svelte +58 -2
- package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts +9 -0
- package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts.map +1 -1
- package/dist/svelte/components/assistant/FloatingAssistant.svelte +309 -0
- package/dist/svelte/components/assistant/FloatingAssistant.svelte.d.ts +27 -0
- package/dist/svelte/components/assistant/FloatingAssistant.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/CaptionOverlay.svelte +105 -0
- package/dist/svelte/components/assistant/captions/CaptionOverlay.svelte.d.ts +11 -0
- package/dist/svelte/components/assistant/captions/CaptionOverlay.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/HeardCaptions.svelte +65 -0
- package/dist/svelte/components/assistant/captions/HeardCaptions.svelte.d.ts +19 -0
- package/dist/svelte/components/assistant/captions/HeardCaptions.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/SpokenCaptions.svelte +65 -0
- package/dist/svelte/components/assistant/captions/SpokenCaptions.svelte.d.ts +21 -0
- package/dist/svelte/components/assistant/captions/SpokenCaptions.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/caption-overlay-context.d.ts +3 -0
- package/dist/svelte/components/assistant/captions/caption-overlay-context.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/caption-overlay-context.js +2 -0
- package/dist/svelte/components/assistant/captions/caption-state.svelte.d.ts +71 -0
- package/dist/svelte/components/assistant/captions/caption-state.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/caption-state.svelte.js +225 -0
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.d.ts.map +1 -1
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.js +3 -1
- package/dist/svelte/components/helper/HelperControlPanel.svelte +401 -0
- package/dist/svelte/components/helper/HelperControlPanel.svelte.d.ts +23 -0
- package/dist/svelte/components/helper/HelperControlPanel.svelte.d.ts.map +1 -0
- package/dist/svelte/components/helper/registry.d.ts +53 -0
- package/dist/svelte/components/helper/registry.d.ts.map +1 -0
- package/dist/svelte/components/helper/registry.js +100 -0
- package/dist/svelte/i18n.d.ts +14 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +17 -0
- package/dist/svelte/i18n.messages.d.ts +32 -0
- package/dist/svelte/i18n.messages.d.ts.map +1 -1
- package/dist/svelte/i18n.messages.js +33 -0
- package/dist/svelte/index.d.ts +7 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +15 -5
- package/dist/svelte/playground.d.ts +94 -0
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +16 -5
- package/dist/sveltekit.js +2 -2
- package/package.json +27 -17
- package/dist/chunks/chat-stream-BBk_5_Ph.js +0 -2027
- package/dist/chunks/chat-stream-BBk_5_Ph.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -5,7 +5,9 @@ Chat rooms, threads, and agent sessions with app-controlled tool whitelisting.
|
|
|
5
5
|
## Dev Server
|
|
6
6
|
|
|
7
7
|
`pnpm --dir packages/chat dev` runs a package-local SvelteKit workbench. The root
|
|
8
|
-
route
|
|
8
|
+
route separates **Character setup** and **Chat dev** into tabs; switching tabs
|
|
9
|
+
retains the current character rig and chat state. The chat tab is an interactive
|
|
10
|
+
surface with a dev-only `/api/dev-chat` endpoint:
|
|
9
11
|
it uses `@happyvertical/ai` when local provider credentials are present and
|
|
10
12
|
falls back to a deterministic local assistant otherwise. `/api/dev-chat-stream`
|
|
11
13
|
is its SSE companion (#1936) — the same provider/local-fallback resolution wired
|
|
@@ -21,12 +23,10 @@ over `src/routes/chat-dev.intents.ts`, #2588). Their dev-config dependencies
|
|
|
21
23
|
stylesheet import) and the stage→apply tool-call gotchas are in
|
|
22
24
|
[agents/dev-workbench.md](agents/dev-workbench.md).
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
`SMRT_CHAT_DEV_VOICE_GATEWAY_TOKEN` to the browser requires
|
|
29
|
-
`SMRT_CHAT_DEV_VOICE_GATEWAY_EXPOSE_TOKEN=true`; keep that local-only.
|
|
26
|
+
Dev-only voice and opt-in local character persistence are documented in
|
|
27
|
+
[agents/dev-workbench.md](agents/dev-workbench.md#voice-and-local-character-persistence).
|
|
28
|
+
These endpoints are loopback-only demos; production hosts supply authenticated
|
|
29
|
+
principals, tenancy, and authorization to the owning services.
|
|
30
30
|
|
|
31
31
|
## Models
|
|
32
32
|
|
|
@@ -122,6 +122,16 @@ reads/saves run under the principal's tenant (`withPrincipalTenant`), refuse a
|
|
|
122
122
|
mismatching ambient tenant, and filter by the principal tenant explicitly. Contract and authority model:
|
|
123
123
|
[`packages/reports/agents/runtime-reports.md`](../reports/agents/runtime-reports.md).
|
|
124
124
|
|
|
125
|
+
## Overview tools (#3727)
|
|
126
|
+
|
|
127
|
+
`createOverviewTools({ host })` (`overview-tools.ts`) returns
|
|
128
|
+
`overviews.describe`, `overviews.apply` (an atomic batch of structured widget
|
|
129
|
+
operations) and `overviews.undo` (single step, keyed by tenant, user and page).
|
|
130
|
+
The host opens a page per `PrincipalRun` (smrt-svelte's
|
|
131
|
+
`createOverviewAssistantSurface`, typed structurally here); validation and
|
|
132
|
+
`canCustomize` are the surface's. Contract:
|
|
133
|
+
[agents/overview-tools.md](agents/overview-tools.md).
|
|
134
|
+
|
|
125
135
|
## Conversational Harness (L3, #1891)
|
|
126
136
|
|
|
127
137
|
The `AgentSession` runtime depends on personas, agents, and users. Keep those
|
|
@@ -138,14 +148,15 @@ Voice is an input mode for the existing persona chat harness, not a separate cha
|
|
|
138
148
|
|
|
139
149
|
The gateway bearer token proves only "this request came from the gateway"; it never authorizes the end user. The short-lived `VoiceSession` binding is the user/session proof, and untrusted gateway metadata must be validated against that binding before any chat write or tool loop. Tool execution remains fail-closed through the persona allow-list mirrored onto `AgentSession` by `bindPersonaToSession()`.
|
|
140
150
|
|
|
141
|
-
## Token
|
|
151
|
+
## Token streaming
|
|
142
152
|
|
|
143
|
-
`chat-stream.ts`
|
|
144
|
-
`
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
[agents/token-streaming.md](agents/token-streaming.md)
|
|
153
|
+
`chat-stream.ts` serves browser-safe SSE through `./client`. The host's
|
|
154
|
+
`authorize(request, body)` is the sole trust boundary: resolve identity, tenant,
|
|
155
|
+
capabilities, and generation limits server-side; never authorize from request
|
|
156
|
+
session metadata. Persona turns reuse the principal and fail-closed tool gates.
|
|
157
|
+
The engine and browser-safe `SmrtChatBackend` keep custom-tool resolution
|
|
158
|
+
server-side. See [agents/token-streaming.md](agents/token-streaming.md) for the
|
|
159
|
+
event contract, CORS policy, and streaming behavior.
|
|
149
160
|
|
|
150
161
|
## Streamed assistant turns with browser tools (#2908)
|
|
151
162
|
|
package/README.md
CHANGED
|
@@ -29,6 +29,18 @@ trusting browser data or throwing raw transport errors.
|
|
|
29
29
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
32
|
+
### Team Chat recipe
|
|
33
|
+
|
|
34
|
+
`ChatRoomsRecipe` declares the `chat.rooms` unit for app navigation and
|
|
35
|
+
generated read-only room, message, membership, thread, and reaction views.
|
|
36
|
+
It places the Chat Rooms entry in the Team section under the Team collaboration
|
|
37
|
+
card, using the shell's `users` icon.
|
|
38
|
+
Hosts can mount its exported `ChatLayout` at `/chat/rooms`; the recipe also
|
|
39
|
+
points demo hosts at the browser-safe static chat fixture from `./playground`.
|
|
40
|
+
Its [user help](src/chat-rooms.recipe.md) describes how to find rooms and
|
|
41
|
+
follow conversations. The package playground previews use static mock fixtures;
|
|
42
|
+
they do not connect to a live chat service.
|
|
43
|
+
|
|
32
44
|
### Local dev server
|
|
33
45
|
|
|
34
46
|
Run the chat package workbench directly:
|
package/agents/dev-workbench.md
CHANGED
|
@@ -69,3 +69,72 @@ are non-obvious and cost time to find:
|
|
|
69
69
|
programmatic `element.click()` (`event.isTrusted === false`) for `apply`, which
|
|
70
70
|
is the stage→apply consent split doing its job. Verification must use a real
|
|
71
71
|
input event, not `evaluate(() => button.click())`.
|
|
72
|
+
|
|
73
|
+
## Voice and local character persistence
|
|
74
|
+
|
|
75
|
+
The root workbench also has a dev-only voice conversation mode. It reads voice
|
|
76
|
+
gateway connection details through `/api/dev-voice/config`, streams browser mic
|
|
77
|
+
audio to `WS /ws/voice` as PCM16 mono, appends gateway transcripts/responses to
|
|
78
|
+
the chat, and plays returned TTS audio. Exposing
|
|
79
|
+
`SMRT_CHAT_DEV_VOICE_GATEWAY_TOKEN` to the browser requires
|
|
80
|
+
`SMRT_CHAT_DEV_VOICE_GATEWAY_EXPOSE_TOKEN=true`; keep that local-only.
|
|
81
|
+
|
|
82
|
+
### Local character persistence
|
|
83
|
+
|
|
84
|
+
`/api/dev-character-persistence` is an opt-in loopback-only demo bridge for
|
|
85
|
+
the photographic character workbench. It is disabled unless
|
|
86
|
+
`SMRT_CHAT_DEV_CHARACTER_PERSISTENCE=true`; it accepts neither actor, tenant,
|
|
87
|
+
nor profile ids from the browser. Its server configuration names one
|
|
88
|
+
pre-provisioned profile and tenant plus a temporary SQLite database and asset
|
|
89
|
+
directory, all under the operating system temporary directory. First run
|
|
90
|
+
`DATABASE_URL=<the same SQLite path> pnpm smrt db:migrate`, then run
|
|
91
|
+
`pnpm exec tsx src/scripts/provision-dev-character-persistence.ts` with the
|
|
92
|
+
`SMRT_CHAT_DEV_CHARACTER_*` variables set. The route never creates schema.
|
|
93
|
+
Use fresh UUIDs for `SMRT_CHAT_DEV_CHARACTER_PROFILE_ID` and
|
|
94
|
+
`SMRT_CHAT_DEV_CHARACTER_TENANT_ID`; keep the database and asset directory in
|
|
95
|
+
the OS temporary directory, for example `/tmp/smrt-character-setup/` on Linux
|
|
96
|
+
or the path reported by `node -p "require('node:os').tmpdir()"` on macOS.
|
|
97
|
+
|
|
98
|
+
This is not production authentication. A production host must provide its
|
|
99
|
+
authenticated principal, tenant, and authorization policy to
|
|
100
|
+
`PhotoCutoutProfileStore`; it must not copy this fixed local identity pattern.
|
|
101
|
+
|
|
102
|
+
### Local helper control panel
|
|
103
|
+
|
|
104
|
+
`/api/dev-helper` uses the same loopback-only identity and durable Profile
|
|
105
|
+
metadata store. Provisioning also creates its application-scoped metafield;
|
|
106
|
+
the route never creates schema or accepts actor, tenant, policy, provider, or
|
|
107
|
+
asset URL fields from the browser. It resolves the current gallery, allowed
|
|
108
|
+
voice, and helper policy on every load/save/reset. Its whole-value persistence
|
|
109
|
+
semantic is last-successful-write-wins, so a production host with concurrent
|
|
110
|
+
editors must serialize writes per profile/application or supply a stronger
|
|
111
|
+
adapter. The speech route resolves the saved offered voice server-side; clients
|
|
112
|
+
submit only text. Helper preference loading never starts audio or a microphone.
|
|
113
|
+
|
|
114
|
+
### Character conversation lifecycle
|
|
115
|
+
|
|
116
|
+
The Character conversation tab refreshes the persisted rig each time it becomes
|
|
117
|
+
active, retaining its AssistantDock controller and history. Leaving the tab stops
|
|
118
|
+
microphone input and reply playback; listening mode requires an explicit restart.
|
|
119
|
+
An outstanding turn keeps both the listening input and full dock composer busy
|
|
120
|
+
across mode changes and restarts until it settles. Both inputs share the retained
|
|
121
|
+
controller’s pending-send state; microphone Stop remains reachable. Polling
|
|
122
|
+
retains active sends until their transport completes, even when older history
|
|
123
|
+
contains the same message text.
|
|
124
|
+
Late persistence loads and cancelled speech responses cannot replace the current
|
|
125
|
+
rig or audio. Spoken captions begin only when SDK playback actually starts.
|
|
126
|
+
Listening mode also keeps the newest assistant reply in a dedicated reply
|
|
127
|
+
region, so a muted or failed spoken reply is still readable while history stays
|
|
128
|
+
hidden. A speech failure is shown beside the conversation controls; disabling
|
|
129
|
+
spoken replies aborts its outstanding request and playback. While the tab is
|
|
130
|
+
inactive, its fixed dock is hidden and polling is paused.
|
|
131
|
+
|
|
132
|
+
The development workbench links the SDK speech and animation packages from
|
|
133
|
+
sibling worktrees. Keep those worktrees available until their released package
|
|
134
|
+
versions replace the links; a fresh browser or Vite load cannot resolve a
|
|
135
|
+
linked package whose sibling checkout is absent.
|
|
136
|
+
|
|
137
|
+
The development transport sends at most 24 context messages within a 16 KiB UTF-8
|
|
138
|
+
JSON body, dropping oldest context first. Failed turns are not committed to its
|
|
139
|
+
history, so retrying does not duplicate them. Draft proposals use one shared
|
|
140
|
+
200-character limit in the model tool, route, preview, and execution boundary.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Helper preferences server contract
|
|
2
|
+
|
|
3
|
+
`HelperPreferencesService` is presentation policy, never an identity, persona,
|
|
4
|
+
tool, or authorization authority. A host creates `HelperContext` from its
|
|
5
|
+
authenticated request and supplies `authorize`, `resolvePolicy`, registered
|
|
6
|
+
`styleIds`, and `validateOffering`. Browser payloads never supply those values.
|
|
7
|
+
|
|
8
|
+
Use `createHelperProfilePreferenceStore({ db, applicationId, metafieldSlug })` only with an
|
|
9
|
+
already provisioned, application-scoped ProfileMetafield. The adapter checks
|
|
10
|
+
the context profile and tenant on every operation, fails duplicate rows, and
|
|
11
|
+
persists one complete versioned preference value. `clear()` writes the explicit
|
|
12
|
+
`{ version: 1, cleared: true }` marker. Its durable guarantee is complete
|
|
13
|
+
last-successful-write-wins values **only when the host serializes writes for a
|
|
14
|
+
profile/application across instances**; it does not offer distributed locking
|
|
15
|
+
or compare-and-swap. Hosts requiring either must supply their own store adapter.
|
|
16
|
+
|
|
17
|
+
The service re-resolves policy and validates saved values on each load, save,
|
|
18
|
+
and reset. Invalid old values produce a recoverable snapshot without silently
|
|
19
|
+
overwriting metadata. Owner-assigned policies ignore personal state and deny
|
|
20
|
+
save/reset. An offering validator must re-check saved asset ownership, tenant,
|
|
21
|
+
relationship, and payload before it can become effective.
|
|
22
|
+
|
|
23
|
+
When an editable policy has no valid effective helper but can preserve its
|
|
24
|
+
locked baseline fields while selecting an offered replacement, the snapshot
|
|
25
|
+
contains `recoveryDraft`. It is an edit candidate only: UI must submit it to
|
|
26
|
+
`save()` and wait for the returned snapshot before applying it.
|
|
27
|
+
|
|
28
|
+
## Browser DTO import
|
|
29
|
+
|
|
30
|
+
Browser clients import the DTO, parser, and field list from
|
|
31
|
+
`@happyvertical/smrt-chat/helper-preferences`. This public subpath deliberately
|
|
32
|
+
contains no Profile/SQL adapter or `HelperPreferencesService`; authenticated
|
|
33
|
+
servers import those server APIs from the package root.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import {
|
|
37
|
+
helperPreferenceFields,
|
|
38
|
+
parseHelperPreferences,
|
|
39
|
+
type HelperClient,
|
|
40
|
+
type HelperPreferences,
|
|
41
|
+
} from '@happyvertical/smrt-chat/helper-preferences';
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Svelte helper controls
|
|
45
|
+
|
|
46
|
+
`HelperControlPanel` distinguishes a local draft from the applied server
|
|
47
|
+
snapshot. Selecting a gallery item, changing a control, or completing photo
|
|
48
|
+
setup creates an unsaved draft; only the snapshot returned by `save()` is
|
|
49
|
+
applied through `onchanged`. Photo setup returns to the settings panel, selects
|
|
50
|
+
the newly returned offering in the draft, and restores focus to its gallery
|
|
51
|
+
choice so the next action is explicit: **Save changes**.
|
|
52
|
+
|
|
53
|
+
Gallery choices use roving radio keyboard behavior (Arrow keys, Home, End).
|
|
54
|
+
Hosts should provide distinct safe offering labels, such as a user-provided
|
|
55
|
+
name with a saved timestamp, rather than raw duplicate filenames.
|
|
56
|
+
|
|
57
|
+
`FloatingAssistant` accepts an optional bottom-corner placement. Conversation
|
|
58
|
+
hosts pass the effective saved placement to keep the dock with its selected
|
|
59
|
+
helper. Opening listening mode may reveal the existing assistant surface, but
|
|
60
|
+
must never start microphone capture or audio playback; those remain explicit
|
|
61
|
+
user actions.
|
|
62
|
+
|
|
63
|
+
`FloatingAssistant.hideIdleControls` is opt-in and defaults to false for existing hosts.
|
|
64
|
+
The character listening workbench enables it to hide idle controls chrome while
|
|
65
|
+
keeping active runs, errors and confirmation surfaces reachable.
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Overview assistant tools (#3727 phase 4)
|
|
2
|
+
|
|
3
|
+
`createOverviewTools({ host, undoStore?, audit?, maxOperations? })`
|
|
4
|
+
(`src/overview-tools.ts`, package index) returns three `PrincipalTool`s for the
|
|
5
|
+
persona / assistant-route `extraTools` seam. They let the assistant add and
|
|
6
|
+
configure widgets on a customizable overview page ("add a chart of overdue
|
|
7
|
+
invoices") with structured operations, and undo its last change. The overview
|
|
8
|
+
model, widget registry and option schemas are smrt-svelte's
|
|
9
|
+
([overview-surfaces.md](../../smrt-svelte/agents/overview-surfaces.md)).
|
|
10
|
+
|
|
11
|
+
| Slug (function name) | Arguments | Result |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `overviews.describe` (`overviews-describe`) | `{ page? }` | `{ page, overview }`: allowed widget types with option fields (type, required, default, range, `choices`, page `models`), span range, the current widgets in order, `canCustomize`. Without `page`: `{ pages }` from `host.pages` |
|
|
14
|
+
| `overviews.apply` (`overviews-apply`) | `{ page, operations }` | `{ page, changed, applied: [{ index, op, id }], undoToken, overview }`; `changed: false` and `undoToken: null` for a batch that changes nothing |
|
|
15
|
+
| `overviews.undo` (`overviews-undo`) | `{ page, undoToken }` | `{ page, undone: true, overview }` |
|
|
16
|
+
|
|
17
|
+
Operations (1 to `maxOperations`, default 20): `add { type, span?, options?,
|
|
18
|
+
index? }`, `configure { id, options }` (merged onto the current options; `null`
|
|
19
|
+
clears a key to its default), `move { id, index }`, `resize { id, span }`,
|
|
20
|
+
`remove { id }`. Option values are JSON primitives over the widget's closed
|
|
21
|
+
field vocabulary; there is no query, filter expression, URL or SQL anywhere.
|
|
22
|
+
|
|
23
|
+
## Authority
|
|
24
|
+
|
|
25
|
+
- **The host decides, per principal.** `host.open(run, pageId)` returns the
|
|
26
|
+
page for that `PrincipalRun` or `null` (unknown and not offered answer the
|
|
27
|
+
same 404). Build it with smrt-svelte's `createOverviewAssistantSurface`
|
|
28
|
+
(`./overview/server`) from the page's `OverviewDefinition`, the
|
|
29
|
+
principal's own stored override, `canCustomize` from the principal's role,
|
|
30
|
+
and `persist`. Never derive any of these from model input except the page id.
|
|
31
|
+
- **Tenant.** Like the runtime-report tools, with tenancy on a run without
|
|
32
|
+
`run.context.tenantId` is refused (403 `tenant_required`), `host.open` and
|
|
33
|
+
every surface call (including `persist`) run under the principal's tenant
|
|
34
|
+
(entered when no tenant context is active), and an ambient tenant that
|
|
35
|
+
differs from the principal's is refused (`DataSurfaceDeniedError`) unless
|
|
36
|
+
the caller is in a system-context or super-admin path. The host scopes its
|
|
37
|
+
reads and writes by `run.context.tenantId`.
|
|
38
|
+
- **Validation is the overview model's.** The surface plans each batch with
|
|
39
|
+
`planOverviewOperations` and checks the result with `checkOverviewOverride`
|
|
40
|
+
(allowed set and `allowedIn`, page-confined `models`, option schemas, span
|
|
41
|
+
ranges, the widget cap), so the assistant can only store an override the
|
|
42
|
+
page's own save endpoint accepts. A batch is atomic.
|
|
43
|
+
- **Errors** are `OverviewToolError` with `status`, `code`, `publicMessage`,
|
|
44
|
+
`issues`. `runToolLoop` classifies 404/409/422 as `invalid_request` (the model
|
|
45
|
+
sees `publicMessage`, which lists every issue as `#<n> <op>: <message>`, and is
|
|
46
|
+
told to fix and retry) and 403 as `not_permitted`. Codes: `invalid_page`,
|
|
47
|
+
`invalid_operations` (422), `unknown_page` (404), `tenant_required` (403),
|
|
48
|
+
`not_allowed` (403,
|
|
49
|
+
`canCustomize` false, on apply and on undo), `conflict` (409, apply lost a
|
|
50
|
+
race with another save), `nothing_to_undo`, `changed_since`,
|
|
51
|
+
`cannot_restore` (409).
|
|
52
|
+
- **Writes are conditional.** `surface.persist(override)` answers
|
|
53
|
+
`OverviewToolPersistResult` (`{ ok: true }` or `{ ok: false, reason:
|
|
54
|
+
'conflict' | 'not_allowed' | 'invalid', issues? }`) and must write only if
|
|
55
|
+
the stored row is still the revision the surface was opened on. A save that
|
|
56
|
+
lands between `host.open` and the write is a `conflict`: apply answers 409
|
|
57
|
+
`conflict` (describe again, rebuild the batch), undo answers 409
|
|
58
|
+
`changed_since` and keeps its entry. smrt-svelte's surface takes the loaded
|
|
59
|
+
`revision` and passes `{ revision }` to its `persist` option, so the
|
|
60
|
+
revision-guarded `save` of `@happyvertical/smrt-preferences`'
|
|
61
|
+
`createOverviewStore` plugs in directly (see below).
|
|
62
|
+
- **Undo** is single-step. Apply stores `{ token, before, after }` under
|
|
63
|
+
`[tenantId, userId, pageId]` of the calling run; undo looks it up under the
|
|
64
|
+
calling run only, so another principal's (or tenant's) token is simply not
|
|
65
|
+
found. It refuses when the stored override is no longer `after` (someone
|
|
66
|
+
changed the page since), re-checks `before` with `check`, persists it
|
|
67
|
+
conditionally, and deletes the entry only after that write succeeded.
|
|
68
|
+
Removal is compare-and-delete: `OverviewUndoStore.delete(key, token)` is a
|
|
69
|
+
no-op unless the stored entry still carries that token, so an undo that
|
|
70
|
+
finishes after a newer batch stored its entry never removes that entry.
|
|
71
|
+
Storing is a conditional set: an entry's `createdAt` is stamped right after
|
|
72
|
+
its batch's conditional write succeeded (strictly increasing per process),
|
|
73
|
+
and `set` is a no-op when the stored entry is newer, so an older apply whose
|
|
74
|
+
entry write lands late never replaces a newer batch's entry. Ordering by
|
|
75
|
+
write-success time rather than serializing per key works across replicas
|
|
76
|
+
sharing one store (a lock would only cover one process), and the revision
|
|
77
|
+
guard already serializes the writes themselves; revisions are opaque here,
|
|
78
|
+
so they cannot order entries. Across replicas the stamps rely on clocks
|
|
79
|
+
agreeing to within the time between two applies by the same person on the
|
|
80
|
+
same page.
|
|
81
|
+
- **The undo store must outlive a request and a turn.** Without `undoStore`
|
|
82
|
+
the tools use `defaultOverviewUndoStore()`, one in-memory store per process
|
|
83
|
+
(30-minute TTL, 1000 keys), so tools built per turn
|
|
84
|
+
(`extraTools: (context) => createOverviewTools(...)`) still undo an earlier
|
|
85
|
+
turn's batch. Never pass a store created inside the per-turn factory. A
|
|
86
|
+
multi-replica host passes one shared `OverviewUndoStore`, created once,
|
|
87
|
+
whose `set` (newer `createdAt` wins) and `delete(key, token)` are atomic
|
|
88
|
+
conditional operations.
|
|
89
|
+
- **After a successful write the call never fails.** The undo entry is written
|
|
90
|
+
after the write; if the store throws, apply still answers `changed: true`
|
|
91
|
+
with `undoToken: null` and a `note` not to apply the batch again (a lost entry
|
|
92
|
+
only costs the Undo, while an entry written first could later restore over a
|
|
93
|
+
value the batch never wrote). Audit is best effort; failures go to
|
|
94
|
+
`onError` (default `console.warn`).
|
|
95
|
+
|
|
96
|
+
## Wiring to persistence (phase 3)
|
|
97
|
+
|
|
98
|
+
The surface is the only seam; this package has no table and no dependency on
|
|
99
|
+
smrt-svelte or `@happyvertical/smrt-preferences` (smrt-svelte depends on chat
|
|
100
|
+
for tests, so the types are structural). Phase 3 stores overview layouts in
|
|
101
|
+
`@happyvertical/smrt-preferences` (table `_smrt_ui_preferences`, preference
|
|
102
|
+
kind `overview`); its overview layer is `createOverviewStore({ db })`, built
|
|
103
|
+
over the generic `createPreferenceStore({ db })` (pass `preferences` to share
|
|
104
|
+
one). Every store call acts as the ambient principal, which is the run's
|
|
105
|
+
principal inside the tool loop, under the tenant the tools enter.
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { createOverviewTools } from '@happyvertical/smrt-chat';
|
|
109
|
+
import {
|
|
110
|
+
createOverviewStore,
|
|
111
|
+
withTenantDefaults,
|
|
112
|
+
} from '@happyvertical/smrt-preferences';
|
|
113
|
+
import { createOverviewAssistantSurface } from '@happyvertical/smrt-svelte/overview/server';
|
|
114
|
+
|
|
115
|
+
// Module scope: created once, shared by every turn and request.
|
|
116
|
+
const overviewUndo = createSharedOverviewUndoStore(); // or omit for the process default
|
|
117
|
+
|
|
118
|
+
const overviewTools = (db: DatabaseInterface /* per turn */) => {
|
|
119
|
+
const store = createOverviewStore({ db });
|
|
120
|
+
return createOverviewTools({
|
|
121
|
+
undoStore: overviewUndo,
|
|
122
|
+
host: {
|
|
123
|
+
async open(run, pageId) {
|
|
124
|
+
const definition = overviewDefinitions.get(pageId);
|
|
125
|
+
if (!definition) return null;
|
|
126
|
+
const state = await store.load(definition, registry);
|
|
127
|
+
if (!state.user) return null;
|
|
128
|
+
return createOverviewAssistantSurface({
|
|
129
|
+
// The user tier is a delta over the tenant default.
|
|
130
|
+
definition: withTenantDefaults(definition, state.tenant.document),
|
|
131
|
+
registry,
|
|
132
|
+
override: state.user.override,
|
|
133
|
+
revision: state.user.revision,
|
|
134
|
+
canCustomize: state.canCustomize.user,
|
|
135
|
+
// The page definition: the store merges the tenant tier itself.
|
|
136
|
+
persist: (override, { revision }) =>
|
|
137
|
+
store.save(definition, registry, { scope: 'user', override, revision }),
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`createSharedOverviewUndoStore` stands for the host's own shared store (any
|
|
146
|
+
`OverviewUndoStore`). How the store's answer maps onto the tools:
|
|
147
|
+
|
|
148
|
+
| `store.save(definition, registry, { scope, override, revision })` (`OverviewSaveResult`) | Surface / tool |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `{ ok: true, override, revision }` | the surface follows the new `revision`; apply answers `changed: true` |
|
|
151
|
+
| `{ ok: false, reason: 'conflict' }` (the loaded `revision` is stale, or a first write raced another; nothing is overwritten) | apply: 409 `conflict`; undo: 409 `changed_since`, entry kept |
|
|
152
|
+
| `{ ok: false, reason: 'not_allowed' }` (no `overviews.personalize` / `overviews.customize`) | 403 `not_allowed` |
|
|
153
|
+
| `{ ok: false, reason: 'invalid', issues }` (`checkOverviewOverride` refused it) | 422 `invalid_operations` |
|
|
154
|
+
|
|
155
|
+
`revision` is `null` when the principal had no row yet; the first write then
|
|
156
|
+
creates it, and a concurrent first write answers `conflict`. Add the three
|
|
157
|
+
slugs to the persona's / route's `allowedTools`.
|
|
158
|
+
|
|
159
|
+
## In the browser
|
|
160
|
+
|
|
161
|
+
When the overview is mounted on the page the dock runs on, use the browser
|
|
162
|
+
tools instead: smrt-svelte's `createOverviewAssistant(controller)` +
|
|
163
|
+
`useOverviewAssistantTools` register `overview_describe`, `overview_apply` and
|
|
164
|
+
`overview_undo` as page tools (add `overview_*` to the route's
|
|
165
|
+
`clientToolAllowList`). A batch lands through `controller.restore`, so the grid
|
|
166
|
+
updates live and the host's `onchange` persists it, and `OverviewAssistantUndo`
|
|
167
|
+
(or a toast) offers Undo. Same operations, same validation, same error shape
|
|
168
|
+
(returned as JSON, not thrown).
|
|
169
|
+
|
|
170
|
+
Tests: `src/overview-tools.test.ts` (contract against a structural surface) and
|
|
171
|
+
`packages/smrt-svelte/src/components/overview/__tests__/assistant-tools.integration.test.ts`
|
|
172
|
+
(these tools over the real model).
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Token Streaming (SSE, #1936)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Reference linked from [../AGENTS.md](../AGENTS.md). This contract was moved from
|
|
4
|
+
`packages/chat/AGENTS.md` to keep the instruction chain under the 32 KB cap
|
|
5
|
+
(`pnpm check:agents-chain`); AGENTS.md retains the cap-oriented summary.
|
|
5
6
|
|
|
6
7
|
`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
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ObjectRegistry, SmrtCollection, SmrtObject, crossPackageRef, field, foreignKey, isPostgresDatabase, smrt } from "@happyvertical/smrt-core";
|
|
2
2
|
import { TenantScoped, tenantId } from "@happyvertical/smrt-tenancy";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
3
|
import { ProfileCollection, resolveAgentProfileId } from "@happyvertical/smrt-profiles";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
5
|
//#region \0rolldown/runtime.js
|
|
6
6
|
var __defProp$7 = Object.defineProperty;
|
|
7
7
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -2225,4 +2225,4 @@ function sendAgentReply(service, params) {
|
|
|
2225
2225
|
//#endregion
|
|
2226
2226
|
export { sendAgentReply as a, ChatThread as c, ChatParticipant as d, ChatMessage as f, clientRequestMessageId as i, ChatRoom as l, ChatService as n, VoiceSessionCollection as o, AgentSession as p, ChatService_exports as r, VoiceSession as s, ChatClientRequestConflictError as t, ChatReaction as u };
|
|
2227
2227
|
|
|
2228
|
-
//# sourceMappingURL=ChatService-
|
|
2228
|
+
//# sourceMappingURL=ChatService-CFaaDHyU.js.map
|