@mgamadeus/ddd-agents 0.1.0-alpha.23 → 0.1.0-alpha.24
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/dist/chat.js +71 -827
- package/dist/chat.js.map +1 -1
- package/dist/chunks/ConversationMessageInputComposerWidgets-BxyaFBA6.js +631 -0
- package/dist/chunks/ConversationMessageInputComposerWidgets-BxyaFBA6.js.map +1 -0
- package/dist/chunks/{WebSearchResultsConversationWidget-DRBQvycE.js → WebSearchResultsConversationWidget-as3p5bHw.js} +92 -85
- package/dist/chunks/WebSearchResultsConversationWidget-as3p5bHw.js.map +1 -0
- package/dist/chunks/{decide-canvas-open-DBWoWtAC.js → canvas-behaviours-AvzKSYyv.js} +3 -19
- package/dist/chunks/canvas-behaviours-AvzKSYyv.js.map +1 -0
- package/dist/chunks/canvas-reducers-Bfp_i7lc.js +88 -0
- package/dist/chunks/canvas-reducers-Bfp_i7lc.js.map +1 -0
- package/dist/chunks/{canvasBodies-DLjDkviP.js → canvasBodies-Cvu3l38p.js} +6 -3
- package/dist/chunks/{canvasBodies-DLjDkviP.js.map → canvasBodies-Cvu3l38p.js.map} +1 -1
- package/dist/chunks/canvasStorage-EvQsGdto.js +65 -0
- package/dist/chunks/canvasStorage-EvQsGdto.js.map +1 -0
- package/dist/chunks/contract-BxmMxYPF.js +96 -0
- package/dist/chunks/contract-BxmMxYPF.js.map +1 -0
- package/dist/chunks/conversation-BEF0aesf.js +31 -0
- package/dist/chunks/conversation-BEF0aesf.js.map +1 -0
- package/dist/chunks/{ConversationMarkdown-CJhAnuVa.js → widget-click-signal-sources-DulRkNvZ.js} +22 -77
- package/dist/chunks/widget-click-signal-sources-DulRkNvZ.js.map +1 -0
- package/dist/chunks/{widget-name-resolver-C8SfpiR5.js → widget-name-resolver-BGN3HpoG.js} +61 -30
- package/dist/chunks/widget-name-resolver-BGN3HpoG.js.map +1 -0
- package/dist/chunks/{withPollingChangeSubscription-BLDAWF-o.js → withPollingChangeSubscription-B16htQGd.js} +2 -2
- package/dist/chunks/{withPollingChangeSubscription-BLDAWF-o.js.map → withPollingChangeSubscription-B16htQGd.js.map} +1 -1
- package/dist/client-testing.js +28 -6
- package/dist/client-testing.js.map +1 -1
- package/dist/client.js +1 -1
- package/dist/contract.js +8 -27
- package/dist/contract.js.map +1 -1
- package/dist/image-render.js +8 -8
- package/dist/index.js +6 -5
- package/dist/kit.js +2 -2
- package/dist/preview.js +44 -16
- package/dist/preview.js.map +1 -1
- package/dist/renderer.js +62 -57
- package/dist/renderer.js.map +1 -1
- package/dist/styles.css +55 -22
- package/dist/types/client/testing/widget-hydration-stand-ins.d.ts +12 -3
- package/dist/types/entries/chat.d.ts +2 -0
- package/dist/types/entries/client-testing.d.ts +2 -0
- package/dist/types/entries/contract.d.ts +1 -0
- package/dist/types/entries/index.d.ts +1 -1
- package/dist/types/entries/preview.d.ts +1 -0
- package/dist/types/entries/renderer.d.ts +3 -1
- package/dist/types/kit/components/data/chart-model.d.ts +2 -2
- package/dist/types/kit/components/data/use-chart-palette.d.ts +2 -2
- package/dist/types/kit/components/map/use-google-maps-auth-failure.d.ts +18 -0
- package/dist/types/kit/entity-detail/entity-detail-bodies.d.ts +2 -0
- package/dist/types/renderer/canvas/entity-detail-instance.d.ts +5 -3
- package/dist/types/renderer/widget-hydration/use-widget-hydration.d.ts +5 -0
- package/dist/types/widgets/EntityMentionConversationWidget/stories/EntityMentionAskFlowStory.d.ts +8 -0
- package/dist/widgets.js +33 -33
- package/package.json +1 -1
- package/public-api.json +19 -1
- package/dist/chunks/ConversationMarkdown-CJhAnuVa.js.map +0 -1
- package/dist/chunks/WebSearchResultsConversationWidget-DRBQvycE.js.map +0 -1
- package/dist/chunks/decide-canvas-open-DBWoWtAC.js.map +0 -1
- package/dist/chunks/widget-name-resolver-C8SfpiR5.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { readConversationRunStatusOfRunState, CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION, isAgentsClientError } from "
|
|
2
|
+
import { r as readConversationRunStatusOfRunState, C as CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION, i as isAgentsClientError } from "./conversation-BEF0aesf.js";
|
|
3
3
|
const POLLING_RUNNING_INTERVAL_MS = 2e3;
|
|
4
4
|
const POLLING_IDLE_INTERVAL_MS = 2e4;
|
|
5
5
|
const POLLING_MAX_BACKOFF_MS = 6e4;
|
|
@@ -248,4 +248,4 @@ export {
|
|
|
248
248
|
readPolledRunStatus as r,
|
|
249
249
|
withPollingChangeSubscription as w
|
|
250
250
|
};
|
|
251
|
-
//# sourceMappingURL=withPollingChangeSubscription-
|
|
251
|
+
//# sourceMappingURL=withPollingChangeSubscription-B16htQGd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withPollingChangeSubscription-BLDAWF-o.js","sources":["../../src/client/polling/createPollingConversationChangeSubscription.ts","../../src/client/polling/withPollingChangeSubscription.ts"],"sourcesContent":["import {\n type AgentsConversation,\n type ConversationChangeSignal,\n type ConversationRunStatus,\n type ConversationSubscriptionEndReason,\n CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,\n isAgentsClientError,\n readConversationRunStatusOfRunState,\n} from \"../../contract/conversation\";\n\n/** Read interval while a turn runs (or right after a local send). */\nexport const POLLING_RUNNING_INTERVAL_MS = 2_000;\n/** Read interval while the conversation is idle or failed. */\nexport const POLLING_IDLE_INTERVAL_MS = 20_000;\n/** Ceiling of the exponential backoff after failed reads. */\nexport const POLLING_MAX_BACKOFF_MS = 60_000;\n/**\n * How long after a local send the poller keeps the running interval although the conversation still reads idle: the\n * server may store the owner's message before it marks the turn as running.\n */\nexport const POLLING_LOCAL_ACTIVITY_WINDOW_MS = 10_000;\n\n/** Reads a conversation's metadata — the port's `getConversationMetadata`. */\nexport type ConversationMetadataReader = (conversationId: number) => Promise<AgentsConversation>;\n\n/** The timer functions the poller uses; a spec hands in fake ones. */\nexport interface PollingTimers {\n setTimeout: (callback: () => void, delayMs: number) => unknown;\n clearTimeout: (timerHandle: unknown) => void;\n now: () => number;\n}\n\n/** Whether the page is hidden, and a way to hear when that changes; a spec hands in its own. */\nexport interface PollingDocumentVisibility {\n isHidden: () => boolean;\n /** Calls `listener` on every visibility change; returns the function that stops it. */\n listen: (listener: () => void) => () => void;\n}\n\nexport interface PollingConversationChangeSubscriptionOptions {\n /** Default `POLLING_RUNNING_INTERVAL_MS` (2 s). */\n runningIntervalMs?: number;\n /** Default `POLLING_IDLE_INTERVAL_MS` (20 s). */\n idleIntervalMs?: number;\n /** Default `POLLING_MAX_BACKOFF_MS` (60 s). */\n maxBackoffMs?: number;\n /** Default `POLLING_LOCAL_ACTIVITY_WINDOW_MS` (10 s). */\n localActivityWindowMs?: number;\n /** Default: the global `setTimeout` / `clearTimeout` and `Date.now`. */\n timers?: PollingTimers;\n /** Default: the browser `document`; `null` = the page counts as always visible (no `document`, a worker). */\n documentVisibility?: PollingDocumentVisibility | null;\n}\n\n/**\n * The polling default of the live updates: the port's `subscribeToConversationChanges` plus\n * `notifyLocalActivity`, which `withPollingChangeSubscription` calls after every send the server accepted.\n */\nexport interface PollingConversationChangeSubscription {\n subscribeToConversationChanges: (\n conversationId: number,\n onSignal: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ) => () => void;\n /** Reads the conversation's metadata now and keeps the running interval for the local-activity window. */\n notifyLocalActivity: (conversationId: number) => void;\n}\n\ninterface ResolvedPollingOptions {\n runningIntervalMs: number;\n idleIntervalMs: number;\n maxBackoffMs: number;\n localActivityWindowMs: number;\n timers: PollingTimers;\n documentVisibility: PollingDocumentVisibility | null;\n}\n\n/** The global timer functions, looked up on each call (so a spec's fake timers installed later still apply). */\nclass GlobalPollingTimers implements PollingTimers {\n setTimeout = (callback: () => void, delayMs: number): unknown => globalThis.setTimeout(callback, delayMs);\n\n clearTimeout = (timerHandle: unknown): void => globalThis.clearTimeout(timerHandle as ReturnType<typeof setTimeout>);\n\n now = (): number => Date.now();\n}\n\n/** The browser document's `visibilityState` and `visibilitychange` event. */\nclass BrowserDocumentVisibility implements PollingDocumentVisibility {\n constructor(protected readonly browserDocument: Document) {}\n\n isHidden = (): boolean => this.browserDocument.visibilityState === \"hidden\";\n\n listen = (listener: () => void): (() => void) => {\n this.browserDocument.addEventListener(\"visibilitychange\", listener);\n return this.browserDocument.removeEventListener.bind(this.browserDocument, \"visibilitychange\", listener);\n };\n}\n\n/**\n * The three-value run status a polled read reports: the stored run state, and \"running\" for an idle conversation the\n * server owes a turn (`agentTurnOwedAt`), as the chat's own derivation does.\n */\nexport const readPolledRunStatus = (conversation: AgentsConversation): ConversationRunStatus | null => {\n const runStatus = readConversationRunStatusOfRunState(conversation.runState);\n return runStatus === \"idle\" && conversation.agentTurnOwedAt ? \"running\" : runStatus;\n};\n\n/** The end of a subscription a failed read means: a vanished conversation or lost access; null for a passing failure. */\nconst readSubscriptionEndReasonOfReadFailure = (failure: unknown): ConversationSubscriptionEndReason | null => {\n if (!isAgentsClientError(failure)) return null;\n if (failure.kind === \"notFound\") return \"gone\";\n if (failure.kind === \"forbidden\") return \"forbidden\";\n return null;\n};\n\n/**\n * One subscription: reads the conversation's metadata on a timer and turns each read into the change signal.\n *\n * Numbering (the signal leaves it open; the metadata carries no server change counter): the poller keeps its own counter. It\n * starts at 0 with the first read and advances by one whenever a read differs from the previous one in the run status,\n * `lastMessageId` or `updated` — so every polled signal is an ordinary change signal (no `isStatusCheck`) and the\n * package rules apply unchanged: an advance reads the changed tail (throttled), a terminal status reads at once,\n * re-reads the metadata and converges on `lastMessageId`. A status check would carry only the run status as news and\n * would miss a new message under an unchanged status. Without `lastMessageId` in the metadata every poll of a running\n * turn counts as a change (the changed tail is read every poll). `summaryUpdatedAt` is a second local counter that\n * advances when `title` or `summary` changed. A read with no change emits nothing.\n */\nclass ConversationMetadataPoller {\n protected isStopped = false;\n protected isReading = false;\n protected readAgainAfterCurrentRead = false;\n protected timerHandle: unknown = null;\n protected consecutiveFailedReads = 0;\n protected changeCounter = 0;\n protected summaryRevision = 0;\n protected previousChangeFingerprint: string | null = null;\n protected previousSummaryFingerprint: string | null = null;\n protected runStatus: ConversationRunStatus | null = null;\n protected hasConversationEnded = false;\n protected runningIntervalUntil = 0;\n protected stopListeningToVisibility: (() => void) | null = null;\n\n constructor(\n readonly conversationId: number,\n protected readonly readMetadata: ConversationMetadataReader,\n protected readonly onSignal: (signal: ConversationChangeSignal) => void,\n protected readonly onEnded: (reason: ConversationSubscriptionEndReason) => void,\n protected readonly options: ResolvedPollingOptions,\n protected readonly activePollers: Set<ConversationMetadataPoller>,\n ) {}\n\n start = (): void => {\n this.activePollers.add(this);\n this.stopListeningToVisibility = this.options.documentVisibility?.listen(this.receiveVisibilityChange) ?? null;\n this.readNow();\n };\n\n /** The unsubscribe: no read, no timer and no signal after it; a read in flight is dropped. */\n stop = (): void => {\n this.isStopped = true;\n this.activePollers.delete(this);\n this.clearTimer();\n this.stopListeningToVisibility?.();\n this.stopListeningToVisibility = null;\n };\n\n receiveLocalActivity = (): void => {\n this.runningIntervalUntil = this.options.timers.now() + this.options.localActivityWindowMs;\n this.readNow();\n };\n\n /** Reads now; a read already in flight is followed by one more read instead (two reads never overlap). */\n protected readNow = (): void => {\n if (this.isStopped) return;\n if (this.isReading) {\n this.readAgainAfterCurrentRead = true;\n return;\n }\n this.clearTimer();\n this.isReading = true;\n void this.readMetadataOnce();\n };\n\n protected async readMetadataOnce(): Promise<void> {\n let conversation: AgentsConversation;\n try {\n conversation = await this.readMetadata(this.conversationId);\n } catch (failure) {\n this.isReading = false;\n this.receiveReadFailure(failure);\n return;\n }\n this.isReading = false;\n if (this.isStopped) return;\n this.consecutiveFailedReads = 0;\n this.receiveConversation(conversation);\n this.continueAfterRead(this.readIntervalMs());\n }\n\n protected receiveConversation(conversation: AgentsConversation): void {\n const runStatus = readPolledRunStatus(conversation);\n const reportsLastMessageId = conversation.lastMessageId !== undefined;\n const changeFingerprint = JSON.stringify([runStatus, conversation.lastMessageId ?? null, conversation.updated]);\n const summaryFingerprint = JSON.stringify([conversation.title, conversation.summary]);\n const isFirstRead = this.previousChangeFingerprint === null;\n const hasChanged =\n changeFingerprint !== this.previousChangeFingerprint || (!reportsLastMessageId && runStatus === \"running\");\n const hasSummaryChanged = !isFirstRead && summaryFingerprint !== this.previousSummaryFingerprint;\n if (!isFirstRead && hasChanged) this.changeCounter += 1;\n if (hasSummaryChanged) this.summaryRevision += 1;\n this.previousChangeFingerprint = changeFingerprint;\n this.previousSummaryFingerprint = summaryFingerprint;\n this.runStatus = runStatus;\n this.hasConversationEnded =\n conversation.runState !== null && CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION.includes(conversation.runState);\n if (!isFirstRead && !hasChanged && !hasSummaryChanged) return;\n this.onSignal({\n changeCounter: this.changeCounter,\n lastMessageId: conversation.lastMessageId ?? null,\n runStatus,\n summaryUpdatedAt: this.summaryRevision,\n });\n }\n\n protected receiveReadFailure(failure: unknown): void {\n if (this.isStopped) return;\n const endReason = readSubscriptionEndReasonOfReadFailure(failure);\n if (endReason !== null) {\n this.stop();\n this.onEnded(endReason);\n return;\n }\n this.consecutiveFailedReads += 1;\n const backoffMs = this.readIntervalMs() * 2 ** this.consecutiveFailedReads;\n this.continueAfterRead(Math.min(backoffMs, this.options.maxBackoffMs));\n }\n\n protected continueAfterRead(nextReadDelayMs: number): void {\n if (this.readAgainAfterCurrentRead) {\n this.readAgainAfterCurrentRead = false;\n this.readNow();\n return;\n }\n // A completed or cancelled conversation changes no more: only a local send or the page's return reads it again.\n if (this.hasConversationEnded) return;\n this.scheduleRead(nextReadDelayMs);\n }\n\n protected readIntervalMs(): number {\n const isShortIntervalDue = this.runStatus === \"running\" || this.options.timers.now() < this.runningIntervalUntil;\n return isShortIntervalDue ? this.options.runningIntervalMs : this.options.idleIntervalMs;\n }\n\n /** Arms the next read — not while the page is hidden; its return reads at once. */\n protected scheduleRead(delayMs: number): void {\n this.clearTimer();\n if (this.isStopped || this.options.documentVisibility?.isHidden()) return;\n this.timerHandle = this.options.timers.setTimeout(this.readOnTimer, delayMs);\n }\n\n protected readOnTimer = (): void => {\n this.timerHandle = null;\n this.readNow();\n };\n\n protected receiveVisibilityChange = (): void => {\n if (this.isStopped) return;\n if (this.options.documentVisibility?.isHidden()) {\n this.clearTimer();\n return;\n }\n this.readNow();\n };\n\n protected clearTimer(): void {\n if (this.timerHandle === null) return;\n this.options.timers.clearTimeout(this.timerHandle);\n this.timerHandle = null;\n }\n}\n\n/** Every subscription of one polling default; `notifyLocalActivity` reaches the subscriptions of a conversation. */\nclass PollingConversationChangeSubscriptions implements PollingConversationChangeSubscription {\n protected readonly activePollers = new Set<ConversationMetadataPoller>();\n\n constructor(\n protected readonly readMetadata: ConversationMetadataReader,\n protected readonly options: ResolvedPollingOptions,\n ) {}\n\n subscribeToConversationChanges = (\n conversationId: number,\n onSignal: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => {\n const poller = new ConversationMetadataPoller(\n conversationId,\n this.readMetadata,\n onSignal,\n onEnded,\n this.options,\n this.activePollers,\n );\n poller.start();\n return poller.stop;\n };\n\n notifyLocalActivity = (conversationId: number): void => {\n for (const poller of [...this.activePollers]) {\n if (poller.conversationId === conversationId) poller.receiveLocalActivity();\n }\n };\n}\n\nconst readDefaultDocumentVisibility = (): PollingDocumentVisibility | null =>\n typeof document === \"undefined\" ? null : new BrowserDocumentVisibility(document);\n\n/**\n * The polling default of `subscribeToConversationChanges` over a metadata read: every 2 s while a turn\n * runs, every 20 s while idle, at once after `notifyLocalActivity`; paused while the page is hidden and read once when\n * it shows again; exponential backoff on failed reads up to 60 s; a `notFound` / `forbidden` read ends the subscription\n * with `gone` / `forbidden`. One poller per subscription; see `ConversationMetadataPoller` for how a read becomes a\n * signal.\n */\nexport const createPollingConversationChangeSubscription = (\n readMetadata: ConversationMetadataReader,\n options: PollingConversationChangeSubscriptionOptions = {},\n): PollingConversationChangeSubscription =>\n new PollingConversationChangeSubscriptions(readMetadata, {\n runningIntervalMs: options.runningIntervalMs ?? POLLING_RUNNING_INTERVAL_MS,\n idleIntervalMs: options.idleIntervalMs ?? POLLING_IDLE_INTERVAL_MS,\n maxBackoffMs: options.maxBackoffMs ?? POLLING_MAX_BACKOFF_MS,\n localActivityWindowMs: options.localActivityWindowMs ?? POLLING_LOCAL_ACTIVITY_WINDOW_MS,\n timers: options.timers ?? new GlobalPollingTimers(),\n documentVisibility: options.documentVisibility === undefined ? readDefaultDocumentVisibility() : options.documentVisibility,\n });\n","import type { AgentsConversationClient } from \"../AgentsConversationClient\";\nimport type {\n AgentsConversation,\n ChangedConversationMessages,\n ChangedMessagesRequest,\n ConversationActivity,\n ConversationAttachmentReference,\n ConversationChangeSignal,\n ConversationListQueryOptions,\n ConversationMessageSearchHit,\n ConversationMessageSentPayload,\n ConversationMessagesPage,\n ConversationSearchGroup,\n ConversationSearchOptions,\n ConversationSignal,\n ConversationSubscriptionEndReason,\n PostedMessage,\n SelectableModel,\n StartedConversation,\n} from \"../../contract/conversation\";\nimport {\n createPollingConversationChangeSubscription,\n type PollingConversationChangeSubscription,\n type PollingConversationChangeSubscriptionOptions,\n} from \"./createPollingConversationChangeSubscription\";\n\n/** A conversation client whose `subscribeToConversationChanges` is present. */\nexport type AgentsConversationClientWithChangeSubscription = AgentsConversationClient &\n Required<Pick<AgentsConversationClient, \"subscribeToConversationChanges\">>;\n\n/**\n * A binding without push, with the polling default as its subscription. Every port function goes to the binding; the\n * three that make the server start a turn on an existing conversation (post, signal, retry) tell the poller once the\n * server accepted them, so the first read does not wait for the idle interval. A start needs no notice: the chat\n * subscribes to the new conversation afterwards, and a subscription reads at once.\n */\nclass PollingAgentsConversationClient implements AgentsConversationClientWithChangeSubscription {\n readonly getSelectableModels?: () => Promise<SelectableModel[]>;\n readonly composeSentMessageText?: (text: string, attachments: ConversationAttachmentReference[] | undefined) => string;\n readonly getMessageSentPayload?: (conversationId: number, messageId: number) => Promise<ConversationMessageSentPayload>;\n\n constructor(\n protected readonly binding: AgentsConversationClient,\n protected readonly polling: PollingConversationChangeSubscription,\n ) {\n // The optional functions exist exactly when the binding has them (the chat reads their presence).\n this.getSelectableModels = binding.getSelectableModels?.bind(binding);\n this.composeSentMessageText = binding.composeSentMessageText?.bind(binding);\n this.getMessageSentPayload = binding.getMessageSentPayload?.bind(binding);\n }\n\n getConversationMetadata = (conversationId: number): Promise<AgentsConversation> =>\n this.binding.getConversationMetadata(conversationId);\n\n getNewestMessagesPage = (conversationId: number, pageSize: number): Promise<ConversationMessagesPage> =>\n this.binding.getNewestMessagesPage(conversationId, pageSize);\n\n getOlderMessagesPage = (conversationId: number, beforeIndex: number, pageSize: number): Promise<ConversationMessagesPage> =>\n this.binding.getOlderMessagesPage(conversationId, beforeIndex, pageSize);\n\n getChangedMessages = (conversationId: number, request: ChangedMessagesRequest): Promise<ChangedConversationMessages> =>\n this.binding.getChangedMessages(conversationId, request);\n\n startConversation = (request: Parameters<AgentsConversationClient[\"startConversation\"]>[0]): Promise<StartedConversation> =>\n this.binding.startConversation(request);\n\n postMessage = async (\n conversationId: number,\n message: { text: string; model?: string; attachments?: ConversationAttachmentReference[] },\n ): Promise<PostedMessage> => {\n const postedMessage = await this.binding.postMessage(conversationId, message);\n this.polling.notifyLocalActivity(conversationId);\n return postedMessage;\n };\n\n injectSignal = async (conversationId: number, signal: ConversationSignal): Promise<void> => {\n await this.binding.injectSignal(conversationId, signal);\n this.polling.notifyLocalActivity(conversationId);\n };\n\n retryConversation = async (conversationId: number): Promise<void> => {\n await this.binding.retryConversation(conversationId);\n this.polling.notifyLocalActivity(conversationId);\n };\n\n renameConversation = (conversationId: number, title: string): Promise<AgentsConversation> =>\n this.binding.renameConversation(conversationId, title);\n\n deleteConversation = (conversationId: number): Promise<void> => this.binding.deleteConversation(conversationId);\n\n listConversations = (agentSpaceKey: number, options?: ConversationListQueryOptions): Promise<AgentsConversation[]> =>\n this.binding.listConversations(agentSpaceKey, options);\n\n searchConversations = (text: string, options?: ConversationSearchOptions): Promise<ConversationSearchGroup[]> =>\n this.binding.searchConversations(text, options);\n\n searchConversationMessages = (\n conversationId: number,\n text: string,\n options?: ConversationSearchOptions,\n ): Promise<ConversationMessageSearchHit[]> => this.binding.searchConversationMessages(conversationId, text, options);\n\n getConversationActivity = (conversationId: number, turnMessageId?: number | null): Promise<ConversationActivity> =>\n this.binding.getConversationActivity(conversationId, turnMessageId);\n\n subscribeToConversationChanges = (\n conversationId: number,\n onSignal: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => this.polling.subscribeToConversationChanges(conversationId, onSignal, onEnded);\n}\n\n/**\n * The client with live updates: a client that brings its own `subscribeToConversationChanges` (a push\n * transport — the host application's Firebase binding) is returned as it is; any other gets the polling default over its\n * `getConversationMetadata` (`createPollingConversationChangeSubscription`, `options` tune the intervals). The default\n * `AgentChat` calls this by itself.\n */\nexport const withPollingChangeSubscription = (\n client: AgentsConversationClient,\n options?: PollingConversationChangeSubscriptionOptions,\n): AgentsConversationClientWithChangeSubscription => {\n if (client.subscribeToConversationChanges) return client as AgentsConversationClientWithChangeSubscription;\n return new PollingAgentsConversationClient(\n client,\n createPollingConversationChangeSubscription(client.getConversationMetadata.bind(client), options),\n );\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"withPollingChangeSubscription-B16htQGd.js","sources":["../../src/client/polling/createPollingConversationChangeSubscription.ts","../../src/client/polling/withPollingChangeSubscription.ts"],"sourcesContent":["import {\n type AgentsConversation,\n type ConversationChangeSignal,\n type ConversationRunStatus,\n type ConversationSubscriptionEndReason,\n CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,\n isAgentsClientError,\n readConversationRunStatusOfRunState,\n} from \"../../contract/conversation\";\n\n/** Read interval while a turn runs (or right after a local send). */\nexport const POLLING_RUNNING_INTERVAL_MS = 2_000;\n/** Read interval while the conversation is idle or failed. */\nexport const POLLING_IDLE_INTERVAL_MS = 20_000;\n/** Ceiling of the exponential backoff after failed reads. */\nexport const POLLING_MAX_BACKOFF_MS = 60_000;\n/**\n * How long after a local send the poller keeps the running interval although the conversation still reads idle: the\n * server may store the owner's message before it marks the turn as running.\n */\nexport const POLLING_LOCAL_ACTIVITY_WINDOW_MS = 10_000;\n\n/** Reads a conversation's metadata — the port's `getConversationMetadata`. */\nexport type ConversationMetadataReader = (conversationId: number) => Promise<AgentsConversation>;\n\n/** The timer functions the poller uses; a spec hands in fake ones. */\nexport interface PollingTimers {\n setTimeout: (callback: () => void, delayMs: number) => unknown;\n clearTimeout: (timerHandle: unknown) => void;\n now: () => number;\n}\n\n/** Whether the page is hidden, and a way to hear when that changes; a spec hands in its own. */\nexport interface PollingDocumentVisibility {\n isHidden: () => boolean;\n /** Calls `listener` on every visibility change; returns the function that stops it. */\n listen: (listener: () => void) => () => void;\n}\n\nexport interface PollingConversationChangeSubscriptionOptions {\n /** Default `POLLING_RUNNING_INTERVAL_MS` (2 s). */\n runningIntervalMs?: number;\n /** Default `POLLING_IDLE_INTERVAL_MS` (20 s). */\n idleIntervalMs?: number;\n /** Default `POLLING_MAX_BACKOFF_MS` (60 s). */\n maxBackoffMs?: number;\n /** Default `POLLING_LOCAL_ACTIVITY_WINDOW_MS` (10 s). */\n localActivityWindowMs?: number;\n /** Default: the global `setTimeout` / `clearTimeout` and `Date.now`. */\n timers?: PollingTimers;\n /** Default: the browser `document`; `null` = the page counts as always visible (no `document`, a worker). */\n documentVisibility?: PollingDocumentVisibility | null;\n}\n\n/**\n * The polling default of the live updates: the port's `subscribeToConversationChanges` plus\n * `notifyLocalActivity`, which `withPollingChangeSubscription` calls after every send the server accepted.\n */\nexport interface PollingConversationChangeSubscription {\n subscribeToConversationChanges: (\n conversationId: number,\n onSignal: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ) => () => void;\n /** Reads the conversation's metadata now and keeps the running interval for the local-activity window. */\n notifyLocalActivity: (conversationId: number) => void;\n}\n\ninterface ResolvedPollingOptions {\n runningIntervalMs: number;\n idleIntervalMs: number;\n maxBackoffMs: number;\n localActivityWindowMs: number;\n timers: PollingTimers;\n documentVisibility: PollingDocumentVisibility | null;\n}\n\n/** The global timer functions, looked up on each call (so a spec's fake timers installed later still apply). */\nclass GlobalPollingTimers implements PollingTimers {\n setTimeout = (callback: () => void, delayMs: number): unknown => globalThis.setTimeout(callback, delayMs);\n\n clearTimeout = (timerHandle: unknown): void => globalThis.clearTimeout(timerHandle as ReturnType<typeof setTimeout>);\n\n now = (): number => Date.now();\n}\n\n/** The browser document's `visibilityState` and `visibilitychange` event. */\nclass BrowserDocumentVisibility implements PollingDocumentVisibility {\n constructor(protected readonly browserDocument: Document) {}\n\n isHidden = (): boolean => this.browserDocument.visibilityState === \"hidden\";\n\n listen = (listener: () => void): (() => void) => {\n this.browserDocument.addEventListener(\"visibilitychange\", listener);\n return this.browserDocument.removeEventListener.bind(this.browserDocument, \"visibilitychange\", listener);\n };\n}\n\n/**\n * The three-value run status a polled read reports: the stored run state, and \"running\" for an idle conversation the\n * server owes a turn (`agentTurnOwedAt`), as the chat's own derivation does.\n */\nexport const readPolledRunStatus = (conversation: AgentsConversation): ConversationRunStatus | null => {\n const runStatus = readConversationRunStatusOfRunState(conversation.runState);\n return runStatus === \"idle\" && conversation.agentTurnOwedAt ? \"running\" : runStatus;\n};\n\n/** The end of a subscription a failed read means: a vanished conversation or lost access; null for a passing failure. */\nconst readSubscriptionEndReasonOfReadFailure = (failure: unknown): ConversationSubscriptionEndReason | null => {\n if (!isAgentsClientError(failure)) return null;\n if (failure.kind === \"notFound\") return \"gone\";\n if (failure.kind === \"forbidden\") return \"forbidden\";\n return null;\n};\n\n/**\n * One subscription: reads the conversation's metadata on a timer and turns each read into the change signal.\n *\n * Numbering (the signal leaves it open; the metadata carries no server change counter): the poller keeps its own counter. It\n * starts at 0 with the first read and advances by one whenever a read differs from the previous one in the run status,\n * `lastMessageId` or `updated` — so every polled signal is an ordinary change signal (no `isStatusCheck`) and the\n * package rules apply unchanged: an advance reads the changed tail (throttled), a terminal status reads at once,\n * re-reads the metadata and converges on `lastMessageId`. A status check would carry only the run status as news and\n * would miss a new message under an unchanged status. Without `lastMessageId` in the metadata every poll of a running\n * turn counts as a change (the changed tail is read every poll). `summaryUpdatedAt` is a second local counter that\n * advances when `title` or `summary` changed. A read with no change emits nothing.\n */\nclass ConversationMetadataPoller {\n protected isStopped = false;\n protected isReading = false;\n protected readAgainAfterCurrentRead = false;\n protected timerHandle: unknown = null;\n protected consecutiveFailedReads = 0;\n protected changeCounter = 0;\n protected summaryRevision = 0;\n protected previousChangeFingerprint: string | null = null;\n protected previousSummaryFingerprint: string | null = null;\n protected runStatus: ConversationRunStatus | null = null;\n protected hasConversationEnded = false;\n protected runningIntervalUntil = 0;\n protected stopListeningToVisibility: (() => void) | null = null;\n\n constructor(\n readonly conversationId: number,\n protected readonly readMetadata: ConversationMetadataReader,\n protected readonly onSignal: (signal: ConversationChangeSignal) => void,\n protected readonly onEnded: (reason: ConversationSubscriptionEndReason) => void,\n protected readonly options: ResolvedPollingOptions,\n protected readonly activePollers: Set<ConversationMetadataPoller>,\n ) {}\n\n start = (): void => {\n this.activePollers.add(this);\n this.stopListeningToVisibility = this.options.documentVisibility?.listen(this.receiveVisibilityChange) ?? null;\n this.readNow();\n };\n\n /** The unsubscribe: no read, no timer and no signal after it; a read in flight is dropped. */\n stop = (): void => {\n this.isStopped = true;\n this.activePollers.delete(this);\n this.clearTimer();\n this.stopListeningToVisibility?.();\n this.stopListeningToVisibility = null;\n };\n\n receiveLocalActivity = (): void => {\n this.runningIntervalUntil = this.options.timers.now() + this.options.localActivityWindowMs;\n this.readNow();\n };\n\n /** Reads now; a read already in flight is followed by one more read instead (two reads never overlap). */\n protected readNow = (): void => {\n if (this.isStopped) return;\n if (this.isReading) {\n this.readAgainAfterCurrentRead = true;\n return;\n }\n this.clearTimer();\n this.isReading = true;\n void this.readMetadataOnce();\n };\n\n protected async readMetadataOnce(): Promise<void> {\n let conversation: AgentsConversation;\n try {\n conversation = await this.readMetadata(this.conversationId);\n } catch (failure) {\n this.isReading = false;\n this.receiveReadFailure(failure);\n return;\n }\n this.isReading = false;\n if (this.isStopped) return;\n this.consecutiveFailedReads = 0;\n this.receiveConversation(conversation);\n this.continueAfterRead(this.readIntervalMs());\n }\n\n protected receiveConversation(conversation: AgentsConversation): void {\n const runStatus = readPolledRunStatus(conversation);\n const reportsLastMessageId = conversation.lastMessageId !== undefined;\n const changeFingerprint = JSON.stringify([runStatus, conversation.lastMessageId ?? null, conversation.updated]);\n const summaryFingerprint = JSON.stringify([conversation.title, conversation.summary]);\n const isFirstRead = this.previousChangeFingerprint === null;\n const hasChanged =\n changeFingerprint !== this.previousChangeFingerprint || (!reportsLastMessageId && runStatus === \"running\");\n const hasSummaryChanged = !isFirstRead && summaryFingerprint !== this.previousSummaryFingerprint;\n if (!isFirstRead && hasChanged) this.changeCounter += 1;\n if (hasSummaryChanged) this.summaryRevision += 1;\n this.previousChangeFingerprint = changeFingerprint;\n this.previousSummaryFingerprint = summaryFingerprint;\n this.runStatus = runStatus;\n this.hasConversationEnded =\n conversation.runState !== null && CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION.includes(conversation.runState);\n if (!isFirstRead && !hasChanged && !hasSummaryChanged) return;\n this.onSignal({\n changeCounter: this.changeCounter,\n lastMessageId: conversation.lastMessageId ?? null,\n runStatus,\n summaryUpdatedAt: this.summaryRevision,\n });\n }\n\n protected receiveReadFailure(failure: unknown): void {\n if (this.isStopped) return;\n const endReason = readSubscriptionEndReasonOfReadFailure(failure);\n if (endReason !== null) {\n this.stop();\n this.onEnded(endReason);\n return;\n }\n this.consecutiveFailedReads += 1;\n const backoffMs = this.readIntervalMs() * 2 ** this.consecutiveFailedReads;\n this.continueAfterRead(Math.min(backoffMs, this.options.maxBackoffMs));\n }\n\n protected continueAfterRead(nextReadDelayMs: number): void {\n if (this.readAgainAfterCurrentRead) {\n this.readAgainAfterCurrentRead = false;\n this.readNow();\n return;\n }\n // A completed or cancelled conversation changes no more: only a local send or the page's return reads it again.\n if (this.hasConversationEnded) return;\n this.scheduleRead(nextReadDelayMs);\n }\n\n protected readIntervalMs(): number {\n const isShortIntervalDue = this.runStatus === \"running\" || this.options.timers.now() < this.runningIntervalUntil;\n return isShortIntervalDue ? this.options.runningIntervalMs : this.options.idleIntervalMs;\n }\n\n /** Arms the next read — not while the page is hidden; its return reads at once. */\n protected scheduleRead(delayMs: number): void {\n this.clearTimer();\n if (this.isStopped || this.options.documentVisibility?.isHidden()) return;\n this.timerHandle = this.options.timers.setTimeout(this.readOnTimer, delayMs);\n }\n\n protected readOnTimer = (): void => {\n this.timerHandle = null;\n this.readNow();\n };\n\n protected receiveVisibilityChange = (): void => {\n if (this.isStopped) return;\n if (this.options.documentVisibility?.isHidden()) {\n this.clearTimer();\n return;\n }\n this.readNow();\n };\n\n protected clearTimer(): void {\n if (this.timerHandle === null) return;\n this.options.timers.clearTimeout(this.timerHandle);\n this.timerHandle = null;\n }\n}\n\n/** Every subscription of one polling default; `notifyLocalActivity` reaches the subscriptions of a conversation. */\nclass PollingConversationChangeSubscriptions implements PollingConversationChangeSubscription {\n protected readonly activePollers = new Set<ConversationMetadataPoller>();\n\n constructor(\n protected readonly readMetadata: ConversationMetadataReader,\n protected readonly options: ResolvedPollingOptions,\n ) {}\n\n subscribeToConversationChanges = (\n conversationId: number,\n onSignal: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => {\n const poller = new ConversationMetadataPoller(\n conversationId,\n this.readMetadata,\n onSignal,\n onEnded,\n this.options,\n this.activePollers,\n );\n poller.start();\n return poller.stop;\n };\n\n notifyLocalActivity = (conversationId: number): void => {\n for (const poller of [...this.activePollers]) {\n if (poller.conversationId === conversationId) poller.receiveLocalActivity();\n }\n };\n}\n\nconst readDefaultDocumentVisibility = (): PollingDocumentVisibility | null =>\n typeof document === \"undefined\" ? null : new BrowserDocumentVisibility(document);\n\n/**\n * The polling default of `subscribeToConversationChanges` over a metadata read: every 2 s while a turn\n * runs, every 20 s while idle, at once after `notifyLocalActivity`; paused while the page is hidden and read once when\n * it shows again; exponential backoff on failed reads up to 60 s; a `notFound` / `forbidden` read ends the subscription\n * with `gone` / `forbidden`. One poller per subscription; see `ConversationMetadataPoller` for how a read becomes a\n * signal.\n */\nexport const createPollingConversationChangeSubscription = (\n readMetadata: ConversationMetadataReader,\n options: PollingConversationChangeSubscriptionOptions = {},\n): PollingConversationChangeSubscription =>\n new PollingConversationChangeSubscriptions(readMetadata, {\n runningIntervalMs: options.runningIntervalMs ?? POLLING_RUNNING_INTERVAL_MS,\n idleIntervalMs: options.idleIntervalMs ?? POLLING_IDLE_INTERVAL_MS,\n maxBackoffMs: options.maxBackoffMs ?? POLLING_MAX_BACKOFF_MS,\n localActivityWindowMs: options.localActivityWindowMs ?? POLLING_LOCAL_ACTIVITY_WINDOW_MS,\n timers: options.timers ?? new GlobalPollingTimers(),\n documentVisibility: options.documentVisibility === undefined ? readDefaultDocumentVisibility() : options.documentVisibility,\n });\n","import type { AgentsConversationClient } from \"../AgentsConversationClient\";\nimport type {\n AgentsConversation,\n ChangedConversationMessages,\n ChangedMessagesRequest,\n ConversationActivity,\n ConversationAttachmentReference,\n ConversationChangeSignal,\n ConversationListQueryOptions,\n ConversationMessageSearchHit,\n ConversationMessageSentPayload,\n ConversationMessagesPage,\n ConversationSearchGroup,\n ConversationSearchOptions,\n ConversationSignal,\n ConversationSubscriptionEndReason,\n PostedMessage,\n SelectableModel,\n StartedConversation,\n} from \"../../contract/conversation\";\nimport {\n createPollingConversationChangeSubscription,\n type PollingConversationChangeSubscription,\n type PollingConversationChangeSubscriptionOptions,\n} from \"./createPollingConversationChangeSubscription\";\n\n/** A conversation client whose `subscribeToConversationChanges` is present. */\nexport type AgentsConversationClientWithChangeSubscription = AgentsConversationClient &\n Required<Pick<AgentsConversationClient, \"subscribeToConversationChanges\">>;\n\n/**\n * A binding without push, with the polling default as its subscription. Every port function goes to the binding; the\n * three that make the server start a turn on an existing conversation (post, signal, retry) tell the poller once the\n * server accepted them, so the first read does not wait for the idle interval. A start needs no notice: the chat\n * subscribes to the new conversation afterwards, and a subscription reads at once.\n */\nclass PollingAgentsConversationClient implements AgentsConversationClientWithChangeSubscription {\n readonly getSelectableModels?: () => Promise<SelectableModel[]>;\n readonly composeSentMessageText?: (text: string, attachments: ConversationAttachmentReference[] | undefined) => string;\n readonly getMessageSentPayload?: (conversationId: number, messageId: number) => Promise<ConversationMessageSentPayload>;\n\n constructor(\n protected readonly binding: AgentsConversationClient,\n protected readonly polling: PollingConversationChangeSubscription,\n ) {\n // The optional functions exist exactly when the binding has them (the chat reads their presence).\n this.getSelectableModels = binding.getSelectableModels?.bind(binding);\n this.composeSentMessageText = binding.composeSentMessageText?.bind(binding);\n this.getMessageSentPayload = binding.getMessageSentPayload?.bind(binding);\n }\n\n getConversationMetadata = (conversationId: number): Promise<AgentsConversation> =>\n this.binding.getConversationMetadata(conversationId);\n\n getNewestMessagesPage = (conversationId: number, pageSize: number): Promise<ConversationMessagesPage> =>\n this.binding.getNewestMessagesPage(conversationId, pageSize);\n\n getOlderMessagesPage = (conversationId: number, beforeIndex: number, pageSize: number): Promise<ConversationMessagesPage> =>\n this.binding.getOlderMessagesPage(conversationId, beforeIndex, pageSize);\n\n getChangedMessages = (conversationId: number, request: ChangedMessagesRequest): Promise<ChangedConversationMessages> =>\n this.binding.getChangedMessages(conversationId, request);\n\n startConversation = (request: Parameters<AgentsConversationClient[\"startConversation\"]>[0]): Promise<StartedConversation> =>\n this.binding.startConversation(request);\n\n postMessage = async (\n conversationId: number,\n message: { text: string; model?: string; attachments?: ConversationAttachmentReference[] },\n ): Promise<PostedMessage> => {\n const postedMessage = await this.binding.postMessage(conversationId, message);\n this.polling.notifyLocalActivity(conversationId);\n return postedMessage;\n };\n\n injectSignal = async (conversationId: number, signal: ConversationSignal): Promise<void> => {\n await this.binding.injectSignal(conversationId, signal);\n this.polling.notifyLocalActivity(conversationId);\n };\n\n retryConversation = async (conversationId: number): Promise<void> => {\n await this.binding.retryConversation(conversationId);\n this.polling.notifyLocalActivity(conversationId);\n };\n\n renameConversation = (conversationId: number, title: string): Promise<AgentsConversation> =>\n this.binding.renameConversation(conversationId, title);\n\n deleteConversation = (conversationId: number): Promise<void> => this.binding.deleteConversation(conversationId);\n\n listConversations = (agentSpaceKey: number, options?: ConversationListQueryOptions): Promise<AgentsConversation[]> =>\n this.binding.listConversations(agentSpaceKey, options);\n\n searchConversations = (text: string, options?: ConversationSearchOptions): Promise<ConversationSearchGroup[]> =>\n this.binding.searchConversations(text, options);\n\n searchConversationMessages = (\n conversationId: number,\n text: string,\n options?: ConversationSearchOptions,\n ): Promise<ConversationMessageSearchHit[]> => this.binding.searchConversationMessages(conversationId, text, options);\n\n getConversationActivity = (conversationId: number, turnMessageId?: number | null): Promise<ConversationActivity> =>\n this.binding.getConversationActivity(conversationId, turnMessageId);\n\n subscribeToConversationChanges = (\n conversationId: number,\n onSignal: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => this.polling.subscribeToConversationChanges(conversationId, onSignal, onEnded);\n}\n\n/**\n * The client with live updates: a client that brings its own `subscribeToConversationChanges` (a push\n * transport — the host application's Firebase binding) is returned as it is; any other gets the polling default over its\n * `getConversationMetadata` (`createPollingConversationChangeSubscription`, `options` tune the intervals). The default\n * `AgentChat` calls this by itself.\n */\nexport const withPollingChangeSubscription = (\n client: AgentsConversationClient,\n options?: PollingConversationChangeSubscriptionOptions,\n): AgentsConversationClientWithChangeSubscription => {\n if (client.subscribeToConversationChanges) return client as AgentsConversationClientWithChangeSubscription;\n return new PollingAgentsConversationClient(\n client,\n createPollingConversationChangeSubscription(client.getConversationMetadata.bind(client), options),\n );\n};\n"],"names":[],"mappings":"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"}
|
package/dist/client-testing.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { AgentsClientError, CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION, readConversationRunStatusOfRunState } from "./
|
|
3
|
-
import { l as listWidgetHydratorTypes, g as getWidgetHydrator, b as registerWidgetHydrator, u as unregisterWidgetHydrator } from "./chunks/WebSearchResultsConversationWidget-
|
|
4
|
-
import { j as conversationWidgetRegistry, k as replaceConversationWidgetRegistryEntries } from "./chunks/widget-name-resolver-
|
|
2
|
+
import { A as AgentsClientError, C as CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION, r as readConversationRunStatusOfRunState } from "./chunks/conversation-BEF0aesf.js";
|
|
3
|
+
import { l as listWidgetHydratorTypes, g as getWidgetHydrator, b as registerWidgetHydrator, c as abandonWidgetHydrationReads, u as unregisterWidgetHydrator } from "./chunks/WebSearchResultsConversationWidget-as3p5bHw.js";
|
|
4
|
+
import { j as conversationWidgetRegistry, k as replaceConversationWidgetRegistryEntries } from "./chunks/widget-name-resolver-BGN3HpoG.js";
|
|
5
|
+
import { l, s } from "./chunks/widget-name-resolver-BGN3HpoG.js";
|
|
6
|
+
import { c, r, s as s2 } from "./chunks/canvasStorage-EvQsGdto.js";
|
|
5
7
|
const DEFAULT_SCRIPTED_ASSISTANT_ANSWER = "Done.";
|
|
6
8
|
const DEFAULT_PAGE_SIZE = 50;
|
|
7
9
|
const copyOf = (value) => JSON.parse(JSON.stringify(value));
|
|
@@ -348,6 +350,7 @@ class WidgetHydrationStandIns {
|
|
|
348
350
|
this.answers = answers;
|
|
349
351
|
this.replacedHydrators = /* @__PURE__ */ new Map();
|
|
350
352
|
this.addedTypes = [];
|
|
353
|
+
this.unsettledReadKeys = /* @__PURE__ */ new Map();
|
|
351
354
|
for (const type of listWidgetHydratorTypes()) {
|
|
352
355
|
const registered = getWidgetHydrator(type);
|
|
353
356
|
if (registered) this.replaceHydrator(type, registered);
|
|
@@ -360,8 +363,14 @@ class WidgetHydrationStandIns {
|
|
|
360
363
|
});
|
|
361
364
|
}
|
|
362
365
|
}
|
|
363
|
-
/**
|
|
366
|
+
/**
|
|
367
|
+
* Puts the real hydrators back, removes the stand-ins added for types nothing had registered, and aborts and
|
|
368
|
+
* forgets the reads still pending, so the next mount of their keys reads anew.
|
|
369
|
+
*/
|
|
364
370
|
restore() {
|
|
371
|
+
abandonWidgetHydrationReads(new Set(this.unsettledReadKeys.values()));
|
|
372
|
+
for (const rejectRead of this.unsettledReadKeys.keys()) rejectRead(new Error("hydration stand-ins restored"));
|
|
373
|
+
this.unsettledReadKeys.clear();
|
|
365
374
|
for (const type of this.addedTypes) unregisterWidgetHydrator(type);
|
|
366
375
|
for (const [type, registered] of this.replacedHydrators) {
|
|
367
376
|
unregisterWidgetHydrator(type);
|
|
@@ -376,8 +385,16 @@ class WidgetHydrationStandIns {
|
|
|
376
385
|
hydrator: (payload, context) => this.answer(type, registered.deriveKey(type, payload), payload, context, registered)
|
|
377
386
|
});
|
|
378
387
|
}
|
|
379
|
-
/**
|
|
388
|
+
/** One read, abandoned by `restore()` while it has not settled. */
|
|
380
389
|
answer(type, derivedKey, payload, context, registered) {
|
|
390
|
+
const read = this.readAnswer(type, derivedKey, payload, context, registered);
|
|
391
|
+
return new Promise((resolve, reject) => {
|
|
392
|
+
this.unsettledReadKeys.set(reject, derivedKey);
|
|
393
|
+
read.then(resolve, reject).finally(() => this.unsettledReadKeys.delete(reject));
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
/** The answer for one read: the derived key's entry, else the type's, else the real hydrator (none: a failed read). */
|
|
397
|
+
readAnswer(type, derivedKey, payload, context, registered) {
|
|
381
398
|
const answerKey = Object.prototype.hasOwnProperty.call(this.answers, derivedKey) ? derivedKey : Object.prototype.hasOwnProperty.call(this.answers, type) ? type : void 0;
|
|
382
399
|
if (answerKey === void 0) {
|
|
383
400
|
return registered ? registered.hydrator(payload, context) : Promise.reject(new Error(`no hydration stand-in for ${derivedKey}`));
|
|
@@ -406,8 +423,13 @@ export {
|
|
|
406
423
|
InMemoryAgentsConversationClient,
|
|
407
424
|
TestConversationWidgetsInstallation,
|
|
408
425
|
WidgetHydrationStandIns,
|
|
426
|
+
c as clearStoredCanvas,
|
|
409
427
|
createInMemoryAgentsConversationClient,
|
|
410
428
|
installTestConversationWidgets,
|
|
411
|
-
installWidgetHydrationStandIns
|
|
429
|
+
installWidgetHydrationStandIns,
|
|
430
|
+
r as readStoredCanvas,
|
|
431
|
+
l as resetGoogleMapsAuthFailureForTests,
|
|
432
|
+
s as simulateGoogleMapsAuthFailure,
|
|
433
|
+
s2 as storeOpenCanvas
|
|
412
434
|
};
|
|
413
435
|
//# sourceMappingURL=client-testing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-testing.js","sources":["../src/client/testing/createInMemoryAgentsConversationClient.ts","../src/client/testing/widget-hydration-stand-ins.ts","../src/client/testing/test-conversation-widgets.ts"],"sourcesContent":["import type { AgentsConversationClient, AgentsConversationOperation } from \"../AgentsConversationClient\";\nimport {\n AgentsClientError,\n type AgentsConversation,\n type AgentsConversationMessage,\n type ChangedConversationMessages,\n type ChangedMessagesRequest,\n type ConversationActivity,\n type ConversationAttachmentReference,\n type ConversationChangeSignal,\n type ConversationListQueryOptions,\n type ConversationMessageSearchHit,\n type ConversationMessageSentPayload,\n type ConversationMessagesPage,\n type ConversationRunStatus,\n CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,\n type ConversationSearchGroup,\n type ConversationSearchOptions,\n type ConversationSignal,\n type ConversationSubscriptionEndReason,\n type PostedMessage,\n readConversationRunStatusOfRunState,\n type SelectableModel,\n type StartedConversation,\n} from \"../../contract/conversation\";\n\n/** One recorded conversation handed to the in-memory client: plain data, as a fixture file holds it. */\nexport interface RecordedConversation {\n conversation: AgentsConversation;\n messages: AgentsConversationMessage[];\n /** What `getConversationActivity` answers for this conversation; an empty, finished turn when omitted. */\n activity?: ConversationActivity;\n}\n\nexport interface InMemoryAgentsConversationClientData {\n conversations: RecordedConversation[];\n /**\n * The assistant's answers to a post, an injected signal, a start or a retry, used in order; the last one repeats.\n * Default: one answer, \"Done.\".\n */\n scriptedAssistantAnswers?: string[];\n /** What `getSelectableModels` answers; the client has no `getSelectableModels` when omitted. */\n selectableModels?: SelectableModel[];\n /** The page size of `getChangedMessages` when the request names none. Default 50. */\n defaultPageSize?: number;\n /** What `getMessageSentPayload` answers, by message id; the client has no `getMessageSentPayload` when omitted. */\n sentPayloadsByMessageId?: Record<number, ConversationMessageSentPayload>;\n /**\n * The assistant answers this many milliseconds after the send, like a server's turn: the run state is RUNNING\n * meanwhile, then the answer is stored and the run state goes back to IDLE — no `emit()` involved, so a poller sees it.\n * Default 0: the answer is stored together with the send.\n */\n assistantAnswerDelayMs?: number;\n /**\n * The client has no `subscribeToConversationChanges` — a backend without push. The chat then follows the\n * conversation through the polling default (`withPollingChangeSubscription`). Default false.\n */\n withoutChangeSubscription?: boolean;\n}\n\n/** A subscriber of one conversation's live updates. */\ninterface ConversationChangeSubscriber {\n onChange: (signal: ConversationChangeSignal) => void;\n onEnded: (reason: ConversationSubscriptionEndReason) => void;\n}\n\n/** The live-update state of one conversation. */\ninterface ConversationSignalState {\n changeCounter: number;\n summaryUpdatedAt: number | null;\n}\n\nconst DEFAULT_SCRIPTED_ASSISTANT_ANSWER = \"Done.\";\nconst DEFAULT_PAGE_SIZE = 50;\n\n/** A deep copy, so a caller can never change the client's records through a returned object. */\nconst copyOf = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nconst sortByIndexAscending = (messages: AgentsConversationMessage[]): AgentsConversationMessage[] =>\n [...messages].sort((first, second) => (first.indexInConversation ?? 0) - (second.indexInConversation ?? 0));\n\nconst textContains = (text: string | null | undefined, searchText: string): boolean =>\n (text ?? \"\").toLowerCase().includes(searchText.trim().toLowerCase());\n\n/** The message text a send stores: the typed text plus one reference line per attachment (as the host application's chat sends it). */\nconst buildStoredMessageText = (text: string, attachments: ConversationAttachmentReference[] | undefined): string =>\n attachments?.length\n ? `${text}\\n\\n${attachments\n .map((attachment) => `[Attached file: ${attachment.fileName ?? \"file\"} — ${attachment.publicUrl ?? \"\"}]`)\n .join(\"\\n\")}`\n : text;\n\n/**\n * The conversation client over recorded conversations, for specs, stories and the reference app\n * (`./testing`). Behaves like the server where the chat can see it: a post stores the owner's message and, at once,\n * the next scripted assistant answer — but live updates are driven by hand: `emit()` sends the conversation's next\n * signal to its subscribers (counter advanced, newest message id, the run status), `end()` ends the subscriptions.\n * With `assistantAnswerDelayMs` the answer is stored later, as a server turn would (RUNNING meanwhile); with\n * `withoutChangeSubscription` the client has no subscription at all and the chat polls it.\n */\nexport class InMemoryAgentsConversationClient implements AgentsConversationClient {\n protected readonly recordsByConversationId = new Map<number, RecordedConversation>();\n protected readonly subscribersByConversationId = new Map<number, Set<ConversationChangeSubscriber>>();\n protected readonly signalStateByConversationId = new Map<number, ConversationSignalState>();\n protected readonly scriptedAssistantAnswers: string[];\n protected readonly defaultPageSize: number;\n protected readonly selectableModels: SelectableModel[] | undefined;\n protected readonly sentPayloadsByMessageId: Record<number, ConversationMessageSentPayload> | undefined;\n protected readonly assistantAnswerDelayMs: number;\n protected readonly requestCountsByOperation = new Map<AgentsConversationOperation, number>();\n protected scriptedAssistantAnswersUsed = 0;\n protected nextMessageId: number;\n protected nextConversationId: number;\n\n /** Present only when the data names selectable models (the port function is optional). */\n readonly getSelectableModels?: () => Promise<SelectableModel[]>;\n /** Present only when the data names sent payloads (the port function is optional). */\n readonly getMessageSentPayload?: (conversationId: number, messageId: number) => Promise<ConversationMessageSentPayload>;\n /** Present unless the data asks for a client without push (`withoutChangeSubscription`). */\n readonly subscribeToConversationChanges?: (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ) => () => void;\n\n constructor(data: InMemoryAgentsConversationClientData) {\n for (const recordedConversation of copyOf(data.conversations)) {\n this.recordsByConversationId.set(recordedConversation.conversation.id, recordedConversation);\n }\n const scriptedAssistantAnswers = data.scriptedAssistantAnswers ?? [];\n this.scriptedAssistantAnswers =\n scriptedAssistantAnswers.length > 0 ? [...scriptedAssistantAnswers] : [DEFAULT_SCRIPTED_ASSISTANT_ANSWER];\n this.defaultPageSize = data.defaultPageSize ?? DEFAULT_PAGE_SIZE;\n this.selectableModels = data.selectableModels ? copyOf(data.selectableModels) : undefined;\n if (this.selectableModels) {\n this.getSelectableModels = this.readSelectableModels;\n }\n this.sentPayloadsByMessageId = data.sentPayloadsByMessageId ? copyOf(data.sentPayloadsByMessageId) : undefined;\n if (this.sentPayloadsByMessageId) {\n this.getMessageSentPayload = this.readMessageSentPayload;\n }\n this.assistantAnswerDelayMs = data.assistantAnswerDelayMs ?? 0;\n if (!data.withoutChangeSubscription) {\n this.subscribeToConversationChanges = this.subscribeToRecordedConversationChanges;\n }\n const allMessageIds = data.conversations.flatMap((recorded) => recorded.messages.map((message) => message.id));\n const allConversationIds = data.conversations.map((recorded) => recorded.conversation.id);\n this.nextMessageId = Math.max(0, ...allMessageIds) + 1;\n this.nextConversationId = Math.max(0, ...allConversationIds) + 1;\n }\n\n // ── Port ──────────────────────────────────────────────────────────────────────────────────────────────────────\n\n listConversations = async (\n agentSpaceKey: number,\n options: ConversationListQueryOptions = {},\n ): Promise<AgentsConversation[]> => {\n this.countRequest(\"listConversations\");\n const { includeUnsummarized = false, paging = {} } = options;\n const { top, skip = 0 } = paging;\n const conversationsOfAgentSpace = [...this.recordsByConversationId.values()]\n .map((recorded) => recorded.conversation)\n .filter((conversation) => conversation.agentSpaceKey === agentSpaceKey)\n .filter((conversation) => includeUnsummarized || conversation.title !== null)\n .sort((first, second) => second.id - first.id);\n const end = top === undefined ? undefined : skip + top;\n return copyOf(conversationsOfAgentSpace.slice(skip, end));\n };\n\n /** Account-wide, like the server: every recorded conversation is searched. */\n searchConversations = async (text: string, options: ConversationSearchOptions = {}): Promise<ConversationSearchGroup[]> => {\n this.countRequest(\"searchConversations\");\n const searchGroups: ConversationSearchGroup[] = [];\n for (const recorded of this.recordsByConversationId.values()) {\n const hits = this.findMessageSearchHits(recorded, text);\n if (hits.length === 0) continue;\n searchGroups.push({\n conversationId: recorded.conversation.id,\n title: recorded.conversation.title,\n summary: null,\n created: recorded.messages[0]?.createdDate ?? null,\n updated: recorded.messages[recorded.messages.length - 1]?.createdDate ?? null,\n relevance: 1,\n hits,\n });\n }\n return copyOf(searchGroups.slice(0, options.limit));\n };\n\n /** The stored conversation with `lastMessageId` (the newest stored message), as a backend without push answers it. */\n getConversationMetadata = async (conversationId: number): Promise<AgentsConversation> => {\n this.countRequest(\"getConversationMetadata\");\n const recorded = this.readRecord(conversationId);\n return copyOf({ ...recorded.conversation, lastMessageId: sortByIndexAscending(recorded.messages).pop()?.id ?? null });\n };\n\n getNewestMessagesPage = async (conversationId: number, pageSize: number): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getNewestMessagesPage\");\n const newestFirst = this.readMessagesNewestFirst(conversationId);\n return copyOf({ messages: newestFirst.slice(0, pageSize), hasMoreOlder: newestFirst.length > pageSize });\n };\n\n getOlderMessagesPage = async (\n conversationId: number,\n beforeIndex: number,\n pageSize: number,\n ): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getOlderMessagesPage\");\n const olderMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => (message.indexInConversation ?? Number.MAX_SAFE_INTEGER) < beforeIndex,\n );\n return copyOf({ messages: olderMessages.slice(0, pageSize), hasMoreOlder: olderMessages.length > pageSize });\n };\n\n getChangedMessages = async (\n conversationId: number,\n request: ChangedMessagesRequest,\n ): Promise<ChangedConversationMessages> => {\n this.countRequest(\"getChangedMessages\");\n const changedMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => message.id > request.highestKnownId || (message.indexInConversation ?? -1) >= request.fromIndex,\n );\n return copyOf({ messages: changedMessages.slice(0, request.pageSize ?? this.defaultPageSize) });\n };\n\n searchConversationMessages = async (\n conversationId: number,\n text: string,\n options: ConversationSearchOptions = {},\n ): Promise<ConversationMessageSearchHit[]> => {\n this.countRequest(\"searchConversationMessages\");\n return copyOf(this.findMessageSearchHits(this.readRecord(conversationId), text).slice(0, options.limit));\n };\n\n getConversationActivity = async (conversationId: number): Promise<ConversationActivity> => {\n this.countRequest(\"getConversationActivity\");\n return copyOf(this.readRecord(conversationId).activity ?? { turnMessageId: null, turnRunning: false, elements: [] });\n };\n\n startConversation = async (request: {\n agentSpaceKey: number;\n text: string;\n startSignalText?: string;\n entryPointKey?: string;\n model?: string;\n attachments?: ConversationAttachmentReference[];\n }): Promise<StartedConversation> => {\n this.countRequest(\"startConversation\");\n if (request.text === \"\" && !request.startSignalText) {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"A start needs a message or a signal.\" });\n }\n const reopenedConversation = request.entryPointKey\n ? [...this.recordsByConversationId.values()].find(\n (recorded) =>\n recorded.conversation.agentSpaceKey === request.agentSpaceKey &&\n recorded.conversation.entryPointKey === request.entryPointKey,\n )\n : undefined;\n if (reopenedConversation) {\n return { conversation: copyOf(reopenedConversation.conversation), messages: [], reopenedExisting: true };\n }\n const recorded: RecordedConversation = {\n conversation: {\n id: this.nextConversationId,\n agentSpaceKey: request.agentSpaceKey,\n runState: \"IDLE\",\n agentTurnOwedAt: null,\n title: null,\n titleManuallyEdited: false,\n summary: null,\n created: null,\n updated: null,\n purposeName: null,\n initiatorType: null,\n scheduleId: null,\n entryPointKey: request.entryPointKey ?? null,\n progressSteps: [],\n },\n messages: [],\n };\n this.nextConversationId += 1;\n this.recordsByConversationId.set(recorded.conversation.id, recorded);\n if (request.startSignalText) {\n this.appendMessage(recorded, { role: \"USER\", messageType: \"SIGNAL\", authorType: \"USER\", message: request.startSignalText });\n }\n if (request.text !== \"\") {\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(request.text, request.attachments),\n });\n }\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { conversation: copyOf(recorded.conversation), messages: messagesBeforeTheAnswer, reopenedExisting: false };\n };\n\n postMessage = async (\n conversationId: number,\n message: { text: string; model?: string; attachments?: ConversationAttachmentReference[] },\n ): Promise<PostedMessage> => {\n this.countRequest(\"postMessage\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(message.text, message.attachments),\n });\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { messages: messagesBeforeTheAnswer };\n };\n\n injectSignal = async (conversationId: number, signal: ConversationSignal): Promise<void> => {\n this.countRequest(\"injectSignal\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"SIGNAL\",\n authorType: \"USER\",\n message: signal.text,\n signalSource: signal.source ?? \"FRONTEND_WIDGET_CLICK\",\n });\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n retryConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"retryConversation\");\n const recorded = this.readRecord(conversationId);\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n if (newestMessage === undefined || newestMessage.role === \"ASSISTANT\") {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"Nothing to retry.\" });\n }\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n renameConversation = async (conversationId: number, title: string): Promise<AgentsConversation> => {\n this.countRequest(\"renameConversation\");\n const recorded = this.readRecord(conversationId);\n recorded.conversation.title = title;\n recorded.conversation.titleManuallyEdited = true;\n return copyOf(recorded.conversation);\n };\n\n deleteConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"deleteConversation\");\n this.readRecord(conversationId);\n this.recordsByConversationId.delete(conversationId);\n };\n\n /** The port's `subscribeToConversationChanges` unless `withoutChangeSubscription`; signals come from `emit()`. */\n protected subscribeToRecordedConversationChanges = (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => {\n const subscriber: ConversationChangeSubscriber = { onChange, onEnded };\n const subscribers = this.subscribersByConversationId.get(conversationId) ?? new Set<ConversationChangeSubscriber>();\n subscribers.add(subscriber);\n this.subscribersByConversationId.set(conversationId, subscribers);\n // Like the realtime database: the current value arrives at once, before any change.\n const record = this.recordsByConversationId.get(conversationId);\n if (record) {\n onChange(this.buildSignal(record, this.readSignalState(conversationId), null));\n }\n return () => {\n subscribers.delete(subscriber);\n };\n };\n\n // ── Driving live updates by hand ──────────────────────────────────────────────────────────────────────────────\n\n /**\n * Sends the conversation's next signal to its subscribers: the counter advances, `lastMessageId` is the newest\n * stored message, the run status follows the stored run state unless `runStatus` is given. `summaryChanged` also\n * advances `summaryUpdatedAt`. Returns the sent signal.\n */\n emit = (\n conversationId: number,\n change: { runStatus?: ConversationRunStatus; summaryChanged?: boolean } = {},\n ): ConversationChangeSignal => {\n const record = this.readRecord(conversationId);\n const signalState = this.readSignalState(conversationId);\n signalState.changeCounter += 1;\n if (change.summaryChanged) {\n signalState.summaryUpdatedAt = (signalState.summaryUpdatedAt ?? 0) + 1;\n }\n const signal = this.buildSignal(record, signalState, change.runStatus ?? null);\n for (const subscriber of [...(this.subscribersByConversationId.get(conversationId) ?? [])]) {\n subscriber.onChange(copyOf(signal));\n }\n return signal;\n };\n\n /** Ends every subscription of the conversation with `reason`, as the server's deletion or a lost access would. */\n end = (conversationId: number, reason: ConversationSubscriptionEndReason): void => {\n const subscribers = [...(this.subscribersByConversationId.get(conversationId) ?? [])];\n this.subscribersByConversationId.delete(conversationId);\n for (const subscriber of subscribers) {\n subscriber.onEnded(reason);\n }\n };\n\n /** Sets a conversation's stored run state (a turn that runs, fails, or a conversation that closes). */\n setRunState = (conversationId: number, runState: AgentsConversation[\"runState\"]): void => {\n this.readRecord(conversationId).conversation.runState = runState;\n };\n\n /**\n * How many requests the client answered — of one port function, or of all when `operation` is omitted (the\n * subscription is no request). For assertions such as \"no read after the chat unmounted\".\n */\n readRequestCount = (operation?: AgentsConversationOperation): number => {\n if (operation !== undefined) return this.requestCountsByOperation.get(operation) ?? 0;\n let requestCount = 0;\n for (const operationRequestCount of this.requestCountsByOperation.values()) requestCount += operationRequestCount;\n return requestCount;\n };\n\n /** Every stored message of a conversation in index order — for assertions. */\n readStoredMessages = (conversationId: number): AgentsConversationMessage[] =>\n copyOf(sortByIndexAscending(this.readRecord(conversationId).messages));\n\n // ── Internals ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n protected countRequest(operation: AgentsConversationOperation): void {\n this.requestCountsByOperation.set(operation, (this.requestCountsByOperation.get(operation) ?? 0) + 1);\n }\n\n protected readSelectableModels = async (): Promise<SelectableModel[]> => {\n this.countRequest(\"getSelectableModels\");\n return copyOf(this.selectableModels ?? []);\n };\n\n protected readMessageSentPayload = async (\n conversationId: number,\n messageId: number,\n ): Promise<ConversationMessageSentPayload> => {\n this.countRequest(\"getMessageSentPayload\");\n this.readRecord(conversationId);\n const sentPayload = this.sentPayloadsByMessageId?.[messageId];\n if (sentPayload === undefined) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `No sent payload for message ${messageId}.` });\n }\n return copyOf(sentPayload);\n };\n\n protected readMessagesNewestFirst(conversationId: number): AgentsConversationMessage[] {\n return sortByIndexAscending(this.readRecord(conversationId).messages).reverse();\n }\n\n protected readRecord(conversationId: number): RecordedConversation {\n const recorded = this.recordsByConversationId.get(conversationId);\n if (!recorded) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `Conversation ${conversationId} not found.` });\n }\n return recorded;\n }\n\n /** The record of a conversation that still takes input; a completed or cancelled one refuses like the server. */\n protected readOpenRecord(conversationId: number): RecordedConversation {\n const recorded = this.readRecord(conversationId);\n const runState = recorded.conversation.runState;\n if (runState !== null && CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION.includes(runState)) {\n throw new AgentsClientError(\"closed\", { httpStatus: 400, code: \"CONVERSATION_CLOSED\" });\n }\n return recorded;\n }\n\n protected readSignalState(conversationId: number): ConversationSignalState {\n const existingSignalState = this.signalStateByConversationId.get(conversationId);\n if (existingSignalState) return existingSignalState;\n const signalState: ConversationSignalState = { changeCounter: 0, summaryUpdatedAt: null };\n this.signalStateByConversationId.set(conversationId, signalState);\n return signalState;\n }\n\n protected buildSignal(\n recorded: RecordedConversation,\n signalState: ConversationSignalState,\n runStatus: ConversationRunStatus | null,\n ): ConversationChangeSignal {\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n return {\n changeCounter: signalState.changeCounter,\n lastMessageId: newestMessage?.id ?? null,\n runStatus: runStatus ?? readConversationRunStatusOfRunState(recorded.conversation.runState),\n summaryUpdatedAt: signalState.summaryUpdatedAt,\n };\n }\n\n protected appendMessage(\n recorded: RecordedConversation,\n fields: Pick<AgentsConversationMessage, \"role\" | \"messageType\" | \"authorType\" | \"message\"> &\n Partial<Pick<AgentsConversationMessage, \"signalSource\">>,\n ): AgentsConversationMessage {\n const highestIndex = Math.max(-1, ...recorded.messages.map((message) => message.indexInConversation ?? -1));\n const storedMessage: AgentsConversationMessage = {\n id: this.nextMessageId,\n conversationId: recorded.conversation.id,\n indexInConversation: highestIndex + 1,\n status: \"COMPLETED\",\n signalSource: null,\n createdDate: null,\n toolCall: null,\n delegatedConversations: [],\n subAgent: null,\n ...fields,\n };\n this.nextMessageId += 1;\n recorded.messages.push(storedMessage);\n return storedMessage;\n }\n\n /** The next scripted answer: stored at once, or after `assistantAnswerDelayMs` with the run state RUNNING meanwhile. */\n protected appendScriptedAssistantAnswer(recorded: RecordedConversation): void {\n const answerIndex = Math.min(this.scriptedAssistantAnswersUsed, this.scriptedAssistantAnswers.length - 1);\n this.scriptedAssistantAnswersUsed += 1;\n const answerText = this.scriptedAssistantAnswers[answerIndex];\n if (this.assistantAnswerDelayMs <= 0) {\n this.storeAssistantAnswer(recorded, answerText);\n return;\n }\n recorded.conversation.runState = \"RUNNING\";\n setTimeout(this.storeAssistantAnswer, this.assistantAnswerDelayMs, recorded, answerText);\n }\n\n protected storeAssistantAnswer = (recorded: RecordedConversation, answerText: string): void => {\n this.appendMessage(recorded, {\n role: \"ASSISTANT\",\n messageType: \"TEXT\",\n authorType: \"ASSISTANT\",\n message: answerText,\n });\n recorded.conversation.runState = \"IDLE\";\n };\n\n protected findMessageSearchHits(recorded: RecordedConversation, text: string): ConversationMessageSearchHit[] {\n if (text.trim() === \"\") return [];\n return sortByIndexAscending(recorded.messages)\n .filter((message) => message.messageType === \"TEXT\" && textContains(message.message, text))\n .map((message) => ({\n messageId: message.id,\n conversationId: recorded.conversation.id,\n indexInConversation: message.indexInConversation,\n role: message.role,\n excerpt: message.message,\n relevance: 1,\n created: message.createdDate,\n updated: null,\n title: recorded.conversation.title,\n }));\n }\n}\n\n/** The in-memory conversation client over recorded conversations (plain data). */\nexport const createInMemoryAgentsConversationClient = (\n data: InMemoryAgentsConversationClientData,\n): InMemoryAgentsConversationClient => new InMemoryAgentsConversationClient(data);\n","import {\n getWidgetHydrator,\n listWidgetHydratorTypes,\n registerWidgetHydrator,\n unregisterWidgetHydrator,\n} from \"../../renderer/widget-hydration/widget-hydration.registry\";\nimport type { RegisteredWidget, WidgetHydratorContext } from \"../../renderer/widget-hydration/widget-hydration.types\";\n\n/**\n * What a stand-in answers for one hydration: the data itself (a copy is handed out), `{ __status: <code> }` for a\n * failed read (the widget observes `error`), or `{ __pending: true }` for a read that never ends (`loading`). The same\n * shape as a preview fixture's `hydration` entry.\n */\nexport type WidgetHydrationStandInAnswer = unknown | { __status: number; __body?: unknown } | { __pending: true };\n\nconst isPendingAnswer = (answer: unknown): boolean =>\n typeof answer === \"object\" && answer !== null && (answer as { __pending?: unknown }).__pending === true;\n\nconst isStatusAnswer = (answer: unknown): answer is { __status: number } =>\n typeof answer === \"object\" && answer !== null && typeof (answer as { __status?: unknown }).__status === \"number\";\n\n/**\n * Hydration answered by a spec instead of mocking `useWidgetHydration`: every registered hydrator is replaced by one\n * that answers from `answers` — by the hydration's derived key first, then by its type — and otherwise runs the real\n * hydrator. A key that names no registered hydrator gets a stand-in hydrator of that type, so a widget hydrates even\n * when the spec did not import the app's hydrator module. The widget runs its live code path (`useWidgetHydration`\n * with the provider's state store); the answer arrives asynchronously, as a real read does. `restore()` puts the\n * real hydrators back and removes the added ones.\n */\nexport class WidgetHydrationStandIns {\n protected readonly replacedHydrators = new Map<string, RegisteredWidget<unknown, unknown>>();\n\n protected readonly addedTypes: string[] = [];\n\n constructor(protected readonly answers: Readonly<Record<string, WidgetHydrationStandInAnswer>>) {\n for (const type of listWidgetHydratorTypes()) {\n const registered = getWidgetHydrator<unknown, unknown>(type);\n if (registered) this.replaceHydrator(type, registered);\n }\n for (const key of Object.keys(answers)) {\n if (this.replacedHydrators.has(key)) continue;\n this.addedTypes.push(key);\n registerWidgetHydrator<unknown, unknown>(key, {\n hydrator: (payload, context) => this.answer(key, key, payload, context, undefined),\n });\n }\n }\n\n /** Puts the real hydrators back and removes the stand-ins added for types nothing had registered. */\n restore(): void {\n for (const type of this.addedTypes) unregisterWidgetHydrator(type);\n for (const [type, registered] of this.replacedHydrators) {\n unregisterWidgetHydrator(type);\n registerWidgetHydrator(type, registered);\n }\n }\n\n protected replaceHydrator(type: string, registered: RegisteredWidget<unknown, unknown>): void {\n this.replacedHydrators.set(type, registered);\n unregisterWidgetHydrator(type);\n registerWidgetHydrator<unknown, unknown>(type, {\n ...registered,\n hydrator: (payload, context) => this.answer(type, registered.deriveKey(type, payload), payload, context, registered),\n });\n }\n\n /** The answer for one read: the derived key's entry, else the type's, else the real hydrator (none: a failed read). */\n protected answer(\n type: string,\n derivedKey: string,\n payload: unknown,\n context: WidgetHydratorContext,\n registered: RegisteredWidget<unknown, unknown> | undefined,\n ): Promise<unknown> {\n const answerKey = Object.prototype.hasOwnProperty.call(this.answers, derivedKey)\n ? derivedKey\n : Object.prototype.hasOwnProperty.call(this.answers, type)\n ? type\n : undefined;\n if (answerKey === undefined) {\n return registered\n ? registered.hydrator(payload, context)\n : Promise.reject(new Error(`no hydration stand-in for ${derivedKey}`));\n }\n const answer = this.answers[answerKey];\n if (isPendingAnswer(answer)) return new Promise<never>(() => {});\n if (isStatusAnswer(answer)) return Promise.reject(new Error(`hydration stand-in ${answer.__status}`));\n return Promise.resolve(structuredClone(answer));\n }\n}\n\n/** Installs hydration stand-ins (`WidgetHydrationStandIns`); call `restore()` on the result after the spec. */\nexport const installWidgetHydrationStandIns = (\n answers: Readonly<Record<string, WidgetHydrationStandInAnswer>>,\n): WidgetHydrationStandIns => new WidgetHydrationStandIns(answers);\n","import {\n conversationWidgetRegistry,\n replaceConversationWidgetRegistryEntries,\n type ConversationWidgetEntry,\n} from \"../../renderer/conversationWidgetRegistry\";\n\nexport interface TestConversationWidgetsOptions {\n /**\n * Keeps the registered widgets beside the test widgets (a test widget replaces the registered one of its wire name).\n * Default false: the registry holds the test widgets only, so an app's registrations made with its environment land\n * nowhere while the installation lasts.\n */\n keepRegisteredWidgets?: boolean;\n}\n\n/**\n * The registry entries a spec renders against, and the way back: `restore()` puts the entries the registry held before\n * the installation. Everything that reads the registry — the widget host, the name resolver, the markdown renderer, an\n * embedded widget — sees the test widgets while it lasts.\n */\nexport class TestConversationWidgetsInstallation {\n protected readonly previousWidgets: ConversationWidgetEntry[];\n\n constructor(widgets: readonly ConversationWidgetEntry[], options: TestConversationWidgetsOptions = {}) {\n const testWidgetNames = new Set(widgets.map((widget) => widget.name));\n const keptWidgets = options.keepRegisteredWidgets\n ? conversationWidgetRegistry.widgets.filter((widget) => !testWidgetNames.has(widget.name))\n : [];\n this.previousWidgets = replaceConversationWidgetRegistryEntries([...keptWidgets, ...widgets]);\n }\n\n /** Puts the entries back that the registry held before the installation. */\n restore(): void {\n replaceConversationWidgetRegistryEntries(this.previousWidgets);\n }\n}\n\n/**\n * Renders a spec against widgets of its own instead of mocking the registry module: the test widgets take the\n * registry's place (or join it, `keepRegisteredWidgets`) until `restore()`.\n */\nexport const installTestConversationWidgets = (\n widgets: readonly ConversationWidgetEntry[],\n options: TestConversationWidgetsOptions = {},\n): TestConversationWidgetsInstallation => new TestConversationWidgetsInstallation(widgets, options);\n"],"names":["recorded"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client-testing.js","sources":["../src/client/testing/createInMemoryAgentsConversationClient.ts","../src/client/testing/widget-hydration-stand-ins.ts","../src/client/testing/test-conversation-widgets.ts"],"sourcesContent":["import type { AgentsConversationClient, AgentsConversationOperation } from \"../AgentsConversationClient\";\nimport {\n AgentsClientError,\n type AgentsConversation,\n type AgentsConversationMessage,\n type ChangedConversationMessages,\n type ChangedMessagesRequest,\n type ConversationActivity,\n type ConversationAttachmentReference,\n type ConversationChangeSignal,\n type ConversationListQueryOptions,\n type ConversationMessageSearchHit,\n type ConversationMessageSentPayload,\n type ConversationMessagesPage,\n type ConversationRunStatus,\n CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,\n type ConversationSearchGroup,\n type ConversationSearchOptions,\n type ConversationSignal,\n type ConversationSubscriptionEndReason,\n type PostedMessage,\n readConversationRunStatusOfRunState,\n type SelectableModel,\n type StartedConversation,\n} from \"../../contract/conversation\";\n\n/** One recorded conversation handed to the in-memory client: plain data, as a fixture file holds it. */\nexport interface RecordedConversation {\n conversation: AgentsConversation;\n messages: AgentsConversationMessage[];\n /** What `getConversationActivity` answers for this conversation; an empty, finished turn when omitted. */\n activity?: ConversationActivity;\n}\n\nexport interface InMemoryAgentsConversationClientData {\n conversations: RecordedConversation[];\n /**\n * The assistant's answers to a post, an injected signal, a start or a retry, used in order; the last one repeats.\n * Default: one answer, \"Done.\".\n */\n scriptedAssistantAnswers?: string[];\n /** What `getSelectableModels` answers; the client has no `getSelectableModels` when omitted. */\n selectableModels?: SelectableModel[];\n /** The page size of `getChangedMessages` when the request names none. Default 50. */\n defaultPageSize?: number;\n /** What `getMessageSentPayload` answers, by message id; the client has no `getMessageSentPayload` when omitted. */\n sentPayloadsByMessageId?: Record<number, ConversationMessageSentPayload>;\n /**\n * The assistant answers this many milliseconds after the send, like a server's turn: the run state is RUNNING\n * meanwhile, then the answer is stored and the run state goes back to IDLE — no `emit()` involved, so a poller sees it.\n * Default 0: the answer is stored together with the send.\n */\n assistantAnswerDelayMs?: number;\n /**\n * The client has no `subscribeToConversationChanges` — a backend without push. The chat then follows the\n * conversation through the polling default (`withPollingChangeSubscription`). Default false.\n */\n withoutChangeSubscription?: boolean;\n}\n\n/** A subscriber of one conversation's live updates. */\ninterface ConversationChangeSubscriber {\n onChange: (signal: ConversationChangeSignal) => void;\n onEnded: (reason: ConversationSubscriptionEndReason) => void;\n}\n\n/** The live-update state of one conversation. */\ninterface ConversationSignalState {\n changeCounter: number;\n summaryUpdatedAt: number | null;\n}\n\nconst DEFAULT_SCRIPTED_ASSISTANT_ANSWER = \"Done.\";\nconst DEFAULT_PAGE_SIZE = 50;\n\n/** A deep copy, so a caller can never change the client's records through a returned object. */\nconst copyOf = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nconst sortByIndexAscending = (messages: AgentsConversationMessage[]): AgentsConversationMessage[] =>\n [...messages].sort((first, second) => (first.indexInConversation ?? 0) - (second.indexInConversation ?? 0));\n\nconst textContains = (text: string | null | undefined, searchText: string): boolean =>\n (text ?? \"\").toLowerCase().includes(searchText.trim().toLowerCase());\n\n/** The message text a send stores: the typed text plus one reference line per attachment (as the host application's chat sends it). */\nconst buildStoredMessageText = (text: string, attachments: ConversationAttachmentReference[] | undefined): string =>\n attachments?.length\n ? `${text}\\n\\n${attachments\n .map((attachment) => `[Attached file: ${attachment.fileName ?? \"file\"} — ${attachment.publicUrl ?? \"\"}]`)\n .join(\"\\n\")}`\n : text;\n\n/**\n * The conversation client over recorded conversations, for specs, stories and the reference app\n * (`./testing`). Behaves like the server where the chat can see it: a post stores the owner's message and, at once,\n * the next scripted assistant answer — but live updates are driven by hand: `emit()` sends the conversation's next\n * signal to its subscribers (counter advanced, newest message id, the run status), `end()` ends the subscriptions.\n * With `assistantAnswerDelayMs` the answer is stored later, as a server turn would (RUNNING meanwhile); with\n * `withoutChangeSubscription` the client has no subscription at all and the chat polls it.\n */\nexport class InMemoryAgentsConversationClient implements AgentsConversationClient {\n protected readonly recordsByConversationId = new Map<number, RecordedConversation>();\n protected readonly subscribersByConversationId = new Map<number, Set<ConversationChangeSubscriber>>();\n protected readonly signalStateByConversationId = new Map<number, ConversationSignalState>();\n protected readonly scriptedAssistantAnswers: string[];\n protected readonly defaultPageSize: number;\n protected readonly selectableModels: SelectableModel[] | undefined;\n protected readonly sentPayloadsByMessageId: Record<number, ConversationMessageSentPayload> | undefined;\n protected readonly assistantAnswerDelayMs: number;\n protected readonly requestCountsByOperation = new Map<AgentsConversationOperation, number>();\n protected scriptedAssistantAnswersUsed = 0;\n protected nextMessageId: number;\n protected nextConversationId: number;\n\n /** Present only when the data names selectable models (the port function is optional). */\n readonly getSelectableModels?: () => Promise<SelectableModel[]>;\n /** Present only when the data names sent payloads (the port function is optional). */\n readonly getMessageSentPayload?: (conversationId: number, messageId: number) => Promise<ConversationMessageSentPayload>;\n /** Present unless the data asks for a client without push (`withoutChangeSubscription`). */\n readonly subscribeToConversationChanges?: (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ) => () => void;\n\n constructor(data: InMemoryAgentsConversationClientData) {\n for (const recordedConversation of copyOf(data.conversations)) {\n this.recordsByConversationId.set(recordedConversation.conversation.id, recordedConversation);\n }\n const scriptedAssistantAnswers = data.scriptedAssistantAnswers ?? [];\n this.scriptedAssistantAnswers =\n scriptedAssistantAnswers.length > 0 ? [...scriptedAssistantAnswers] : [DEFAULT_SCRIPTED_ASSISTANT_ANSWER];\n this.defaultPageSize = data.defaultPageSize ?? DEFAULT_PAGE_SIZE;\n this.selectableModels = data.selectableModels ? copyOf(data.selectableModels) : undefined;\n if (this.selectableModels) {\n this.getSelectableModels = this.readSelectableModels;\n }\n this.sentPayloadsByMessageId = data.sentPayloadsByMessageId ? copyOf(data.sentPayloadsByMessageId) : undefined;\n if (this.sentPayloadsByMessageId) {\n this.getMessageSentPayload = this.readMessageSentPayload;\n }\n this.assistantAnswerDelayMs = data.assistantAnswerDelayMs ?? 0;\n if (!data.withoutChangeSubscription) {\n this.subscribeToConversationChanges = this.subscribeToRecordedConversationChanges;\n }\n const allMessageIds = data.conversations.flatMap((recorded) => recorded.messages.map((message) => message.id));\n const allConversationIds = data.conversations.map((recorded) => recorded.conversation.id);\n this.nextMessageId = Math.max(0, ...allMessageIds) + 1;\n this.nextConversationId = Math.max(0, ...allConversationIds) + 1;\n }\n\n // ── Port ──────────────────────────────────────────────────────────────────────────────────────────────────────\n\n listConversations = async (\n agentSpaceKey: number,\n options: ConversationListQueryOptions = {},\n ): Promise<AgentsConversation[]> => {\n this.countRequest(\"listConversations\");\n const { includeUnsummarized = false, paging = {} } = options;\n const { top, skip = 0 } = paging;\n const conversationsOfAgentSpace = [...this.recordsByConversationId.values()]\n .map((recorded) => recorded.conversation)\n .filter((conversation) => conversation.agentSpaceKey === agentSpaceKey)\n .filter((conversation) => includeUnsummarized || conversation.title !== null)\n .sort((first, second) => second.id - first.id);\n const end = top === undefined ? undefined : skip + top;\n return copyOf(conversationsOfAgentSpace.slice(skip, end));\n };\n\n /** Account-wide, like the server: every recorded conversation is searched. */\n searchConversations = async (text: string, options: ConversationSearchOptions = {}): Promise<ConversationSearchGroup[]> => {\n this.countRequest(\"searchConversations\");\n const searchGroups: ConversationSearchGroup[] = [];\n for (const recorded of this.recordsByConversationId.values()) {\n const hits = this.findMessageSearchHits(recorded, text);\n if (hits.length === 0) continue;\n searchGroups.push({\n conversationId: recorded.conversation.id,\n title: recorded.conversation.title,\n summary: null,\n created: recorded.messages[0]?.createdDate ?? null,\n updated: recorded.messages[recorded.messages.length - 1]?.createdDate ?? null,\n relevance: 1,\n hits,\n });\n }\n return copyOf(searchGroups.slice(0, options.limit));\n };\n\n /** The stored conversation with `lastMessageId` (the newest stored message), as a backend without push answers it. */\n getConversationMetadata = async (conversationId: number): Promise<AgentsConversation> => {\n this.countRequest(\"getConversationMetadata\");\n const recorded = this.readRecord(conversationId);\n return copyOf({ ...recorded.conversation, lastMessageId: sortByIndexAscending(recorded.messages).pop()?.id ?? null });\n };\n\n getNewestMessagesPage = async (conversationId: number, pageSize: number): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getNewestMessagesPage\");\n const newestFirst = this.readMessagesNewestFirst(conversationId);\n return copyOf({ messages: newestFirst.slice(0, pageSize), hasMoreOlder: newestFirst.length > pageSize });\n };\n\n getOlderMessagesPage = async (\n conversationId: number,\n beforeIndex: number,\n pageSize: number,\n ): Promise<ConversationMessagesPage> => {\n this.countRequest(\"getOlderMessagesPage\");\n const olderMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => (message.indexInConversation ?? Number.MAX_SAFE_INTEGER) < beforeIndex,\n );\n return copyOf({ messages: olderMessages.slice(0, pageSize), hasMoreOlder: olderMessages.length > pageSize });\n };\n\n getChangedMessages = async (\n conversationId: number,\n request: ChangedMessagesRequest,\n ): Promise<ChangedConversationMessages> => {\n this.countRequest(\"getChangedMessages\");\n const changedMessages = this.readMessagesNewestFirst(conversationId).filter(\n (message) => message.id > request.highestKnownId || (message.indexInConversation ?? -1) >= request.fromIndex,\n );\n return copyOf({ messages: changedMessages.slice(0, request.pageSize ?? this.defaultPageSize) });\n };\n\n searchConversationMessages = async (\n conversationId: number,\n text: string,\n options: ConversationSearchOptions = {},\n ): Promise<ConversationMessageSearchHit[]> => {\n this.countRequest(\"searchConversationMessages\");\n return copyOf(this.findMessageSearchHits(this.readRecord(conversationId), text).slice(0, options.limit));\n };\n\n getConversationActivity = async (conversationId: number): Promise<ConversationActivity> => {\n this.countRequest(\"getConversationActivity\");\n return copyOf(this.readRecord(conversationId).activity ?? { turnMessageId: null, turnRunning: false, elements: [] });\n };\n\n startConversation = async (request: {\n agentSpaceKey: number;\n text: string;\n startSignalText?: string;\n entryPointKey?: string;\n model?: string;\n attachments?: ConversationAttachmentReference[];\n }): Promise<StartedConversation> => {\n this.countRequest(\"startConversation\");\n if (request.text === \"\" && !request.startSignalText) {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"A start needs a message or a signal.\" });\n }\n const reopenedConversation = request.entryPointKey\n ? [...this.recordsByConversationId.values()].find(\n (recorded) =>\n recorded.conversation.agentSpaceKey === request.agentSpaceKey &&\n recorded.conversation.entryPointKey === request.entryPointKey,\n )\n : undefined;\n if (reopenedConversation) {\n return { conversation: copyOf(reopenedConversation.conversation), messages: [], reopenedExisting: true };\n }\n const recorded: RecordedConversation = {\n conversation: {\n id: this.nextConversationId,\n agentSpaceKey: request.agentSpaceKey,\n runState: \"IDLE\",\n agentTurnOwedAt: null,\n title: null,\n titleManuallyEdited: false,\n summary: null,\n created: null,\n updated: null,\n purposeName: null,\n initiatorType: null,\n scheduleId: null,\n entryPointKey: request.entryPointKey ?? null,\n progressSteps: [],\n },\n messages: [],\n };\n this.nextConversationId += 1;\n this.recordsByConversationId.set(recorded.conversation.id, recorded);\n if (request.startSignalText) {\n this.appendMessage(recorded, { role: \"USER\", messageType: \"SIGNAL\", authorType: \"USER\", message: request.startSignalText });\n }\n if (request.text !== \"\") {\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(request.text, request.attachments),\n });\n }\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { conversation: copyOf(recorded.conversation), messages: messagesBeforeTheAnswer, reopenedExisting: false };\n };\n\n postMessage = async (\n conversationId: number,\n message: { text: string; model?: string; attachments?: ConversationAttachmentReference[] },\n ): Promise<PostedMessage> => {\n this.countRequest(\"postMessage\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"TEXT\",\n authorType: \"USER\",\n message: buildStoredMessageText(message.text, message.attachments),\n });\n const messagesBeforeTheAnswer = copyOf(recorded.messages);\n this.appendScriptedAssistantAnswer(recorded);\n return { messages: messagesBeforeTheAnswer };\n };\n\n injectSignal = async (conversationId: number, signal: ConversationSignal): Promise<void> => {\n this.countRequest(\"injectSignal\");\n const recorded = this.readOpenRecord(conversationId);\n this.appendMessage(recorded, {\n role: \"USER\",\n messageType: \"SIGNAL\",\n authorType: \"USER\",\n message: signal.text,\n signalSource: signal.source ?? \"FRONTEND_WIDGET_CLICK\",\n });\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n retryConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"retryConversation\");\n const recorded = this.readRecord(conversationId);\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n if (newestMessage === undefined || newestMessage.role === \"ASSISTANT\") {\n throw new AgentsClientError(\"other\", { httpStatus: 400, message: \"Nothing to retry.\" });\n }\n this.appendScriptedAssistantAnswer(recorded);\n };\n\n renameConversation = async (conversationId: number, title: string): Promise<AgentsConversation> => {\n this.countRequest(\"renameConversation\");\n const recorded = this.readRecord(conversationId);\n recorded.conversation.title = title;\n recorded.conversation.titleManuallyEdited = true;\n return copyOf(recorded.conversation);\n };\n\n deleteConversation = async (conversationId: number): Promise<void> => {\n this.countRequest(\"deleteConversation\");\n this.readRecord(conversationId);\n this.recordsByConversationId.delete(conversationId);\n };\n\n /** The port's `subscribeToConversationChanges` unless `withoutChangeSubscription`; signals come from `emit()`. */\n protected subscribeToRecordedConversationChanges = (\n conversationId: number,\n onChange: (signal: ConversationChangeSignal) => void,\n onEnded: (reason: ConversationSubscriptionEndReason) => void,\n ): (() => void) => {\n const subscriber: ConversationChangeSubscriber = { onChange, onEnded };\n const subscribers = this.subscribersByConversationId.get(conversationId) ?? new Set<ConversationChangeSubscriber>();\n subscribers.add(subscriber);\n this.subscribersByConversationId.set(conversationId, subscribers);\n // Like the realtime database: the current value arrives at once, before any change.\n const record = this.recordsByConversationId.get(conversationId);\n if (record) {\n onChange(this.buildSignal(record, this.readSignalState(conversationId), null));\n }\n return () => {\n subscribers.delete(subscriber);\n };\n };\n\n // ── Driving live updates by hand ──────────────────────────────────────────────────────────────────────────────\n\n /**\n * Sends the conversation's next signal to its subscribers: the counter advances, `lastMessageId` is the newest\n * stored message, the run status follows the stored run state unless `runStatus` is given. `summaryChanged` also\n * advances `summaryUpdatedAt`. Returns the sent signal.\n */\n emit = (\n conversationId: number,\n change: { runStatus?: ConversationRunStatus; summaryChanged?: boolean } = {},\n ): ConversationChangeSignal => {\n const record = this.readRecord(conversationId);\n const signalState = this.readSignalState(conversationId);\n signalState.changeCounter += 1;\n if (change.summaryChanged) {\n signalState.summaryUpdatedAt = (signalState.summaryUpdatedAt ?? 0) + 1;\n }\n const signal = this.buildSignal(record, signalState, change.runStatus ?? null);\n for (const subscriber of [...(this.subscribersByConversationId.get(conversationId) ?? [])]) {\n subscriber.onChange(copyOf(signal));\n }\n return signal;\n };\n\n /** Ends every subscription of the conversation with `reason`, as the server's deletion or a lost access would. */\n end = (conversationId: number, reason: ConversationSubscriptionEndReason): void => {\n const subscribers = [...(this.subscribersByConversationId.get(conversationId) ?? [])];\n this.subscribersByConversationId.delete(conversationId);\n for (const subscriber of subscribers) {\n subscriber.onEnded(reason);\n }\n };\n\n /** Sets a conversation's stored run state (a turn that runs, fails, or a conversation that closes). */\n setRunState = (conversationId: number, runState: AgentsConversation[\"runState\"]): void => {\n this.readRecord(conversationId).conversation.runState = runState;\n };\n\n /**\n * How many requests the client answered — of one port function, or of all when `operation` is omitted (the\n * subscription is no request). For assertions such as \"no read after the chat unmounted\".\n */\n readRequestCount = (operation?: AgentsConversationOperation): number => {\n if (operation !== undefined) return this.requestCountsByOperation.get(operation) ?? 0;\n let requestCount = 0;\n for (const operationRequestCount of this.requestCountsByOperation.values()) requestCount += operationRequestCount;\n return requestCount;\n };\n\n /** Every stored message of a conversation in index order — for assertions. */\n readStoredMessages = (conversationId: number): AgentsConversationMessage[] =>\n copyOf(sortByIndexAscending(this.readRecord(conversationId).messages));\n\n // ── Internals ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n protected countRequest(operation: AgentsConversationOperation): void {\n this.requestCountsByOperation.set(operation, (this.requestCountsByOperation.get(operation) ?? 0) + 1);\n }\n\n protected readSelectableModels = async (): Promise<SelectableModel[]> => {\n this.countRequest(\"getSelectableModels\");\n return copyOf(this.selectableModels ?? []);\n };\n\n protected readMessageSentPayload = async (\n conversationId: number,\n messageId: number,\n ): Promise<ConversationMessageSentPayload> => {\n this.countRequest(\"getMessageSentPayload\");\n this.readRecord(conversationId);\n const sentPayload = this.sentPayloadsByMessageId?.[messageId];\n if (sentPayload === undefined) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `No sent payload for message ${messageId}.` });\n }\n return copyOf(sentPayload);\n };\n\n protected readMessagesNewestFirst(conversationId: number): AgentsConversationMessage[] {\n return sortByIndexAscending(this.readRecord(conversationId).messages).reverse();\n }\n\n protected readRecord(conversationId: number): RecordedConversation {\n const recorded = this.recordsByConversationId.get(conversationId);\n if (!recorded) {\n throw new AgentsClientError(\"notFound\", { httpStatus: 404, message: `Conversation ${conversationId} not found.` });\n }\n return recorded;\n }\n\n /** The record of a conversation that still takes input; a completed or cancelled one refuses like the server. */\n protected readOpenRecord(conversationId: number): RecordedConversation {\n const recorded = this.readRecord(conversationId);\n const runState = recorded.conversation.runState;\n if (runState !== null && CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION.includes(runState)) {\n throw new AgentsClientError(\"closed\", { httpStatus: 400, code: \"CONVERSATION_CLOSED\" });\n }\n return recorded;\n }\n\n protected readSignalState(conversationId: number): ConversationSignalState {\n const existingSignalState = this.signalStateByConversationId.get(conversationId);\n if (existingSignalState) return existingSignalState;\n const signalState: ConversationSignalState = { changeCounter: 0, summaryUpdatedAt: null };\n this.signalStateByConversationId.set(conversationId, signalState);\n return signalState;\n }\n\n protected buildSignal(\n recorded: RecordedConversation,\n signalState: ConversationSignalState,\n runStatus: ConversationRunStatus | null,\n ): ConversationChangeSignal {\n const newestMessage = sortByIndexAscending(recorded.messages).pop();\n return {\n changeCounter: signalState.changeCounter,\n lastMessageId: newestMessage?.id ?? null,\n runStatus: runStatus ?? readConversationRunStatusOfRunState(recorded.conversation.runState),\n summaryUpdatedAt: signalState.summaryUpdatedAt,\n };\n }\n\n protected appendMessage(\n recorded: RecordedConversation,\n fields: Pick<AgentsConversationMessage, \"role\" | \"messageType\" | \"authorType\" | \"message\"> &\n Partial<Pick<AgentsConversationMessage, \"signalSource\">>,\n ): AgentsConversationMessage {\n const highestIndex = Math.max(-1, ...recorded.messages.map((message) => message.indexInConversation ?? -1));\n const storedMessage: AgentsConversationMessage = {\n id: this.nextMessageId,\n conversationId: recorded.conversation.id,\n indexInConversation: highestIndex + 1,\n status: \"COMPLETED\",\n signalSource: null,\n createdDate: null,\n toolCall: null,\n delegatedConversations: [],\n subAgent: null,\n ...fields,\n };\n this.nextMessageId += 1;\n recorded.messages.push(storedMessage);\n return storedMessage;\n }\n\n /** The next scripted answer: stored at once, or after `assistantAnswerDelayMs` with the run state RUNNING meanwhile. */\n protected appendScriptedAssistantAnswer(recorded: RecordedConversation): void {\n const answerIndex = Math.min(this.scriptedAssistantAnswersUsed, this.scriptedAssistantAnswers.length - 1);\n this.scriptedAssistantAnswersUsed += 1;\n const answerText = this.scriptedAssistantAnswers[answerIndex];\n if (this.assistantAnswerDelayMs <= 0) {\n this.storeAssistantAnswer(recorded, answerText);\n return;\n }\n recorded.conversation.runState = \"RUNNING\";\n setTimeout(this.storeAssistantAnswer, this.assistantAnswerDelayMs, recorded, answerText);\n }\n\n protected storeAssistantAnswer = (recorded: RecordedConversation, answerText: string): void => {\n this.appendMessage(recorded, {\n role: \"ASSISTANT\",\n messageType: \"TEXT\",\n authorType: \"ASSISTANT\",\n message: answerText,\n });\n recorded.conversation.runState = \"IDLE\";\n };\n\n protected findMessageSearchHits(recorded: RecordedConversation, text: string): ConversationMessageSearchHit[] {\n if (text.trim() === \"\") return [];\n return sortByIndexAscending(recorded.messages)\n .filter((message) => message.messageType === \"TEXT\" && textContains(message.message, text))\n .map((message) => ({\n messageId: message.id,\n conversationId: recorded.conversation.id,\n indexInConversation: message.indexInConversation,\n role: message.role,\n excerpt: message.message,\n relevance: 1,\n created: message.createdDate,\n updated: null,\n title: recorded.conversation.title,\n }));\n }\n}\n\n/** The in-memory conversation client over recorded conversations (plain data). */\nexport const createInMemoryAgentsConversationClient = (\n data: InMemoryAgentsConversationClientData,\n): InMemoryAgentsConversationClient => new InMemoryAgentsConversationClient(data);\n","import {\n getWidgetHydrator,\n listWidgetHydratorTypes,\n registerWidgetHydrator,\n unregisterWidgetHydrator,\n} from \"../../renderer/widget-hydration/widget-hydration.registry\";\nimport type { RegisteredWidget, WidgetHydratorContext } from \"../../renderer/widget-hydration/widget-hydration.types\";\nimport { abandonWidgetHydrationReads } from \"../../renderer/widget-hydration/use-widget-hydration\";\n\n/**\n * What a stand-in answers for one hydration: the data itself (a copy is handed out), `{ __status: <code> }` for a\n * failed read (the widget observes `error`), or `{ __pending: true }` for a read that never ends (`loading`). The same\n * shape as a preview fixture's `hydration` entry.\n */\nexport type WidgetHydrationStandInAnswer = unknown | { __status: number; __body?: unknown } | { __pending: true };\n\nconst isPendingAnswer = (answer: unknown): boolean =>\n typeof answer === \"object\" && answer !== null && (answer as { __pending?: unknown }).__pending === true;\n\nconst isStatusAnswer = (answer: unknown): answer is { __status: number } =>\n typeof answer === \"object\" && answer !== null && typeof (answer as { __status?: unknown }).__status === \"number\";\n\n/**\n * Hydration answered by a spec instead of mocking `useWidgetHydration`: every registered hydrator is replaced by one\n * that answers from `answers` — by the hydration's derived key first, then by its type — and otherwise runs the real\n * hydrator. A key that names no registered hydrator gets a stand-in hydrator of that type, so a widget hydrates even\n * when the spec did not import the app's hydrator module. The widget runs its live code path (`useWidgetHydration`\n * with the provider's state store); the answer arrives asynchronously, as a real read does. `restore()` puts the\n * real hydrators back, removes the added ones and abandons every read started through the stand-ins that has not\n * settled yet: the hydration hook keeps one in-flight read per key beyond a store and a mount, so a read left pending\n * would otherwise hold every later mount of the same key in loading.\n */\nexport class WidgetHydrationStandIns {\n protected readonly replacedHydrators = new Map<string, RegisteredWidget<unknown, unknown>>();\n\n protected readonly addedTypes: string[] = [];\n\n /** The hydration key of every read started through the stand-ins that has not settled yet, by its rejection. */\n protected readonly unsettledReadKeys = new Map<(reason: Error) => void, string>();\n\n constructor(protected readonly answers: Readonly<Record<string, WidgetHydrationStandInAnswer>>) {\n for (const type of listWidgetHydratorTypes()) {\n const registered = getWidgetHydrator<unknown, unknown>(type);\n if (registered) this.replaceHydrator(type, registered);\n }\n for (const key of Object.keys(answers)) {\n if (this.replacedHydrators.has(key)) continue;\n this.addedTypes.push(key);\n registerWidgetHydrator<unknown, unknown>(key, {\n hydrator: (payload, context) => this.answer(key, key, payload, context, undefined),\n });\n }\n }\n\n /**\n * Puts the real hydrators back, removes the stand-ins added for types nothing had registered, and aborts and\n * forgets the reads still pending, so the next mount of their keys reads anew.\n */\n restore(): void {\n abandonWidgetHydrationReads(new Set(this.unsettledReadKeys.values()));\n for (const rejectRead of this.unsettledReadKeys.keys()) rejectRead(new Error(\"hydration stand-ins restored\"));\n this.unsettledReadKeys.clear();\n for (const type of this.addedTypes) unregisterWidgetHydrator(type);\n for (const [type, registered] of this.replacedHydrators) {\n unregisterWidgetHydrator(type);\n registerWidgetHydrator(type, registered);\n }\n }\n\n protected replaceHydrator(type: string, registered: RegisteredWidget<unknown, unknown>): void {\n this.replacedHydrators.set(type, registered);\n unregisterWidgetHydrator(type);\n registerWidgetHydrator<unknown, unknown>(type, {\n ...registered,\n hydrator: (payload, context) => this.answer(type, registered.deriveKey(type, payload), payload, context, registered),\n });\n }\n\n /** One read, abandoned by `restore()` while it has not settled. */\n protected answer(\n type: string,\n derivedKey: string,\n payload: unknown,\n context: WidgetHydratorContext,\n registered: RegisteredWidget<unknown, unknown> | undefined,\n ): Promise<unknown> {\n const read = this.readAnswer(type, derivedKey, payload, context, registered);\n return new Promise<unknown>((resolve, reject) => {\n this.unsettledReadKeys.set(reject, derivedKey);\n read.then(resolve, reject).finally(() => this.unsettledReadKeys.delete(reject));\n });\n }\n\n /** The answer for one read: the derived key's entry, else the type's, else the real hydrator (none: a failed read). */\n protected readAnswer(\n type: string,\n derivedKey: string,\n payload: unknown,\n context: WidgetHydratorContext,\n registered: RegisteredWidget<unknown, unknown> | undefined,\n ): Promise<unknown> {\n const answerKey = Object.prototype.hasOwnProperty.call(this.answers, derivedKey)\n ? derivedKey\n : Object.prototype.hasOwnProperty.call(this.answers, type)\n ? type\n : undefined;\n if (answerKey === undefined) {\n return registered\n ? registered.hydrator(payload, context)\n : Promise.reject(new Error(`no hydration stand-in for ${derivedKey}`));\n }\n const answer = this.answers[answerKey];\n if (isPendingAnswer(answer)) return new Promise<never>(() => {});\n if (isStatusAnswer(answer)) return Promise.reject(new Error(`hydration stand-in ${answer.__status}`));\n return Promise.resolve(structuredClone(answer));\n }\n}\n\n/** Installs hydration stand-ins (`WidgetHydrationStandIns`); call `restore()` on the result after the spec. */\nexport const installWidgetHydrationStandIns = (\n answers: Readonly<Record<string, WidgetHydrationStandInAnswer>>,\n): WidgetHydrationStandIns => new WidgetHydrationStandIns(answers);\n","import {\n conversationWidgetRegistry,\n replaceConversationWidgetRegistryEntries,\n type ConversationWidgetEntry,\n} from \"../../renderer/conversationWidgetRegistry\";\n\nexport interface TestConversationWidgetsOptions {\n /**\n * Keeps the registered widgets beside the test widgets (a test widget replaces the registered one of its wire name).\n * Default false: the registry holds the test widgets only, so an app's registrations made with its environment land\n * nowhere while the installation lasts.\n */\n keepRegisteredWidgets?: boolean;\n}\n\n/**\n * The registry entries a spec renders against, and the way back: `restore()` puts the entries the registry held before\n * the installation. Everything that reads the registry — the widget host, the name resolver, the markdown renderer, an\n * embedded widget — sees the test widgets while it lasts.\n */\nexport class TestConversationWidgetsInstallation {\n protected readonly previousWidgets: ConversationWidgetEntry[];\n\n constructor(widgets: readonly ConversationWidgetEntry[], options: TestConversationWidgetsOptions = {}) {\n const testWidgetNames = new Set(widgets.map((widget) => widget.name));\n const keptWidgets = options.keepRegisteredWidgets\n ? conversationWidgetRegistry.widgets.filter((widget) => !testWidgetNames.has(widget.name))\n : [];\n this.previousWidgets = replaceConversationWidgetRegistryEntries([...keptWidgets, ...widgets]);\n }\n\n /** Puts the entries back that the registry held before the installation. */\n restore(): void {\n replaceConversationWidgetRegistryEntries(this.previousWidgets);\n }\n}\n\n/**\n * Renders a spec against widgets of its own instead of mocking the registry module: the test widgets take the\n * registry's place (or join it, `keepRegisteredWidgets`) until `restore()`.\n */\nexport const installTestConversationWidgets = (\n widgets: readonly ConversationWidgetEntry[],\n options: TestConversationWidgetsOptions = {},\n): TestConversationWidgetsInstallation => new TestConversationWidgetsInstallation(widgets, options);\n"],"names":["recorded"],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { A, u, a } from "./chunks/AgentsConversationClientContext-C4T7g_Qk.js";
|
|
3
|
-
import { P, a as a2, b, c, d, r, w } from "./chunks/withPollingChangeSubscription-
|
|
3
|
+
import { P, a as a2, b, c, d, r, w } from "./chunks/withPollingChangeSubscription-B16htQGd.js";
|
|
4
4
|
export {
|
|
5
5
|
A as AgentsConversationClientProvider,
|
|
6
6
|
P as POLLING_IDLE_INTERVAL_MS,
|
package/dist/contract.js
CHANGED
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
"CANCELLED"
|
|
5
|
-
];
|
|
6
|
-
const readConversationRunStatusOfRunState = (runState) => {
|
|
7
|
-
if (!runState) return null;
|
|
8
|
-
if (runState === "FAILED") return "failed";
|
|
9
|
-
if (runState === "RUNNING" || runState === "AWAITING_DELEGATES" || runState === "QUEUED") return "running";
|
|
10
|
-
return "idle";
|
|
11
|
-
};
|
|
12
|
-
const AGENTS_CLIENT_ERROR_CODE_REQUEST_ABORTED = "REQUEST_ABORTED";
|
|
13
|
-
class AgentsClientError extends Error {
|
|
14
|
-
constructor(kind, details = {}) {
|
|
15
|
-
super(details.message ?? kind);
|
|
16
|
-
this.name = "AgentsClientError";
|
|
17
|
-
this.kind = kind;
|
|
18
|
-
this.code = details.code ?? null;
|
|
19
|
-
this.retryAfterSeconds = kind === "rateLimit" ? details.retryAfterSeconds ?? null : null;
|
|
20
|
-
this.httpStatus = details.httpStatus ?? null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
const isAgentsClientError = (value) => value instanceof AgentsClientError;
|
|
2
|
+
import { a, A, C, i, r } from "./chunks/conversation-BEF0aesf.js";
|
|
3
|
+
import { b } from "./chunks/contract-BxmMxYPF.js";
|
|
24
4
|
export {
|
|
25
|
-
AGENTS_CLIENT_ERROR_CODE_REQUEST_ABORTED,
|
|
26
|
-
AgentsClientError,
|
|
27
|
-
CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,
|
|
28
|
-
|
|
29
|
-
|
|
5
|
+
a as AGENTS_CLIENT_ERROR_CODE_REQUEST_ABORTED,
|
|
6
|
+
A as AgentsClientError,
|
|
7
|
+
C as CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION,
|
|
8
|
+
b as buildChangedMessagesRequest,
|
|
9
|
+
i as isAgentsClientError,
|
|
10
|
+
r as readConversationRunStatusOfRunState
|
|
30
11
|
};
|
|
31
12
|
//# sourceMappingURL=contract.js.map
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sources":["../src/contract/conversation.ts"],"sourcesContent":["/**\n * The conversation contract of the package: plain TypeScript types the conversation client port\n * (`client/AgentsConversationClient.ts`) returns and the chat reads. Every field is derived from what the chat files and\n * the chat controller read from the server today (the host application's generated `AIConversation`, `AIConversationMessage`,\n * `AgentActivityLine`, `SemanticSearchHit`, `AIConversationSearchGroup`, `AgentCustomerSelectableModel`); nothing the\n * chat does not read is here.\n *\n * Naming: a field keeps the wire name where the name is neutral (`message`, `indexInConversation`, `role`, `status`,\n * `toolCall`); a name that carries an app's vocabulary is renamed (`aiConversationId` → `conversationId`,\n * the app's own key of the thing the agent works for → `agentSpaceKey`, `summary.title` → `title`). Each field carries a\n * one-line note:\n * - `generic` — every app that runs the agent conversation backend has it;\n * - `app extension` — read by the chat today but specific to one app's backend or debug surface; optional, and a\n * binding may leave it out.\n *\n * Client-side state is NOT part of the contract: the `fresh` tag of a live-arrived message, the optimistic user\n * message without an id and the `isNewConversation` / `isReopenedConversation` flags belong to the package store.\n */\n\n// ── Vocabulary ───────────────────────────────────────────────────────────────────────────────────────────────────\n\n/** The run state of a conversation as the server stores it (`AIConversationRunState`). */\nexport type ConversationRunState =\n | \"IDLE\"\n | \"QUEUED\"\n | \"RUNNING\"\n | \"AWAITING_DELEGATES\"\n | \"FAILED\"\n | \"COMPLETED\"\n | \"CANCELLED\";\n\n/** The three-value run status the live-update signal speaks; the chat keys its indicator and error card off it. */\nexport type ConversationRunStatus = \"running\" | \"idle\" | \"failed\";\n\nexport type ConversationMessageRole = \"SYSTEM\" | \"USER\" | \"ASSISTANT\";\n\nexport type ConversationMessageType = \"TEXT\" | \"TOOL_CALL\" | \"CHECKPOINT\" | \"ERROR\" | \"SIGNAL\" | \"TOOL_MESSAGE\";\n\nexport type ConversationMessageStatus =\n | \"PENDING\"\n | \"RUNNING\"\n | \"CANCEL_REQUESTED\"\n | \"CANCELLED\"\n | \"COMPLETED\"\n | \"FAILED\";\n\nexport type ConversationMessageAuthorType = \"USER\" | \"ASSISTANT\" | \"AGENT\" | \"TOOL\";\n\n/** Run states after which nothing runs any more and the server refuses further customer input. */\nexport const CONVERSATION_RUN_STATES_THAT_END_THE_CONVERSATION: readonly ConversationRunState[] = [\n \"COMPLETED\",\n \"CANCELLED\",\n];\n\n/** Maps a stored run state onto the signal's three-value status (`runStateToSignalStatus` of the live-update hook). */\nexport const readConversationRunStatusOfRunState = (\n runState: string | null | undefined,\n): ConversationRunStatus | null => {\n if (!runState) return null;\n if (runState === \"FAILED\") return \"failed\";\n if (runState === \"RUNNING\" || runState === \"AWAITING_DELEGATES\" || runState === \"QUEUED\") return \"running\";\n return \"idle\";\n};\n\n// ── Conversation ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n/** One step of the progress stepper a purposed conversation declares (`CustomerProgressStep`). */\nexport interface ConversationProgressStep {\n /** generic */\n key: string;\n /** generic — already in the owner's language */\n label: string;\n /** generic */\n status: \"done\" | \"active\" | \"upcoming\";\n}\n\n/** A conversation as the chat, the conversation list and the window around the chat read it. */\nexport interface AgentsConversation {\n /** generic */\n id: number;\n /** generic — the key of the agent space the conversation belongs to */\n agentSpaceKey: number | null;\n /** generic */\n runState: ConversationRunState | null;\n /**\n * generic — set while the server owes a turn on an IDLE conversation (a failed hand-over the recovery re-drives);\n * an IDLE conversation with this set counts as running (`effectiveRunState`)\n */\n agentTurnOwedAt: string | null;\n /** generic — the display title (`summary.title`); null until the first title was generated */\n title: string | null;\n /** generic — the owner set the title by hand; the generated title never replaces it then */\n titleManuallyEdited: boolean;\n /** generic — the generated summary of what the conversation is about (`summary.text`); null until generated */\n summary: string | null;\n /** generic — when the conversation was created, in the server's date-time format */\n created: string | null;\n /** generic — when the conversation was last modified, in the server's date-time format */\n updated: string | null;\n /** generic — the purpose of a purposed conversation (e.g. `Example.Build`, a first-run setup purpose); null otherwise */\n purposeName: string | null;\n /** generic — who started the conversation (`SCHEDULE` for a run of a scheduled task) */\n initiatorType: string | null;\n /** generic — the scheduled task a `SCHEDULE` conversation is a run of */\n scheduleId: number | null;\n /** generic — the entry point a start came from; a start from the same entry point reopens this conversation */\n entryPointKey: string | null;\n /** generic — empty unless the purpose declares a progress stepper */\n progressSteps: ConversationProgressStep[];\n /**\n * generic — the id of the newest stored message; null before the first. The polling default of the live updates\n * (`client/polling/`) reads it to see a new message while the run status stays the same, and hands it to the rules as\n * the signal's `lastMessageId`. Optional: a binding with a push transport may leave it out (the host application's metadata\n * read does not carry it); without it the poller treats every poll of a running turn as a change.\n */\n lastMessageId?: number | null;\n /** app extension — the prompt extension of the host application's older AI-Extensions chat */\n promptExtensionId?: number | null;\n /** app extension — the category of that prompt extension (its colour and product name in the conversation list) */\n promptExtensionCategory?: { alias: string | null; name: string | null } | null;\n}\n\n// ── Messages ─────────────────────────────────────────────────────────────────────────────────────────────────────\n\n/** A sub-agent conversation a delegation started, as far as the chat reads it (`delegatedConversations(select=id,runState)`). */\nexport interface ConversationDelegatedConversation {\n /** generic */\n id: number;\n /** generic */\n runState: ConversationRunState | null;\n}\n\n/** One run of a sub-agent, as the activity derivation reads it (`DelegationSubAgentRun`). */\nexport interface ConversationSubAgentRun {\n /** generic */\n runNumber: number | null;\n /** generic */\n status: string | null;\n}\n\n/** The sub-agent record a newer `delegate` call row carries instead of expanded children (`DelegationSubAgentRecord`). */\nexport interface ConversationSubAgent {\n /** generic */\n currentRunNumber: number | null;\n /** generic */\n runs: ConversationSubAgentRun[];\n}\n\n/** The tool call a `TOOL_CALL` row carries, as far as the chat reads it. */\nexport interface ConversationToolCall {\n /** generic */\n toolCallId: string | null;\n /** generic */\n name: string | null;\n /** generic — the call's arguments as the model wrote them (the `delegate` call's `tasks` is read) */\n arguments: unknown;\n /** generic — the call's own progress; a row's `status` says nothing about it */\n status: string | null;\n}\n\n/** The admin debug numbers of one message (the message forensics panel). */\nexport interface ConversationMessageForensics {\n /** app extension */\n inputTokens: number | null;\n /** app extension */\n outputTokens: number | null;\n /** app extension */\n costInUSD: number | null;\n /** app extension — the stored request snapshot of the model call (model name, reasoning effort, sent at) */\n requestSnapshot: unknown;\n}\n\n/** One stored message of a conversation. */\nexport interface AgentsConversationMessage {\n /** generic */\n id: number;\n /** generic */\n conversationId: number | null;\n /** generic — the position in the conversation; the merge key of every page */\n indexInConversation: number | null;\n /** generic */\n role: ConversationMessageRole | null;\n /** generic */\n messageType: ConversationMessageType | null;\n /** generic — non-final only on a waiting signal row */\n status: ConversationMessageStatus | null;\n /** generic */\n authorType: ConversationMessageAuthorType | null;\n /** generic — the source of a SIGNAL or ERROR row (`AI_BUDGET_REACHED`, `FRONTEND_WIDGET_CLICK`, …) */\n signalSource: string | null;\n /** generic — the text in markdown, widget blocks included */\n message: string;\n /** generic */\n createdDate: string | null;\n /** generic */\n toolCall: ConversationToolCall | null;\n /** generic — the sub-agent conversations a `delegate` call started (older rows) */\n delegatedConversations: ConversationDelegatedConversation[];\n /** generic — the sub-agent record of a `delegate` call (newer rows) */\n subAgent: ConversationSubAgent | null;\n /** app extension — admin debug numbers; absent outside the admin surfaces */\n forensics?: ConversationMessageForensics;\n}\n\n/** A file from the app's media library that rides along with a message. */\nexport interface ConversationAttachmentReference {\n /** generic — the app's id of the file (host application: the media manager file id) */\n fileId: string | number | null;\n /** generic */\n fileName: string | null;\n /** generic — a URL the model and the tools can fetch */\n publicUrl: string | null;\n}\n\n// ── Pages and results of the port ────────────────────────────────────────────────────────────────────────────────\n\n/** A page of messages read newest first (the newest page on open, an older page on scroll-up). */\nexport interface ConversationMessagesPage {\n /** generic — in any order; the store merges by `indexInConversation` */\n messages: AgentsConversationMessage[];\n /** generic — an older page exists */\n hasMoreOlder: boolean;\n}\n\n/**\n * What a live update reads: every message written since the newest known id, plus every message from the lowest\n * loaded index that can still change (`build-live-refetch-query.ts`). The package computes both values.\n */\nexport interface ChangedMessagesRequest {\n /** generic — the highest message id loaded */\n highestKnownId: number;\n /** generic — the lowest index that can still change, never below the newest page */\n fromIndex: number;\n /** generic — the page size of the read; the binding's default (host application: 50, as the chat sends) when omitted */\n pageSize?: number;\n}\n\n/** The answer of a changed-messages read; it says nothing about older pages (the store keeps `hasMoreOlder`). */\nexport interface ChangedConversationMessages {\n /** generic */\n messages: AgentsConversationMessage[];\n}\n\n/** The answer of a start: the new conversation with what it holds so far, or the reopened one of the entry point. */\nexport interface StartedConversation {\n /** generic */\n conversation: AgentsConversation;\n /** generic — the stored messages of a new conversation; empty for a reopened one (the chat reads its newest page) */\n messages: AgentsConversationMessage[];\n /** generic — the start returned the recent conversation of its entry point; nothing was appended, no turn started */\n reopenedExisting: boolean;\n}\n\n/** The answer of a post: the conversation's messages so far (the reply arrives through live updates). */\nexport interface PostedMessage {\n /** generic */\n messages: AgentsConversationMessage[];\n}\n\n/** A model-visible, owner-invisible note into a conversation (a widget click, a render report, a correction). */\nexport interface ConversationSignal {\n /** generic */\n text: string;\n /** generic — the signal source; the server's default applies when omitted (a widget click) */\n source?: string;\n}\n\n/** How a list read pages and which conversations it includes. */\nexport interface ConversationListQueryOptions {\n /** generic — include conversations that have no summary (title) yet, e.g. a just-started one */\n includeUnsummarized?: boolean;\n /** generic — the host application's history reads the newest 50 */\n paging?: { top?: number; skip?: number };\n}\n\n/** The size of a search answer. */\nexport interface ConversationSearchOptions {\n /** generic — the most groups (or hits) returned; the host application's chat asks for 10 */\n limit?: number;\n /** generic — the excerpt length in characters; the host application's chat asks for 420 */\n excerptLength?: number;\n}\n\n/** app extension — the reconstructed request a model call was sent with (the admin details panel); opaque data. */\nexport type ConversationMessageSentPayload = unknown[];\n\n/** One matched message of a message search (`SemanticSearchHit`). */\nexport interface ConversationMessageSearchHit {\n /** generic */\n messageId: number | null;\n /** generic */\n conversationId: number | null;\n /** generic */\n indexInConversation: number | null;\n /** generic */\n role: string | null;\n /** generic */\n excerpt: string;\n /** generic */\n relevance: number;\n /** generic */\n created: string | null;\n /** generic */\n updated: string | null;\n /** generic — the conversation's title */\n title: string | null;\n}\n\n/** One conversation of a search across conversations, with its matched messages (`AIConversationSearchGroup`). */\nexport interface ConversationSearchGroup {\n /** generic */\n conversationId: number | null;\n /** generic */\n title: string | null;\n /** generic */\n summary: string | null;\n /** generic */\n created: string | null;\n /** generic */\n updated: string | null;\n /** generic */\n relevance: number;\n /** generic */\n hits: ConversationMessageSearchHit[];\n}\n\n/** One generated line of what the agent is doing (`AgentActivityLine`). */\nexport interface ConversationActivityElement {\n /** generic — the row the line narrates; the merge key */\n toolCallMessageId: number;\n /** generic */\n batchId: string | null;\n /** generic */\n state: \"queued\" | \"running\" | \"finished\" | \"failed\" | \"skipped\" | null;\n /** generic */\n kind: \"tool\" | \"delegation\" | \"background\" | \"long\" | \"turnStart\" | null;\n /** generic */\n childCount: number | null;\n /** generic — the 1-based delegation helper that ran the step; null for the assistant's own steps */\n helperIndex: number | null;\n /** generic — the sentence; null while the narrator has not answered */\n line: string | null;\n}\n\n/** The activity of one turn (`GET …/activity`). */\nexport interface ConversationActivity {\n /** generic — the message that started the narrated turn */\n turnMessageId: number | null;\n /** generic */\n turnRunning: boolean;\n /** generic */\n elements: ConversationActivityElement[];\n}\n\n/** A model the owner (host application: an impersonating admin) may pick for the next turns (`AgentCustomerSelectableModel`). */\nexport interface SelectableModel {\n /** generic — the name a send carries */\n name: string;\n /** generic */\n displayName: string;\n /** generic */\n description: string;\n /** generic */\n strengthLevel: number | null;\n /** generic */\n speedLevel: number | null;\n /** app extension — the host application's credit multiplier of the model */\n creditsMultiplier?: number | null;\n}\n\n// ── Live updates ─────────────────────────────────────────────────────────────────────────────────────────────────\n\n/**\n * What a live update carries: a claim check, never content. The chat reads the conversation again when\n * `changeCounter` advances, at once on a terminal status, and until `lastMessageId` is loaded.\n */\nexport interface ConversationChangeSignal {\n /** generic — advances on every change of the conversation */\n changeCounter: number;\n /** generic — the newest message the server wrote; null before the first */\n lastMessageId: number | null;\n /** generic */\n runStatus: ConversationRunStatus | null;\n /** generic — advances when the title or summary was generated again (a light metadata read is enough then) */\n summaryUpdatedAt: number | null;\n /**\n * generic — the binding produced this emission from a run-state read, not from a pushed change (a binding without\n * push, or a push transport whose path is not proven readable, or a check that corrects a stale status). It carries\n * only `runStatus` as news: `changeCounter`, `lastMessageId` and `summaryUpdatedAt` repeat the last pushed values\n * (before any push: a counter that counts down from -1, never equal to a pushed one). The package sets the status\n * from it and reads the conversation again on a status change while the push path is not alive (`isPushPathAlive`).\n */\n isStatusCheck?: boolean;\n /**\n * generic, on a status-check emission — the binding's push path delivers: a pushed change arrived and the path was not\n * denied since. False after a denied read until a push arrives again; then the status checks are the live path and a\n * status change reads the conversation again. A binding without push leaves it out: the package then counts the\n * pushed changes it saw itself.\n */\n isPushPathAlive?: boolean;\n}\n\n/** Why a subscription ended for good: the conversation was deleted or is no longer readable, or the account lost access. */\nexport type ConversationSubscriptionEndReason = \"gone\" | \"forbidden\";\n\n// ── Errors ───────────────────────────────────────────────────────────────────────────────────────────────────────\n\n/**\n * What went wrong, as the chat tells the owner. The codes the chat's error handling reads today map\n * onto it: `AI_BUDGET_REACHED` → `budget`, HTTP 429 / `RATE_LIMIT_EXCEEDED` → `rateLimit`, `CONVERSATION_CLOSED` →\n * `closed`, the not-found and no-access answers → `notFound` / `forbidden` (`conversation-not-found.ts`), no answer or a\n * server failure → `network`, everything else (a refused retry, a cancelled request) → `other` with its `code`.\n */\nexport type AgentsClientErrorKind = \"budget\" | \"rateLimit\" | \"closed\" | \"notFound\" | \"forbidden\" | \"network\" | \"other\";\n\n/** The code `other` carries for a request the client cancelled; the chat shows no error card for it. */\nexport const AGENTS_CLIENT_ERROR_CODE_REQUEST_ABORTED = \"REQUEST_ABORTED\";\n\n/** The one error shape every port function rejects with. */\nexport class AgentsClientError extends Error {\n readonly kind: AgentsClientErrorKind;\n /** The server's stable error code, or the client's own code (`REQUEST_ABORTED`); null when none arrived. */\n readonly code: string | null;\n /** Seconds until a quota frees up again; set only for `rateLimit`. */\n readonly retryAfterSeconds: number | null;\n /** The HTTP status when one arrived (not part of the contract's shape; kept for diagnostics). */\n readonly httpStatus: number | null;\n\n constructor(\n kind: AgentsClientErrorKind,\n details: { message?: string; code?: string | null; retryAfterSeconds?: number | null; httpStatus?: number | null } = {},\n ) {\n super(details.message ?? kind);\n this.name = \"AgentsClientError\";\n this.kind = kind;\n this.code = details.code ?? null;\n this.retryAfterSeconds = kind === \"rateLimit\" ? (details.retryAfterSeconds ?? null) : null;\n this.httpStatus = details.httpStatus ?? null;\n }\n}\n\nexport const isAgentsClientError = (value: unknown): value is AgentsClientError => value instanceof AgentsClientError;\n"],"names":[],"mappings":";AAiDO,MAAM,oDAAqF;AAAA,EAChG;AAAA,EACA;AACF;AAGO,MAAM,sCAAsC,CACjD,aACiC;AACjC,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,aAAa,SAAU,QAAO;AAClC,MAAI,aAAa,aAAa,aAAa,wBAAwB,aAAa,SAAU,QAAO;AACjG,SAAO;AACT;AAkWO,MAAM,2CAA2C;AAGjD,MAAM,0BAA0B,MAAM;AAAA,EAS3C,YACE,MACA,UAAqH,IACrH;AACA,UAAM,QAAQ,WAAW,IAAI;AAC7B,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ,QAAQ;AAC5B,SAAK,oBAAoB,SAAS,cAAe,QAAQ,qBAAqB,OAAQ;AACtF,SAAK,aAAa,QAAQ,cAAc;AAAA,EAC1C;AACF;AAEO,MAAM,sBAAsB,CAAC,UAA+C,iBAAiB;"}
|
|
1
|
+
{"version":3,"file":"contract.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
package/dist/image-render.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import React, { useRef, useEffect } from "react";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
4
|
+
import { G as resolveWidgetName, j as conversationWidgetRegistry } from "./chunks/widget-name-resolver-BGN3HpoG.js";
|
|
5
|
+
import { H } from "./chunks/widget-name-resolver-BGN3HpoG.js";
|
|
6
|
+
import { i as ConversationWidgetRenderContext, K as WidgetImageReadinessContext, R as ReadOnlyConversationWidgetProvider, L as WidgetImageRenderContext, N as WidgetImageLiveHydrationContext, j as buildWidgetAgentSpaceKeyProps } from "./chunks/WebSearchResultsConversationWidget-as3p5bHw.js";
|
|
7
|
+
import { O, P, Q } from "./chunks/WebSearchResultsConversationWidget-as3p5bHw.js";
|
|
8
8
|
import { r as readWidgetImageReadiness, p as publishWidgetImageReadiness } from "./chunks/widget-image-readiness-signal-D3ci53xr.js";
|
|
9
9
|
import { W, a } from "./chunks/widget-image-readiness-signal-D3ci53xr.js";
|
|
10
10
|
import axios from "axios";
|
|
@@ -323,13 +323,13 @@ export {
|
|
|
323
323
|
installLiveWidgetRequests,
|
|
324
324
|
isGoogleMapCapture,
|
|
325
325
|
publishWidgetImageReadiness,
|
|
326
|
-
|
|
327
|
-
|
|
326
|
+
H as readComposedViewDataTimes,
|
|
327
|
+
O as readFrozenHydration,
|
|
328
328
|
readWidgetImageReadiness,
|
|
329
329
|
a as readWidgetImageReadinessGlobalNames,
|
|
330
330
|
registerStaticWidgetImageComponents,
|
|
331
331
|
staticWidgetImageComponents,
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
P as useWidgetImageLiveHydration,
|
|
333
|
+
Q as useWidgetImageRender
|
|
334
334
|
};
|
|
335
335
|
//# sourceMappingURL=image-render.js.map
|