@happyvertical/smrt-chat 0.57.36 → 0.57.37
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/tool-loop.md +60 -6
- package/dist/assistant-turn-events.d.ts +12 -4
- package/dist/assistant-turn-events.d.ts.map +1 -1
- package/dist/assistant-turn.d.ts +29 -1
- package/dist/assistant-turn.d.ts.map +1 -1
- package/dist/chunks/{ChatService-D0XuiCTk.js → ChatService-BGBAqmZq.js} +14 -3
- package/dist/chunks/ChatService-BGBAqmZq.js.map +1 -0
- package/dist/chunks/assistant-turn-events-BbZUf-Ju.js.map +1 -1
- package/dist/chunks/chat-stream-DK67O_WB.js +2689 -0
- package/dist/chunks/chat-stream-DK67O_WB.js.map +1 -0
- package/dist/collections/ChatMessageCollection.d.ts +5 -1
- package/dist/collections/ChatMessageCollection.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/agent-runtime.js +1 -1
- package/dist/manifest.json +2 -1
- package/dist/persona-conversation.d.ts +5 -0
- package/dist/persona-conversation.d.ts.map +1 -1
- package/dist/services/ChatService.d.ts +12 -1
- package/dist/services/ChatService.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +5 -5
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.d.ts +6 -2
- 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 +4 -1
- package/dist/sveltekit.d.ts +13 -2
- package/dist/sveltekit.d.ts.map +1 -1
- package/dist/sveltekit.js +7 -3
- package/dist/sveltekit.js.map +1 -1
- package/dist/tool-loop.d.ts +47 -9
- package/dist/tool-loop.d.ts.map +1 -1
- package/dist/voice.d.ts +4 -0
- package/dist/voice.d.ts.map +1 -1
- package/package.json +21 -21
- package/dist/chunks/ChatService-D0XuiCTk.js.map +0 -1
- package/dist/chunks/chat-stream-D6B3InMe.js +0 -2601
- package/dist/chunks/chat-stream-D6B3InMe.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/chat-feedback.ts","../src/helper-preferences-service.ts","../src/helper-profile-store.ts","../src/models/VoiceGatewayTurn.ts","../src/overview-tools.ts","../src/recipes.ts","../src/collections/VoiceGatewayTurnCollection.ts","../src/voice.ts"],"sourcesContent":["/**\n * Chat feedback capture — turn an in-conversation judgement into a first-class\n * learning signal (L3 of the learning-agents epic, #1891).\n *\n * A tenant end-user accepting or rejecting an applied change, giving a\n * thumbs-up/down, or typing an inline correction produces a {@link Feedback}\n * row — carrying the conversation's **correlation-id** back to the turn it\n * judges — and (by default) immediately reinforces the persona's learning\n * memory. Because recall draws on that same memory next turn, captured feedback\n * *influences subsequent behaviour*: a rejected strategy decays below the reuse\n * floor and stops resurfacing; a correction supersedes it with the corrected\n * value.\n *\n * This is the human-signal half of the loop the personas package already models\n * ({@link reinforceFromFeedback}); the gated half — rewriting a persona's\n * instructions — stays in the directive-proposal flow.\n *\n * @module\n */\n\nimport {\n type LearningMemoryRecord,\n type LearningSemanticSearch,\n resolveDatabase,\n type SmrtClassOptions,\n} from '@happyvertical/smrt-core';\nimport {\n type Feedback,\n FeedbackCollection,\n type FeedbackSignalType,\n feedbackSourceFor,\n personaLearningMemory,\n personaMemoryScope,\n reinforceFromFeedback,\n} from '@happyvertical/smrt-personas';\n\n/** The minimal persona shape chat feedback needs to route the signal. */\nexport interface ChatFeedbackPersona {\n /** Persona id — required (a signal always judges a specific persona). */\n id?: string | null;\n /** Owning tenant. */\n tenantId?: string | null;\n /** Canonical agent class the persona configures (denormalised onto the row). */\n agentClass?: string;\n /** Learning memory partition key. */\n memoryScope?: string;\n}\n\n/**\n * Options for {@link captureChatFeedback}.\n */\nexport interface CaptureChatFeedbackOptions {\n /** Database handle. */\n db: SmrtClassOptions['db'];\n /** The persona the signal judges. */\n persona: ChatFeedbackPersona;\n /** The kind of signal. */\n signalType: FeedbackSignalType;\n /** Correlation-id of the conversation turn this signal judges. */\n correlationId: string;\n /** What {@link correlationId} names. Default `'chat_message'`. */\n correlationType?: string;\n /** Learning episode scope the signal reinforces (matches recall/capture). */\n scope: string;\n /** Learning episode key the signal reinforces. */\n key: string;\n /** The user id that authored the signal (null for autonomous). */\n actorId?: string | null;\n /** Numeric rating for a `rating` signal. */\n rating?: number | null;\n /** Corrected value for a `correction` signal. */\n correction?: string | null;\n /** Freeform note. */\n comment?: string | null;\n /** Structured metadata persisted on the row. */\n metadata?: Record<string, unknown>;\n /** Apply the signal to memory immediately. Default `true`. */\n reinforce?: boolean;\n /** Optional embedding search wired into the reinforced memory. */\n semanticSearch?: LearningSemanticSearch;\n /** Neutral point of a `rating` scale (see `FeedbackOutcomeOptions`). Default 0. */\n ratingNeutral?: number;\n}\n\n/** The outcome of capturing chat feedback. */\nexport interface ChatFeedbackResult {\n /** The persisted feedback row. */\n feedback: Feedback;\n /** The memory record the signal reinforced, or `null` when it carried none. */\n reinforced: LearningMemoryRecord | null;\n}\n\n/**\n * Capture one in-chat feedback signal as a {@link Feedback} row and (by default)\n * reinforce the persona's learning memory from it.\n *\n * @throws when the persona has no id (a signal must name a persisted persona).\n */\nexport async function captureChatFeedback(\n options: CaptureChatFeedbackOptions,\n): Promise<ChatFeedbackResult> {\n if (!options.persona.id) {\n throw new Error(\n 'captureChatFeedback requires a persisted persona (missing id)',\n );\n }\n const memoryScope = personaMemoryScope(options.persona);\n\n const feedbacks = await FeedbackCollection.create({ db: options.db });\n const feedback = await feedbacks.create({\n tenantId: options.persona.tenantId ?? null,\n personaId: options.persona.id,\n agentClass: options.persona.agentClass ?? '',\n memoryScope,\n scope: options.scope,\n key: options.key,\n signalType: options.signalType,\n source: feedbackSourceFor(options.signalType),\n correlationId: options.correlationId,\n correlationType: options.correlationType ?? 'chat_message',\n rating: options.rating ?? null,\n correction: options.correction ?? null,\n comment: options.comment ?? null,\n actorId: options.actorId ?? null,\n });\n if (options.metadata) {\n feedback.setMetadata(options.metadata);\n }\n await feedback.save();\n\n let reinforced: LearningMemoryRecord | null = null;\n if (options.reinforce !== false) {\n // LearningMemory operates on a resolved DB handle; core's `resolveDatabase`\n // accepts a config or a handle and returns a handle (identity for a handle).\n const memory = personaLearningMemory({\n db: await resolveDatabase(options.db ?? {}),\n persona: options.persona,\n semanticSearch: options.semanticSearch,\n });\n reinforced = await reinforceFromFeedback(memory, feedback, {\n ratingNeutral: options.ratingNeutral,\n });\n // Gate exactly-once reinforcement so a later reflection pass never\n // re-applies this signal (mirrors the personas reflection runner).\n feedback.reinforcedAt = new Date();\n await feedback.save();\n }\n\n return { feedback, reinforced };\n}\n\n/** Shared options for the signal-typed convenience wrappers. */\nexport type ChatFeedbackBase = Omit<\n CaptureChatFeedbackOptions,\n 'signalType' | 'rating' | 'correction'\n>;\n\n/**\n * Accept an applied change — reinforces the judged strategy as a success.\n */\nexport function acceptAppliedChange(\n options: ChatFeedbackBase,\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'accept' });\n}\n\n/**\n * Reject an applied change — decays the judged strategy toward the failure floor\n * so it stops being recalled.\n */\nexport function rejectAppliedChange(\n options: ChatFeedbackBase & { comment?: string | null },\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'reject' });\n}\n\n/**\n * Record an inline correction — decays the wrong strategy AND supersedes its\n * stored value with the corrected one, so the next recall returns the fix.\n */\nexport function correctResponse(\n options: ChatFeedbackBase & { correction: string; comment?: string | null },\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({\n ...options,\n signalType: 'correction',\n correction: options.correction,\n });\n}\n\n/**\n * Record a numeric rating for a response (scale is caller-defined; pass\n * `ratingNeutral` for a mid-point).\n */\nexport function rateResponse(\n options: ChatFeedbackBase & { rating: number },\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({\n ...options,\n signalType: 'rating',\n rating: options.rating,\n });\n}\n\n/** Thumbs-up — a `+1` rating (reinforces as a success against neutral 0). */\nexport function thumbsUp(\n options: ChatFeedbackBase,\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'rating', rating: 1 });\n}\n\n/** Thumbs-down — a `-1` rating (decays as a failure against neutral 0). */\nexport function thumbsDown(\n options: ChatFeedbackBase,\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'rating', rating: -1 });\n}\n","import {\n type HelperContext,\n type HelperOffering,\n type HelperPolicy,\n type HelperPreferenceField,\n type HelperPreferenceStore,\n type HelperPreferences,\n type HelperRecovery,\n type HelperSnapshot,\n isHelperClearedPreferences,\n parseHelperPreferences,\n} from './helper-preferences.js';\nexport class HelperPreferencesAuthorizationError extends Error {\n constructor() {\n super('Helper preference access is not authorized.');\n this.name = 'HelperPreferencesAuthorizationError';\n }\n}\nexport class HelperPreferencesValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'HelperPreferencesValidationError';\n }\n}\nexport interface HelperPreferencesServiceOptions {\n store: HelperPreferenceStore;\n authorize(\n context: HelperContext,\n operation: 'load' | 'save' | 'reset',\n ): boolean | Promise<boolean>;\n resolvePolicy(context: HelperContext): HelperPolicy | Promise<HelperPolicy>;\n styleIds: readonly string[];\n validateOffering(\n context: HelperContext,\n offering: HelperOffering,\n ): void | Promise<void>;\n}\nexport class HelperPreferencesService {\n readonly #o: HelperPreferencesServiceOptions;\n constructor(options: HelperPreferencesServiceOptions) {\n this.#o = options;\n }\n async load(c: HelperContext): Promise<HelperSnapshot> {\n await this.#auth(c, 'load');\n return this.#resolve(c, await this.#o.store.load(c));\n }\n async save(c: HelperContext, value: unknown): Promise<HelperSnapshot> {\n await this.#auth(c, 'save');\n const p = await this.#visiblePolicy(c, await this.#policy(c));\n if (p.selection === 'owner-assigned')\n throw new HelperPreferencesAuthorizationError();\n const next = parseHelperPreferences(value);\n if (!next)\n throw new HelperPreferencesValidationError(\n 'Helper preferences are invalid.',\n );\n for (const f of this.#fields())\n if (!p.customizable.includes(f) && next[f] !== p.defaultPreferences[f])\n throw new HelperPreferencesValidationError(\n `The ${f} preference is not customizable.`,\n );\n await this.#validate(c, p, next);\n await this.#o.store.save(c, next);\n return this.#resolve(c, await this.#o.store.load(c));\n }\n async reset(c: HelperContext): Promise<HelperSnapshot> {\n await this.#auth(c, 'reset');\n const p = await this.#policy(c);\n if (p.selection === 'owner-assigned')\n throw new HelperPreferencesAuthorizationError();\n await this.#o.store.clear(c);\n return this.#resolve(c, await this.#o.store.load(c));\n }\n async #auth(c: HelperContext, op: 'load' | 'save' | 'reset') {\n if (!(await this.#o.authorize(c, op)))\n throw new HelperPreferencesAuthorizationError();\n }\n async #policy(c: HelperContext) {\n const p = await this.#o.resolvePolicy(c);\n if (\n !parseHelperPreferences(p.defaultPreferences) ||\n (p.assignedPreferences !== undefined &&\n !parseHelperPreferences(p.assignedPreferences))\n )\n throw new HelperPreferencesValidationError(\n 'Helper policy preferences are invalid.',\n );\n return p;\n }\n async #resolve(c: HelperContext, raw: unknown): Promise<HelperSnapshot> {\n const p = await this.#visiblePolicy(c, await this.#policy(c));\n const permissions =\n p.selection === 'owner-assigned'\n ? { editableFields: [], canReset: false, customStyleIds: [] }\n : {\n editableFields: p.customizable,\n canReset: raw !== null && !this.#cleared(raw),\n customStyleIds: p.customStyleIds,\n };\n const assigned = p.selection === 'owner-assigned';\n if (assigned && !p.assignedPreferences)\n return this.#snapshot(p, permissions, null, 'unavailable', false, {\n code: 'invalid-preferences',\n message: 'The assigned helper preferences are unavailable.',\n });\n const baseline = assigned ? p.assignedPreferences! : p.defaultPreferences;\n const persisted = !assigned && raw !== null && !this.#cleared(raw);\n const parsed = assigned\n ? baseline\n : !persisted\n ? baseline\n : parseHelperPreferences(this.#decode(raw));\n if (!parsed) {\n try {\n await this.#validate(c, p, baseline);\n return this.#snapshot(p, permissions, baseline, 'default', true, {\n code: 'invalid-preferences',\n message:\n 'Saved helper preferences are invalid. Reset to use current defaults.',\n });\n } catch (error) {\n if (error instanceof HelperPreferencesAuthorizationError) throw error;\n return this.#snapshot(p, permissions, null, 'unavailable', true, {\n code: 'invalid-preferences',\n message: 'The application default helper is unavailable.',\n });\n }\n }\n try {\n if (persisted)\n for (const f of this.#fields())\n if (!p.customizable.includes(f) && parsed[f] !== baseline[f])\n throw new HelperPreferencesValidationError('invalid-preferences');\n await this.#validate(c, p, parsed);\n return this.#snapshot(\n p,\n permissions,\n parsed,\n assigned ? 'assigned' : persisted ? 'personal' : 'default',\n persisted,\n null,\n );\n } catch (error) {\n if (error instanceof HelperPreferencesAuthorizationError) throw error;\n if (assigned)\n return this.#snapshot(p, permissions, null, 'unavailable', false, {\n code: 'invalid-preferences',\n message: 'The assigned helper preferences are unavailable.',\n });\n try {\n await this.#validate(c, p, baseline);\n return this.#snapshot(\n p,\n permissions,\n baseline,\n 'default',\n persisted,\n this.#recovery(error),\n );\n } catch (fallbackError) {\n if (fallbackError instanceof HelperPreferencesAuthorizationError)\n throw fallbackError;\n return this.#snapshot(p, permissions, null, 'unavailable', persisted, {\n code: 'invalid-preferences',\n message: 'The application default helper is unavailable.',\n });\n }\n }\n }\n async #visiblePolicy(\n c: HelperContext,\n p: HelperPolicy,\n ): Promise<HelperPolicy> {\n const offerings: HelperOffering[] = [];\n for (const offering of p.offerings) {\n if (!this.#o.styleIds.includes(offering.styleId)) continue;\n try {\n await this.#o.validateOffering(c, offering);\n offerings.push(offering);\n } catch (error) {\n if (error instanceof HelperPreferencesAuthorizationError) throw error;\n }\n }\n return { ...p, offerings };\n }\n #decode(raw: unknown) {\n if (typeof raw !== 'string') return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n }\n #cleared(raw: unknown) {\n return isHelperClearedPreferences(\n typeof raw === 'string' ? this.#decode(raw) : raw,\n );\n }\n async #validate(c: HelperContext, p: HelperPolicy, next: HelperPreferences) {\n const offering = p.offerings.find((x) => x.id === next.offeringId);\n if (!offering)\n throw new HelperPreferencesValidationError('unavailable-offering');\n if (!this.#o.styleIds.includes(offering.styleId))\n throw new HelperPreferencesValidationError('unavailable-style');\n if (!p.voices.some((x) => x.id === next.voiceId))\n throw new HelperPreferencesValidationError('unavailable-voice');\n await this.#o.validateOffering(c, offering);\n }\n #recovery(error: unknown): HelperRecovery {\n const code =\n error instanceof HelperPreferencesValidationError &&\n [\n 'unavailable-offering',\n 'unavailable-style',\n 'unavailable-voice',\n ].includes(error.message)\n ? (error.message as HelperRecovery['code'])\n : 'invalid-preferences';\n return {\n code,\n message:\n 'Saved helper preferences are no longer available. Reset to use current defaults.',\n };\n }\n #snapshot(\n p: HelperPolicy,\n permissions: HelperSnapshot['permissions'],\n prefs: HelperPreferences | null,\n source: HelperSnapshot['source'],\n hasOverride: boolean,\n recovery: HelperRecovery | null,\n ): HelperSnapshot {\n return {\n preferences: prefs,\n recoveryDraft:\n prefs === null && p.selection !== 'owner-assigned'\n ? this.#recoveryDraft(p, permissions)\n : null,\n offering: prefs\n ? (p.offerings.find((x) => x.id === prefs.offeringId) ?? null)\n : null,\n selection: p.selection,\n source,\n offerings: p.offerings,\n voices: p.voices,\n permissions,\n hasOverride,\n recovery,\n };\n }\n #recoveryDraft(\n p: HelperPolicy,\n permissions: HelperSnapshot['permissions'],\n ): HelperPreferences | null {\n const candidate = { ...p.defaultPreferences };\n const offering = p.offerings.find(\n (item) => item.id === candidate.offeringId,\n );\n if (!offering) {\n if (!permissions.editableFields.includes('offeringId') || !p.offerings[0])\n return null;\n candidate.offeringId = p.offerings[0].id;\n }\n if (!p.voices.some((voice) => voice.id === candidate.voiceId)) {\n if (!permissions.editableFields.includes('voiceId') || !p.voices[0])\n return null;\n candidate.voiceId = p.voices[0].id;\n }\n return parseHelperPreferences(candidate);\n }\n #fields(): readonly HelperPreferenceField[] {\n return [\n 'offeringId',\n 'name',\n 'voiceId',\n 'placement',\n 'heardSubtitles',\n 'spokenSubtitles',\n ];\n }\n}\n","import {\n ProfileCollection,\n ProfileMetadataCollection,\n ProfileMetafieldCollection,\n} from '@happyvertical/smrt-profiles';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n HELPER_PREFERENCES_VERSION,\n type HelperContext,\n type HelperPreferenceStore,\n type HelperPreferences,\n} from './helper-preferences.js';\n\nexport class HelperProfileStoreError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'HelperProfileStoreError';\n }\n}\n\nexport interface HelperProfilePreferenceStoreOptions {\n db: DatabaseInterface;\n /** Trusted application binding; one app must never read another app's field. */\n applicationId: string;\n /** Pre-provisioned per-application ProfileMetafield slug. */\n metafieldSlug: string;\n}\n\n/**\n * Canonical Profile metadata adapter. It deliberately does not authorize an\n * actor: `HelperPreferencesService` owns that policy decision. This adapter\n * binds every read and write to the requested profile and tenant, detects\n * duplicate metadata rows, and stores one versioned JSON value.\n */\nexport class ProfileHelperPreferenceStore implements HelperPreferenceStore {\n readonly #db: DatabaseInterface;\n readonly #metafieldSlug: string;\n readonly #applicationId: string;\n\n constructor(options: HelperProfilePreferenceStoreOptions) {\n this.#db = options.db;\n this.#metafieldSlug = options.metafieldSlug;\n this.#applicationId = options.applicationId;\n }\n\n async load(context: HelperContext): Promise<unknown | null> {\n const { profile, metafield, metadata } = await this.#boundRecords(context);\n const rows = await metadata.list({\n where: { profileId: profile.id, metafieldId: metafield.id },\n });\n if (rows.length > 1) {\n throw new HelperProfileStoreError(\n 'Duplicate helper preference metadata rows.',\n );\n }\n const row = rows[0];\n if (!row) return null;\n if ((row.tenantId ?? null) !== (profile.tenantId ?? null)) {\n throw new HelperProfileStoreError(\n 'Helper preference metadata tenant mismatch.',\n );\n }\n return row.value;\n }\n\n async save(context: HelperContext, value: HelperPreferences): Promise<void> {\n const { profile, metafield, metadata } = await this.#boundRecords(context);\n const rows = await metadata.list({\n where: { profileId: profile.id, metafieldId: metafield.id },\n });\n if (rows.length > 1) {\n throw new HelperProfileStoreError(\n 'Duplicate helper preference metadata rows.',\n );\n }\n const serialized = JSON.stringify(value);\n const row = rows[0];\n if (row) {\n if ((row.tenantId ?? null) !== (profile.tenantId ?? null)) {\n throw new HelperProfileStoreError(\n 'Helper preference metadata tenant mismatch.',\n );\n }\n row.value = serialized;\n await row.save();\n return;\n }\n const created = await metadata.create({\n profileId: profile.id as string,\n metafieldId: metafield.id as string,\n value: serialized,\n tenantId: profile.tenantId ?? null,\n });\n await created.save();\n }\n\n async clear(context: HelperContext): Promise<void> {\n const { profile, metafield, metadata } = await this.#boundRecords(context);\n const rows = await metadata.list({\n where: { profileId: profile.id, metafieldId: metafield.id },\n });\n if (rows.length > 1)\n throw new HelperProfileStoreError(\n 'Duplicate helper preference metadata rows.',\n );\n const serialized = JSON.stringify({\n version: HELPER_PREFERENCES_VERSION,\n cleared: true,\n });\n const row = rows[0];\n if (row) {\n if ((row.tenantId ?? null) !== (profile.tenantId ?? null)) {\n throw new HelperProfileStoreError(\n 'Helper preference metadata tenant mismatch.',\n );\n }\n row.value = serialized;\n await row.save();\n return;\n }\n const created = await metadata.create({\n profileId: profile.id as string,\n metafieldId: metafield.id as string,\n value: serialized,\n tenantId: profile.tenantId ?? null,\n });\n await created.save();\n }\n\n async #boundRecords(context: HelperContext) {\n if (context.applicationId !== this.#applicationId) {\n throw new HelperProfileStoreError(\n 'Helper preference application mismatch.',\n );\n }\n const profiles = await ProfileCollection.create({ db: this.#db });\n const profile = await profiles.get({ id: context.profileId });\n if (!profile || (profile.tenantId ?? null) !== context.tenantId) {\n throw new HelperProfileStoreError(\n 'Helper preference profile is unavailable.',\n );\n }\n const metafields = await ProfileMetafieldCollection.create({\n db: this.#db,\n });\n const candidates = await metafields.list({\n where: { slug: this.#metafieldSlug },\n });\n const matching = candidates.filter(\n (field) => (field.tenantId ?? null) === (profile.tenantId ?? null),\n );\n const global = candidates.filter((field) => field.tenantId === null);\n const chosen = matching[0] ?? global[0];\n if (\n !chosen ||\n matching.length > 1 ||\n (matching.length === 0 && global.length > 1)\n ) {\n throw new HelperProfileStoreError(\n 'The pre-provisioned helper preference metafield is unavailable or ambiguous.',\n );\n }\n return {\n profile,\n metafield: chosen,\n metadata: await ProfileMetadataCollection.create({ db: this.#db }),\n };\n }\n}\n\nexport function createHelperProfilePreferenceStore(\n options: HelperProfilePreferenceStoreOptions,\n): ProfileHelperPreferenceStore {\n return new ProfileHelperPreferenceStore(options);\n}\n","import { field, foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n VoiceGatewayTurnOptions,\n VoiceGatewayTurnStatus,\n} from '../types.js';\n\n/**\n * Durable reservation for a gateway turn. The natural key makes `turn_id`\n * replay checks concurrency-safe before transcript messages are written.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'voice_gateway_turns',\n conflictColumns: ['voice_session_id', 'gateway_turn_id'],\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: false,\n})\nexport class VoiceGatewayTurn extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-chat\";\n @tenantId()\n tenantId: string = '';\n\n @foreignKey('VoiceSession', { required: true })\n voiceSessionId: string = '';\n\n @field({ required: true })\n gatewaySessionId: string = '';\n\n @field({ required: true })\n gatewayTurnId: string = '';\n\n @field({ required: true })\n target: string = 'smrt:chat';\n\n @field({ required: true })\n status: VoiceGatewayTurnStatus = 'processing';\n\n @field()\n completedAt: Date | null = null;\n\n @field()\n failedAt: Date | null = null;\n\n constructor(options: VoiceGatewayTurnOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.voiceSessionId !== undefined)\n this.voiceSessionId = options.voiceSessionId;\n if (options.gatewaySessionId !== undefined)\n this.gatewaySessionId = options.gatewaySessionId;\n if (options.gatewayTurnId !== undefined)\n this.gatewayTurnId = options.gatewayTurnId;\n if (options.target !== undefined) this.target = options.target;\n if (options.status !== undefined) this.status = options.status;\n if (options.completedAt !== undefined)\n this.completedAt = options.completedAt;\n if (options.failedAt !== undefined) this.failedAt = options.failedAt;\n }\n\n async complete(now: Date = new Date()): Promise<void> {\n this.status = 'completed';\n this.completedAt = now;\n this.failedAt = null;\n await this.save();\n }\n\n async fail(now: Date = new Date()): Promise<void> {\n this.status = 'failed';\n this.failedAt = now;\n await this.save();\n }\n}\n","/**\n * Assistant tools for customizable overview pages (#3727 phase 4): the\n * assistant adds and configures widgets (\"add a chart of overdue invoices\")\n * with structured operations, and the person can undo the change.\n *\n * The model only ever supplies data: a page id and a list of operations\n * (`add`, `configure`, `move`, `resize`, `remove`) over the page's closed\n * option vocabulary. It never names a query, a table or a tenant. Every\n * authority decision is the host's, made from the live {@link PrincipalRun}:\n *\n * - `open(run, pageId)` returns the overview for THIS principal (its\n * definition, the principal's stored override, whether the principal may\n * customize, and how to persist), or `null` when the page is not offered.\n * - The surface validates every batch with the overview model and\n * `checkOverviewOverride` (smrt-svelte's `createOverviewAssistantSurface`\n * is the implementation), so the assistant can only store an override the\n * page's own save endpoint would accept. A batch is atomic.\n * - Undo is single-step and keyed by tenant, user and page: an undo token is\n * only ever looked up under the principal that is calling, so one\n * principal can never restore another's state.\n *\n * This package depends on no UI package, so the surface is typed\n * structurally.\n *\n * @module\n */\n\nimport type { AITool } from '@happyvertical/ai';\nimport {\n DataSurfaceDeniedError,\n type PrincipalRun,\n type PrincipalTool,\n type PrincipalToolContext,\n} from '@happyvertical/smrt-agents';\nimport {\n getCurrentTenant,\n isSuperAdminBypass,\n isSystemContext,\n isTenancyEnabled,\n withTenant,\n} from '@happyvertical/smrt-tenancy';\n\n/** Tool slug (permission/allow-list name) for describing an overview. */\nexport const OVERVIEW_DESCRIBE_TOOL_SLUG = 'overviews.describe';\n/** Tool slug for applying a batch of operations. */\nexport const OVERVIEW_APPLY_TOOL_SLUG = 'overviews.apply';\n/** Tool slug for undoing the last applied batch. */\nexport const OVERVIEW_UNDO_TOOL_SLUG = 'overviews.undo';\n\n/** Provider function name for {@link OVERVIEW_DESCRIBE_TOOL_SLUG}. */\nexport const OVERVIEW_DESCRIBE_FUNCTION_NAME = 'overviews-describe';\n/** Provider function name for {@link OVERVIEW_APPLY_TOOL_SLUG}. */\nexport const OVERVIEW_APPLY_FUNCTION_NAME = 'overviews-apply';\n/** Provider function name for {@link OVERVIEW_UNDO_TOOL_SLUG}. */\nexport const OVERVIEW_UNDO_FUNCTION_NAME = 'overviews-undo';\n\n/** Default cap on operations in one batch (matches smrt-svelte's). */\nexport const OVERVIEW_TOOL_MAX_OPERATIONS = 20;\n\nconst PAGE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,95}$/;\n\n/** A plan result, as the surface returns it. */\nexport type OverviewToolPlan =\n | {\n ok: true;\n override: unknown;\n document: unknown;\n results: readonly unknown[];\n unchanged?: boolean;\n }\n | { ok: false; issues: readonly unknown[] };\n\n/** A save-style check of an override, as the surface returns it. */\nexport type OverviewToolCheck =\n | { ok: true; override: unknown }\n | { ok: false; issues: readonly unknown[] };\n\n/**\n * One overview for one principal. smrt-svelte's\n * `createOverviewAssistantSurface` (on `./overview/server`) implements it.\n */\nexport interface OverviewToolSurface {\n readonly pageId: string;\n /** Whether this principal may customize the page (the role gate). */\n readonly canCustomize: boolean;\n /** Allowed widget types with their option fields, and the arrangement. */\n describe(): unknown;\n /** The canonical override in effect (`null` = defaults). */\n current(): unknown;\n /** Validate and plan a batch against {@link current}. Applies nothing. */\n plan(operations: unknown): OverviewToolPlan;\n /** Validate an override the way a save is validated. */\n check(override: unknown): OverviewToolCheck;\n /**\n * Persist a canonical override in the principal's tier, CONDITIONALLY: only\n * if the stored value is still the one this surface loaded (its revision).\n * A save that landed since the surface was opened must answer\n * `{ ok: false, reason: 'conflict' }`, never be overwritten. On success the\n * surface's `current()` and revision follow the written value.\n */\n persist(override: unknown): Promise<OverviewToolPersistResult>;\n}\n\n/**\n * Outcome of {@link OverviewToolSurface.persist}. Structurally the phase-3\n * store's save result (`OverviewSaveResult` of `@happyvertical/smrt-preferences`'\n * `createOverviewStore`).\n */\nexport type OverviewToolPersistResult =\n | { ok: true }\n | {\n ok: false;\n reason: 'conflict' | 'not_allowed' | 'invalid';\n issues?: readonly unknown[];\n };\n\n/** The application side: which overviews a principal may reach. */\nexport interface OverviewToolsHost {\n /**\n * The page for this principal, or `null` when it is unknown or not offered\n * to them. Build the surface from the principal's own stored override and\n * role; never from model input other than the page id. Scope every read\n * and the surface's `persist` by `run.context.tenantId` (with tenancy on,\n * the tools call `open` and every surface method inside that tenant's\n * context and refuse a run without one).\n */\n open(\n run: PrincipalRun,\n pageId: string,\n ): OverviewToolSurface | null | Promise<OverviewToolSurface | null>;\n /** Overview pages offered to this principal (for `describe` without a page). */\n pages?(\n run: PrincipalRun,\n ):\n | ReadonlyArray<{ id: string; title?: string }>\n | Promise<ReadonlyArray<{ id: string; title?: string }>>;\n}\n\n/** What Undo restores: the override before the last applied batch. */\nexport interface OverviewUndoEntry {\n token: string;\n before: unknown;\n after: unknown;\n /**\n * When the batch's conditional write succeeded (epoch ms, strictly\n * increasing within a process). Orders entries for\n * {@link OverviewUndoStore.set} and drives expiry.\n */\n createdAt: number;\n}\n\n/**\n * Where undo entries live, keyed by an opaque principal-and-page key the\n * tools compute. The default is in-process memory; a multi-replica host\n * passes a shared store (for example the assistant session context).\n */\nexport interface OverviewUndoStore {\n get(\n key: string,\n ): OverviewUndoEntry | undefined | Promise<OverviewUndoEntry | undefined>;\n /**\n * Conditional set: store `entry` under `key` unless the stored entry is\n * NEWER (a larger `createdAt`); then it is a no-op. Overlapping applies for\n * one principal and page can finish their entry writes out of order, and an\n * older apply's late write must not replace the newer batch's entry. A\n * shared store implements this atomically (for example a conditional\n * upsert on `createdAt`).\n */\n set(key: string, entry: OverviewUndoEntry): void | Promise<void>;\n /**\n * Compare-and-delete: remove the entry under `key` ONLY if its `token` is\n * `token`; a no-op otherwise. An undo that finishes after a newer batch\n * stored its own entry under the same key must not remove that entry. A\n * shared store implements this atomically (for example a conditional\n * delete on the token).\n */\n delete(key: string, token: string): void | Promise<void>;\n}\n\n/** Agent-level audit record of an overview change. */\nexport interface OverviewToolAuditEntry {\n action: 'apply' | 'undo';\n userId: string;\n tenantId: string | null;\n pageId: string;\n operations?: number;\n}\n\nexport interface OverviewToolsOptions {\n host: OverviewToolsHost;\n /**\n * Where undo entries live. It must outlive a request and a turn. Defaults\n * to {@link defaultOverviewUndoStore} (process-wide memory); pass a shared\n * store when several replicas serve one assistant.\n */\n undoStore?: OverviewUndoStore;\n audit?: (entry: OverviewToolAuditEntry) => void | Promise<void>;\n /** Batch cap (default 20); keep it equal to the surface's. */\n maxOperations?: number;\n /** Token source (tests). Defaults to `crypto.randomUUID`. */\n createToken?: () => string;\n /**\n * Receives best-effort failures after a change was stored (undo entry,\n * audit). Defaults to `console.warn`; route it to the host's logger.\n */\n onError?: (error: unknown, context: string) => void;\n}\n\n/**\n * A tool refusal the loop classifies by `status`: 422/404/409 are repairable\n * (`invalid_request`, the model sees `publicMessage`), 403 is `not_permitted`.\n */\nexport class OverviewToolError extends Error {\n readonly status: number;\n readonly code: string;\n readonly publicMessage: string;\n readonly issues: readonly unknown[];\n\n constructor(\n status: number,\n code: string,\n message: string,\n issues: readonly unknown[] = [],\n ) {\n super(message);\n this.name = 'OverviewToolError';\n this.status = status;\n this.code = code;\n this.publicMessage = message;\n this.issues = issues;\n }\n}\n\n/**\n * In-process undo store: single entry per key, expiring after `ttlMs`\n * (default 30 minutes), at most `maxEntries` keys (oldest dropped).\n */\nexport function createMemoryOverviewUndoStore(\n options: { ttlMs?: number; maxEntries?: number; now?: () => number } = {},\n): OverviewUndoStore {\n const ttl = options.ttlMs ?? 30 * 60_000;\n const max = options.maxEntries ?? 1000;\n const now = options.now ?? Date.now;\n const entries = new Map<string, OverviewUndoEntry>();\n return {\n get(key) {\n const entry = entries.get(key);\n if (entry && now() - entry.createdAt > ttl) {\n entries.delete(key);\n return undefined;\n }\n return entry;\n },\n set(key, entry) {\n const existing = entries.get(key);\n // Conditional: an older apply that stores its entry late never replaces\n // a newer one.\n if (existing && existing.createdAt > entry.createdAt) return;\n entries.delete(key);\n entries.set(key, entry);\n while (entries.size > max) {\n const oldest = entries.keys().next().value;\n if (oldest === undefined) break;\n entries.delete(oldest);\n }\n },\n delete(key, token) {\n if (entries.get(key)?.token === token) entries.delete(key);\n },\n };\n}\n\nlet processUndoStore: OverviewUndoStore | undefined;\nlet lastUndoStamp = 0;\n\n/**\n * The ordering stamp of an undo entry, taken right after the batch's\n * conditional write succeeded: epoch ms, strictly increasing within a\n * process. The revision guard serializes writes to one stored row, so the\n * order the writes succeeded in is the order of the batches; revisions are\n * opaque to this package (only compared for equality), so they cannot order\n * entries themselves.\n */\nfunction nextUndoStamp(): number {\n lastUndoStamp = Math.max(Date.now(), lastUndoStamp + 1);\n return lastUndoStamp;\n}\n\n/**\n * The process-wide in-memory undo store `createOverviewTools` uses when no\n * `undoStore` is passed. It outlives every tool set, so tools built per turn\n * (`extraTools: (context) => createOverviewTools(...)`) can undo a batch an\n * earlier turn applied. Keys carry tenant, user and page, so sharing it is\n * safe; it is per process, so a multi-replica host passes a shared store.\n */\nexport function defaultOverviewUndoStore(): OverviewUndoStore {\n processUndoStore ??= createMemoryOverviewUndoStore();\n return processUndoStore;\n}\n\nfunction principalFromRun(run: PrincipalRun): {\n userId: string;\n tenantId: string | null;\n} {\n const userId = run.context.userId;\n if (!userId) throw new DataSurfaceDeniedError();\n return { userId, tenantId: run.context.tenantId };\n}\n\n/**\n * Run under the AUTHENTICATED principal's tenant (the runtime-report tools'\n * guard): with tenancy on, a run without a tenant is refused, the principal's\n * tenant is entered when no tenant context is active, and an ambient tenant\n * that disagrees with the principal is refused (unless the caller is\n * deliberately in a system-context or super-admin path). `host.open` and\n * every surface call, including `persist`, run inside it.\n */\nasync function withPrincipalTenant<T>(\n run: PrincipalRun,\n fn: () => Promise<T>,\n): Promise<T> {\n if (!isTenancyEnabled()) return fn();\n const tenantId = run.context.tenantId;\n if (!tenantId) {\n throw new OverviewToolError(\n 403,\n 'tenant_required',\n 'A tenant is required to use overview tools.',\n );\n }\n const active = getCurrentTenant();\n if (active === undefined) return withTenant({ tenantId }, fn);\n if (\n active.tenantId !== tenantId &&\n !isSystemContext() &&\n !isSuperAdminBypass()\n ) {\n throw new DataSurfaceDeniedError();\n }\n return fn();\n}\n\n/** The allow-list gate, the principal check and the tenant guard, in order. */\nfunction guarded(\n slug: string,\n body: (context: PrincipalToolContext) => Promise<unknown>,\n): PrincipalTool['execute'] {\n return async (context) => {\n context.run.assertToolAllowed(slug);\n principalFromRun(context.run);\n return withPrincipalTenant(context.run, () => body(context));\n };\n}\n\nfunction aiTool(\n slug: string,\n name: string,\n description: string,\n parameters: Record<string, unknown>,\n execute: PrincipalTool['execute'],\n): PrincipalTool {\n return {\n slug,\n aiTool: {\n type: 'function',\n function: { name, description, parameters },\n } satisfies AITool,\n execute,\n };\n}\n\n/** A refused conditional write, as a tool error. */\nfunction persistFailure(\n result: Exclude<OverviewToolPersistResult, { ok: true }>,\n conflict: { code: string; message: string },\n): OverviewToolError {\n if (result.reason === 'conflict') {\n return new OverviewToolError(409, conflict.code, conflict.message);\n }\n if (result.reason === 'not_allowed') {\n return new OverviewToolError(\n 403,\n 'not_allowed',\n 'You may not change this overview.',\n );\n }\n const issues = result.issues ?? [];\n return new OverviewToolError(\n 422,\n 'invalid_operations',\n `The overview rejected the change and nothing changed. ${formatOverviewIssues(issues)}`,\n issues,\n );\n}\n\nfunction sameJson(a: unknown, b: unknown): boolean {\n return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);\n}\n\n/** \"#2 add: widget type \"map\" is not registered; ...\" for the model. */\nexport function formatOverviewIssues(issues: readonly unknown[]): string {\n return issues\n .slice(0, 10)\n .map((issue) => {\n const record =\n issue && typeof issue === 'object'\n ? (issue as Record<string, unknown>)\n : {};\n const at =\n typeof record.index === 'number' ? `#${record.index + 1}` : 'batch';\n const op = typeof record.op === 'string' ? ` ${record.op}` : '';\n const message =\n typeof record.message === 'string'\n ? record.message\n : typeof record.code === 'string'\n ? record.code\n : 'invalid';\n return `${at}${op}: ${message}`;\n })\n .join('; ');\n}\n\nconst OPERATION_PARAMETERS: Record<string, unknown> = {\n type: 'object',\n required: ['op'],\n additionalProperties: false,\n properties: {\n op: {\n type: 'string',\n enum: ['add', 'configure', 'move', 'resize', 'remove'],\n },\n type: {\n type: 'string',\n description: 'add: a widget type overviews-describe lists.',\n },\n id: {\n type: 'string',\n description: 'configure/move/resize/remove: an existing widget id.',\n },\n span: {\n type: 'integer',\n minimum: 1,\n maximum: 4,\n description: \"add/resize: columns, within the type's span range.\",\n },\n index: {\n type: 'integer',\n minimum: 0,\n description: 'add/move: zero-based position.',\n },\n options: {\n type: 'object',\n description:\n 'add: the options; configure: keys to change (null resets a key). Only the option fields overviews-describe lists, with their allowed values.',\n additionalProperties: {\n type: ['string', 'number', 'boolean', 'null'],\n },\n },\n },\n};\n\n/**\n * Build the overview assistant tools. Offer them through `extraTools`; each\n * is gated by the same fail-closed `allowedTools` offer and execution gates\n * as every other principal tool.\n *\n * @param options - The host adapter, undo store, audit sink and batch cap.\n * @returns The `describe`, `apply` and `undo` principal tools.\n */\nexport function createOverviewTools(\n options: OverviewToolsOptions,\n): PrincipalTool[] {\n const { host } = options;\n const undoStore = options.undoStore ?? defaultOverviewUndoStore();\n const maxOperations = options.maxOperations ?? OVERVIEW_TOOL_MAX_OPERATIONS;\n const createToken = options.createToken ?? (() => crypto.randomUUID());\n const onError =\n options.onError ??\n ((error: unknown, context: string) => {\n // biome-ignore lint/suspicious/noConsole: server-side default; hosts pass `onError` to route it to their logger\n console.warn(`[smrt-chat] ${context}`, error);\n });\n\n const undoKey = (run: PrincipalRun, pageId: string): string => {\n const { userId, tenantId } = principalFromRun(run);\n return JSON.stringify([tenantId, userId, pageId]);\n };\n\n const pageArg = (value: unknown): string => {\n if (typeof value !== 'string' || !PAGE_ID_PATTERN.test(value)) {\n throw new OverviewToolError(\n 422,\n 'invalid_page',\n 'page must be an overview page id from overviews-describe',\n );\n }\n return value;\n };\n\n const open = async (\n run: PrincipalRun,\n pageId: string,\n ): Promise<OverviewToolSurface> => {\n const surface = await host.open(run, pageId);\n // Unknown, and not offered to this principal, answer the same.\n if (!surface || surface.pageId !== pageId) {\n throw new OverviewToolError(\n 404,\n 'unknown_page',\n `no overview page \"${pageId}\" is available`,\n );\n }\n return surface;\n };\n\n const requireCustomize = (surface: OverviewToolSurface): void => {\n if (!surface.canCustomize) {\n throw new OverviewToolError(\n 403,\n 'not_allowed',\n 'You may not change this overview.',\n );\n }\n };\n\n /**\n * Best effort: by the time it runs the change is stored, and reporting the\n * tool as failed would make the model retry an applied batch.\n */\n const audit = async (\n run: PrincipalRun,\n entry: Omit<OverviewToolAuditEntry, 'userId' | 'tenantId'>,\n ): Promise<void> => {\n try {\n await options.audit?.({ ...entry, ...principalFromRun(run) });\n } catch (error) {\n onError(error, `overview tool audit failed (${entry.action})`);\n }\n };\n\n const describe = aiTool(\n OVERVIEW_DESCRIBE_TOOL_SLUG,\n OVERVIEW_DESCRIBE_FUNCTION_NAME,\n 'Describe an overview page: the widget types it allows (with their option fields, allowed values and span range) and the widgets it shows now, in order, plus whether you may change it. Without a page, lists the overview pages available. Call before changing an overview; never invent types, option keys or models.',\n {\n type: 'object',\n additionalProperties: false,\n properties: {\n page: { type: 'string', description: 'Overview page id.' },\n },\n },\n guarded(OVERVIEW_DESCRIBE_TOOL_SLUG, async ({ run, args }) => {\n if (args.page === undefined) {\n return { pages: host.pages ? await host.pages(run) : [] };\n }\n const surface = await open(run, pageArg(args.page));\n return { page: surface.pageId, overview: surface.describe() };\n }),\n );\n\n const apply = aiTool(\n OVERVIEW_APPLY_TOOL_SLUG,\n OVERVIEW_APPLY_FUNCTION_NAME,\n 'Change an overview page with a batch of structured operations: add {type, span?, options, index?}, configure {id, options}, move {id, index}, resize {id, span}, remove {id}. The batch is all-or-nothing: if any operation is invalid nothing changes and the issues are returned to fix. Returns an undoToken; the person can undo the batch.',\n {\n type: 'object',\n required: ['page', 'operations'],\n additionalProperties: false,\n properties: {\n page: { type: 'string', description: 'Overview page id.' },\n operations: {\n type: 'array',\n minItems: 1,\n maxItems: maxOperations,\n items: OPERATION_PARAMETERS,\n },\n },\n },\n guarded(OVERVIEW_APPLY_TOOL_SLUG, async ({ run, args }) => {\n const pageId = pageArg(args.page);\n const operations = args.operations;\n if (\n !Array.isArray(operations) ||\n operations.length === 0 ||\n operations.length > maxOperations\n ) {\n throw new OverviewToolError(\n 422,\n 'invalid_operations',\n `operations must be an array of 1 to ${maxOperations} operations`,\n );\n }\n const surface = await open(run, pageId);\n requireCustomize(surface);\n const before = surface.current();\n const plan = surface.plan(operations);\n if (!plan.ok) {\n throw new OverviewToolError(\n 422,\n 'invalid_operations',\n `The batch was rejected and nothing changed. ${formatOverviewIssues(plan.issues)}`,\n plan.issues,\n );\n }\n const changed = !(plan.unchanged ?? sameJson(plan.override, before));\n if (!changed) {\n return {\n page: pageId,\n changed: false,\n applied: plan.results,\n undoToken: null,\n };\n }\n const written = await surface.persist(plan.override);\n if (!written.ok) {\n throw persistFailure(written, {\n code: 'conflict',\n message:\n 'The overview changed while the batch was being applied, so nothing changed. Call overviews-describe again and rebuild the batch.',\n });\n }\n // The batch is stored. From here nothing may report the call as failed:\n // the model would retry it and duplicate the change. The undo entry is\n // written after the write (not before it and deleted on failure) because\n // a lost entry only costs the Undo, while an entry for a write that did\n // not happen could later \"undo\" someone else's value.\n let token: string | null = createToken();\n try {\n await undoStore.set(undoKey(run, pageId), {\n token,\n before,\n after: plan.override,\n createdAt: nextUndoStamp(),\n });\n } catch (error) {\n onError(error, 'overview undo entry was not stored');\n token = null;\n }\n await audit(run, {\n action: 'apply',\n pageId,\n operations: operations.length,\n });\n return {\n page: pageId,\n changed: true,\n applied: plan.results,\n undoToken: token,\n ...(token === null\n ? {\n note: 'The change was applied but cannot be undone with overviews-undo. Do not apply it again.',\n }\n : {}),\n overview: surface.describe(),\n };\n }),\n );\n\n const undo = aiTool(\n OVERVIEW_UNDO_TOOL_SLUG,\n OVERVIEW_UNDO_FUNCTION_NAME,\n 'Undo the last batch applied to an overview page (pass the undoToken overviews-apply returned). Single step: only the latest batch can be undone, and not once the page changed since.',\n {\n type: 'object',\n required: ['page', 'undoToken'],\n additionalProperties: false,\n properties: {\n page: { type: 'string', description: 'Overview page id.' },\n undoToken: { type: 'string' },\n },\n },\n guarded(OVERVIEW_UNDO_TOOL_SLUG, async ({ run, args }) => {\n const pageId = pageArg(args.page);\n // Looked up under the CALLING principal only: another principal's\n // token is simply not found.\n const key = undoKey(run, pageId);\n const entry = await undoStore.get(key);\n if (\n !entry ||\n typeof args.undoToken !== 'string' ||\n entry.token !== args.undoToken\n ) {\n throw new OverviewToolError(\n 409,\n 'nothing_to_undo',\n 'There is no assistant change with that undoToken to undo on this page; only the latest batch can be undone.',\n );\n }\n const surface = await open(run, pageId);\n requireCustomize(surface);\n if (!sameJson(surface.current(), entry.after)) {\n await undoStore.delete(key, entry.token);\n throw new OverviewToolError(\n 409,\n 'changed_since',\n 'The overview changed after that batch, so it was not undone.',\n );\n }\n const checked = surface.check(entry.before);\n if (!checked.ok) {\n await undoStore.delete(key, entry.token);\n throw new OverviewToolError(\n 409,\n 'cannot_restore',\n `The earlier arrangement is no longer valid on this page. ${formatOverviewIssues(checked.issues)}`,\n checked.issues,\n );\n }\n const written = await surface.persist(checked.override);\n if (!written.ok) {\n // Keep the entry: only a successful conditional restore consumes it.\n throw persistFailure(written, {\n code: 'changed_since',\n message:\n 'The overview changed after that batch, so it was not undone.',\n });\n }\n try {\n await undoStore.delete(key, entry.token);\n } catch (error) {\n // A leftover entry no longer matches the stored value, so a second\n // undo answers changed_since; the restore itself succeeded.\n onError(error, 'overview undo entry was not removed');\n }\n await audit(run, { action: 'undo', pageId });\n return { page: pageId, undone: true, overview: surface.describe() };\n }),\n );\n\n return [describe, apply, undo];\n}\n","/**\n * Declared recipes for smrt-chat (#3719): small, user-facing units of\n * functionality an app or agent can pick instead of the whole package.\n * The scanner reads these statics into the `recipes` array of `manifest.json`\n * and `smrt-knowledge.json`; nothing here runs at that point.\n *\n * @packageDocumentation\n */\n\nimport { SmrtRecipe } from '@happyvertical/smrt-core';\nimport { AgentSession } from './models/AgentSession.js';\nimport { ChatMessage } from './models/ChatMessage.js';\nimport { ChatParticipant } from './models/ChatParticipant.js';\nimport { ChatReaction } from './models/ChatReaction.js';\nimport { ChatRoom } from './models/ChatRoom.js';\nimport { ChatThread } from './models/ChatThread.js';\n\n/**\n * Assistant: an AI chat dock in the app shell that sees the page you are on.\n *\n * The shell hosts the dock through its `dockToggles`/`ShellDockTool` pair; the\n * toggle button lives in `header.end` by default and the host passes the\n * dock a transport and the shell's data surface registry. Conversations are\n * kept in the agent-session room, thread and message records below, which no\n * screen lists: they are read through the member-checked assistant routes.\n */\nexport class AssistantRecipe extends SmrtRecipe {\n static id = 'chat.assistant';\n static help = './assistant.recipe.md';\n static label = 'Assistant';\n static summary =\n 'Ask an AI assistant about the page you are on, from a dock in the app shell.';\n static synonyms = [\n 'ai assistant',\n 'ai chat',\n 'copilot',\n 'chatbot',\n 'ask ai',\n 'helper',\n ];\n static models = [\n AgentSession,\n ChatRoom,\n ChatParticipant,\n ChatThread,\n ChatMessage,\n ];\n // The dock runs in the browser, with the in-memory transport or a browser\n // model; saved conversations and tool use go through server routes.\n static runtime = 'both' as const;\n static surfaces = [\n {\n kind: 'shell-widget',\n slot: 'header.end',\n export: '@happyvertical/smrt-chat/svelte#AssistantDock',\n label: 'Assistant',\n icon: 'bot',\n },\n ] as const;\n // Names only, never values. A hosted model needs the key for the option the\n // app picks (the other two are unused); the browser options need none.\n static providers = [\n {\n id: 'llm',\n kind: 'llm',\n options: ['openai', 'anthropic', 'gemini', 'webllm', 'bitgpu'],\n required: true,\n secrets: ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'GEMINI_API_KEY'],\n browserOptions: ['webllm', 'bitgpu'],\n },\n ] as const;\n}\n/** Team rooms and their conversations. */\nexport class ChatRoomsRecipe extends SmrtRecipe {\n static id = 'chat.rooms';\n static help = './chat-rooms.recipe.md';\n static label = 'Team Chat';\n static summary =\n 'Organize team conversations in rooms with threads and reactions.';\n static synonyms = ['team chat', 'channels', 'group chat', 'conversations'];\n static models = [\n ChatRoom,\n ChatMessage,\n ChatParticipant,\n ChatThread,\n ChatReaction,\n ];\n static group = {\n id: 'team-collaboration',\n label: 'Team collaboration',\n summary: 'Keep conversations and shared work in one place.',\n };\n static section = {\n id: 'team',\n label: 'Team',\n icon: 'users',\n description: 'People and the conversations that keep them aligned.',\n };\n static nav = [\n {\n label: 'Chat rooms',\n model: ChatRoom,\n icon: 'users',\n description: 'Team conversations organized by topic or audience.',\n noun: 'chat room',\n },\n ];\n static runtime = 'both' as const;\n static surfaces = [\n {\n kind: 'route',\n path: '/chat/rooms',\n export: '@happyvertical/smrt-chat/svelte#ChatLayout',\n label: 'Team chat',\n },\n ] as const;\n static demoSeed = {\n export: '@happyvertical/smrt-chat/playground#chatRoomsFixture',\n } as const;\n static options = {\n ChatRoom: {\n fields: {\n metadata: { visibility: 'hidden' },\n createdByProfileId: { visibility: 'hidden' },\n lastMessageAt: { label: 'Last message', order: 1 },\n },\n },\n ChatMessage: {\n fields: {\n metadata: { visibility: 'hidden' },\n toolCallData: { visibility: 'hidden' },\n attachments: { visibility: 'advanced' },\n },\n },\n ChatParticipant: {\n fields: {\n lastReadMessageId: { visibility: 'hidden' },\n lastSeenAt: { visibility: 'advanced' },\n },\n },\n } as const;\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { VoiceGatewayTurn } from '../models/VoiceGatewayTurn.js';\n\nexport class VoiceGatewayTurnCollection extends SmrtCollection<VoiceGatewayTurn> {\n static readonly _itemClass = VoiceGatewayTurn;\n\n async reserveTurn(input: {\n tenantId: string;\n voiceSessionId: string;\n gatewaySessionId: string;\n gatewayTurnId: string;\n target: string;\n }): Promise<VoiceGatewayTurn> {\n return this.create({\n ...input,\n status: 'processing',\n _insertOnly: true,\n });\n }\n}\n","import type { AIInterface, AIMessage } from '@happyvertical/ai';\nimport type { PrincipalAuditSink } from '@happyvertical/smrt-agents';\nimport {\n type SmrtClassOptions,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { VoiceGatewayTurnCollection } from './collections/VoiceGatewayTurnCollection.js';\nimport { VoiceSessionCollection } from './collections/VoiceSessionCollection.js';\nimport type { ChatMessage } from './models/ChatMessage.js';\nimport type { VoiceGatewayTurn } from './models/VoiceGatewayTurn.js';\nimport type { VoiceSession } from './models/VoiceSession.js';\nimport {\n bindPersonaToSession,\n type ConversationPersona,\n type PersonaRecallOptions,\n runPersonaConversationTurn,\n} from './persona-conversation.js';\nimport type { ChatService } from './services/index.js';\nimport { FALLBACK_HISTORY_PLACEHOLDER, isFallbackReply } from './tool-loop.js';\n\nexport const SMRT_CHAT_VOICE_TARGET = 'smrt:chat';\nexport const MAX_VOICE_GATEWAY_TEXT_LENGTH = 12_000;\nconst DEFAULT_VOICE_SESSION_TTL_SECONDS = 10 * 60;\nconst DEFAULT_HISTORY_LIMIT = 24;\n\nexport interface VoiceGatewayTurnMetadata {\n tenantId?: string;\n actorProfileId?: string;\n chatRoomId?: string;\n threadId?: string;\n agentSessionId?: string;\n personaId?: string;\n voiceSessionId?: string;\n source?: string;\n [key: string]: unknown;\n}\n\nexport interface VoiceGatewayTurnPayload {\n session_id: string;\n turn_id: string;\n target: string;\n actor?: string;\n text: string;\n metadata?: VoiceGatewayTurnMetadata;\n}\n\nexport interface VoiceGatewayTurnResponseMetadata {\n tenantId: string;\n chatRoomId: string;\n threadId: string | null;\n agentSessionId: string;\n userMessageId: string;\n assistantMessageId: string | null;\n personaId: string;\n correlationId: string;\n voiceSessionId: string;\n source: string;\n}\n\nexport interface VoiceGatewayTurnResponse {\n session_id: string;\n turn_id: string;\n text: string;\n metadata: VoiceGatewayTurnResponseMetadata;\n}\n\nexport interface CreateVoiceChatSessionOptions {\n chatService: ChatService;\n db: SmrtClassOptions['db'];\n tenantId: string;\n actorProfileId: string;\n actorUserId?: string | null;\n persona: ConversationPersona;\n /** Existing agent session to bind voice to. Omit to create/reuse one. */\n agentSessionId?: string;\n /** Agent profile/session author id when creating a new agent session. */\n agentId?: string;\n /** Optional existing thread within the bound session room. */\n threadId?: string | null;\n /** Stable gateway-level `session_id`. Generated when omitted. */\n gatewaySessionId?: string;\n /** Subject key forwarded to ChatService.createAgentSession(). */\n sessionKey?: string | null;\n /** Voice binding TTL. Ignored when `expiresAt` is supplied. */\n ttlSeconds?: number;\n expiresAt?: Date;\n metadata?: Record<string, unknown>;\n target?: string;\n maxTokens?: number;\n maxMessages?: number;\n instructions?: string;\n}\n\nexport interface VoiceChatSessionCreationResult {\n voiceSession: VoiceSession;\n voiceSessionId: string;\n gatewaySessionId: string;\n expiresAt: Date;\n tenantId: string;\n actorProfileId: string;\n personaId: string;\n agentSessionId: string;\n chatRoomId: string;\n threadId: string | null;\n metadata: VoiceGatewayTurnMetadata;\n}\n\nexport interface HandleVoiceGatewayTurnOptions {\n chatService: ChatService;\n db: SmrtClassOptions['db'];\n ai: AIInterface;\n payload: VoiceGatewayTurnPayload;\n now?: Date;\n historyLimit?: number;\n recall?: PersonaRecallOptions | false;\n model?: string;\n temperature?: number;\n maxTokens?: number;\n maxSteps?: number;\n postgresRls?: boolean;\n audit?: PrincipalAuditSink;\n}\n\nexport interface VoiceGatewayTurnHandlerOptions\n extends Omit<HandleVoiceGatewayTurnOptions, 'payload'> {\n gatewayToken: string | (() => string | Promise<string>);\n}\n\nexport class VoiceGatewayError extends Error {\n readonly status: number;\n readonly code: string;\n\n constructor(message: string, status: number, code: string) {\n super(message);\n this.name = 'VoiceGatewayError';\n this.status = status;\n this.code = code;\n }\n}\n\nexport class VoiceGatewayBadRequestError extends VoiceGatewayError {\n constructor(message: string) {\n super(message, 400, 'voice_gateway_bad_request');\n this.name = 'VoiceGatewayBadRequestError';\n }\n}\n\nexport class VoiceGatewayUnauthorizedError extends VoiceGatewayError {\n constructor(message = 'Voice gateway authorization failed') {\n super(message, 401, 'voice_gateway_unauthorized');\n this.name = 'VoiceGatewayUnauthorizedError';\n }\n}\n\nexport class VoiceSessionRejectedError extends VoiceGatewayError {\n constructor(message: string) {\n super(message, 403, 'voice_session_rejected');\n this.name = 'VoiceSessionRejectedError';\n }\n}\n\nexport class VoiceSessionExpiredError extends VoiceGatewayError {\n constructor(message = 'Voice session is expired') {\n super(message, 410, 'voice_session_expired');\n this.name = 'VoiceSessionExpiredError';\n }\n}\n\nexport class VoiceGatewayReplayError extends VoiceGatewayError {\n constructor(message = 'Voice gateway turn has already been processed') {\n super(message, 409, 'voice_gateway_replay');\n this.name = 'VoiceGatewayReplayError';\n }\n}\n\nexport async function createVoiceChatSession(\n options: CreateVoiceChatSessionOptions,\n): Promise<VoiceChatSessionCreationResult> {\n const target = options.target ?? SMRT_CHAT_VOICE_TARGET;\n const personaId = requireNonEmptyString(\n options.persona.id,\n 'createVoiceChatSession requires a persisted persona id',\n );\n if (\n options.persona.tenantId !== null &&\n options.persona.tenantId !== options.tenantId\n ) {\n throw new VoiceSessionRejectedError(\n 'Persona tenant does not match the voice session tenant',\n );\n }\n\n const persona: ConversationPersona = {\n ...options.persona,\n id: personaId,\n tenantId: options.tenantId,\n };\n\n let agentSession = options.agentSessionId\n ? await options.chatService.getAgentSession({\n agentSessionId: options.agentSessionId,\n tenantId: options.tenantId,\n })\n : null;\n\n if (options.agentSessionId) {\n if (!agentSession) {\n throw new VoiceSessionRejectedError('Agent session not found');\n }\n assertAgentSessionMatchesActor(agentSession, options.actorProfileId);\n assertActiveAgentSession(agentSession);\n } else {\n // `agentId` is the agent's logical identity and `agentProfileId` is the\n // Profile it AUTHORS as — two different things since #2995. The persona's\n // `actsAsProfileId` is the latter (a `crossPackageRef` to `Profile`), so it\n // is passed as the authoring profile rather than collapsed into `agentId`;\n // otherwise a synthetic `bot` profile would be minted for it and persona\n // replies would stop being attributed to the configured acting identity.\n const agentId =\n options.agentId ?? persona.id ?? persona.agentClass ?? undefined;\n if (!agentId) {\n throw new VoiceGatewayBadRequestError(\n 'createVoiceChatSession requires agentId when the persona has no id or agent class',\n );\n }\n const created = await options.chatService.createAgentSession({\n tenantId: options.tenantId,\n agentId,\n agentProfileId: persona.actsAsProfileId ?? null,\n actorProfileId: options.actorProfileId,\n allowedTools: persona.allowedTools,\n systemPrompt: options.instructions ?? persona.instructions,\n maxTokens: options.maxTokens,\n maxMessages: options.maxMessages,\n sessionKey: options.sessionKey,\n });\n agentSession = created.session;\n }\n\n const boundSession = await bindPersonaToSession({\n chatService: options.chatService,\n session: agentSession,\n actorProfileId: options.actorProfileId,\n tenantId: options.tenantId,\n persona,\n instructions: options.instructions,\n db: options.db,\n });\n assertActiveAgentSession(boundSession);\n\n const chatRoomId = requireNonEmptyString(\n boundSession.chatRoomId,\n 'Agent session has no chat room',\n );\n await options.chatService.getRoomForMember(\n chatRoomId,\n options.actorProfileId,\n options.tenantId,\n );\n\n const threadId = options.threadId ?? null;\n if (threadId) {\n const thread = await options.chatService.getThread({\n threadId,\n tenantId: options.tenantId,\n });\n if (!thread || thread.roomId !== chatRoomId) {\n throw new VoiceSessionRejectedError(\n 'threadId does not belong to the bound agent session room',\n );\n }\n }\n\n const voiceSessions = await VoiceSessionCollection.create({ db: options.db });\n const gatewaySessionId = options.gatewaySessionId ?? crypto.randomUUID();\n const expiresAt =\n options.expiresAt ??\n new Date(\n Date.now() +\n (options.ttlSeconds ?? DEFAULT_VOICE_SESSION_TTL_SECONDS) * 1000,\n );\n\n const voiceSession = await voiceSessions.create({\n tenantId: options.tenantId,\n gatewaySessionId,\n actorProfileId: options.actorProfileId,\n actorUserId: options.actorUserId ?? null,\n personaId,\n agentSessionId: boundSession.id as string,\n chatRoomId,\n threadId,\n target,\n status: 'active',\n expiresAt,\n personaSnapshot: JSON.stringify(persona),\n metadata: JSON.stringify(options.metadata ?? {}),\n });\n\n const voiceSessionId = voiceSession.id as string;\n return {\n voiceSession,\n voiceSessionId,\n gatewaySessionId,\n expiresAt,\n tenantId: options.tenantId,\n actorProfileId: options.actorProfileId,\n personaId,\n agentSessionId: boundSession.id as string,\n chatRoomId,\n threadId,\n metadata: {\n tenantId: options.tenantId,\n actorProfileId: options.actorProfileId,\n chatRoomId,\n threadId: threadId ?? undefined,\n agentSessionId: boundSession.id as string,\n personaId,\n voiceSessionId,\n source: 'smrt-chat',\n },\n };\n}\n\nexport async function handleVoiceGatewayTurn(\n options: HandleVoiceGatewayTurnOptions,\n): Promise<VoiceGatewayTurnResponse> {\n const payload = normalizeGatewayPayload(options.payload);\n const metadata = payload.metadata ?? {};\n const voiceSessionId = requireNonEmptyString(\n metadata.voiceSessionId,\n 'Voice gateway payload metadata.voiceSessionId is required',\n );\n\n const voiceSessions = await VoiceSessionCollection.create({ db: options.db });\n const voiceSession = await voiceSessions.get({\n id: voiceSessionId,\n target: payload.target,\n });\n if (!voiceSession) {\n throw new VoiceSessionRejectedError('Voice session not found');\n }\n if (voiceSession.isExpired(options.now)) {\n if (voiceSession.status === 'active') {\n await voiceSession.expire();\n }\n throw new VoiceSessionExpiredError();\n }\n if (!voiceSession.isActive(options.now)) {\n throw new VoiceSessionRejectedError('Voice session is not active');\n }\n if (voiceSession.hasProcessedTurn(payload.turn_id)) {\n throw new VoiceGatewayReplayError();\n }\n\n validateGatewayPayloadAgainstBinding(payload, voiceSession);\n const persona = requireVoiceSessionPersona(voiceSession);\n\n const agentSession = await options.chatService.getAgentSession({\n agentSessionId: voiceSession.agentSessionId,\n tenantId: voiceSession.tenantId,\n });\n if (!agentSession) {\n throw new VoiceSessionRejectedError('Bound agent session not found');\n }\n assertAgentSessionMatchesActor(agentSession, voiceSession.actorProfileId);\n assertActiveAgentSession(agentSession);\n if (agentSession.chatRoomId !== voiceSession.chatRoomId) {\n throw new VoiceSessionRejectedError(\n 'Bound agent session no longer belongs to the voice session room',\n );\n }\n\n if (voiceSession.threadId) {\n const thread = await options.chatService.getThread({\n threadId: voiceSession.threadId,\n tenantId: voiceSession.tenantId,\n });\n if (!thread || thread.roomId !== voiceSession.chatRoomId) {\n throw new VoiceSessionRejectedError(\n 'Bound thread no longer belongs to the voice session room',\n );\n }\n }\n\n const gatewayTurn = await reserveVoiceGatewayTurn(\n options.db,\n voiceSession,\n payload,\n );\n\n try {\n const history = await loadConversationHistory({\n chatService: options.chatService,\n tenantId: voiceSession.tenantId,\n actorProfileId: voiceSession.actorProfileId,\n roomId: voiceSession.chatRoomId,\n threadId: voiceSession.threadId,\n limit: options.historyLimit ?? DEFAULT_HISTORY_LIMIT,\n });\n const correlationId = crypto.randomUUID();\n const commonMetadata = {\n source: 'voice-gateway',\n target: payload.target,\n voiceSessionId,\n gatewaySessionId: payload.session_id,\n gatewayTurnId: payload.turn_id,\n correlationId,\n };\n\n const userMessage = await options.chatService.sendAgentUserMessage({\n tenantId: voiceSession.tenantId,\n agentSessionId: voiceSession.agentSessionId,\n actorProfileId: voiceSession.actorProfileId,\n content: payload.text,\n });\n await mergeAndSaveMetadata(userMessage, {\n ...commonMetadata,\n voiceRole: 'transcript',\n });\n\n const turn = await runPersonaConversationTurn({\n ai: options.ai,\n db: options.db,\n persona,\n tenantId: voiceSession.tenantId,\n userMessage: payload.text,\n history,\n chatService: options.chatService,\n session: agentSession,\n threadId: voiceSession.threadId,\n recall: options.recall,\n model: options.model,\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n maxSteps: options.maxSteps,\n postgresRls: options.postgresRls,\n audit: options.audit,\n onBehalfOfUserId: voiceSession.actorUserId ?? undefined,\n correlationId,\n });\n\n for (const toolMessage of turn.authoredMessages?.toolMessages ?? []) {\n await mergeAndSaveMetadata(toolMessage, {\n ...commonMetadata,\n voiceRole: 'tool',\n });\n }\n const assistantMessage = turn.authoredMessages?.assistantMessage ?? null;\n if (assistantMessage) {\n await mergeAndSaveMetadata(assistantMessage, {\n ...commonMetadata,\n voiceRole: 'assistant',\n });\n }\n\n voiceSession.recordGatewayTurn(payload.turn_id);\n await voiceSession.save();\n await gatewayTurn.complete();\n\n return {\n session_id: payload.session_id,\n turn_id: payload.turn_id,\n text: turn.reply,\n metadata: {\n tenantId: voiceSession.tenantId,\n chatRoomId: voiceSession.chatRoomId,\n threadId: voiceSession.threadId,\n agentSessionId: voiceSession.agentSessionId,\n userMessageId: userMessage.id as string,\n assistantMessageId:\n (assistantMessage?.id as string | undefined) ?? null,\n personaId: voiceSession.personaId,\n correlationId: turn.correlationId,\n voiceSessionId,\n source: 'voice-gateway',\n },\n };\n } catch (error) {\n await markVoiceGatewayTurnFailed(gatewayTurn);\n throw error;\n }\n}\n\nexport function createVoiceGatewayTurnHandler(\n options: VoiceGatewayTurnHandlerOptions,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n try {\n if (request.method !== 'POST') {\n return jsonResponse({ error: 'Method not allowed' }, 405);\n }\n await assertVoiceGatewayBearer(\n request.headers,\n await resolveGatewayToken(options.gatewayToken),\n );\n const payload = normalizeGatewayPayload(await request.json());\n const response = await handleVoiceGatewayTurn({ ...options, payload });\n return jsonResponse(response, 200);\n } catch (error) {\n const status = error instanceof VoiceGatewayError ? error.status : 500;\n const message =\n error instanceof Error ? error.message : 'Voice gateway turn failed';\n const code =\n error instanceof VoiceGatewayError ? error.code : 'voice_gateway_error';\n return jsonResponse({ error: message, code }, status);\n }\n };\n}\n\nexport async function assertVoiceGatewayBearer(\n headers: Headers,\n expectedToken: string,\n): Promise<void> {\n if (!expectedToken) {\n throw new VoiceGatewayUnauthorizedError(\n 'Voice gateway token is not configured',\n );\n }\n const authorization = headers.get('authorization') ?? '';\n const match = authorization.match(/^Bearer\\s+(.+)$/i);\n const actual = match?.[1] ?? '';\n if (!constantTimeEquals(actual, expectedToken)) {\n throw new VoiceGatewayUnauthorizedError();\n }\n}\n\nasync function resolveGatewayToken(\n token: string | (() => string | Promise<string>),\n): Promise<string> {\n return typeof token === 'function' ? token() : token;\n}\n\nfunction normalizeGatewayPayload(input: unknown): VoiceGatewayTurnPayload {\n if (!isRecord(input)) {\n throw new VoiceGatewayBadRequestError('Voice gateway payload must be JSON');\n }\n const sessionId = requireNonEmptyString(\n input.session_id,\n 'Voice gateway payload session_id is required',\n );\n const turnId = requireNonEmptyString(\n input.turn_id,\n 'Voice gateway payload turn_id is required',\n );\n const target = requireNonEmptyString(\n input.target,\n 'Voice gateway payload target is required',\n );\n if (target !== SMRT_CHAT_VOICE_TARGET) {\n throw new VoiceGatewayBadRequestError(\n `Unsupported voice gateway target '${target}'`,\n );\n }\n const text = requireNonEmptyString(\n input.text,\n 'Voice gateway payload text is required',\n );\n if (text.length > MAX_VOICE_GATEWAY_TEXT_LENGTH) {\n throw new VoiceGatewayBadRequestError(\n `Voice gateway payload text must be ${MAX_VOICE_GATEWAY_TEXT_LENGTH} characters or fewer`,\n );\n }\n const metadata = input.metadata;\n if (metadata !== undefined && !isRecord(metadata)) {\n throw new VoiceGatewayBadRequestError(\n 'Voice gateway payload metadata must be an object',\n );\n }\n\n return {\n session_id: sessionId,\n turn_id: turnId,\n target,\n actor:\n typeof input.actor === 'string' && input.actor.length > 0\n ? input.actor\n : undefined,\n text,\n metadata: metadata as VoiceGatewayTurnMetadata | undefined,\n };\n}\n\nfunction validateGatewayPayloadAgainstBinding(\n payload: VoiceGatewayTurnPayload,\n voiceSession: VoiceSession,\n): void {\n if (payload.session_id !== voiceSession.gatewaySessionId) {\n throw new VoiceSessionRejectedError(\n 'Gateway session_id does not match the voice session binding',\n );\n }\n const metadata = payload.metadata ?? {};\n assertMetadataMatches(\n metadata,\n 'tenantId',\n voiceSession.tenantId,\n 'tenantId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'actorProfileId',\n voiceSession.actorProfileId,\n 'actorProfileId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'chatRoomId',\n voiceSession.chatRoomId,\n 'chatRoomId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'threadId',\n voiceSession.threadId,\n 'threadId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'agentSessionId',\n voiceSession.agentSessionId,\n 'agentSessionId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'personaId',\n voiceSession.personaId,\n 'personaId does not match the voice session binding',\n );\n}\n\nfunction requireVoiceSessionPersona(\n voiceSession: VoiceSession,\n): ConversationPersona {\n const persona = voiceSession.getPersonaSnapshot();\n if (!hasNonEmptyString(persona.id)) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot has no persona id',\n );\n }\n if (persona.id !== voiceSession.personaId) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot does not match the voice session persona',\n );\n }\n if (persona.tenantId !== voiceSession.tenantId) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot tenant does not match the voice session tenant',\n );\n }\n if (!hasNonEmptyString(persona.runAsUserId)) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot has no runnable user',\n );\n }\n if (!Array.isArray(persona.allowedTools)) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot has an invalid tool allow-list',\n );\n }\n return persona;\n}\n\nasync function reserveVoiceGatewayTurn(\n db: SmrtClassOptions['db'],\n voiceSession: VoiceSession,\n payload: VoiceGatewayTurnPayload,\n): Promise<VoiceGatewayTurn> {\n const gatewayTurns = await VoiceGatewayTurnCollection.create({ db });\n try {\n return await gatewayTurns.reserveTurn({\n tenantId: voiceSession.tenantId,\n voiceSessionId: voiceSession.id as string,\n gatewaySessionId: payload.session_id,\n gatewayTurnId: payload.turn_id,\n target: payload.target,\n });\n } catch (error) {\n if (isUniqueConstraintError(error)) {\n throw new VoiceGatewayReplayError();\n }\n throw error;\n }\n}\n\nasync function markVoiceGatewayTurnFailed(\n gatewayTurn: VoiceGatewayTurn,\n): Promise<void> {\n try {\n await gatewayTurn.fail();\n } catch {\n // Preserve the original turn failure for the gateway response.\n }\n}\n\nfunction isUniqueConstraintError(error: unknown): boolean {\n return (\n error instanceof ValidationError &&\n error.code === 'VALIDATION_UNIQUE_CONSTRAINT'\n );\n}\n\nfunction assertMetadataMatches(\n metadata: VoiceGatewayTurnMetadata,\n key: keyof VoiceGatewayTurnMetadata,\n expected: string | null,\n message: string,\n): void {\n const actual = metadata[key];\n if (actual === undefined || actual === null || actual === '') {\n return;\n }\n if (typeof actual !== 'string' || actual !== expected) {\n throw new VoiceSessionRejectedError(message);\n }\n}\n\nfunction assertAgentSessionMatchesActor(\n session: { participantProfileId: string },\n actorProfileId: string,\n): void {\n if (session.participantProfileId !== actorProfileId) {\n throw new VoiceSessionRejectedError(\n 'Agent session participant does not match the voice session actor',\n );\n }\n}\n\nfunction assertActiveAgentSession(session: {\n isActive: () => boolean;\n chatRoomId: string | null;\n}): void {\n if (!session.isActive()) {\n throw new VoiceSessionRejectedError('Agent session is not active');\n }\n if (!session.chatRoomId) {\n throw new VoiceSessionRejectedError('Agent session has no chat room');\n }\n}\n\nasync function loadConversationHistory(input: {\n chatService: ChatService;\n tenantId: string;\n actorProfileId: string;\n roomId: string;\n threadId: string | null;\n limit: number;\n}): Promise<AIMessage[]> {\n const messages = input.threadId\n ? await input.chatService.getThreadMessages({\n threadId: input.threadId,\n actorProfileId: input.actorProfileId,\n tenantId: input.tenantId,\n limit: input.limit,\n })\n : (\n await input.chatService.getRoomMessages({\n roomId: input.roomId,\n actorProfileId: input.actorProfileId,\n tenantId: input.tenantId,\n limit: input.limit,\n })\n ).reverse();\n\n return messages\n .map(chatMessageToAIMessage)\n .filter((message): message is AIMessage => message !== null);\n}\n\nfunction chatMessageToAIMessage(message: ChatMessage): AIMessage | null {\n if (\n message.role !== 'user' &&\n message.role !== 'assistant' &&\n message.role !== 'system'\n ) {\n return null;\n }\n // A fallback reply is not a model answer: never replay it as one (#4379);\n // a fixed marker keeps the roles alternating.\n if (message.role === 'assistant' && isFallbackReply(message)) {\n return { role: 'assistant', content: FALLBACK_HISTORY_PLACEHOLDER };\n }\n return { role: message.role, content: message.content } as AIMessage;\n}\n\nasync function mergeAndSaveMetadata(\n message: ChatMessage,\n metadata: Record<string, unknown>,\n): Promise<void> {\n message.setMetadata({ ...message.getMetadata(), ...metadata });\n await message.save();\n}\n\nfunction requireNonEmptyString(value: unknown, message: string): string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new VoiceGatewayBadRequestError(message);\n }\n return value;\n}\n\nfunction hasNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction constantTimeEquals(actual: string, expected: string): boolean {\n let diff = actual.length ^ expected.length;\n const length = Math.max(actual.length, expected.length);\n for (let index = 0; index < length; index++) {\n const actualCode = index < actual.length ? actual.charCodeAt(index) : 0;\n const expectedCode =\n index < expected.length ? expected.charCodeAt(index) : 0;\n diff |= actualCode ^ expectedCode;\n }\n return diff === 0;\n}\n\nfunction jsonResponse(body: unknown, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/chat-feedback.ts","../src/helper-preferences-service.ts","../src/helper-profile-store.ts","../src/models/VoiceGatewayTurn.ts","../src/overview-tools.ts","../src/recipes.ts","../src/collections/VoiceGatewayTurnCollection.ts","../src/voice.ts"],"sourcesContent":["/**\n * Chat feedback capture — turn an in-conversation judgement into a first-class\n * learning signal (L3 of the learning-agents epic, #1891).\n *\n * A tenant end-user accepting or rejecting an applied change, giving a\n * thumbs-up/down, or typing an inline correction produces a {@link Feedback}\n * row — carrying the conversation's **correlation-id** back to the turn it\n * judges — and (by default) immediately reinforces the persona's learning\n * memory. Because recall draws on that same memory next turn, captured feedback\n * *influences subsequent behaviour*: a rejected strategy decays below the reuse\n * floor and stops resurfacing; a correction supersedes it with the corrected\n * value.\n *\n * This is the human-signal half of the loop the personas package already models\n * ({@link reinforceFromFeedback}); the gated half — rewriting a persona's\n * instructions — stays in the directive-proposal flow.\n *\n * @module\n */\n\nimport {\n type LearningMemoryRecord,\n type LearningSemanticSearch,\n resolveDatabase,\n type SmrtClassOptions,\n} from '@happyvertical/smrt-core';\nimport {\n type Feedback,\n FeedbackCollection,\n type FeedbackSignalType,\n feedbackSourceFor,\n personaLearningMemory,\n personaMemoryScope,\n reinforceFromFeedback,\n} from '@happyvertical/smrt-personas';\n\n/** The minimal persona shape chat feedback needs to route the signal. */\nexport interface ChatFeedbackPersona {\n /** Persona id — required (a signal always judges a specific persona). */\n id?: string | null;\n /** Owning tenant. */\n tenantId?: string | null;\n /** Canonical agent class the persona configures (denormalised onto the row). */\n agentClass?: string;\n /** Learning memory partition key. */\n memoryScope?: string;\n}\n\n/**\n * Options for {@link captureChatFeedback}.\n */\nexport interface CaptureChatFeedbackOptions {\n /** Database handle. */\n db: SmrtClassOptions['db'];\n /** The persona the signal judges. */\n persona: ChatFeedbackPersona;\n /** The kind of signal. */\n signalType: FeedbackSignalType;\n /** Correlation-id of the conversation turn this signal judges. */\n correlationId: string;\n /** What {@link correlationId} names. Default `'chat_message'`. */\n correlationType?: string;\n /** Learning episode scope the signal reinforces (matches recall/capture). */\n scope: string;\n /** Learning episode key the signal reinforces. */\n key: string;\n /** The user id that authored the signal (null for autonomous). */\n actorId?: string | null;\n /** Numeric rating for a `rating` signal. */\n rating?: number | null;\n /** Corrected value for a `correction` signal. */\n correction?: string | null;\n /** Freeform note. */\n comment?: string | null;\n /** Structured metadata persisted on the row. */\n metadata?: Record<string, unknown>;\n /** Apply the signal to memory immediately. Default `true`. */\n reinforce?: boolean;\n /** Optional embedding search wired into the reinforced memory. */\n semanticSearch?: LearningSemanticSearch;\n /** Neutral point of a `rating` scale (see `FeedbackOutcomeOptions`). Default 0. */\n ratingNeutral?: number;\n}\n\n/** The outcome of capturing chat feedback. */\nexport interface ChatFeedbackResult {\n /** The persisted feedback row. */\n feedback: Feedback;\n /** The memory record the signal reinforced, or `null` when it carried none. */\n reinforced: LearningMemoryRecord | null;\n}\n\n/**\n * Capture one in-chat feedback signal as a {@link Feedback} row and (by default)\n * reinforce the persona's learning memory from it.\n *\n * @throws when the persona has no id (a signal must name a persisted persona).\n */\nexport async function captureChatFeedback(\n options: CaptureChatFeedbackOptions,\n): Promise<ChatFeedbackResult> {\n if (!options.persona.id) {\n throw new Error(\n 'captureChatFeedback requires a persisted persona (missing id)',\n );\n }\n const memoryScope = personaMemoryScope(options.persona);\n\n const feedbacks = await FeedbackCollection.create({ db: options.db });\n const feedback = await feedbacks.create({\n tenantId: options.persona.tenantId ?? null,\n personaId: options.persona.id,\n agentClass: options.persona.agentClass ?? '',\n memoryScope,\n scope: options.scope,\n key: options.key,\n signalType: options.signalType,\n source: feedbackSourceFor(options.signalType),\n correlationId: options.correlationId,\n correlationType: options.correlationType ?? 'chat_message',\n rating: options.rating ?? null,\n correction: options.correction ?? null,\n comment: options.comment ?? null,\n actorId: options.actorId ?? null,\n });\n if (options.metadata) {\n feedback.setMetadata(options.metadata);\n }\n await feedback.save();\n\n let reinforced: LearningMemoryRecord | null = null;\n if (options.reinforce !== false) {\n // LearningMemory operates on a resolved DB handle; core's `resolveDatabase`\n // accepts a config or a handle and returns a handle (identity for a handle).\n const memory = personaLearningMemory({\n db: await resolveDatabase(options.db ?? {}),\n persona: options.persona,\n semanticSearch: options.semanticSearch,\n });\n reinforced = await reinforceFromFeedback(memory, feedback, {\n ratingNeutral: options.ratingNeutral,\n });\n // Gate exactly-once reinforcement so a later reflection pass never\n // re-applies this signal (mirrors the personas reflection runner).\n feedback.reinforcedAt = new Date();\n await feedback.save();\n }\n\n return { feedback, reinforced };\n}\n\n/** Shared options for the signal-typed convenience wrappers. */\nexport type ChatFeedbackBase = Omit<\n CaptureChatFeedbackOptions,\n 'signalType' | 'rating' | 'correction'\n>;\n\n/**\n * Accept an applied change — reinforces the judged strategy as a success.\n */\nexport function acceptAppliedChange(\n options: ChatFeedbackBase,\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'accept' });\n}\n\n/**\n * Reject an applied change — decays the judged strategy toward the failure floor\n * so it stops being recalled.\n */\nexport function rejectAppliedChange(\n options: ChatFeedbackBase & { comment?: string | null },\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'reject' });\n}\n\n/**\n * Record an inline correction — decays the wrong strategy AND supersedes its\n * stored value with the corrected one, so the next recall returns the fix.\n */\nexport function correctResponse(\n options: ChatFeedbackBase & { correction: string; comment?: string | null },\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({\n ...options,\n signalType: 'correction',\n correction: options.correction,\n });\n}\n\n/**\n * Record a numeric rating for a response (scale is caller-defined; pass\n * `ratingNeutral` for a mid-point).\n */\nexport function rateResponse(\n options: ChatFeedbackBase & { rating: number },\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({\n ...options,\n signalType: 'rating',\n rating: options.rating,\n });\n}\n\n/** Thumbs-up — a `+1` rating (reinforces as a success against neutral 0). */\nexport function thumbsUp(\n options: ChatFeedbackBase,\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'rating', rating: 1 });\n}\n\n/** Thumbs-down — a `-1` rating (decays as a failure against neutral 0). */\nexport function thumbsDown(\n options: ChatFeedbackBase,\n): Promise<ChatFeedbackResult> {\n return captureChatFeedback({ ...options, signalType: 'rating', rating: -1 });\n}\n","import {\n type HelperContext,\n type HelperOffering,\n type HelperPolicy,\n type HelperPreferenceField,\n type HelperPreferenceStore,\n type HelperPreferences,\n type HelperRecovery,\n type HelperSnapshot,\n isHelperClearedPreferences,\n parseHelperPreferences,\n} from './helper-preferences.js';\nexport class HelperPreferencesAuthorizationError extends Error {\n constructor() {\n super('Helper preference access is not authorized.');\n this.name = 'HelperPreferencesAuthorizationError';\n }\n}\nexport class HelperPreferencesValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'HelperPreferencesValidationError';\n }\n}\nexport interface HelperPreferencesServiceOptions {\n store: HelperPreferenceStore;\n authorize(\n context: HelperContext,\n operation: 'load' | 'save' | 'reset',\n ): boolean | Promise<boolean>;\n resolvePolicy(context: HelperContext): HelperPolicy | Promise<HelperPolicy>;\n styleIds: readonly string[];\n validateOffering(\n context: HelperContext,\n offering: HelperOffering,\n ): void | Promise<void>;\n}\nexport class HelperPreferencesService {\n readonly #o: HelperPreferencesServiceOptions;\n constructor(options: HelperPreferencesServiceOptions) {\n this.#o = options;\n }\n async load(c: HelperContext): Promise<HelperSnapshot> {\n await this.#auth(c, 'load');\n return this.#resolve(c, await this.#o.store.load(c));\n }\n async save(c: HelperContext, value: unknown): Promise<HelperSnapshot> {\n await this.#auth(c, 'save');\n const p = await this.#visiblePolicy(c, await this.#policy(c));\n if (p.selection === 'owner-assigned')\n throw new HelperPreferencesAuthorizationError();\n const next = parseHelperPreferences(value);\n if (!next)\n throw new HelperPreferencesValidationError(\n 'Helper preferences are invalid.',\n );\n for (const f of this.#fields())\n if (!p.customizable.includes(f) && next[f] !== p.defaultPreferences[f])\n throw new HelperPreferencesValidationError(\n `The ${f} preference is not customizable.`,\n );\n await this.#validate(c, p, next);\n await this.#o.store.save(c, next);\n return this.#resolve(c, await this.#o.store.load(c));\n }\n async reset(c: HelperContext): Promise<HelperSnapshot> {\n await this.#auth(c, 'reset');\n const p = await this.#policy(c);\n if (p.selection === 'owner-assigned')\n throw new HelperPreferencesAuthorizationError();\n await this.#o.store.clear(c);\n return this.#resolve(c, await this.#o.store.load(c));\n }\n async #auth(c: HelperContext, op: 'load' | 'save' | 'reset') {\n if (!(await this.#o.authorize(c, op)))\n throw new HelperPreferencesAuthorizationError();\n }\n async #policy(c: HelperContext) {\n const p = await this.#o.resolvePolicy(c);\n if (\n !parseHelperPreferences(p.defaultPreferences) ||\n (p.assignedPreferences !== undefined &&\n !parseHelperPreferences(p.assignedPreferences))\n )\n throw new HelperPreferencesValidationError(\n 'Helper policy preferences are invalid.',\n );\n return p;\n }\n async #resolve(c: HelperContext, raw: unknown): Promise<HelperSnapshot> {\n const p = await this.#visiblePolicy(c, await this.#policy(c));\n const permissions =\n p.selection === 'owner-assigned'\n ? { editableFields: [], canReset: false, customStyleIds: [] }\n : {\n editableFields: p.customizable,\n canReset: raw !== null && !this.#cleared(raw),\n customStyleIds: p.customStyleIds,\n };\n const assigned = p.selection === 'owner-assigned';\n if (assigned && !p.assignedPreferences)\n return this.#snapshot(p, permissions, null, 'unavailable', false, {\n code: 'invalid-preferences',\n message: 'The assigned helper preferences are unavailable.',\n });\n const baseline = assigned ? p.assignedPreferences! : p.defaultPreferences;\n const persisted = !assigned && raw !== null && !this.#cleared(raw);\n const parsed = assigned\n ? baseline\n : !persisted\n ? baseline\n : parseHelperPreferences(this.#decode(raw));\n if (!parsed) {\n try {\n await this.#validate(c, p, baseline);\n return this.#snapshot(p, permissions, baseline, 'default', true, {\n code: 'invalid-preferences',\n message:\n 'Saved helper preferences are invalid. Reset to use current defaults.',\n });\n } catch (error) {\n if (error instanceof HelperPreferencesAuthorizationError) throw error;\n return this.#snapshot(p, permissions, null, 'unavailable', true, {\n code: 'invalid-preferences',\n message: 'The application default helper is unavailable.',\n });\n }\n }\n try {\n if (persisted)\n for (const f of this.#fields())\n if (!p.customizable.includes(f) && parsed[f] !== baseline[f])\n throw new HelperPreferencesValidationError('invalid-preferences');\n await this.#validate(c, p, parsed);\n return this.#snapshot(\n p,\n permissions,\n parsed,\n assigned ? 'assigned' : persisted ? 'personal' : 'default',\n persisted,\n null,\n );\n } catch (error) {\n if (error instanceof HelperPreferencesAuthorizationError) throw error;\n if (assigned)\n return this.#snapshot(p, permissions, null, 'unavailable', false, {\n code: 'invalid-preferences',\n message: 'The assigned helper preferences are unavailable.',\n });\n try {\n await this.#validate(c, p, baseline);\n return this.#snapshot(\n p,\n permissions,\n baseline,\n 'default',\n persisted,\n this.#recovery(error),\n );\n } catch (fallbackError) {\n if (fallbackError instanceof HelperPreferencesAuthorizationError)\n throw fallbackError;\n return this.#snapshot(p, permissions, null, 'unavailable', persisted, {\n code: 'invalid-preferences',\n message: 'The application default helper is unavailable.',\n });\n }\n }\n }\n async #visiblePolicy(\n c: HelperContext,\n p: HelperPolicy,\n ): Promise<HelperPolicy> {\n const offerings: HelperOffering[] = [];\n for (const offering of p.offerings) {\n if (!this.#o.styleIds.includes(offering.styleId)) continue;\n try {\n await this.#o.validateOffering(c, offering);\n offerings.push(offering);\n } catch (error) {\n if (error instanceof HelperPreferencesAuthorizationError) throw error;\n }\n }\n return { ...p, offerings };\n }\n #decode(raw: unknown) {\n if (typeof raw !== 'string') return raw;\n try {\n return JSON.parse(raw);\n } catch {\n return undefined;\n }\n }\n #cleared(raw: unknown) {\n return isHelperClearedPreferences(\n typeof raw === 'string' ? this.#decode(raw) : raw,\n );\n }\n async #validate(c: HelperContext, p: HelperPolicy, next: HelperPreferences) {\n const offering = p.offerings.find((x) => x.id === next.offeringId);\n if (!offering)\n throw new HelperPreferencesValidationError('unavailable-offering');\n if (!this.#o.styleIds.includes(offering.styleId))\n throw new HelperPreferencesValidationError('unavailable-style');\n if (!p.voices.some((x) => x.id === next.voiceId))\n throw new HelperPreferencesValidationError('unavailable-voice');\n await this.#o.validateOffering(c, offering);\n }\n #recovery(error: unknown): HelperRecovery {\n const code =\n error instanceof HelperPreferencesValidationError &&\n [\n 'unavailable-offering',\n 'unavailable-style',\n 'unavailable-voice',\n ].includes(error.message)\n ? (error.message as HelperRecovery['code'])\n : 'invalid-preferences';\n return {\n code,\n message:\n 'Saved helper preferences are no longer available. Reset to use current defaults.',\n };\n }\n #snapshot(\n p: HelperPolicy,\n permissions: HelperSnapshot['permissions'],\n prefs: HelperPreferences | null,\n source: HelperSnapshot['source'],\n hasOverride: boolean,\n recovery: HelperRecovery | null,\n ): HelperSnapshot {\n return {\n preferences: prefs,\n recoveryDraft:\n prefs === null && p.selection !== 'owner-assigned'\n ? this.#recoveryDraft(p, permissions)\n : null,\n offering: prefs\n ? (p.offerings.find((x) => x.id === prefs.offeringId) ?? null)\n : null,\n selection: p.selection,\n source,\n offerings: p.offerings,\n voices: p.voices,\n permissions,\n hasOverride,\n recovery,\n };\n }\n #recoveryDraft(\n p: HelperPolicy,\n permissions: HelperSnapshot['permissions'],\n ): HelperPreferences | null {\n const candidate = { ...p.defaultPreferences };\n const offering = p.offerings.find(\n (item) => item.id === candidate.offeringId,\n );\n if (!offering) {\n if (!permissions.editableFields.includes('offeringId') || !p.offerings[0])\n return null;\n candidate.offeringId = p.offerings[0].id;\n }\n if (!p.voices.some((voice) => voice.id === candidate.voiceId)) {\n if (!permissions.editableFields.includes('voiceId') || !p.voices[0])\n return null;\n candidate.voiceId = p.voices[0].id;\n }\n return parseHelperPreferences(candidate);\n }\n #fields(): readonly HelperPreferenceField[] {\n return [\n 'offeringId',\n 'name',\n 'voiceId',\n 'placement',\n 'heardSubtitles',\n 'spokenSubtitles',\n ];\n }\n}\n","import {\n ProfileCollection,\n ProfileMetadataCollection,\n ProfileMetafieldCollection,\n} from '@happyvertical/smrt-profiles';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n HELPER_PREFERENCES_VERSION,\n type HelperContext,\n type HelperPreferenceStore,\n type HelperPreferences,\n} from './helper-preferences.js';\n\nexport class HelperProfileStoreError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'HelperProfileStoreError';\n }\n}\n\nexport interface HelperProfilePreferenceStoreOptions {\n db: DatabaseInterface;\n /** Trusted application binding; one app must never read another app's field. */\n applicationId: string;\n /** Pre-provisioned per-application ProfileMetafield slug. */\n metafieldSlug: string;\n}\n\n/**\n * Canonical Profile metadata adapter. It deliberately does not authorize an\n * actor: `HelperPreferencesService` owns that policy decision. This adapter\n * binds every read and write to the requested profile and tenant, detects\n * duplicate metadata rows, and stores one versioned JSON value.\n */\nexport class ProfileHelperPreferenceStore implements HelperPreferenceStore {\n readonly #db: DatabaseInterface;\n readonly #metafieldSlug: string;\n readonly #applicationId: string;\n\n constructor(options: HelperProfilePreferenceStoreOptions) {\n this.#db = options.db;\n this.#metafieldSlug = options.metafieldSlug;\n this.#applicationId = options.applicationId;\n }\n\n async load(context: HelperContext): Promise<unknown | null> {\n const { profile, metafield, metadata } = await this.#boundRecords(context);\n const rows = await metadata.list({\n where: { profileId: profile.id, metafieldId: metafield.id },\n });\n if (rows.length > 1) {\n throw new HelperProfileStoreError(\n 'Duplicate helper preference metadata rows.',\n );\n }\n const row = rows[0];\n if (!row) return null;\n if ((row.tenantId ?? null) !== (profile.tenantId ?? null)) {\n throw new HelperProfileStoreError(\n 'Helper preference metadata tenant mismatch.',\n );\n }\n return row.value;\n }\n\n async save(context: HelperContext, value: HelperPreferences): Promise<void> {\n const { profile, metafield, metadata } = await this.#boundRecords(context);\n const rows = await metadata.list({\n where: { profileId: profile.id, metafieldId: metafield.id },\n });\n if (rows.length > 1) {\n throw new HelperProfileStoreError(\n 'Duplicate helper preference metadata rows.',\n );\n }\n const serialized = JSON.stringify(value);\n const row = rows[0];\n if (row) {\n if ((row.tenantId ?? null) !== (profile.tenantId ?? null)) {\n throw new HelperProfileStoreError(\n 'Helper preference metadata tenant mismatch.',\n );\n }\n row.value = serialized;\n await row.save();\n return;\n }\n const created = await metadata.create({\n profileId: profile.id as string,\n metafieldId: metafield.id as string,\n value: serialized,\n tenantId: profile.tenantId ?? null,\n });\n await created.save();\n }\n\n async clear(context: HelperContext): Promise<void> {\n const { profile, metafield, metadata } = await this.#boundRecords(context);\n const rows = await metadata.list({\n where: { profileId: profile.id, metafieldId: metafield.id },\n });\n if (rows.length > 1)\n throw new HelperProfileStoreError(\n 'Duplicate helper preference metadata rows.',\n );\n const serialized = JSON.stringify({\n version: HELPER_PREFERENCES_VERSION,\n cleared: true,\n });\n const row = rows[0];\n if (row) {\n if ((row.tenantId ?? null) !== (profile.tenantId ?? null)) {\n throw new HelperProfileStoreError(\n 'Helper preference metadata tenant mismatch.',\n );\n }\n row.value = serialized;\n await row.save();\n return;\n }\n const created = await metadata.create({\n profileId: profile.id as string,\n metafieldId: metafield.id as string,\n value: serialized,\n tenantId: profile.tenantId ?? null,\n });\n await created.save();\n }\n\n async #boundRecords(context: HelperContext) {\n if (context.applicationId !== this.#applicationId) {\n throw new HelperProfileStoreError(\n 'Helper preference application mismatch.',\n );\n }\n const profiles = await ProfileCollection.create({ db: this.#db });\n const profile = await profiles.get({ id: context.profileId });\n if (!profile || (profile.tenantId ?? null) !== context.tenantId) {\n throw new HelperProfileStoreError(\n 'Helper preference profile is unavailable.',\n );\n }\n const metafields = await ProfileMetafieldCollection.create({\n db: this.#db,\n });\n const candidates = await metafields.list({\n where: { slug: this.#metafieldSlug },\n });\n const matching = candidates.filter(\n (field) => (field.tenantId ?? null) === (profile.tenantId ?? null),\n );\n const global = candidates.filter((field) => field.tenantId === null);\n const chosen = matching[0] ?? global[0];\n if (\n !chosen ||\n matching.length > 1 ||\n (matching.length === 0 && global.length > 1)\n ) {\n throw new HelperProfileStoreError(\n 'The pre-provisioned helper preference metafield is unavailable or ambiguous.',\n );\n }\n return {\n profile,\n metafield: chosen,\n metadata: await ProfileMetadataCollection.create({ db: this.#db }),\n };\n }\n}\n\nexport function createHelperProfilePreferenceStore(\n options: HelperProfilePreferenceStoreOptions,\n): ProfileHelperPreferenceStore {\n return new ProfileHelperPreferenceStore(options);\n}\n","import { field, foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type {\n VoiceGatewayTurnOptions,\n VoiceGatewayTurnStatus,\n} from '../types.js';\n\n/**\n * Durable reservation for a gateway turn. The natural key makes `turn_id`\n * replay checks concurrency-safe before transcript messages are written.\n */\n@TenantScoped({ mode: 'required' })\n@smrt({\n tableName: 'voice_gateway_turns',\n conflictColumns: ['voice_session_id', 'gateway_turn_id'],\n api: { include: ['list', 'get'] },\n mcp: { include: ['list', 'get'] },\n cli: false,\n})\nexport class VoiceGatewayTurn extends SmrtObject { static __smrtPackage__ = \"@happyvertical/smrt-chat\";\n @tenantId()\n tenantId: string = '';\n\n @foreignKey('VoiceSession', { required: true })\n voiceSessionId: string = '';\n\n @field({ required: true })\n gatewaySessionId: string = '';\n\n @field({ required: true })\n gatewayTurnId: string = '';\n\n @field({ required: true })\n target: string = 'smrt:chat';\n\n @field({ required: true })\n status: VoiceGatewayTurnStatus = 'processing';\n\n @field()\n completedAt: Date | null = null;\n\n @field()\n failedAt: Date | null = null;\n\n constructor(options: VoiceGatewayTurnOptions = {}) {\n super(options);\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.voiceSessionId !== undefined)\n this.voiceSessionId = options.voiceSessionId;\n if (options.gatewaySessionId !== undefined)\n this.gatewaySessionId = options.gatewaySessionId;\n if (options.gatewayTurnId !== undefined)\n this.gatewayTurnId = options.gatewayTurnId;\n if (options.target !== undefined) this.target = options.target;\n if (options.status !== undefined) this.status = options.status;\n if (options.completedAt !== undefined)\n this.completedAt = options.completedAt;\n if (options.failedAt !== undefined) this.failedAt = options.failedAt;\n }\n\n async complete(now: Date = new Date()): Promise<void> {\n this.status = 'completed';\n this.completedAt = now;\n this.failedAt = null;\n await this.save();\n }\n\n async fail(now: Date = new Date()): Promise<void> {\n this.status = 'failed';\n this.failedAt = now;\n await this.save();\n }\n}\n","/**\n * Assistant tools for customizable overview pages (#3727 phase 4): the\n * assistant adds and configures widgets (\"add a chart of overdue invoices\")\n * with structured operations, and the person can undo the change.\n *\n * The model only ever supplies data: a page id and a list of operations\n * (`add`, `configure`, `move`, `resize`, `remove`) over the page's closed\n * option vocabulary. It never names a query, a table or a tenant. Every\n * authority decision is the host's, made from the live {@link PrincipalRun}:\n *\n * - `open(run, pageId)` returns the overview for THIS principal (its\n * definition, the principal's stored override, whether the principal may\n * customize, and how to persist), or `null` when the page is not offered.\n * - The surface validates every batch with the overview model and\n * `checkOverviewOverride` (smrt-svelte's `createOverviewAssistantSurface`\n * is the implementation), so the assistant can only store an override the\n * page's own save endpoint would accept. A batch is atomic.\n * - Undo is single-step and keyed by tenant, user and page: an undo token is\n * only ever looked up under the principal that is calling, so one\n * principal can never restore another's state.\n *\n * This package depends on no UI package, so the surface is typed\n * structurally.\n *\n * @module\n */\n\nimport type { AITool } from '@happyvertical/ai';\nimport {\n DataSurfaceDeniedError,\n type PrincipalRun,\n type PrincipalTool,\n type PrincipalToolContext,\n} from '@happyvertical/smrt-agents';\nimport {\n getCurrentTenant,\n isSuperAdminBypass,\n isSystemContext,\n isTenancyEnabled,\n withTenant,\n} from '@happyvertical/smrt-tenancy';\n\n/** Tool slug (permission/allow-list name) for describing an overview. */\nexport const OVERVIEW_DESCRIBE_TOOL_SLUG = 'overviews.describe';\n/** Tool slug for applying a batch of operations. */\nexport const OVERVIEW_APPLY_TOOL_SLUG = 'overviews.apply';\n/** Tool slug for undoing the last applied batch. */\nexport const OVERVIEW_UNDO_TOOL_SLUG = 'overviews.undo';\n\n/** Provider function name for {@link OVERVIEW_DESCRIBE_TOOL_SLUG}. */\nexport const OVERVIEW_DESCRIBE_FUNCTION_NAME = 'overviews-describe';\n/** Provider function name for {@link OVERVIEW_APPLY_TOOL_SLUG}. */\nexport const OVERVIEW_APPLY_FUNCTION_NAME = 'overviews-apply';\n/** Provider function name for {@link OVERVIEW_UNDO_TOOL_SLUG}. */\nexport const OVERVIEW_UNDO_FUNCTION_NAME = 'overviews-undo';\n\n/** Default cap on operations in one batch (matches smrt-svelte's). */\nexport const OVERVIEW_TOOL_MAX_OPERATIONS = 20;\n\nconst PAGE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,95}$/;\n\n/** A plan result, as the surface returns it. */\nexport type OverviewToolPlan =\n | {\n ok: true;\n override: unknown;\n document: unknown;\n results: readonly unknown[];\n unchanged?: boolean;\n }\n | { ok: false; issues: readonly unknown[] };\n\n/** A save-style check of an override, as the surface returns it. */\nexport type OverviewToolCheck =\n | { ok: true; override: unknown }\n | { ok: false; issues: readonly unknown[] };\n\n/**\n * One overview for one principal. smrt-svelte's\n * `createOverviewAssistantSurface` (on `./overview/server`) implements it.\n */\nexport interface OverviewToolSurface {\n readonly pageId: string;\n /** Whether this principal may customize the page (the role gate). */\n readonly canCustomize: boolean;\n /** Allowed widget types with their option fields, and the arrangement. */\n describe(): unknown;\n /** The canonical override in effect (`null` = defaults). */\n current(): unknown;\n /** Validate and plan a batch against {@link current}. Applies nothing. */\n plan(operations: unknown): OverviewToolPlan;\n /** Validate an override the way a save is validated. */\n check(override: unknown): OverviewToolCheck;\n /**\n * Persist a canonical override in the principal's tier, CONDITIONALLY: only\n * if the stored value is still the one this surface loaded (its revision).\n * A save that landed since the surface was opened must answer\n * `{ ok: false, reason: 'conflict' }`, never be overwritten. On success the\n * surface's `current()` and revision follow the written value.\n */\n persist(override: unknown): Promise<OverviewToolPersistResult>;\n}\n\n/**\n * Outcome of {@link OverviewToolSurface.persist}. Structurally the phase-3\n * store's save result (`OverviewSaveResult` of `@happyvertical/smrt-preferences`'\n * `createOverviewStore`).\n */\nexport type OverviewToolPersistResult =\n | { ok: true }\n | {\n ok: false;\n reason: 'conflict' | 'not_allowed' | 'invalid';\n issues?: readonly unknown[];\n };\n\n/** The application side: which overviews a principal may reach. */\nexport interface OverviewToolsHost {\n /**\n * The page for this principal, or `null` when it is unknown or not offered\n * to them. Build the surface from the principal's own stored override and\n * role; never from model input other than the page id. Scope every read\n * and the surface's `persist` by `run.context.tenantId` (with tenancy on,\n * the tools call `open` and every surface method inside that tenant's\n * context and refuse a run without one).\n */\n open(\n run: PrincipalRun,\n pageId: string,\n ): OverviewToolSurface | null | Promise<OverviewToolSurface | null>;\n /** Overview pages offered to this principal (for `describe` without a page). */\n pages?(\n run: PrincipalRun,\n ):\n | ReadonlyArray<{ id: string; title?: string }>\n | Promise<ReadonlyArray<{ id: string; title?: string }>>;\n}\n\n/** What Undo restores: the override before the last applied batch. */\nexport interface OverviewUndoEntry {\n token: string;\n before: unknown;\n after: unknown;\n /**\n * When the batch's conditional write succeeded (epoch ms, strictly\n * increasing within a process). Orders entries for\n * {@link OverviewUndoStore.set} and drives expiry.\n */\n createdAt: number;\n}\n\n/**\n * Where undo entries live, keyed by an opaque principal-and-page key the\n * tools compute. The default is in-process memory; a multi-replica host\n * passes a shared store (for example the assistant session context).\n */\nexport interface OverviewUndoStore {\n get(\n key: string,\n ): OverviewUndoEntry | undefined | Promise<OverviewUndoEntry | undefined>;\n /**\n * Conditional set: store `entry` under `key` unless the stored entry is\n * NEWER (a larger `createdAt`); then it is a no-op. Overlapping applies for\n * one principal and page can finish their entry writes out of order, and an\n * older apply's late write must not replace the newer batch's entry. A\n * shared store implements this atomically (for example a conditional\n * upsert on `createdAt`).\n */\n set(key: string, entry: OverviewUndoEntry): void | Promise<void>;\n /**\n * Compare-and-delete: remove the entry under `key` ONLY if its `token` is\n * `token`; a no-op otherwise. An undo that finishes after a newer batch\n * stored its own entry under the same key must not remove that entry. A\n * shared store implements this atomically (for example a conditional\n * delete on the token).\n */\n delete(key: string, token: string): void | Promise<void>;\n}\n\n/** Agent-level audit record of an overview change. */\nexport interface OverviewToolAuditEntry {\n action: 'apply' | 'undo';\n userId: string;\n tenantId: string | null;\n pageId: string;\n operations?: number;\n}\n\nexport interface OverviewToolsOptions {\n host: OverviewToolsHost;\n /**\n * Where undo entries live. It must outlive a request and a turn. Defaults\n * to {@link defaultOverviewUndoStore} (process-wide memory); pass a shared\n * store when several replicas serve one assistant.\n */\n undoStore?: OverviewUndoStore;\n audit?: (entry: OverviewToolAuditEntry) => void | Promise<void>;\n /** Batch cap (default 20); keep it equal to the surface's. */\n maxOperations?: number;\n /** Token source (tests). Defaults to `crypto.randomUUID`. */\n createToken?: () => string;\n /**\n * Receives best-effort failures after a change was stored (undo entry,\n * audit). Defaults to `console.warn`; route it to the host's logger.\n */\n onError?: (error: unknown, context: string) => void;\n}\n\n/**\n * A tool refusal the loop classifies by `status`: 422/404/409 are repairable\n * (`invalid_request`, the model sees `publicMessage`), 403 is `not_permitted`.\n */\nexport class OverviewToolError extends Error {\n readonly status: number;\n readonly code: string;\n readonly publicMessage: string;\n readonly issues: readonly unknown[];\n\n constructor(\n status: number,\n code: string,\n message: string,\n issues: readonly unknown[] = [],\n ) {\n super(message);\n this.name = 'OverviewToolError';\n this.status = status;\n this.code = code;\n this.publicMessage = message;\n this.issues = issues;\n }\n}\n\n/**\n * In-process undo store: single entry per key, expiring after `ttlMs`\n * (default 30 minutes), at most `maxEntries` keys (oldest dropped).\n */\nexport function createMemoryOverviewUndoStore(\n options: { ttlMs?: number; maxEntries?: number; now?: () => number } = {},\n): OverviewUndoStore {\n const ttl = options.ttlMs ?? 30 * 60_000;\n const max = options.maxEntries ?? 1000;\n const now = options.now ?? Date.now;\n const entries = new Map<string, OverviewUndoEntry>();\n return {\n get(key) {\n const entry = entries.get(key);\n if (entry && now() - entry.createdAt > ttl) {\n entries.delete(key);\n return undefined;\n }\n return entry;\n },\n set(key, entry) {\n const existing = entries.get(key);\n // Conditional: an older apply that stores its entry late never replaces\n // a newer one.\n if (existing && existing.createdAt > entry.createdAt) return;\n entries.delete(key);\n entries.set(key, entry);\n while (entries.size > max) {\n const oldest = entries.keys().next().value;\n if (oldest === undefined) break;\n entries.delete(oldest);\n }\n },\n delete(key, token) {\n if (entries.get(key)?.token === token) entries.delete(key);\n },\n };\n}\n\nlet processUndoStore: OverviewUndoStore | undefined;\nlet lastUndoStamp = 0;\n\n/**\n * The ordering stamp of an undo entry, taken right after the batch's\n * conditional write succeeded: epoch ms, strictly increasing within a\n * process. The revision guard serializes writes to one stored row, so the\n * order the writes succeeded in is the order of the batches; revisions are\n * opaque to this package (only compared for equality), so they cannot order\n * entries themselves.\n */\nfunction nextUndoStamp(): number {\n lastUndoStamp = Math.max(Date.now(), lastUndoStamp + 1);\n return lastUndoStamp;\n}\n\n/**\n * The process-wide in-memory undo store `createOverviewTools` uses when no\n * `undoStore` is passed. It outlives every tool set, so tools built per turn\n * (`extraTools: (context) => createOverviewTools(...)`) can undo a batch an\n * earlier turn applied. Keys carry tenant, user and page, so sharing it is\n * safe; it is per process, so a multi-replica host passes a shared store.\n */\nexport function defaultOverviewUndoStore(): OverviewUndoStore {\n processUndoStore ??= createMemoryOverviewUndoStore();\n return processUndoStore;\n}\n\nfunction principalFromRun(run: PrincipalRun): {\n userId: string;\n tenantId: string | null;\n} {\n const userId = run.context.userId;\n if (!userId) throw new DataSurfaceDeniedError();\n return { userId, tenantId: run.context.tenantId };\n}\n\n/**\n * Run under the AUTHENTICATED principal's tenant (the runtime-report tools'\n * guard): with tenancy on, a run without a tenant is refused, the principal's\n * tenant is entered when no tenant context is active, and an ambient tenant\n * that disagrees with the principal is refused (unless the caller is\n * deliberately in a system-context or super-admin path). `host.open` and\n * every surface call, including `persist`, run inside it.\n */\nasync function withPrincipalTenant<T>(\n run: PrincipalRun,\n fn: () => Promise<T>,\n): Promise<T> {\n if (!isTenancyEnabled()) return fn();\n const tenantId = run.context.tenantId;\n if (!tenantId) {\n throw new OverviewToolError(\n 403,\n 'tenant_required',\n 'A tenant is required to use overview tools.',\n );\n }\n const active = getCurrentTenant();\n if (active === undefined) return withTenant({ tenantId }, fn);\n if (\n active.tenantId !== tenantId &&\n !isSystemContext() &&\n !isSuperAdminBypass()\n ) {\n throw new DataSurfaceDeniedError();\n }\n return fn();\n}\n\n/** The allow-list gate, the principal check and the tenant guard, in order. */\nfunction guarded(\n slug: string,\n body: (context: PrincipalToolContext) => Promise<unknown>,\n): PrincipalTool['execute'] {\n return async (context) => {\n context.run.assertToolAllowed(slug);\n principalFromRun(context.run);\n return withPrincipalTenant(context.run, () => body(context));\n };\n}\n\nfunction aiTool(\n slug: string,\n name: string,\n description: string,\n parameters: Record<string, unknown>,\n execute: PrincipalTool['execute'],\n): PrincipalTool {\n return {\n slug,\n aiTool: {\n type: 'function',\n function: { name, description, parameters },\n } satisfies AITool,\n execute,\n };\n}\n\n/** A refused conditional write, as a tool error. */\nfunction persistFailure(\n result: Exclude<OverviewToolPersistResult, { ok: true }>,\n conflict: { code: string; message: string },\n): OverviewToolError {\n if (result.reason === 'conflict') {\n return new OverviewToolError(409, conflict.code, conflict.message);\n }\n if (result.reason === 'not_allowed') {\n return new OverviewToolError(\n 403,\n 'not_allowed',\n 'You may not change this overview.',\n );\n }\n const issues = result.issues ?? [];\n return new OverviewToolError(\n 422,\n 'invalid_operations',\n `The overview rejected the change and nothing changed. ${formatOverviewIssues(issues)}`,\n issues,\n );\n}\n\nfunction sameJson(a: unknown, b: unknown): boolean {\n return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);\n}\n\n/** \"#2 add: widget type \"map\" is not registered; ...\" for the model. */\nexport function formatOverviewIssues(issues: readonly unknown[]): string {\n return issues\n .slice(0, 10)\n .map((issue) => {\n const record =\n issue && typeof issue === 'object'\n ? (issue as Record<string, unknown>)\n : {};\n const at =\n typeof record.index === 'number' ? `#${record.index + 1}` : 'batch';\n const op = typeof record.op === 'string' ? ` ${record.op}` : '';\n const message =\n typeof record.message === 'string'\n ? record.message\n : typeof record.code === 'string'\n ? record.code\n : 'invalid';\n return `${at}${op}: ${message}`;\n })\n .join('; ');\n}\n\nconst OPERATION_PARAMETERS: Record<string, unknown> = {\n type: 'object',\n required: ['op'],\n additionalProperties: false,\n properties: {\n op: {\n type: 'string',\n enum: ['add', 'configure', 'move', 'resize', 'remove'],\n },\n type: {\n type: 'string',\n description: 'add: a widget type overviews-describe lists.',\n },\n id: {\n type: 'string',\n description: 'configure/move/resize/remove: an existing widget id.',\n },\n span: {\n type: 'integer',\n minimum: 1,\n maximum: 4,\n description: \"add/resize: columns, within the type's span range.\",\n },\n index: {\n type: 'integer',\n minimum: 0,\n description: 'add/move: zero-based position.',\n },\n options: {\n type: 'object',\n description:\n 'add: the options; configure: keys to change (null resets a key). Only the option fields overviews-describe lists, with their allowed values.',\n additionalProperties: {\n type: ['string', 'number', 'boolean', 'null'],\n },\n },\n },\n};\n\n/**\n * Build the overview assistant tools. Offer them through `extraTools`; each\n * is gated by the same fail-closed `allowedTools` offer and execution gates\n * as every other principal tool.\n *\n * @param options - The host adapter, undo store, audit sink and batch cap.\n * @returns The `describe`, `apply` and `undo` principal tools.\n */\nexport function createOverviewTools(\n options: OverviewToolsOptions,\n): PrincipalTool[] {\n const { host } = options;\n const undoStore = options.undoStore ?? defaultOverviewUndoStore();\n const maxOperations = options.maxOperations ?? OVERVIEW_TOOL_MAX_OPERATIONS;\n const createToken = options.createToken ?? (() => crypto.randomUUID());\n const onError =\n options.onError ??\n ((error: unknown, context: string) => {\n // biome-ignore lint/suspicious/noConsole: server-side default; hosts pass `onError` to route it to their logger\n console.warn(`[smrt-chat] ${context}`, error);\n });\n\n const undoKey = (run: PrincipalRun, pageId: string): string => {\n const { userId, tenantId } = principalFromRun(run);\n return JSON.stringify([tenantId, userId, pageId]);\n };\n\n const pageArg = (value: unknown): string => {\n if (typeof value !== 'string' || !PAGE_ID_PATTERN.test(value)) {\n throw new OverviewToolError(\n 422,\n 'invalid_page',\n 'page must be an overview page id from overviews-describe',\n );\n }\n return value;\n };\n\n const open = async (\n run: PrincipalRun,\n pageId: string,\n ): Promise<OverviewToolSurface> => {\n const surface = await host.open(run, pageId);\n // Unknown, and not offered to this principal, answer the same.\n if (!surface || surface.pageId !== pageId) {\n throw new OverviewToolError(\n 404,\n 'unknown_page',\n `no overview page \"${pageId}\" is available`,\n );\n }\n return surface;\n };\n\n const requireCustomize = (surface: OverviewToolSurface): void => {\n if (!surface.canCustomize) {\n throw new OverviewToolError(\n 403,\n 'not_allowed',\n 'You may not change this overview.',\n );\n }\n };\n\n /**\n * Best effort: by the time it runs the change is stored, and reporting the\n * tool as failed would make the model retry an applied batch.\n */\n const audit = async (\n run: PrincipalRun,\n entry: Omit<OverviewToolAuditEntry, 'userId' | 'tenantId'>,\n ): Promise<void> => {\n try {\n await options.audit?.({ ...entry, ...principalFromRun(run) });\n } catch (error) {\n onError(error, `overview tool audit failed (${entry.action})`);\n }\n };\n\n const describe = aiTool(\n OVERVIEW_DESCRIBE_TOOL_SLUG,\n OVERVIEW_DESCRIBE_FUNCTION_NAME,\n 'Describe an overview page: the widget types it allows (with their option fields, allowed values and span range) and the widgets it shows now, in order, plus whether you may change it. Without a page, lists the overview pages available. Call before changing an overview; never invent types, option keys or models.',\n {\n type: 'object',\n additionalProperties: false,\n properties: {\n page: { type: 'string', description: 'Overview page id.' },\n },\n },\n guarded(OVERVIEW_DESCRIBE_TOOL_SLUG, async ({ run, args }) => {\n if (args.page === undefined) {\n return { pages: host.pages ? await host.pages(run) : [] };\n }\n const surface = await open(run, pageArg(args.page));\n return { page: surface.pageId, overview: surface.describe() };\n }),\n );\n\n const apply = aiTool(\n OVERVIEW_APPLY_TOOL_SLUG,\n OVERVIEW_APPLY_FUNCTION_NAME,\n 'Change an overview page with a batch of structured operations: add {type, span?, options, index?}, configure {id, options}, move {id, index}, resize {id, span}, remove {id}. The batch is all-or-nothing: if any operation is invalid nothing changes and the issues are returned to fix. Returns an undoToken; the person can undo the batch.',\n {\n type: 'object',\n required: ['page', 'operations'],\n additionalProperties: false,\n properties: {\n page: { type: 'string', description: 'Overview page id.' },\n operations: {\n type: 'array',\n minItems: 1,\n maxItems: maxOperations,\n items: OPERATION_PARAMETERS,\n },\n },\n },\n guarded(OVERVIEW_APPLY_TOOL_SLUG, async ({ run, args }) => {\n const pageId = pageArg(args.page);\n const operations = args.operations;\n if (\n !Array.isArray(operations) ||\n operations.length === 0 ||\n operations.length > maxOperations\n ) {\n throw new OverviewToolError(\n 422,\n 'invalid_operations',\n `operations must be an array of 1 to ${maxOperations} operations`,\n );\n }\n const surface = await open(run, pageId);\n requireCustomize(surface);\n const before = surface.current();\n const plan = surface.plan(operations);\n if (!plan.ok) {\n throw new OverviewToolError(\n 422,\n 'invalid_operations',\n `The batch was rejected and nothing changed. ${formatOverviewIssues(plan.issues)}`,\n plan.issues,\n );\n }\n const changed = !(plan.unchanged ?? sameJson(plan.override, before));\n if (!changed) {\n return {\n page: pageId,\n changed: false,\n applied: plan.results,\n undoToken: null,\n };\n }\n const written = await surface.persist(plan.override);\n if (!written.ok) {\n throw persistFailure(written, {\n code: 'conflict',\n message:\n 'The overview changed while the batch was being applied, so nothing changed. Call overviews-describe again and rebuild the batch.',\n });\n }\n // The batch is stored. From here nothing may report the call as failed:\n // the model would retry it and duplicate the change. The undo entry is\n // written after the write (not before it and deleted on failure) because\n // a lost entry only costs the Undo, while an entry for a write that did\n // not happen could later \"undo\" someone else's value.\n let token: string | null = createToken();\n try {\n await undoStore.set(undoKey(run, pageId), {\n token,\n before,\n after: plan.override,\n createdAt: nextUndoStamp(),\n });\n } catch (error) {\n onError(error, 'overview undo entry was not stored');\n token = null;\n }\n await audit(run, {\n action: 'apply',\n pageId,\n operations: operations.length,\n });\n return {\n page: pageId,\n changed: true,\n applied: plan.results,\n undoToken: token,\n ...(token === null\n ? {\n note: 'The change was applied but cannot be undone with overviews-undo. Do not apply it again.',\n }\n : {}),\n overview: surface.describe(),\n };\n }),\n );\n\n const undo = aiTool(\n OVERVIEW_UNDO_TOOL_SLUG,\n OVERVIEW_UNDO_FUNCTION_NAME,\n 'Undo the last batch applied to an overview page (pass the undoToken overviews-apply returned). Single step: only the latest batch can be undone, and not once the page changed since.',\n {\n type: 'object',\n required: ['page', 'undoToken'],\n additionalProperties: false,\n properties: {\n page: { type: 'string', description: 'Overview page id.' },\n undoToken: { type: 'string' },\n },\n },\n guarded(OVERVIEW_UNDO_TOOL_SLUG, async ({ run, args }) => {\n const pageId = pageArg(args.page);\n // Looked up under the CALLING principal only: another principal's\n // token is simply not found.\n const key = undoKey(run, pageId);\n const entry = await undoStore.get(key);\n if (\n !entry ||\n typeof args.undoToken !== 'string' ||\n entry.token !== args.undoToken\n ) {\n throw new OverviewToolError(\n 409,\n 'nothing_to_undo',\n 'There is no assistant change with that undoToken to undo on this page; only the latest batch can be undone.',\n );\n }\n const surface = await open(run, pageId);\n requireCustomize(surface);\n if (!sameJson(surface.current(), entry.after)) {\n await undoStore.delete(key, entry.token);\n throw new OverviewToolError(\n 409,\n 'changed_since',\n 'The overview changed after that batch, so it was not undone.',\n );\n }\n const checked = surface.check(entry.before);\n if (!checked.ok) {\n await undoStore.delete(key, entry.token);\n throw new OverviewToolError(\n 409,\n 'cannot_restore',\n `The earlier arrangement is no longer valid on this page. ${formatOverviewIssues(checked.issues)}`,\n checked.issues,\n );\n }\n const written = await surface.persist(checked.override);\n if (!written.ok) {\n // Keep the entry: only a successful conditional restore consumes it.\n throw persistFailure(written, {\n code: 'changed_since',\n message:\n 'The overview changed after that batch, so it was not undone.',\n });\n }\n try {\n await undoStore.delete(key, entry.token);\n } catch (error) {\n // A leftover entry no longer matches the stored value, so a second\n // undo answers changed_since; the restore itself succeeded.\n onError(error, 'overview undo entry was not removed');\n }\n await audit(run, { action: 'undo', pageId });\n return { page: pageId, undone: true, overview: surface.describe() };\n }),\n );\n\n return [describe, apply, undo];\n}\n","/**\n * Declared recipes for smrt-chat (#3719): small, user-facing units of\n * functionality an app or agent can pick instead of the whole package.\n * The scanner reads these statics into the `recipes` array of `manifest.json`\n * and `smrt-knowledge.json`; nothing here runs at that point.\n *\n * @packageDocumentation\n */\n\nimport { SmrtRecipe } from '@happyvertical/smrt-core';\nimport { AgentSession } from './models/AgentSession.js';\nimport { ChatMessage } from './models/ChatMessage.js';\nimport { ChatParticipant } from './models/ChatParticipant.js';\nimport { ChatReaction } from './models/ChatReaction.js';\nimport { ChatRoom } from './models/ChatRoom.js';\nimport { ChatThread } from './models/ChatThread.js';\n\n/**\n * Assistant: an AI chat dock in the app shell that sees the page you are on.\n *\n * The shell hosts the dock through its `dockToggles`/`ShellDockTool` pair; the\n * toggle button lives in `header.end` by default and the host passes the\n * dock a transport and the shell's data surface registry. Conversations are\n * kept in the agent-session room, thread and message records below, which no\n * screen lists: they are read through the member-checked assistant routes.\n */\nexport class AssistantRecipe extends SmrtRecipe {\n static id = 'chat.assistant';\n static help = './assistant.recipe.md';\n static label = 'Assistant';\n static summary =\n 'Ask an AI assistant about the page you are on, from a dock in the app shell.';\n static synonyms = [\n 'ai assistant',\n 'ai chat',\n 'copilot',\n 'chatbot',\n 'ask ai',\n 'helper',\n ];\n static models = [\n AgentSession,\n ChatRoom,\n ChatParticipant,\n ChatThread,\n ChatMessage,\n ];\n // The dock runs in the browser, with the in-memory transport or a browser\n // model; saved conversations and tool use go through server routes.\n static runtime = 'both' as const;\n static surfaces = [\n {\n kind: 'shell-widget',\n slot: 'header.end',\n export: '@happyvertical/smrt-chat/svelte#AssistantDock',\n label: 'Assistant',\n icon: 'bot',\n },\n ] as const;\n // Names only, never values. A hosted model needs the key for the option the\n // app picks (the other two are unused); the browser options need none.\n static providers = [\n {\n id: 'llm',\n kind: 'llm',\n options: ['openai', 'anthropic', 'gemini', 'webllm', 'bitgpu'],\n required: true,\n secrets: ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'GEMINI_API_KEY'],\n browserOptions: ['webllm', 'bitgpu'],\n },\n ] as const;\n}\n/** Team rooms and their conversations. */\nexport class ChatRoomsRecipe extends SmrtRecipe {\n static id = 'chat.rooms';\n static help = './chat-rooms.recipe.md';\n static label = 'Team Chat';\n static summary =\n 'Organize team conversations in rooms with threads and reactions.';\n static synonyms = ['team chat', 'channels', 'group chat', 'conversations'];\n static models = [\n ChatRoom,\n ChatMessage,\n ChatParticipant,\n ChatThread,\n ChatReaction,\n ];\n static group = {\n id: 'team-collaboration',\n label: 'Team collaboration',\n summary: 'Keep conversations and shared work in one place.',\n };\n static section = {\n id: 'team',\n label: 'Team',\n icon: 'users',\n description: 'People and the conversations that keep them aligned.',\n };\n static nav = [\n {\n label: 'Chat rooms',\n model: ChatRoom,\n icon: 'users',\n description: 'Team conversations organized by topic or audience.',\n noun: 'chat room',\n },\n ];\n static runtime = 'both' as const;\n static surfaces = [\n {\n kind: 'route',\n path: '/chat/rooms',\n export: '@happyvertical/smrt-chat/svelte#ChatLayout',\n label: 'Team chat',\n },\n ] as const;\n static demoSeed = {\n export: '@happyvertical/smrt-chat/playground#chatRoomsFixture',\n } as const;\n static options = {\n ChatRoom: {\n fields: {\n metadata: { visibility: 'hidden' },\n createdByProfileId: { visibility: 'hidden' },\n lastMessageAt: { label: 'Last message', order: 1 },\n },\n },\n ChatMessage: {\n fields: {\n metadata: { visibility: 'hidden' },\n toolCallData: { visibility: 'hidden' },\n attachments: { visibility: 'advanced' },\n },\n },\n ChatParticipant: {\n fields: {\n lastReadMessageId: { visibility: 'hidden' },\n lastSeenAt: { visibility: 'advanced' },\n },\n },\n } as const;\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { VoiceGatewayTurn } from '../models/VoiceGatewayTurn.js';\n\nexport class VoiceGatewayTurnCollection extends SmrtCollection<VoiceGatewayTurn> {\n static readonly _itemClass = VoiceGatewayTurn;\n\n async reserveTurn(input: {\n tenantId: string;\n voiceSessionId: string;\n gatewaySessionId: string;\n gatewayTurnId: string;\n target: string;\n }): Promise<VoiceGatewayTurn> {\n return this.create({\n ...input,\n status: 'processing',\n _insertOnly: true,\n });\n }\n}\n","import type { AIInterface, AIMessage } from '@happyvertical/ai';\nimport type { PrincipalAuditSink } from '@happyvertical/smrt-agents';\nimport {\n type SmrtClassOptions,\n ValidationError,\n} from '@happyvertical/smrt-core';\nimport { VoiceGatewayTurnCollection } from './collections/VoiceGatewayTurnCollection.js';\nimport { VoiceSessionCollection } from './collections/VoiceSessionCollection.js';\nimport type { ChatMessage } from './models/ChatMessage.js';\nimport type { VoiceGatewayTurn } from './models/VoiceGatewayTurn.js';\nimport type { VoiceSession } from './models/VoiceSession.js';\nimport {\n bindPersonaToSession,\n type ConversationPersona,\n type PersonaRecallOptions,\n runPersonaConversationTurn,\n} from './persona-conversation.js';\nimport type { ChatService } from './services/index.js';\nimport { FALLBACK_HISTORY_PLACEHOLDER, isFallbackReply } from './tool-loop.js';\nimport type { ChatMessageRole } from './types.js';\n\nexport const SMRT_CHAT_VOICE_TARGET = 'smrt:chat';\nexport const MAX_VOICE_GATEWAY_TEXT_LENGTH = 12_000;\nconst DEFAULT_VOICE_SESSION_TTL_SECONDS = 10 * 60;\nconst DEFAULT_HISTORY_LIMIT = 24;\n\nexport interface VoiceGatewayTurnMetadata {\n tenantId?: string;\n actorProfileId?: string;\n chatRoomId?: string;\n threadId?: string;\n agentSessionId?: string;\n personaId?: string;\n voiceSessionId?: string;\n source?: string;\n [key: string]: unknown;\n}\n\nexport interface VoiceGatewayTurnPayload {\n session_id: string;\n turn_id: string;\n target: string;\n actor?: string;\n text: string;\n metadata?: VoiceGatewayTurnMetadata;\n}\n\nexport interface VoiceGatewayTurnResponseMetadata {\n tenantId: string;\n chatRoomId: string;\n threadId: string | null;\n agentSessionId: string;\n userMessageId: string;\n assistantMessageId: string | null;\n personaId: string;\n correlationId: string;\n voiceSessionId: string;\n source: string;\n}\n\nexport interface VoiceGatewayTurnResponse {\n session_id: string;\n turn_id: string;\n text: string;\n metadata: VoiceGatewayTurnResponseMetadata;\n}\n\nexport interface CreateVoiceChatSessionOptions {\n chatService: ChatService;\n db: SmrtClassOptions['db'];\n tenantId: string;\n actorProfileId: string;\n actorUserId?: string | null;\n persona: ConversationPersona;\n /** Existing agent session to bind voice to. Omit to create/reuse one. */\n agentSessionId?: string;\n /** Agent profile/session author id when creating a new agent session. */\n agentId?: string;\n /** Optional existing thread within the bound session room. */\n threadId?: string | null;\n /** Stable gateway-level `session_id`. Generated when omitted. */\n gatewaySessionId?: string;\n /** Subject key forwarded to ChatService.createAgentSession(). */\n sessionKey?: string | null;\n /** Voice binding TTL. Ignored when `expiresAt` is supplied. */\n ttlSeconds?: number;\n expiresAt?: Date;\n metadata?: Record<string, unknown>;\n target?: string;\n maxTokens?: number;\n maxMessages?: number;\n instructions?: string;\n}\n\nexport interface VoiceChatSessionCreationResult {\n voiceSession: VoiceSession;\n voiceSessionId: string;\n gatewaySessionId: string;\n expiresAt: Date;\n tenantId: string;\n actorProfileId: string;\n personaId: string;\n agentSessionId: string;\n chatRoomId: string;\n threadId: string | null;\n metadata: VoiceGatewayTurnMetadata;\n}\n\nexport interface HandleVoiceGatewayTurnOptions {\n chatService: ChatService;\n db: SmrtClassOptions['db'];\n ai: AIInterface;\n payload: VoiceGatewayTurnPayload;\n now?: Date;\n /**\n * Earlier user, assistant and system messages replayed with a turn;\n * persisted tool rows do not count (#4458). Default 24.\n */\n historyLimit?: number;\n recall?: PersonaRecallOptions | false;\n model?: string;\n temperature?: number;\n maxTokens?: number;\n maxSteps?: number;\n postgresRls?: boolean;\n audit?: PrincipalAuditSink;\n}\n\nexport interface VoiceGatewayTurnHandlerOptions\n extends Omit<HandleVoiceGatewayTurnOptions, 'payload'> {\n gatewayToken: string | (() => string | Promise<string>);\n}\n\nexport class VoiceGatewayError extends Error {\n readonly status: number;\n readonly code: string;\n\n constructor(message: string, status: number, code: string) {\n super(message);\n this.name = 'VoiceGatewayError';\n this.status = status;\n this.code = code;\n }\n}\n\nexport class VoiceGatewayBadRequestError extends VoiceGatewayError {\n constructor(message: string) {\n super(message, 400, 'voice_gateway_bad_request');\n this.name = 'VoiceGatewayBadRequestError';\n }\n}\n\nexport class VoiceGatewayUnauthorizedError extends VoiceGatewayError {\n constructor(message = 'Voice gateway authorization failed') {\n super(message, 401, 'voice_gateway_unauthorized');\n this.name = 'VoiceGatewayUnauthorizedError';\n }\n}\n\nexport class VoiceSessionRejectedError extends VoiceGatewayError {\n constructor(message: string) {\n super(message, 403, 'voice_session_rejected');\n this.name = 'VoiceSessionRejectedError';\n }\n}\n\nexport class VoiceSessionExpiredError extends VoiceGatewayError {\n constructor(message = 'Voice session is expired') {\n super(message, 410, 'voice_session_expired');\n this.name = 'VoiceSessionExpiredError';\n }\n}\n\nexport class VoiceGatewayReplayError extends VoiceGatewayError {\n constructor(message = 'Voice gateway turn has already been processed') {\n super(message, 409, 'voice_gateway_replay');\n this.name = 'VoiceGatewayReplayError';\n }\n}\n\nexport async function createVoiceChatSession(\n options: CreateVoiceChatSessionOptions,\n): Promise<VoiceChatSessionCreationResult> {\n const target = options.target ?? SMRT_CHAT_VOICE_TARGET;\n const personaId = requireNonEmptyString(\n options.persona.id,\n 'createVoiceChatSession requires a persisted persona id',\n );\n if (\n options.persona.tenantId !== null &&\n options.persona.tenantId !== options.tenantId\n ) {\n throw new VoiceSessionRejectedError(\n 'Persona tenant does not match the voice session tenant',\n );\n }\n\n const persona: ConversationPersona = {\n ...options.persona,\n id: personaId,\n tenantId: options.tenantId,\n };\n\n let agentSession = options.agentSessionId\n ? await options.chatService.getAgentSession({\n agentSessionId: options.agentSessionId,\n tenantId: options.tenantId,\n })\n : null;\n\n if (options.agentSessionId) {\n if (!agentSession) {\n throw new VoiceSessionRejectedError('Agent session not found');\n }\n assertAgentSessionMatchesActor(agentSession, options.actorProfileId);\n assertActiveAgentSession(agentSession);\n } else {\n // `agentId` is the agent's logical identity and `agentProfileId` is the\n // Profile it AUTHORS as — two different things since #2995. The persona's\n // `actsAsProfileId` is the latter (a `crossPackageRef` to `Profile`), so it\n // is passed as the authoring profile rather than collapsed into `agentId`;\n // otherwise a synthetic `bot` profile would be minted for it and persona\n // replies would stop being attributed to the configured acting identity.\n const agentId =\n options.agentId ?? persona.id ?? persona.agentClass ?? undefined;\n if (!agentId) {\n throw new VoiceGatewayBadRequestError(\n 'createVoiceChatSession requires agentId when the persona has no id or agent class',\n );\n }\n const created = await options.chatService.createAgentSession({\n tenantId: options.tenantId,\n agentId,\n agentProfileId: persona.actsAsProfileId ?? null,\n actorProfileId: options.actorProfileId,\n allowedTools: persona.allowedTools,\n systemPrompt: options.instructions ?? persona.instructions,\n maxTokens: options.maxTokens,\n maxMessages: options.maxMessages,\n sessionKey: options.sessionKey,\n });\n agentSession = created.session;\n }\n\n const boundSession = await bindPersonaToSession({\n chatService: options.chatService,\n session: agentSession,\n actorProfileId: options.actorProfileId,\n tenantId: options.tenantId,\n persona,\n instructions: options.instructions,\n db: options.db,\n });\n assertActiveAgentSession(boundSession);\n\n const chatRoomId = requireNonEmptyString(\n boundSession.chatRoomId,\n 'Agent session has no chat room',\n );\n await options.chatService.getRoomForMember(\n chatRoomId,\n options.actorProfileId,\n options.tenantId,\n );\n\n const threadId = options.threadId ?? null;\n if (threadId) {\n const thread = await options.chatService.getThread({\n threadId,\n tenantId: options.tenantId,\n });\n if (!thread || thread.roomId !== chatRoomId) {\n throw new VoiceSessionRejectedError(\n 'threadId does not belong to the bound agent session room',\n );\n }\n }\n\n const voiceSessions = await VoiceSessionCollection.create({ db: options.db });\n const gatewaySessionId = options.gatewaySessionId ?? crypto.randomUUID();\n const expiresAt =\n options.expiresAt ??\n new Date(\n Date.now() +\n (options.ttlSeconds ?? DEFAULT_VOICE_SESSION_TTL_SECONDS) * 1000,\n );\n\n const voiceSession = await voiceSessions.create({\n tenantId: options.tenantId,\n gatewaySessionId,\n actorProfileId: options.actorProfileId,\n actorUserId: options.actorUserId ?? null,\n personaId,\n agentSessionId: boundSession.id as string,\n chatRoomId,\n threadId,\n target,\n status: 'active',\n expiresAt,\n personaSnapshot: JSON.stringify(persona),\n metadata: JSON.stringify(options.metadata ?? {}),\n });\n\n const voiceSessionId = voiceSession.id as string;\n return {\n voiceSession,\n voiceSessionId,\n gatewaySessionId,\n expiresAt,\n tenantId: options.tenantId,\n actorProfileId: options.actorProfileId,\n personaId,\n agentSessionId: boundSession.id as string,\n chatRoomId,\n threadId,\n metadata: {\n tenantId: options.tenantId,\n actorProfileId: options.actorProfileId,\n chatRoomId,\n threadId: threadId ?? undefined,\n agentSessionId: boundSession.id as string,\n personaId,\n voiceSessionId,\n source: 'smrt-chat',\n },\n };\n}\n\nexport async function handleVoiceGatewayTurn(\n options: HandleVoiceGatewayTurnOptions,\n): Promise<VoiceGatewayTurnResponse> {\n const payload = normalizeGatewayPayload(options.payload);\n const metadata = payload.metadata ?? {};\n const voiceSessionId = requireNonEmptyString(\n metadata.voiceSessionId,\n 'Voice gateway payload metadata.voiceSessionId is required',\n );\n\n const voiceSessions = await VoiceSessionCollection.create({ db: options.db });\n const voiceSession = await voiceSessions.get({\n id: voiceSessionId,\n target: payload.target,\n });\n if (!voiceSession) {\n throw new VoiceSessionRejectedError('Voice session not found');\n }\n if (voiceSession.isExpired(options.now)) {\n if (voiceSession.status === 'active') {\n await voiceSession.expire();\n }\n throw new VoiceSessionExpiredError();\n }\n if (!voiceSession.isActive(options.now)) {\n throw new VoiceSessionRejectedError('Voice session is not active');\n }\n if (voiceSession.hasProcessedTurn(payload.turn_id)) {\n throw new VoiceGatewayReplayError();\n }\n\n validateGatewayPayloadAgainstBinding(payload, voiceSession);\n const persona = requireVoiceSessionPersona(voiceSession);\n\n const agentSession = await options.chatService.getAgentSession({\n agentSessionId: voiceSession.agentSessionId,\n tenantId: voiceSession.tenantId,\n });\n if (!agentSession) {\n throw new VoiceSessionRejectedError('Bound agent session not found');\n }\n assertAgentSessionMatchesActor(agentSession, voiceSession.actorProfileId);\n assertActiveAgentSession(agentSession);\n if (agentSession.chatRoomId !== voiceSession.chatRoomId) {\n throw new VoiceSessionRejectedError(\n 'Bound agent session no longer belongs to the voice session room',\n );\n }\n\n if (voiceSession.threadId) {\n const thread = await options.chatService.getThread({\n threadId: voiceSession.threadId,\n tenantId: voiceSession.tenantId,\n });\n if (!thread || thread.roomId !== voiceSession.chatRoomId) {\n throw new VoiceSessionRejectedError(\n 'Bound thread no longer belongs to the voice session room',\n );\n }\n }\n\n const gatewayTurn = await reserveVoiceGatewayTurn(\n options.db,\n voiceSession,\n payload,\n );\n\n try {\n const history = await loadConversationHistory({\n chatService: options.chatService,\n tenantId: voiceSession.tenantId,\n actorProfileId: voiceSession.actorProfileId,\n roomId: voiceSession.chatRoomId,\n threadId: voiceSession.threadId,\n limit: options.historyLimit ?? DEFAULT_HISTORY_LIMIT,\n });\n const correlationId = crypto.randomUUID();\n const commonMetadata = {\n source: 'voice-gateway',\n target: payload.target,\n voiceSessionId,\n gatewaySessionId: payload.session_id,\n gatewayTurnId: payload.turn_id,\n correlationId,\n };\n\n const userMessage = await options.chatService.sendAgentUserMessage({\n tenantId: voiceSession.tenantId,\n agentSessionId: voiceSession.agentSessionId,\n actorProfileId: voiceSession.actorProfileId,\n content: payload.text,\n });\n await mergeAndSaveMetadata(userMessage, {\n ...commonMetadata,\n voiceRole: 'transcript',\n });\n\n const turn = await runPersonaConversationTurn({\n ai: options.ai,\n db: options.db,\n persona,\n tenantId: voiceSession.tenantId,\n userMessage: payload.text,\n history,\n chatService: options.chatService,\n session: agentSession,\n threadId: voiceSession.threadId,\n recall: options.recall,\n model: options.model,\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n maxSteps: options.maxSteps,\n postgresRls: options.postgresRls,\n audit: options.audit,\n onBehalfOfUserId: voiceSession.actorUserId ?? undefined,\n correlationId,\n });\n\n for (const toolMessage of turn.authoredMessages?.toolMessages ?? []) {\n await mergeAndSaveMetadata(toolMessage, {\n ...commonMetadata,\n voiceRole: 'tool',\n });\n }\n const assistantMessage = turn.authoredMessages?.assistantMessage ?? null;\n if (assistantMessage) {\n await mergeAndSaveMetadata(assistantMessage, {\n ...commonMetadata,\n voiceRole: 'assistant',\n });\n }\n\n voiceSession.recordGatewayTurn(payload.turn_id);\n await voiceSession.save();\n await gatewayTurn.complete();\n\n return {\n session_id: payload.session_id,\n turn_id: payload.turn_id,\n text: turn.reply,\n metadata: {\n tenantId: voiceSession.tenantId,\n chatRoomId: voiceSession.chatRoomId,\n threadId: voiceSession.threadId,\n agentSessionId: voiceSession.agentSessionId,\n userMessageId: userMessage.id as string,\n assistantMessageId:\n (assistantMessage?.id as string | undefined) ?? null,\n personaId: voiceSession.personaId,\n correlationId: turn.correlationId,\n voiceSessionId,\n source: 'voice-gateway',\n },\n };\n } catch (error) {\n await markVoiceGatewayTurnFailed(gatewayTurn);\n throw error;\n }\n}\n\nexport function createVoiceGatewayTurnHandler(\n options: VoiceGatewayTurnHandlerOptions,\n): (request: Request) => Promise<Response> {\n return async (request: Request): Promise<Response> => {\n try {\n if (request.method !== 'POST') {\n return jsonResponse({ error: 'Method not allowed' }, 405);\n }\n await assertVoiceGatewayBearer(\n request.headers,\n await resolveGatewayToken(options.gatewayToken),\n );\n const payload = normalizeGatewayPayload(await request.json());\n const response = await handleVoiceGatewayTurn({ ...options, payload });\n return jsonResponse(response, 200);\n } catch (error) {\n const status = error instanceof VoiceGatewayError ? error.status : 500;\n const message =\n error instanceof Error ? error.message : 'Voice gateway turn failed';\n const code =\n error instanceof VoiceGatewayError ? error.code : 'voice_gateway_error';\n return jsonResponse({ error: message, code }, status);\n }\n };\n}\n\nexport async function assertVoiceGatewayBearer(\n headers: Headers,\n expectedToken: string,\n): Promise<void> {\n if (!expectedToken) {\n throw new VoiceGatewayUnauthorizedError(\n 'Voice gateway token is not configured',\n );\n }\n const authorization = headers.get('authorization') ?? '';\n const match = authorization.match(/^Bearer\\s+(.+)$/i);\n const actual = match?.[1] ?? '';\n if (!constantTimeEquals(actual, expectedToken)) {\n throw new VoiceGatewayUnauthorizedError();\n }\n}\n\nasync function resolveGatewayToken(\n token: string | (() => string | Promise<string>),\n): Promise<string> {\n return typeof token === 'function' ? token() : token;\n}\n\nfunction normalizeGatewayPayload(input: unknown): VoiceGatewayTurnPayload {\n if (!isRecord(input)) {\n throw new VoiceGatewayBadRequestError('Voice gateway payload must be JSON');\n }\n const sessionId = requireNonEmptyString(\n input.session_id,\n 'Voice gateway payload session_id is required',\n );\n const turnId = requireNonEmptyString(\n input.turn_id,\n 'Voice gateway payload turn_id is required',\n );\n const target = requireNonEmptyString(\n input.target,\n 'Voice gateway payload target is required',\n );\n if (target !== SMRT_CHAT_VOICE_TARGET) {\n throw new VoiceGatewayBadRequestError(\n `Unsupported voice gateway target '${target}'`,\n );\n }\n const text = requireNonEmptyString(\n input.text,\n 'Voice gateway payload text is required',\n );\n if (text.length > MAX_VOICE_GATEWAY_TEXT_LENGTH) {\n throw new VoiceGatewayBadRequestError(\n `Voice gateway payload text must be ${MAX_VOICE_GATEWAY_TEXT_LENGTH} characters or fewer`,\n );\n }\n const metadata = input.metadata;\n if (metadata !== undefined && !isRecord(metadata)) {\n throw new VoiceGatewayBadRequestError(\n 'Voice gateway payload metadata must be an object',\n );\n }\n\n return {\n session_id: sessionId,\n turn_id: turnId,\n target,\n actor:\n typeof input.actor === 'string' && input.actor.length > 0\n ? input.actor\n : undefined,\n text,\n metadata: metadata as VoiceGatewayTurnMetadata | undefined,\n };\n}\n\nfunction validateGatewayPayloadAgainstBinding(\n payload: VoiceGatewayTurnPayload,\n voiceSession: VoiceSession,\n): void {\n if (payload.session_id !== voiceSession.gatewaySessionId) {\n throw new VoiceSessionRejectedError(\n 'Gateway session_id does not match the voice session binding',\n );\n }\n const metadata = payload.metadata ?? {};\n assertMetadataMatches(\n metadata,\n 'tenantId',\n voiceSession.tenantId,\n 'tenantId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'actorProfileId',\n voiceSession.actorProfileId,\n 'actorProfileId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'chatRoomId',\n voiceSession.chatRoomId,\n 'chatRoomId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'threadId',\n voiceSession.threadId,\n 'threadId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'agentSessionId',\n voiceSession.agentSessionId,\n 'agentSessionId does not match the voice session binding',\n );\n assertMetadataMatches(\n metadata,\n 'personaId',\n voiceSession.personaId,\n 'personaId does not match the voice session binding',\n );\n}\n\nfunction requireVoiceSessionPersona(\n voiceSession: VoiceSession,\n): ConversationPersona {\n const persona = voiceSession.getPersonaSnapshot();\n if (!hasNonEmptyString(persona.id)) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot has no persona id',\n );\n }\n if (persona.id !== voiceSession.personaId) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot does not match the voice session persona',\n );\n }\n if (persona.tenantId !== voiceSession.tenantId) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot tenant does not match the voice session tenant',\n );\n }\n if (!hasNonEmptyString(persona.runAsUserId)) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot has no runnable user',\n );\n }\n if (!Array.isArray(persona.allowedTools)) {\n throw new VoiceSessionRejectedError(\n 'Voice session persona snapshot has an invalid tool allow-list',\n );\n }\n return persona;\n}\n\nasync function reserveVoiceGatewayTurn(\n db: SmrtClassOptions['db'],\n voiceSession: VoiceSession,\n payload: VoiceGatewayTurnPayload,\n): Promise<VoiceGatewayTurn> {\n const gatewayTurns = await VoiceGatewayTurnCollection.create({ db });\n try {\n return await gatewayTurns.reserveTurn({\n tenantId: voiceSession.tenantId,\n voiceSessionId: voiceSession.id as string,\n gatewaySessionId: payload.session_id,\n gatewayTurnId: payload.turn_id,\n target: payload.target,\n });\n } catch (error) {\n if (isUniqueConstraintError(error)) {\n throw new VoiceGatewayReplayError();\n }\n throw error;\n }\n}\n\nasync function markVoiceGatewayTurnFailed(\n gatewayTurn: VoiceGatewayTurn,\n): Promise<void> {\n try {\n await gatewayTurn.fail();\n } catch {\n // Preserve the original turn failure for the gateway response.\n }\n}\n\nfunction isUniqueConstraintError(error: unknown): boolean {\n return (\n error instanceof ValidationError &&\n error.code === 'VALIDATION_UNIQUE_CONSTRAINT'\n );\n}\n\nfunction assertMetadataMatches(\n metadata: VoiceGatewayTurnMetadata,\n key: keyof VoiceGatewayTurnMetadata,\n expected: string | null,\n message: string,\n): void {\n const actual = metadata[key];\n if (actual === undefined || actual === null || actual === '') {\n return;\n }\n if (typeof actual !== 'string' || actual !== expected) {\n throw new VoiceSessionRejectedError(message);\n }\n}\n\nfunction assertAgentSessionMatchesActor(\n session: { participantProfileId: string },\n actorProfileId: string,\n): void {\n if (session.participantProfileId !== actorProfileId) {\n throw new VoiceSessionRejectedError(\n 'Agent session participant does not match the voice session actor',\n );\n }\n}\n\nfunction assertActiveAgentSession(session: {\n isActive: () => boolean;\n chatRoomId: string | null;\n}): void {\n if (!session.isActive()) {\n throw new VoiceSessionRejectedError('Agent session is not active');\n }\n if (!session.chatRoomId) {\n throw new VoiceSessionRejectedError('Agent session has no chat room');\n }\n}\n\n/** The roles voice replays as history (#4458); every other role is skipped. */\nconst VOICE_HISTORY_ROLES: readonly ChatMessageRole[] = [\n 'user',\n 'assistant',\n 'system',\n];\n\nasync function loadConversationHistory(input: {\n chatService: ChatService;\n tenantId: string;\n actorProfileId: string;\n roomId: string;\n threadId: string | null;\n limit: number;\n}): Promise<AIMessage[]> {\n // `limit` counts the replayed rows, not raw rows (#4458): the role filter\n // runs in SQL, so persisted tool rows never take a slot, and the read\n // stays bounded by `limit` whatever the thread holds.\n const messages = input.threadId\n ? await input.chatService.getThreadMessages({\n threadId: input.threadId,\n actorProfileId: input.actorProfileId,\n tenantId: input.tenantId,\n limit: input.limit,\n roles: VOICE_HISTORY_ROLES,\n })\n : (\n await input.chatService.getRoomMessages({\n roomId: input.roomId,\n actorProfileId: input.actorProfileId,\n tenantId: input.tenantId,\n limit: input.limit,\n roles: VOICE_HISTORY_ROLES,\n })\n ).reverse();\n\n return messages\n .map(chatMessageToAIMessage)\n .filter((message): message is AIMessage => message !== null);\n}\n\nfunction chatMessageToAIMessage(message: ChatMessage): AIMessage | null {\n if (\n message.role !== 'user' &&\n message.role !== 'assistant' &&\n message.role !== 'system'\n ) {\n return null;\n }\n // A fallback reply is not a model answer: never replay it as one (#4379);\n // a fixed marker keeps the roles alternating.\n if (message.role === 'assistant' && isFallbackReply(message)) {\n return { role: 'assistant', content: FALLBACK_HISTORY_PLACEHOLDER };\n }\n return { role: message.role, content: message.content } as AIMessage;\n}\n\nasync function mergeAndSaveMetadata(\n message: ChatMessage,\n metadata: Record<string, unknown>,\n): Promise<void> {\n message.setMetadata({ ...message.getMetadata(), ...metadata });\n await message.save();\n}\n\nfunction requireNonEmptyString(value: unknown, message: string): string {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new VoiceGatewayBadRequestError(message);\n }\n return value;\n}\n\nfunction hasNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction constantTimeEquals(actual: string, expected: string): boolean {\n let diff = actual.length ^ expected.length;\n const length = Math.max(actual.length, expected.length);\n for (let index = 0; index < length; index++) {\n const actualCode = index < actual.length ? actual.charCodeAt(index) : 0;\n const expectedCode =\n index < expected.length ? expected.charCodeAt(index) : 0;\n diff |= actualCode ^ expectedCode;\n }\n return diff === 0;\n}\n\nfunction jsonResponse(body: unknown, status: number): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as sendAgentReply } from "../chunks/ChatService-
|
|
1
|
+
import { a as sendAgentReply } from "../chunks/ChatService-BGBAqmZq.js";
|
|
2
2
|
export { sendAgentReply };
|
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-chat",
|
|
5
|
-
"packageVersion": "0.57.
|
|
5
|
+
"packageVersion": "0.57.37",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-chat:AgentSessionCollection": {
|
|
8
8
|
"name": "agentsessioncollection",
|
|
@@ -345,6 +345,7 @@
|
|
|
345
345
|
"name": "options",
|
|
346
346
|
"type": "object",
|
|
347
347
|
"optional": true,
|
|
348
|
+
"typeUnresolved": true,
|
|
348
349
|
"memberTypes": [
|
|
349
350
|
"number",
|
|
350
351
|
"string"
|
|
@@ -157,6 +157,11 @@ export interface PersonaConversationTurnOptions {
|
|
|
157
157
|
extraTools?: PrincipalTool[];
|
|
158
158
|
/** Max tool-executing rounds. */
|
|
159
159
|
maxSteps?: number;
|
|
160
|
+
/**
|
|
161
|
+
* Deduped repeats of one read-only call that end the tool phase (#4391;
|
|
162
|
+
* forwarded). Default `DEFAULT_MAX_REPEATED_CALLS` (2); `0` turns it off.
|
|
163
|
+
*/
|
|
164
|
+
maxRepeatedCalls?: number;
|
|
160
165
|
/** Model id. */
|
|
161
166
|
model?: string;
|
|
162
167
|
/** Sampling temperature. */
|