@happyvertical/smrt-chat 0.51.9 → 0.51.11

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.
Files changed (40) hide show
  1. package/AGENTS.md +6 -3
  2. package/dist/chunks/{ChatService-B2piYas4.js → ChatService-D48KBDZy.js} +110 -7
  3. package/dist/chunks/ChatService-D48KBDZy.js.map +1 -0
  4. package/dist/collections/AgentSessionCollection.d.ts +2 -0
  5. package/dist/collections/AgentSessionCollection.d.ts.map +1 -1
  6. package/dist/index.js +10 -7
  7. package/dist/index.js.map +1 -1
  8. package/dist/internal/agent-runtime.js +1 -1
  9. package/dist/manifest.json +34 -5
  10. package/dist/models/AgentSession.d.ts +15 -0
  11. package/dist/models/AgentSession.d.ts.map +1 -1
  12. package/dist/persona-conversation.d.ts +6 -0
  13. package/dist/persona-conversation.d.ts.map +1 -1
  14. package/dist/services/ChatService.d.ts +16 -1
  15. package/dist/services/ChatService.d.ts.map +1 -1
  16. package/dist/smrt-knowledge.json +23 -9
  17. package/dist/svelte/components/assistant/AssistantComposer.svelte +2 -0
  18. package/dist/svelte/components/assistant/AssistantDock.svelte +339 -144
  19. package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts +18 -3
  20. package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts.map +1 -1
  21. package/dist/svelte/components/assistant/__tests__/AssistantDock.test.js +121 -1
  22. package/dist/svelte/components/assistant/__tests__/assistant-dock-action-outcome.test.js +320 -0
  23. package/dist/svelte/components/assistant/__tests__/assistant-dock-host-actions.test.js +185 -0
  24. package/dist/svelte/components/assistant/__tests__/create-assistant-dock-controller.test.js +10 -11
  25. package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.d.ts +30 -0
  26. package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.d.ts.map +1 -1
  27. package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.js +0 -0
  28. package/dist/svelte/i18n.d.ts +3 -0
  29. package/dist/svelte/i18n.d.ts.map +1 -1
  30. package/dist/svelte/i18n.js +3 -0
  31. package/dist/svelte/index.d.ts +1 -1
  32. package/dist/svelte/index.d.ts.map +1 -1
  33. package/dist/svelte/index.js +1 -1
  34. package/dist/tool-loop.d.ts +7 -0
  35. package/dist/tool-loop.d.ts.map +1 -1
  36. package/dist/types.d.ts +2 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/voice.d.ts.map +1 -1
  39. package/package.json +14 -12
  40. package/dist/chunks/ChatService-B2piYas4.js.map +0 -1
@@ -1,2 +1,2 @@
1
- import { r as sendAgentReply } from "../chunks/ChatService-B2piYas4.js";
1
+ import { r as sendAgentReply } from "../chunks/ChatService-D48KBDZy.js";
2
2
  export { sendAgentReply };
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-chat",
5
- "packageVersion": "0.51.9",
5
+ "packageVersion": "0.51.11",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-chat:AgentSessionCollection": {
8
8
  "name": "agentsessioncollection",
@@ -138,7 +138,7 @@
138
138
  "collectionExportName": "AgentSessionCollectionCollection",
139
139
  "schema": {
140
140
  "tableName": "agent_sessions",
141
- "ddl": "CREATE TABLE IF NOT EXISTS \"agent_sessions\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"agent_id\" TEXT NOT NULL DEFAULT '',\n \"participant_profile_id\" UUID NOT NULL,\n \"chat_room_id\" UUID,\n \"status\" TEXT NOT NULL DEFAULT 'active',\n \"allowed_tools\" TEXT DEFAULT '[]',\n \"session_context\" TEXT DEFAULT '{}',\n \"system_prompt\" TEXT DEFAULT '',\n \"message_count\" INTEGER DEFAULT 0,\n \"total_tokens_used\" INTEGER DEFAULT 0,\n \"max_tokens\" INTEGER DEFAULT 0,\n \"max_messages\" INTEGER DEFAULT 0,\n \"last_message_at\" TIMESTAMP,\n \"expires_at\" TIMESTAMP,\n \"closed_at\" TIMESTAMP\n);",
141
+ "ddl": "CREATE TABLE IF NOT EXISTS \"agent_sessions\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"agent_id\" TEXT NOT NULL DEFAULT '',\n \"agent_profile_id\" UUID,\n \"participant_profile_id\" UUID NOT NULL,\n \"chat_room_id\" UUID,\n \"status\" TEXT NOT NULL DEFAULT 'active',\n \"allowed_tools\" TEXT DEFAULT '[]',\n \"session_context\" TEXT DEFAULT '{}',\n \"system_prompt\" TEXT DEFAULT '',\n \"message_count\" INTEGER DEFAULT 0,\n \"total_tokens_used\" INTEGER DEFAULT 0,\n \"max_tokens\" INTEGER DEFAULT 0,\n \"max_messages\" INTEGER DEFAULT 0,\n \"last_message_at\" TIMESTAMP,\n \"expires_at\" TIMESTAMP,\n \"closed_at\" TIMESTAMP\n);",
142
142
  "columns": {
143
143
  "id": {
144
144
  "type": "UUID",
@@ -177,6 +177,12 @@
177
177
  "unique": false,
178
178
  "default": ""
179
179
  },
180
+ "agent_profile_id": {
181
+ "type": "UUID",
182
+ "referenceKind": "crossPackageRef",
183
+ "notNull": false,
184
+ "unique": false
185
+ },
180
186
  "participant_profile_id": {
181
187
  "type": "UUID",
182
188
  "referenceKind": "crossPackageRef",
@@ -277,6 +283,12 @@
277
283
  "created_at"
278
284
  ]
279
285
  },
286
+ {
287
+ "name": "agent_sessions_agent_profile_id_idx",
288
+ "columns": [
289
+ "agent_profile_id"
290
+ ]
291
+ },
280
292
  {
281
293
  "name": "agent_sessions_participant_profile_id_idx",
282
294
  "columns": [
@@ -290,7 +302,7 @@
290
302
  ]
291
303
  }
292
304
  ],
293
- "version": "43eaea36"
305
+ "version": "56b10067"
294
306
  },
295
307
  "importPath": "@happyvertical/smrt-chat"
296
308
  },
@@ -1965,6 +1977,11 @@
1965
1977
  "required": true
1966
1978
  }
1967
1979
  },
1980
+ "agentProfileId": {
1981
+ "type": "crossPackageRef",
1982
+ "required": false,
1983
+ "related": "@happyvertical/smrt-profiles:Profile"
1984
+ },
1968
1985
  "participantProfileId": {
1969
1986
  "type": "crossPackageRef",
1970
1987
  "required": true,
@@ -2207,7 +2224,7 @@
2207
2224
  ],
2208
2225
  "schema": {
2209
2226
  "tableName": "agent_sessions",
2210
- "ddl": "CREATE TABLE IF NOT EXISTS \"agent_sessions\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"agent_id\" TEXT NOT NULL DEFAULT '',\n \"participant_profile_id\" UUID NOT NULL,\n \"chat_room_id\" UUID,\n \"status\" TEXT NOT NULL DEFAULT 'active',\n \"allowed_tools\" TEXT DEFAULT '[]',\n \"session_context\" TEXT DEFAULT '{}',\n \"system_prompt\" TEXT DEFAULT '',\n \"message_count\" INTEGER DEFAULT 0,\n \"total_tokens_used\" INTEGER DEFAULT 0,\n \"max_tokens\" INTEGER DEFAULT 0,\n \"max_messages\" INTEGER DEFAULT 0,\n \"last_message_at\" TIMESTAMP,\n \"expires_at\" TIMESTAMP,\n \"closed_at\" TIMESTAMP\n);",
2227
+ "ddl": "CREATE TABLE IF NOT EXISTS \"agent_sessions\" (\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID,\n \"agent_id\" TEXT NOT NULL DEFAULT '',\n \"agent_profile_id\" UUID,\n \"participant_profile_id\" UUID NOT NULL,\n \"chat_room_id\" UUID,\n \"status\" TEXT NOT NULL DEFAULT 'active',\n \"allowed_tools\" TEXT DEFAULT '[]',\n \"session_context\" TEXT DEFAULT '{}',\n \"system_prompt\" TEXT DEFAULT '',\n \"message_count\" INTEGER DEFAULT 0,\n \"total_tokens_used\" INTEGER DEFAULT 0,\n \"max_tokens\" INTEGER DEFAULT 0,\n \"max_messages\" INTEGER DEFAULT 0,\n \"last_message_at\" TIMESTAMP,\n \"expires_at\" TIMESTAMP,\n \"closed_at\" TIMESTAMP\n);",
2211
2228
  "columns": {
2212
2229
  "id": {
2213
2230
  "type": "UUID",
@@ -2246,6 +2263,12 @@
2246
2263
  "unique": false,
2247
2264
  "default": ""
2248
2265
  },
2266
+ "agent_profile_id": {
2267
+ "type": "UUID",
2268
+ "referenceKind": "crossPackageRef",
2269
+ "notNull": false,
2270
+ "unique": false
2271
+ },
2249
2272
  "participant_profile_id": {
2250
2273
  "type": "UUID",
2251
2274
  "referenceKind": "crossPackageRef",
@@ -2346,6 +2369,12 @@
2346
2369
  "created_at"
2347
2370
  ]
2348
2371
  },
2372
+ {
2373
+ "name": "agent_sessions_agent_profile_id_idx",
2374
+ "columns": [
2375
+ "agent_profile_id"
2376
+ ]
2377
+ },
2349
2378
  {
2350
2379
  "name": "agent_sessions_participant_profile_id_idx",
2351
2380
  "columns": [
@@ -2359,7 +2388,7 @@
2359
2388
  ]
2360
2389
  }
2361
2390
  ],
2362
- "version": "43eaea36"
2391
+ "version": "56b10067"
2363
2392
  },
2364
2393
  "importPath": "@happyvertical/smrt-chat"
2365
2394
  },
@@ -23,6 +23,21 @@ export declare class AgentSession extends SmrtObject {
23
23
  static readonly SESSION_KEY_CONTEXT_FIELD = "__sessionKey";
24
24
  tenantId: string | null;
25
25
  agentId: string;
26
+ /**
27
+ * Profile the agent authors messages as (#2995).
28
+ *
29
+ * `agentId` is an application-supplied slug and is NOT a representable
30
+ * author: every authoring seam (`ChatMessage.senderProfileId`,
31
+ * `ChatParticipant.profileId`) is a `crossPackageRef` to `Profile` and
32
+ * therefore a native uuid column, so writing the slug fails the uuid cast
33
+ * (22P02) on PostgreSQL. The agent's `bot` Profile is resolved — created on
34
+ * first use — through `resolveAgentProfile()` in `@happyvertical/smrt-profiles`
35
+ * and recorded here.
36
+ *
37
+ * Nullable so sessions created before #2995 keep loading; `ChatService`
38
+ * resolves and persists the profile lazily on their next agent turn.
39
+ */
40
+ agentProfileId: string | null;
26
41
  participantProfileId: string;
27
42
  chatRoomId: string | null;
28
43
  status: AgentSessionStatus;
@@ -1 +1 @@
1
- {"version":3,"file":"AgentSession.d.ts","sourceRoot":"","sources":["../../src/models/AgentSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE3E;;;;;;;;GAQG;AACH,qBAOa,YAAa,SAAQ,UAAU;IAC1C;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,yBAAyB,kBAAkB;IAG3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,OAAO,EAAE,MAAM,CAAM;IAGrB,oBAAoB,EAAE,MAAM,CAAM;IAGlC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGjC,MAAM,EAAE,kBAAkB,CAAY;IAEtC,8EAA8E;IAE9E,YAAY,EAAE,MAAM,CAAQ;IAE5B,qEAAqE;IAErE,cAAc,EAAE,MAAM,CAAQ;IAE9B,8CAA8C;IAE9C,YAAY,EAAE,MAAM,CAAM;IAG1B,YAAY,EAAE,MAAM,CAAK;IAEzB,eAAe,EAAE,MAAM,CAAK;IAE5B,SAAS,EAAE,MAAM,CAAK;IAEtB,WAAW,EAAE,MAAM,CAAK;IAGxB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAQ;IAElC,SAAS,EAAE,IAAI,GAAG,IAAI,CAAQ;IAE9B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAQ;gBAEjB,OAAO,GAAE,mBAAwB;IA2B7C,eAAe,IAAI,MAAM,EAAE;IAW3B,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAItC;;;;;;;OAOG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKxC,QAAQ,IAAI,OAAO;IAUnB,SAAS,IAAI,OAAO;IAId,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAM7B,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQ5C,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIrD;;;;;OAKG;IACH,aAAa,IAAI,MAAM,GAAG,IAAI;IAMxB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrE,aAAa,CAAC,UAAU,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAM3D"}
1
+ {"version":3,"file":"AgentSession.d.ts","sourceRoot":"","sources":["../../src/models/AgentSession.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE3E;;;;;;;;GAQG;AACH,qBAOa,YAAa,SAAQ,UAAU;IAC1C;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,yBAAyB,kBAAkB;IAG3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,OAAO,EAAE,MAAM,CAAM;IAErB;;;;;;;;;;;;;OAaG;IAEH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGrC,oBAAoB,EAAE,MAAM,CAAM;IAGlC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAQ;IAGjC,MAAM,EAAE,kBAAkB,CAAY;IAEtC,8EAA8E;IAE9E,YAAY,EAAE,MAAM,CAAQ;IAE5B,qEAAqE;IAErE,cAAc,EAAE,MAAM,CAAQ;IAE9B,8CAA8C;IAE9C,YAAY,EAAE,MAAM,CAAM;IAG1B,YAAY,EAAE,MAAM,CAAK;IAEzB,eAAe,EAAE,MAAM,CAAK;IAE5B,SAAS,EAAE,MAAM,CAAK;IAEtB,WAAW,EAAE,MAAM,CAAK;IAGxB,aAAa,EAAE,IAAI,GAAG,IAAI,CAAQ;IAElC,SAAS,EAAE,IAAI,GAAG,IAAI,CAAQ;IAE9B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAQ;gBAEjB,OAAO,GAAE,mBAAwB;IA6B7C,eAAe,IAAI,MAAM,EAAE;IAW3B,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAItC;;;;;;;OAOG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKxC,QAAQ,IAAI,OAAO;IAUnB,SAAS,IAAI,OAAO;IAId,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAM7B,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQ5C,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAIrD;;;;;OAKG;IACH,aAAa,IAAI,MAAM,GAAG,IAAI;IAMxB,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrE,aAAa,CAAC,UAAU,GAAE,MAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAM3D"}
@@ -150,6 +150,12 @@ export interface PersonaConversationTurnOptions {
150
150
  audit?: PrincipalAuditSink;
151
151
  /** Opt into Postgres RLS transaction wrapping. */
152
152
  postgresRls?: boolean;
153
+ /**
154
+ * Permission snapshot the turn's tool loop runs with (forwarded to
155
+ * `executeAsPrincipal`). Use it to narrow the assistant below the user's
156
+ * live grants; when omitted, the user's live permissions are resolved.
157
+ */
158
+ permissions?: string[];
153
159
  /** Correlation id for the turn (feedback ties back to it). Auto-generated when omitted. */
154
160
  correlationId?: string;
155
161
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"persona-conversation.d.ts","sourceRoot":"","sources":["../src/persona-conversation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACd,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAMlC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,cAAc,EACpB,MAAM,gBAAgB,CAAC;AAExB;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,2EAA2E;IAC3E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qFAAqF;AACrF,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,mBAAmB,CAWtB;AAED,yEAAyE;AACzE,wBAAgB,mCAAmC,CAAC,OAAO,EAAE;IAC3D,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,MAAM,EAAE,CAAC;CACjC,GAAG,mBAAmB,CAWtB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,mBAAmB,GAC3B,gBAAgB,CAOlB;AAED,+EAA+E;AAC/E,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,cAAc,CAAC,EAAE,sBAAsB,CAAC;CACzC;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAC1B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAiBjC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,CAY5E;AAED;;;;;;;GAOG;AACH,wBAAsB,+BAA+B,CACnD,EAAE,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAC1B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,uDAAuD;AACvD,KAAK,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,YAAY,GAAG,cAAc,CAAC,CAAC;AAE5E,0EAA0E;AAC1E,MAAM,WAAW,wBAAwB;IACvC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,uBAAuB;IACvB,EAAE,EAAE,WAAW,CAAC;IAChB,4DAA4D;IAC5D,EAAE,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC3B,gDAAgD;IAChD,OAAO,EAAE,mBAAmB,CAAC;IAC7B,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC7B,mDAAmD;IACnD,WAAW,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC9C,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,oBAAoB,GAAG,KAAK,CAAC;IACtC,+EAA+E;IAC/E,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;IACvB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iFAAiF;IACjF,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,kDAAkD;IAClD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,wDAAwD;AACxD,MAAM,WAAW,6BAA6B;IAC5C,kEAAkE;IAClE,MAAM,EAAE,cAAc,CAAC;IACvB,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,gDAAgD;IAChD,YAAY,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,4BAA4B,CAAC;CACjD;AAED,oEAAoE;AACpE,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,gBAAgB,EAAE,WAAW,GAAG,IAAI,CAAC;CACtC;AAiBD;;;;;;;;;;GAUG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,6BAA6B,CAAC,CA6ExC;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,0EAA0E;IAC1E,WAAW,EAAE;QACX,wBAAwB,CAAC,MAAM,EAAE;YAC/B,cAAc,EAAE,MAAM,CAAC;YACvB,cAAc,EAAE,MAAM,CAAC;YACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;YACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;YACxB,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;KAC3B,CAAC;IACF,2BAA2B;IAC3B,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAClC,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,0CAA0C;IAC1C,OAAO,EAAE,mBAAmB,CAAC;IAC7B,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,EAAE,CAAC,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,YAAY,CAAC,CAavB"}
1
+ {"version":3,"file":"persona-conversation.d.ts","sourceRoot":"","sources":["../src/persona-conversation.ts"],"names":[],"mappings":"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"}
@@ -227,6 +227,20 @@ export declare class ChatService {
227
227
  maxTokens?: number;
228
228
  maxMessages?: number;
229
229
  sessionKey?: string | null;
230
+ /**
231
+ * Profile the agent authors as (#2995). Optional: when omitted the agent's
232
+ * `bot` Profile is resolved (created on first use) from `agentId`. Supply
233
+ * it when the SERVER already holds the agent's acting identity — e.g. a
234
+ * persona's `actsAsProfileId`.
235
+ *
236
+ * Like `actorProfileId`, this is a SERVER-supplied value and must never be
237
+ * request input: it selects the author of every later assistant message in
238
+ * the session. It is validated in {@link ChatService.requireAgentProfile}
239
+ * (must exist, must be visible in this tenant, must not be the acting
240
+ * participant) and can never re-point a session that already resolved a
241
+ * different agent profile.
242
+ */
243
+ agentProfileId?: string | null;
230
244
  }): Promise<{
231
245
  session: AgentSession;
232
246
  room: import('../index.js').ChatRoom;
@@ -378,7 +392,8 @@ export type AgentReplyService = Pick<ChatService, 'initialize'>;
378
392
  * NOT re-exported from `src/index.ts`. Only in-process, trusted agent-runtime
379
393
  * code that imports this module path directly can author messages as the agent;
380
394
  * route handlers and package consumers (which import from the package index)
381
- * cannot. The author is always `session.agentId`, never a caller-supplied
395
+ * cannot. The author is always the session's resolved agent Profile
396
+ * (`session.agentProfileId`), never a caller-supplied
382
397
  * sender/role, and tool calls are gated fail-closed against `allowedTools`.
383
398
  *
384
399
  * Because this lives in the same module as {@link ChatService}, reaching the
@@ -1 +1 @@
1
- {"version":3,"file":"ChatService.d.ts","sourceRoot":"","sources":["../../src/services/ChatService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ChatService.d.ts","sourceRoot":"","sources":["../../src/services/ChatService.ts"],"names":[],"mappings":"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"}
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-chat",
6
- "packageVersion": "0.51.9",
6
+ "packageVersion": "0.51.11",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "84bcc74c538d87aa22b579ba2a3682e0eea05f8a9f7f5c20e2fe35904c51e625",
11
- "packageJson": "76e1bddee9191ac9005fbe44fc088becd59616e4745ae0b9bdbf3f115209e1e2",
12
- "agents": "09fd59894602d9ff76216fdec92ef2e4eb20d63e0a9a469ba9217af3ed8efca2"
10
+ "manifest": "3a04c2aabb80bae56e0d5830cf1e74fb4f2fbac28a83050377c284e5388bf90b",
11
+ "packageJson": "8f17af1f90529171de4d93ac8aad2a7d8ebe9288bca8361f6bffe1e4659cc746",
12
+ "agents": "a167a59eefa129a89b54bbb34ea7ebe82c7c8840f7aa6e5cbb65bcd96c7395e9"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -28,13 +28,13 @@
28
28
  "@happyvertical/smrt-agents": "workspace:*",
29
29
  "@happyvertical/smrt-core": "workspace:*",
30
30
  "@happyvertical/smrt-personas": "workspace:*",
31
+ "@happyvertical/smrt-profiles": "workspace:*",
31
32
  "@happyvertical/smrt-tenancy": "workspace:*",
32
33
  "@happyvertical/smrt-types": "workspace:*",
33
34
  "@happyvertical/smrt-ui": "workspace:*",
34
35
  "@happyvertical/smrt-users": "workspace:*",
35
36
  "@happyvertical/sql": "catalog:",
36
37
  "@happyvertical/smrt-playground": "workspace:*",
37
- "@happyvertical/smrt-profiles": "workspace:*",
38
38
  "@happyvertical/smrt-vitest": "workspace:*",
39
39
  "@sveltejs/kit": "^2.69.1",
40
40
  "@sveltejs/package": "^2.5.8",
@@ -73,7 +73,7 @@
73
73
  "threads"
74
74
  ],
75
75
  "risks": [
76
- "cross-package-refs:8",
76
+ "cross-package-refs:9",
77
77
  "sensitive-fields-excluded"
78
78
  ],
79
79
  "objects": [
@@ -1244,6 +1244,13 @@
1244
1244
  "columnType": "TEXT",
1245
1245
  "default": ""
1246
1246
  },
1247
+ {
1248
+ "name": "agentProfileId",
1249
+ "type": "crossPackageRef",
1250
+ "required": false,
1251
+ "related": "@happyvertical/smrt-profiles:Profile",
1252
+ "columnType": "UUID"
1253
+ },
1247
1254
  {
1248
1255
  "name": "participantProfileId",
1249
1256
  "type": "crossPackageRef",
@@ -1334,6 +1341,13 @@
1334
1341
  }
1335
1342
  ],
1336
1343
  "relationships": [
1344
+ {
1345
+ "name": "agentProfileId",
1346
+ "type": "crossPackageRef",
1347
+ "required": false,
1348
+ "related": "@happyvertical/smrt-profiles:Profile",
1349
+ "columnType": "UUID"
1350
+ },
1337
1351
  {
1338
1352
  "name": "participantProfileId",
1339
1353
  "type": "crossPackageRef",
@@ -5102,11 +5116,11 @@
5102
5116
  "prompts": [],
5103
5117
  "relationshipsV2": {
5104
5118
  "foreignKeyFields": 14,
5105
- "crossPackageRefFields": 8,
5119
+ "crossPackageRefFields": 9,
5106
5120
  "junctionCollections": 0,
5107
5121
  "hierarchicalObjects": 0,
5108
5122
  "polymorphicAssociations": 0,
5109
- "uuidColumns": 76
5123
+ "uuidColumns": 78
5110
5124
  },
5111
- "agentDoc": "# @happyvertical/smrt-chat\n\nChat rooms, threads, and agent sessions with app-controlled tool whitelisting.\n\n## Dev Server\n\n`pnpm --dir packages/chat dev` runs a package-local SvelteKit workbench. The root\nroute is an interactive chat surface with a dev-only `/api/dev-chat` endpoint:\nit uses `@happyvertical/ai` when local provider credentials are present and\nfalls back to a deterministic local assistant otherwise. `/api/dev-chat-stream`\nis its SSE companion (#1936) — the same provider/local-fallback resolution wired\nthrough `createChatStreamHandler` in plain mode, so an embedded `SmrtChatBackend`\nclient can exercise token streaming locally. `/previews` hosts the shared\ncomponent playground entries from `src/svelte/playground.ts`.\n\nThe root workbench also has a dev-only voice conversation mode. It reads voice\ngateway connection details through `/api/dev-voice/config`, streams browser mic\naudio to `WS /ws/voice` as PCM16 mono, appends gateway transcripts/responses to\nthe chat, and plays returned TTS audio. Exposing\n`SMRT_CHAT_DEV_VOICE_GATEWAY_TOKEN` to the browser requires\n`SMRT_CHAT_DEV_VOICE_GATEWAY_EXPOSE_TOKEN=true`; keep that local-only.\n\n## Models\n\nAll seven models expose only generated REST/MCP `list`/`get`; structural tests\ncheck the registry for accidental mutating operations. Writes go through\nmembership/owner-checked `ChatService` or the binding-checked voice adapter.\n\n`ChatService` is a closed facade. Its collections and security-sensitive\nhelpers use ES `#private` fields/methods (TypeScript `private` is erased).\nCollection classes are not exported from the package index. The agent-reply\nbridge is a Symbol-keyed static accessible through a non-exported symbol held\nby the module-local `sendAgentReply` function.\n\n- **ChatRoom**: `roomType` (public/private/dm/agent), `status`, `topic`, `maxParticipants`, `lastMessageAt`. Tenant-scoped (required).\n- **ChatMessage**: shared by users + agents. `role` (user/assistant/system/tool), `messageType` (text/system/action/file/tool_call/tool_result), `toolCallData` JSON. Unified model — no separate agent message type. Tenant-scoped (required).\n- **ChatParticipant**: `role` (owner/admin/member/viewer), `onlineStatus`, `lastReadMessageId`, `isMuted`. Tenant-scoped (required).\n- **ChatThread**: `rootMessageId`, `isResolved`, `messageCount`. Created via `ChatService.startThread()` (member-checked). Tenant-scoped (required).\n- **ChatReaction**: `messageId`, `profileId`, `emoji`. Added/removed via `ChatService.addReaction()`/`removeReaction()` (member-checked, self-keyed). Tenant-scoped (required).\n- **AgentSession**: `agentId` (string ref, not FK), `allowedTools` (JSON string array), `sessionContext` (JSON), `systemPrompt`, limits (`maxTokens`/`maxMessages`/`expiresAt`). Optional tenancy.\n- **VoiceSession**: short-lived voice-gateway binding over `(tenant, actorProfileId, personaId, room/thread/agentSession)` plus a persona snapshot, gateway `session_id`, expiry, replay tracking, and metadata. Tenant-scoped (required). Generated surface is read-only; creation and turns go through `createVoiceChatSession()` / `handleVoiceGatewayTurn()`.\n\n## ChatService\n\nPublic writes take server-supplied `actorProfileId`; never trust a request's\n`senderProfileId`, `role`, owner id, or participant id as authority.\n\n| Operation | Authorization and behavior |\n|---|---|\n| `sendMessage()` | Room member; authors as actor with `role: 'user'`; no public membership bypass |\n| `createRoom()` | Actor becomes owner |\n| `startThread()` | Member; optional root message bound to room and tenant |\n| `addParticipant()` / `removeParticipant()` | Owner/admin; self-leave allowed |\n| `updateRoom()` | Owner/admin |\n| `addReaction()` / `removeReaction()` | Member; self-keyed |\n| `getOrCreateDM()` | Actor must be a DM participant |\n| `createAgentSession()` | Actor becomes session participant; existing-session lookup tenant-bound; optional `sessionKey` isolates conversation subjects |\n| `updateAgentSessionConfig()` | Owner; mandatory tenant-bound lookup |\n| `sendAgentUserMessage()` | Actor must be session participant; authored as participant |\n\nRead facade: `getAgentSession({agentSessionId, tenantId})`,\n`findActiveAgentSessions({tenantId, agentId, participantProfileId})`, and\n`getThread({threadId, tenantId})` are tenant-bound; consumers still check\nownership/context. `listRoomThreads({roomId, actorProfileId, tenantId})`,\n`getThreadMessages({threadId, actorProfileId, tenantId, limit?})` (chronological),\n`getRoomMessages({roomId, actorProfileId, tenantId})`, and\n`getRoomForMember(roomId, actorProfileId, tenantId)` additionally require\nmembership of the server-supplied actor, not a caller-selected subject.\n\nTrusted agent runtime imports `sendAgentReply(service, params)` only from\n`@happyvertical/smrt-chat/internal/agent-runtime`. It is not a `ChatService`\nmethod or package-index export. It authors as `session.agentId`, accepts a\nsame-room/tenant thread, and enforces `allowedTools` fail-closed.\n\nPrivate `#writeMessage` alone can select arbitrary authors/roles or bypass\nmembership. It binds every supplied `threadId`, `agentSessionId`, and\n`replyToMessageId` to the same room **and** tenant. Internal enrollment supports\nautomatically created rooms, sessions, and participants.\n\n## Agent Tool Whitelisting\n\n`allowedTools` is a JSON array controlled by the consuming app. Fail-closed: an empty/unparseable whitelist permits NO tools. The internal `sendAgentReply(service, params)` function enforces the whitelist before emitting any `tool`/`tool_call` message; a caller cannot supply a `senderProfileId`/`role` to post as the agent, and the function is not reachable from the package index.\n\nPrincipal-bound data discovery and reads are available as `PrincipalTool[]` via\n`createDataSurfaceTools()` (re-exported by the chat package). Pass the tools as\n`extraTools` to the persona conversation/tool loop. They use the same\nfail-closed `allowedTools` offer/execution gates; RBAC, tenant, redaction,\nbounded query results, and audit authority remain in the authenticated\n`PrincipalRun` supplied by `@happyvertical/smrt-agents`.\n\n## Conversational Harness (L3, #1891)\n\nThe `AgentSession` runtime depends on personas, agents, and users. Keep those\nedges acyclic: none of those packages may depend back on chat.\n\n- **`runToolLoop(options)`** (`tool-loop.ts`) — a bounded `tool_call → observe → respond` loop. Tools are **manifest operations** of installed packages: `buildManifestToolCatalog({ allowedTools })` reads the `PermissionCatalogService` catalog and keeps only the `(collection, action)` entries named in the persona's allow-list (the **offer gate**; absent/empty ⇒ NO tools). The loop runs inside one `executeAsPrincipal` context, and `invokeManifestTool` executes each op **in-process (\"side door\")** against `run.context.database` (the RLS tx when Postgres RLS is on), after re-asserting the fail-closed allow-list (`run.assertToolAllowed`) AND the catalog permission (`run.assertOperation`) — the **execution gate**. Bounded by a max-steps ceiling (`DEFAULT_MAX_STEPS = 8`): on the ceiling it disables tools for one final completion so the turn always terminates with text.\n- **`runPersonaConversationTurn(options)`** (`persona-conversation.ts`) — binds a conversation to an `AgentPersona`/`ResolvedPersona`: runs as its principal (`runAsUserId`), offers only its `allowedTools`, speaks its instructions (`resolvePersonaInstructions`, layering approved learned directives), and injects its **recalled learning memory** (`personaLearningMemory`, isolated per `memoryScope`) into the system prompt. `bindPersonaToSession()` mirrors the persona's `allowedTools`/instructions onto the `AgentSession` so the chat authoring gate agrees with the loop's offer gate. Authors the reply (and each executed tool) via the internal `sendAgentReply` bridge.\n- **Agent orchestration** (L3, #1892) — the loop accepts non-manifest **`extraTools`** (`PrincipalTool[]`, from `@happyvertical/smrt-agents`), gated by the *same* fail-closed allow-list. The standard **`invoke-agent`** tool (`createInvokeAgentTool`, slug `agents.invoke`) lets a conversational agent delegate to a **worker agent under its own principal** — the worker runs via `executeAsPrincipal` as the originating user (never its own authority), the principal is immutable along the chain, and its completion is surfaced back into the conversation. `runPersonaConversationTurn` filters `extraTools` by the persona's `allowedTools` (offer gate); the tool's `execute` re-asserts `assertToolAllowed` (execution gate). See `@happyvertical/smrt-agents` for the delegation envelope + transports.\n- **Chat feedback capture** (`chat-feedback.ts`) — `captureChatFeedback()` + `acceptAppliedChange`/`rejectAppliedChange`/`correctResponse`/`rateResponse`/`thumbsUp`/`thumbsDown` write a `Feedback` row (personas) carrying the conversation's **correlation-id**, and (by default) reinforce the persona's learning memory (`reinforceFromFeedback`). So an in-chat reject decays a strategy below the reuse floor and it stops being recalled; a correction supersedes its stored value.\n\n## Voice Gateway Turns (#1910)\n\nVoice is an input mode for the existing persona chat harness, not a separate chat runtime. `createVoiceChatSession()` creates a short-lived `VoiceSession` for an authenticated actor/profile, binding tenant, persona, agent session, room, and optional thread. `handleVoiceGatewayTurn()` resolves that binding from `metadata.voiceSessionId`, checks the gateway's `session_id` and any supplied tenant/profile/persona/session/thread metadata against the server-side binding, persists the transcript through `ChatService.sendAgentUserMessage()`, runs `runPersonaConversationTurn()`, stamps voice/correlation metadata onto the persisted user/assistant/tool messages, records the gateway `turn_id`, and returns the gateway response contract. The Fetch-compatible `createVoiceGatewayTurnHandler()` adds the coarse gateway bearer-token check.\n\nThe 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()`.\n\n## Token Streaming (SSE, #1936)\n\n`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`.\n\n- **`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).\n- **`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.\n- **`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.\n- **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.\n- **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.\n\n## AssistantDock (#2904)\n\n`AssistantDock` (`svelte/components/assistant/`, exported from `./svelte`) is a\nshell-mounted, route-aware assistant surface: it discovers currently-mounted\n`DataSurfaceDescriptor`s from a host-supplied `DataSurfaceRegistry`\n(`@happyvertical/smrt-ui/data-surface`) and fails closed to plain chat when\nnone are mounted. Action proposals flow through\n`normalizeDataSurfaceActionRequest` → a host-supplied `AssistantActionClient`\n(preview/apply against a server-hosted `DataSurfaceActionAdapter`, not through\nthe `data-surface-bridge.ts` live-collaboration command channel) →\n`ToolCallDisplay`'s additive `actionResult` rendering. `AssistantTransport`\n(`assistant-transport.ts`) is a narrower, separate contract from\n`ChatClientBackend` (`client.ts`): `ChatThread`/`ChatMessage` only expose\ngenerated `list`/`get` REST (`api: { include: ['list', 'get'] }`,\n`models/ChatThread.ts:16`, `models/ChatMessage.ts:26`), so writes need a host-\nsupplied `writeEndpoint`. See [`docs/assistant-dock.md`](../../docs/assistant-dock.md)\nfor the full design, the anytown `PortalChatTool.svelte` polling/stale-send\nreconnaissance, and open gaps.\n\n## Gotchas\n\n- **sessionContext, not context**: `context` is reserved for slug scoping. Use `getSessionContext()`/`updateSessionContext()` for agent memory.\n- **Agent rooms auto-created**: `roomType: 'agent'`, `maxParticipants` defaults to 2; the agent is enrolled as a member so its replies pass the membership check. `createAgentSession()` re-enrolls the participant AND the agent on the existing-session path, so legacy sessions created before the agent was enrolled self-heal.\n- **Per-subject sessions need `sessionKey`**: `createAgentSession()` reuses ANY active session for the same `(agentId, participantProfileId, tenantId)`. Callers that open separate conversations per subject (e.g. one content-editor session per content id) MUST pass a stable `sessionKey` (stored in `sessionContext.__sessionKey`, read via `AgentSession.getSessionKey()`); otherwise a session opened for one subject is reused and its context overwritten for another, surfacing the wrong room/threads (S5 #1392). A keyed create never reuses a keyless/legacy session.\n- **Session expiry**: check `isActive()` before allowing messages (expiresAt or limit-based)\n- **DM identity**: derived from the deterministic per-tenant `canonicalDmRoomId()` and the authoritative `chat_participants` join, not client metadata; concurrent creates upsert onto one row.\n- **Tenant-bound lookups**: membership/session/DM lookups REQUIRE `tenantId` and always bind it into the WHERE clause (`findActiveMembership`/`isActiveMember`/`findActiveSession` take a required `tenantId`; AgentSession's `null` tenant is an explicit bound scope, not \"any tenant\") so they can never resolve a row from another tenant.\n"
5125
+ "agentDoc": "# @happyvertical/smrt-chat\n\nChat rooms, threads, and agent sessions with app-controlled tool whitelisting.\n\n## Dev Server\n\n`pnpm --dir packages/chat dev` runs a package-local SvelteKit workbench. The root\nroute is an interactive chat surface with a dev-only `/api/dev-chat` endpoint:\nit uses `@happyvertical/ai` when local provider credentials are present and\nfalls back to a deterministic local assistant otherwise. `/api/dev-chat-stream`\nis its SSE companion (#1936) — the same provider/local-fallback resolution wired\nthrough `createChatStreamHandler` in plain mode, so an embedded `SmrtChatBackend`\nclient can exercise token streaming locally. `/previews` hosts the shared\ncomponent playground entries from `src/svelte/playground.ts`.\n\nThe root workbench also has a dev-only voice conversation mode. It reads voice\ngateway connection details through `/api/dev-voice/config`, streams browser mic\naudio to `WS /ws/voice` as PCM16 mono, appends gateway transcripts/responses to\nthe chat, and plays returned TTS audio. Exposing\n`SMRT_CHAT_DEV_VOICE_GATEWAY_TOKEN` to the browser requires\n`SMRT_CHAT_DEV_VOICE_GATEWAY_EXPOSE_TOKEN=true`; keep that local-only.\n\n## Models\n\nAll seven models expose only generated REST/MCP `list`/`get`; structural tests\ncheck the registry for accidental mutating operations. Writes go through\nmembership/owner-checked `ChatService` or the binding-checked voice adapter.\n\n`ChatService` is a closed facade. Its collections and security-sensitive\nhelpers use ES `#private` fields/methods (TypeScript `private` is erased).\nCollection classes are not exported from the package index. The agent-reply\nbridge is a Symbol-keyed static accessible through a non-exported symbol held\nby the module-local `sendAgentReply` function.\n\n- **ChatRoom**: `roomType` (public/private/dm/agent), `status`, `topic`, `maxParticipants`, `lastMessageAt`. Tenant-scoped (required).\n- **ChatMessage**: shared by users + agents. `role` (user/assistant/system/tool), `messageType` (text/system/action/file/tool_call/tool_result), `toolCallData` JSON. Unified model — no separate agent message type. Tenant-scoped (required).\n- **ChatParticipant**: `role` (owner/admin/member/viewer), `onlineStatus`, `lastReadMessageId`, `isMuted`. Tenant-scoped (required).\n- **ChatThread**: `rootMessageId`, `isResolved`, `messageCount`. Created via `ChatService.startThread()` (member-checked). Tenant-scoped (required).\n- **ChatReaction**: `messageId`, `profileId`, `emoji`. Added/removed via `ChatService.addReaction()`/`removeReaction()` (member-checked, self-keyed). Tenant-scoped (required).\n- **AgentSession**: `agentId` (application slug, string ref, not FK), `agentProfileId` (nullable `crossPackageRef` to `Profile` — the `bot` profile the agent AUTHORS as, #2995), `allowedTools` (JSON string array), `sessionContext` (JSON), `systemPrompt`, limits (`maxTokens`/`maxMessages`/`expiresAt`). Optional tenancy.\n- **VoiceSession**: short-lived voice-gateway binding over `(tenant, actorProfileId, personaId, room/thread/agentSession)` plus a persona snapshot, gateway `session_id`, expiry, replay tracking, and metadata. Tenant-scoped (required). Generated surface is read-only; creation and turns go through `createVoiceChatSession()` / `handleVoiceGatewayTurn()`.\n\n## ChatService\n\nPublic writes take server-supplied `actorProfileId`; never trust a request's\n`senderProfileId`, `role`, owner id, or participant id as authority.\n\n| Operation | Authorization and behavior |\n|---|---|\n| `sendMessage()` | Room member; authors as actor with `role: 'user'`; no public membership bypass |\n| `createRoom()` | Actor becomes owner |\n| `startThread()` | Member; optional root message bound to room and tenant |\n| `addParticipant()` / `removeParticipant()` | Owner/admin; self-leave allowed |\n| `updateRoom()` | Owner/admin |\n| `addReaction()` / `removeReaction()` | Member; self-keyed |\n| `getOrCreateDM()` | Actor must be a DM participant |\n| `createAgentSession()` | Actor becomes session participant; existing-session lookup tenant-bound; optional `sessionKey` isolates conversation subjects |\n| `updateAgentSessionConfig()` | Owner; mandatory tenant-bound lookup |\n| `sendAgentUserMessage()` | Actor must be session participant; authored as participant |\n\nRead facade: `getAgentSession({agentSessionId, tenantId})`,\n`findActiveAgentSessions({tenantId, agentId, participantProfileId})`, and\n`getThread({threadId, tenantId})` are tenant-bound; consumers still check\nownership/context. `listRoomThreads({roomId, actorProfileId, tenantId})`,\n`getThreadMessages({threadId, actorProfileId, tenantId, limit?})` (chronological),\n`getRoomMessages({roomId, actorProfileId, tenantId})`, and\n`getRoomForMember(roomId, actorProfileId, tenantId)` additionally require\nmembership of the server-supplied actor, not a caller-selected subject.\n\nTrusted agent runtime imports `sendAgentReply(service, params)` only from\n`@happyvertical/smrt-chat/internal/agent-runtime`. It is not a `ChatService`\nmethod or package-index export. It authors as `session.agentProfileId` — the\nagent's resolved `bot` Profile, NEVER the `agentId` slug (#2995) — accepts a\nsame-room/tenant thread, and enforces `allowedTools` fail-closed.\n\nPrivate `#writeMessage` alone can select arbitrary authors/roles or bypass\nmembership. It binds every supplied `threadId`, `agentSessionId`, and\n`replyToMessageId` to the same room **and** tenant. Internal enrollment supports\nautomatically created rooms, sessions, and participants.\n\n## Agent Tool Whitelisting\n\n`allowedTools` is a JSON array controlled by the consuming app. Fail-closed: an empty/unparseable whitelist permits NO tools. The internal `sendAgentReply(service, params)` function enforces the whitelist before emitting any `tool`/`tool_call` message; a caller cannot supply a `senderProfileId`/`role` to post as the agent, and the function is not reachable from the package index.\n\nPrincipal-bound data discovery and reads are available as `PrincipalTool[]` via\n`createDataSurfaceTools()` (re-exported by the chat package). Pass the tools as\n`extraTools` to the persona conversation/tool loop. They use the same\nfail-closed `allowedTools` offer/execution gates; RBAC, tenant, redaction,\nbounded query results, and audit authority remain in the authenticated\n`PrincipalRun` supplied by `@happyvertical/smrt-agents`.\n\n## Conversational Harness (L3, #1891)\n\nThe `AgentSession` runtime depends on personas, agents, and users. Keep those\nedges acyclic: none of those packages may depend back on chat.\n\n- **`runToolLoop(options)`** (`tool-loop.ts`) — a bounded `tool_call → observe → respond` loop. Tools are **manifest operations** of installed packages: `buildManifestToolCatalog({ allowedTools })` reads the `PermissionCatalogService` catalog and keeps only the `(collection, action)` entries named in the persona's allow-list (the **offer gate**; absent/empty ⇒ NO tools). The loop runs inside one `executeAsPrincipal` context, and `invokeManifestTool` executes each op **in-process (\"side door\")** against `run.context.database` (the RLS tx when Postgres RLS is on), after re-asserting the fail-closed allow-list (`run.assertToolAllowed`) AND the catalog permission (`run.assertOperation`) — the **execution gate**. Bounded by a max-steps ceiling (`DEFAULT_MAX_STEPS = 8`): on the ceiling it disables tools for one final completion so the turn always terminates with text.\n- **`runPersonaConversationTurn(options)`** (`persona-conversation.ts`) — binds a conversation to an `AgentPersona`/`ResolvedPersona`: runs as its principal (`runAsUserId`), offers only its `allowedTools`, speaks its instructions (`resolvePersonaInstructions`, layering approved learned directives), and injects its **recalled learning memory** (`personaLearningMemory`, isolated per `memoryScope`) into the system prompt. `bindPersonaToSession()` mirrors the persona's `allowedTools`/instructions onto the `AgentSession` so the chat authoring gate agrees with the loop's offer gate. Authors the reply (and each executed tool) via the internal `sendAgentReply` bridge. Tools run with the run-as user's **live** permissions unless the host passes a `permissions` snapshot (#2978), which `runToolLoop` forwards to `executeAsPrincipal` so a narrowed principal (e.g. a shared station) cannot reach grants — such as `readPermission`-gated data-surface fields — beyond that snapshot, including through `agents.invoke` delegation (the snapshot becomes the worker's permission ceiling).\n- **Agent orchestration** (L3, #1892) — the loop accepts non-manifest **`extraTools`** (`PrincipalTool[]`, from `@happyvertical/smrt-agents`), gated by the *same* fail-closed allow-list. The standard **`invoke-agent`** tool (`createInvokeAgentTool`, slug `agents.invoke`) lets a conversational agent delegate to a **worker agent under its own principal** — the worker runs via `executeAsPrincipal` as the originating user (never its own authority), the principal is immutable along the chain, and its completion is surfaced back into the conversation. `runPersonaConversationTurn` filters `extraTools` by the persona's `allowedTools` (offer gate); the tool's `execute` re-asserts `assertToolAllowed` (execution gate). See `@happyvertical/smrt-agents` for the delegation envelope + transports.\n- **Chat feedback capture** (`chat-feedback.ts`) — `captureChatFeedback()` + `acceptAppliedChange`/`rejectAppliedChange`/`correctResponse`/`rateResponse`/`thumbsUp`/`thumbsDown` write a `Feedback` row (personas) carrying the conversation's **correlation-id**, and (by default) reinforce the persona's learning memory (`reinforceFromFeedback`). So an in-chat reject decays a strategy below the reuse floor and it stops being recalled; a correction supersedes its stored value.\n\n## Voice Gateway Turns (#1910)\n\nVoice is an input mode for the existing persona chat harness, not a separate chat runtime. `createVoiceChatSession()` creates a short-lived `VoiceSession` for an authenticated actor/profile, binding tenant, persona, agent session, room, and optional thread. `handleVoiceGatewayTurn()` resolves that binding from `metadata.voiceSessionId`, checks the gateway's `session_id` and any supplied tenant/profile/persona/session/thread metadata against the server-side binding, persists the transcript through `ChatService.sendAgentUserMessage()`, runs `runPersonaConversationTurn()`, stamps voice/correlation metadata onto the persisted user/assistant/tool messages, records the gateway `turn_id`, and returns the gateway response contract. The Fetch-compatible `createVoiceGatewayTurnHandler()` adds the coarse gateway bearer-token check.\n\nThe 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()`.\n\n## Token Streaming (SSE, #1936)\n\n`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`.\n\n- **`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).\n- **`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.\n- **`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.\n- **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.\n- **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.\n\n## AssistantDock (#2904)\n\n`AssistantDock` (`svelte/components/assistant/`, exported from `./svelte`) is a\nshell-mounted, route-aware assistant surface: it discovers currently-mounted\n`DataSurfaceDescriptor`s from a host-supplied `DataSurfaceRegistry`\n(`@happyvertical/smrt-ui/data-surface`) and fails closed to plain chat when\nnone are mounted. Action proposals flow through\n`normalizeDataSurfaceActionRequest` → a host-supplied `AssistantActionClient`\n(preview/apply against a server-hosted `DataSurfaceActionAdapter`, not through\nthe `data-surface-bridge.ts` live-collaboration command channel) →\n`ToolCallDisplay`'s additive `actionResult` rendering. `AssistantTransport`\n(`assistant-transport.ts`) is a narrower, separate contract from\n`ChatClientBackend` (`client.ts`): `ChatThread`/`ChatMessage` only expose\ngenerated `list`/`get` REST (`api: { include: ['list', 'get'] }`,\n`models/ChatThread.ts:16`, `models/ChatMessage.ts:26`), so writes need a host-\nsupplied `writeEndpoint`. See [`docs/assistant-dock.md`](../../docs/assistant-dock.md)\nfor the full design, the anytown `PortalChatTool.svelte` polling/stale-send\nreconnaissance, and open gaps.\n\n## Gotchas\n\n- **sessionContext, not context**: `context` is reserved for slug scoping. Use `getSessionContext()`/`updateSessionContext()` for agent memory.\n- **Agent rooms auto-created**: `roomType: 'agent'`, `maxParticipants` defaults to 2; the agent is enrolled as a member so its replies pass the membership check. `createAgentSession()` re-enrolls the participant AND the agent on the existing-session path, so legacy sessions created before the agent was enrolled self-heal.\n- **The agent's author is a Profile, not `agentId` (#2995)**: `senderProfileId`/`profileId` are `crossPackageRef`s to `Profile` and therefore uuid columns on PostgreSQL, so the slug fails the cast (`22P02`) and no agent reply can persist. `createAgentSession()` resolves the agent's `bot` Profile through `resolveAgentProfile()` in `@happyvertical/smrt-profiles` (created on first use, tenant-bound) and records it as `AgentSession.agentProfileId`; both enrolment and `sendAgentReply` use that uuid. A consumer whose agent already has an acting identity passes `agentProfileId` to skip resolution — the voice path does this with the persona's `actsAsProfileId`, so persona replies stay attributed to the configured acting profile. Like `actorProfileId`, `agentProfileId` is SERVER-supplied, never request input: it selects the author of every later assistant message. It is validated (`#requireAgentProfile`) to exist and be visible in the tenant and to differ from the acting participant, and it can never re-point a session that already resolved one — only a pre-#2995 session with none is backfilled. **Migration**: `agent_profile_id` is an ordinary additive manifest field, so a consumer adopts it the same way as any other — `smrt db:migrate` generates and applies the column from the manifest, and `db:status --parity` verifies it; the framework never creates application schema at runtime. It is nullable and needs no offline DATA backfill — a session created before #2995 resolves and persists its `agentProfileId`, and re-enrols the agent, on its first agent turn after the upgrade. Two pre-#2995 cohorts: a session whose `agentId` is an ordinary slug never committed an author on PostgreSQL (the cast failed), so there is nothing to repair there, while SQLite/DuckDB may hold `chat_messages`/`chat_participants` rows whose id column holds the slug — readable, but not joinable to `profiles`. A session whose `agentId` is already a Profile uuid (the voice path used to collapse a persona's `actsAsProfileId` into it) DID commit on PostgreSQL; that profile is ADOPTED as the author, tenant-checked and never the participant, so the conversation's author does not change and the two-seat room gains no third identity. Known one-time effect: because `voice.ts` no longer derives `agentId` from `actsAsProfileId`, a keyless `createVoiceChatSession()` for such a persona opens a new session and room instead of reusing the legacy one; the old room and its messages stay intact and readable, and resuming with an explicit `agentSessionId` is unaffected.\n- **Only SQLite-backed tests would miss this class of bug**: uuid columns accept any text on SQLite. `src/__tests__/agent-reply-postgres.test.ts` (`pnpm --filter @happyvertical/smrt-chat test:postgres`) pins the agent-reply path against real PostgreSQL. That suite is NOT part of required PR CI — it runs in `postgres-tests.yml`, gated on `vars.CI_POSTGRES_ENABLED` / manual dispatch — so keep a cheap SQLite assertion alongside it (`chat-service.test.ts` asserts the author is the profile uuid, not the slug).\n- **Per-subject sessions need `sessionKey`**: `createAgentSession()` reuses ANY active session for the same `(agentId, participantProfileId, tenantId)`. Callers that open separate conversations per subject (e.g. one content-editor session per content id) MUST pass a stable `sessionKey` (stored in `sessionContext.__sessionKey`, read via `AgentSession.getSessionKey()`); otherwise a session opened for one subject is reused and its context overwritten for another, surfacing the wrong room/threads (S5 #1392). A keyed create never reuses a keyless/legacy session.\n- **Session expiry**: check `isActive()` before allowing messages (expiresAt or limit-based)\n- **DM identity**: derived from the deterministic per-tenant `canonicalDmRoomId()` and the authoritative `chat_participants` join, not client metadata; concurrent creates upsert onto one row.\n- **Tenant-bound lookups**: membership/session/DM lookups REQUIRE `tenantId` and always bind it into the WHERE clause (`findActiveMembership`/`isActiveMember`/`findActiveSession` take a required `tenantId`; AgentSession's `null` tenant is an explicit bound scope, not \"any tenant\") so they can never resolve a row from another tenant.\n"
5112
5126
  }
@@ -288,6 +288,8 @@ function removeAttachment(id: string) {
288
288
  :global(.assistant-composer-textarea) {
289
289
  flex: 1;
290
290
  resize: none;
291
+ /* #3000: form controls don't inherit font; use the theme's family. */
292
+ font-family: var(--smrt-font-family, inherit);
291
293
  }
292
294
 
293
295
  :global(.assistant-composer-send) {