@seed-ship/mcp-ui-solid 4.3.8 → 5.0.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,80 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.0.0] - 2026-04-14
9
+
10
+ ### Major release — synchronized with `@seed-ship/mcp-ui-spec` 5.0.0 and `@seed-ship/mcp-ui-cli` 5.0.0
11
+
12
+ ### Breaking
13
+ - **`ClarificationEvent.options[].file_id` removed from the TypeScript type** (deprecated in v4.3.9, removed in v5.0.0 as announced). The `clarificationToPromptConfig()` helper still migrates runtime `file_id` into `metadata.file_id` transparently, so host apps receiving payloads from older servers continue to work without upgrade pressure. New code should emit `metadata: { file_id }` directly.
14
+ - **`ChatPromptConfig.type = 'select'` and `SelectPromptConfig`** — already removed in 4.3.9 (the variant was declared in 4.0 but `ChatPrompt.tsx` never rendered it). Listed here for the v5 breaking recap.
15
+
16
+ ### Changed
17
+ - Version bump 4.3.9 → 5.0.0 for the synchronized monorepo major release.
18
+ - Root `README.md` + monorepo `CHANGELOG.md` consolidated with the full 4.x → 5.0.0 history.
19
+
20
+ ### Documented
21
+ - v5.1.0 scope: `createScratchpadStore()` factory (multi-instance scratchpad), re-entrant `showChatPrompt` (auto-reject / FIFO queue), `ChatPrompt` AbortSignal wiring, `optionRenderer?` slot on `ChoicePromptConfig`.
22
+
23
+ ## [4.3.9] - 2026-04-14
24
+
25
+ ### Added — Sprint 52 multi-agent primitives
26
+
27
+ #### Type additions (all non-breaking)
28
+ - **`ChoicePromptConfig.options[].metadata?: Record<string, unknown>`** (G1) — free-form metadata on prompt choices (confidence, source, tags...). Opaque to the default renderer, preserved through `showChatPrompt → ChatPromptResponse` roundtrip. Use a custom ChoiceBody wrapper to display it.
29
+ - **`ClarificationEvent.options[].metadata?: Record<string, unknown>`** (G3) — same extension point on clarification events. Legacy `file_id?: number` deprecated in JSDoc (removal in v5.0.0).
30
+ - **`ClarificationEvent.type?: string`** (G3) — free-form type tag for host routing (e.g. `'intent_disambiguate'`, `'file_select'`).
31
+
32
+ #### New helper: `clarificationToPromptConfig()` (G11)
33
+ - Universal bridge converting `ClarificationEvent` → `ChatPromptConfig` for any MCP-UI consumer
34
+ - Transparent `file_id` legacy migration into `metadata.file_id`
35
+ - Explicit `metadata.file_id` takes precedence over legacy field
36
+ - Agnostic — zero Deposium-specific concepts
37
+ - Exported from `@seed-ship/mcp-ui-solid`
38
+
39
+ #### New testing entry point: `createMockChatBus()` (G6)
40
+ - New sub-module `src/testing/` with `createMockChatBus({ promptResponses, onShowChatPrompt })`
41
+ - Pre-programs `showChatPrompt` responses in FIFO order for flow tests
42
+ - Spy hook on `onShowChatPrompt` for assertions
43
+ - Throws helpful error when the queue is exhausted
44
+
45
+ ### Removed — dead code (G7)
46
+ - **BREAKING (theoretical, never implemented)**: `ChatPromptConfig.type = 'select'` removed from the union type. `SelectPromptConfig` interface removed + export dropped from `src/index.ts`. The `'select'` variant was declared in v4.0 but `ChatPrompt.tsx` never had a rendering branch — it was dead code. Use `'form'` with a single `select` field, or `'choice'` for visual picks.
47
+
48
+ ### Documented — known limitations (G5, G8, G9, G10)
49
+ - **`ChatPromptResponse.dismissed`** (G5) — full semantics in JSDoc: X icon/Cancel → `true`, explicit click/submit → `undefined`, AbortSignal → Promise rejection (host responsibility until v4.4.0).
50
+ - **Scratchpad store is a singleton** (G8) — two `ScratchpadPanel` instances share state. Documented as known limitation. Factory `createScratchpadStore()` planned for v4.4.0.
51
+ - **`showChatPrompt` is not re-entrant** (G9) — calling it while another prompt is active leaks the previous Promise. Documented in JSDoc. Host apps must queue/dismiss manually. Auto-reject planned for v4.4.0.
52
+ - **`correlationId` is host-propagated** (G10) — README recipe. mcp-ui does not auto-forward the ID; host SSE parsers must thread it into subsequent event emissions.
53
+
54
+ ### Documented — integration recipes (G2)
55
+ - **Bridging external clarification events** — new README section showing the `onClarificationNeeded → clarificationToPromptConfig → showChatPrompt` flow, with metadata preservation and opaque type tags.
56
+
57
+ ### Documentation catch-up
58
+ - Backfilled `CHANGELOG.md` entries for 4.3.6, 4.3.7, 4.3.8 (previously missing).
59
+
60
+ ## [4.3.8] - 2026-04-11
61
+
62
+ ### Added
63
+ - **Search term highlighting** — matched query terms are wrapped in `<mark>` tags across all visible cells. `bg-yellow-200` in light mode, `bg-[#222F49]` in dark mode. New `highlightQuery` helper skips HTML tag content to preserve markup.
64
+
65
+ ### Fixed
66
+ - **Fullscreen phantom scrollbar** — removed `h-full` from the table wrapper in expanded mode so it shrinks to content. No more empty space or unnecessary scrollbar when rows don't fill the viewport.
67
+
68
+ ## [4.3.7] - 2026-04-11
69
+
70
+ ### Changed
71
+ - **Prev/Next pagination is now the default** — replaced the progressive "show more" mode with unified Prev/Next navigation for consistency between chat and fullscreen views.
72
+ - **Page size selector in fullscreen** — dropdown with 10 / 30 / 60 / 100 / All options.
73
+ - **Fullscreen table fills the viewport** via `calc(100vh - 180px)`.
74
+ - **Header contrast** — thead background bumped from `bg-gray-50` to `bg-gray-100` for better visibility in chat view.
75
+
76
+ ## [4.3.6] - 2026-04-11
77
+
78
+ ### Fixed
79
+ - **Opaque sticky header** — changed from `bg-gray-900/50` (translucent) to `bg-gray-900` (opaque) so the header remains readable over chat bubbles behind it.
80
+ - **Compact search input** — `max-w-xs min-w-[200px]` instead of `w-full` so the filter field doesn't span the entire table width.
81
+
8
82
  ## [4.3.5] - 2026-04-11
9
83
 
10
84
  ### Fixed — Sticky Table Header on Scroll
package/README.md CHANGED
@@ -5,6 +5,21 @@ SolidJS components + chat toolkit for MCP-generated UI. Part of the [MCP UI ecos
5
5
  [![npm version](https://img.shields.io/npm/v/@seed-ship/mcp-ui-solid.svg)](https://www.npmjs.com/package/@seed-ship/mcp-ui-solid)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
+ ## What's New in v5.0.0
9
+
10
+ Synchronized major release — `@seed-ship/mcp-ui-solid`, `@seed-ship/mcp-ui-spec`, and `@seed-ship/mcp-ui-cli` all move to 5.0.0.
11
+
12
+ **Sprint 52 multi-agent primitives** (new)
13
+ - `ChoicePromptConfig.options[].metadata?` — opaque metadata preserved through the `showChatPrompt` roundtrip (confidence, source tags, etc.).
14
+ - `clarificationToPromptConfig()` — universal `ClarificationEvent → ChatPromptConfig` bridge. Legacy runtime `file_id` auto-migrated into `metadata.file_id`.
15
+ - `createMockChatBus()` — new `src/testing/` entry point with FIFO prompt responses and spy hooks. Test agent flows without rendering any UI.
16
+
17
+ **Breaking**
18
+ - `ClarificationEvent.options[].file_id` removed from the TypeScript type (was deprecated in v4.3.9). Runtime fallback still works via `clarificationToPromptConfig()`.
19
+ - `ChatPromptConfig.type = 'select'` / `SelectPromptConfig` removed (dead code — never had a rendering branch).
20
+
21
+ Everything rolled up from the 4.x series is documented in the previous section below.
22
+
8
23
  ## What's New in v4.3
9
24
 
10
25
  - **Prefilled Forms** — Fields render with pre-populated values + source indicators (detected/inferred/default/user)
@@ -347,6 +362,65 @@ function ChatInterface() {
347
362
 
348
363
  See [Chat Bus documentation](https://github.com/theseedship/mcp-ui#chat-bus--agent-interactions-experimental) for the full event/command reference.
349
364
 
365
+ ### Bridging external clarification events (v4.3.9)
366
+
367
+ When your MCP server emits a clarification event via SSE (e.g. a `_pause`
368
+ frame asking the user to disambiguate intent), convert it to a `ChatPrompt`
369
+ using the universal `clarificationToPromptConfig` helper — no app-specific
370
+ glue required:
371
+
372
+ ```tsx
373
+ import { clarificationToPromptConfig } from '@seed-ship/mcp-ui-solid'
374
+
375
+ // In your SSE parser, when you decode a clarification frame:
376
+ bus.events.emit('onClarificationNeeded', {
377
+ streamKey: 'main',
378
+ clarification: {
379
+ question: 'Which space do you mean?',
380
+ options: [
381
+ { value: 'sp-1', label: 'Immobilier', metadata: { confidence: 0.9 } },
382
+ { value: 'sp-2', label: 'Santé', metadata: { confidence: 0.6 } },
383
+ ],
384
+ type: 'intent_disambiguate', // opaque tag for host routing
385
+ },
386
+ })
387
+
388
+ // Wire the event to a prompt:
389
+ bus.events.on('onClarificationNeeded', async ({ clarification }) => {
390
+ const response = await bus.commands.exec(
391
+ 'showChatPrompt',
392
+ clarificationToPromptConfig(clarification)
393
+ )
394
+ // POST response.value to your /api/agent-resume endpoint, etc.
395
+ })
396
+ ```
397
+
398
+ Legacy `option.file_id` is automatically migrated into `metadata.file_id`.
399
+ Arbitrary `metadata` (confidence scores, source tags, ...) flows through
400
+ unchanged and can be rendered by a custom `ChoiceBody` wrapper.
401
+
402
+ ### ChatPromptResponse — dismissed / aborted / answered (v4.3.9)
403
+
404
+ Every `ChatPrompt` resolves to one of three outcomes:
405
+
406
+ | Outcome | How | `response.dismissed` | Promise |
407
+ |---------|-----|----------------------|---------|
408
+ | Explicit answer | Click a choice / submit a form | `undefined` | resolves |
409
+ | Dismissed | Click the X icon, click Cancel (confirm type) | `true` | resolves |
410
+ | Aborted | Host app rejects the Promise via `AbortSignal` | *(never resolves)* | rejects |
411
+
412
+ > **v4.3.9 limitation:** `ChatPrompt` does not listen to `AbortSignal` yet.
413
+ > Host apps are responsible for wiring `signal.addEventListener('abort', ...)`
414
+ > to `Promise.reject`. A built-in helper lands in v4.4.0.
415
+
416
+ ### correlationId — host-propagated (v4.3.9)
417
+
418
+ `ChatEventBase.correlationId` is opaque to mcp-ui. When an agent calls
419
+ `sendPrompt('...')` the returned string is a correlation ID — the host app's
420
+ SSE parser must forward this value into every subsequent event emission
421
+ (`onToken`, `onStreamEnd`, etc.) so agents can match responses back to their
422
+ original prompts. mcp-ui does not auto-propagate it across the bus.
423
+
350
424
  ## ScratchpadPanel — HITL/AITL Shared Workspace (`@experimental`)
351
425
 
352
426
  A shared workspace where agent and human collaborate in real-time. 18 section types:
@@ -438,8 +512,12 @@ import {
438
512
  ChatBusProvider, useChatBus,
439
513
  dispatchScratchpad, useScratchpadState,
440
514
  createChatBus, createEventEmitter, createCommandHandler,
515
+ clarificationToPromptConfig, // v4.3.9 — universal ClarificationEvent → ChatPromptConfig bridge
441
516
  } from '@seed-ship/mcp-ui-solid'
442
517
 
518
+ // Testing utilities (v4.3.9)
519
+ import { createMockChatBus } from '@seed-ship/mcp-ui-solid'
520
+
443
521
  // Validation + Security
444
522
  import {
445
523
  validateComponent, validateLayout,
package/dist/index.cjs CHANGED
@@ -52,6 +52,7 @@ const validation = require("./services/validation.cjs");
52
52
  const componentRegistry = require("./services/component-registry.cjs");
53
53
  const chatBus = require("./services/chat-bus.cjs");
54
54
  const dataValidator = require("./services/data-validator.cjs");
55
+ const index = require("./testing/index.cjs");
55
56
  exports.UIResourceRenderer = UIResourceRenderer.UIResourceRenderer;
56
57
  exports.StreamingUIRenderer = StreamingUIRenderer.StreamingUIRenderer;
57
58
  exports.GenerativeUIErrorBoundary = GenerativeUIErrorBoundary.GenerativeUIErrorBoundary;
@@ -108,9 +109,11 @@ exports.validateComponent = validation.validateComponent;
108
109
  exports.validateIframeDomain = validation.validateIframeDomain;
109
110
  exports.validateLayout = validation.validateLayout;
110
111
  exports.ComponentRegistry = componentRegistry.ComponentRegistry;
112
+ exports.clarificationToPromptConfig = chatBus.clarificationToPromptConfig;
111
113
  exports.createChatBus = chatBus.createChatBus;
112
114
  exports.createCommandHandler = chatBus.createCommandHandler;
113
115
  exports.createEventEmitter = chatBus.createEventEmitter;
114
116
  exports.mergeScratchpadSections = chatBus.mergeScratchpadSections;
115
117
  exports.validateAgainstSource = dataValidator.validateAgainstSource;
118
+ exports.createMockChatBus = index.createMockChatBus;
116
119
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.d.cts CHANGED
@@ -72,5 +72,8 @@ export type { AutocompleteContextValue, AutocompleteProviderProps, } from './con
72
72
  export { createGroqPlugin, createSupabasePlugin, createRestPlugin, } from './plugins';
73
73
  export type { UIComponent, UILayout, GridPosition, ComponentType, RendererError, ChartComponentParams, TableComponentParams, MetricComponentParams, TextComponentParams, ActionComponentParams, GridComponentParams, FormFieldOption, FormFieldType, FormFieldParams, FormComponentParams, ShowWhenOperator, ShowWhenCondition, ActionRequestBase, ActionResultBase, ActionLifecycleCallbacks, ModalSize, ModalComponentParams, ActionGroupLayout, ActionGroupGap, ActionGroupParams, GalleryImage, ImageGalleryParams, VideoComponentParams, CodeComponentParams, MapMarker, MapComponentParams, MapPopupConfig, MapGeoJSONStyle, MapLayer, MapPMTilesConfig, IframePolicy, ValidationOptions, ResizeConstraints, DragDropConfig, DragEventData, DraggableGridItemProps as DraggableGridItemPropsType, AutocompleteResultType, AutocompleteOption, AutocompleteResult, AutocompleteContext, AutocompletePlugin, GroqPluginConfig, SupabasePluginConfig, DuckDBPluginConfig, RestPluginConfig, FieldAutocompleteConfig, AutocompleteProviderConfig, } from './types';
74
74
  export { validateComponent, validateLayout, validateIframeDomain, getIframeSandbox, DEFAULT_RESOURCE_LIMITS, DEFAULT_IFRAME_DOMAINS, TRUSTED_IFRAME_DOMAINS, ComponentRegistry, createEventEmitter, createCommandHandler, createChatBus, mergeScratchpadSections, validateAgainstSource, } from './services';
75
- export type { ChatEventBase, ChatEvents, ChatCommands, ChatBus, ChatEventEmitter, ChatCommandHandler, EventSubscribeOptions, ChatPromptConfig, ChatPromptResponse, ChoicePromptConfig, ConfirmPromptConfig, FormPromptConfig, SelectPromptConfig, SuggestionItem, AgentContext, BriefingEvent, BriefingSection, ScratchpadState, ScratchpadSection, ScratchpadEvent, StreamDoneMetadata, ChatError, Citation, ToolCallEvent, ClarificationEvent, DataValidation, LLMNumber, HallucinatedNumber, DataValidationOptions, VerifiedTextContent, DataPreviewColumn, DataPreviewContent, MapSectionContent, AgentCardContent, SplitStepperContent, AgentHandoffContent, BriefingDiffContent, } from './types/chat-bus';
75
+ export { clarificationToPromptConfig } from './services/chat-bus';
76
+ export { createMockChatBus } from './testing';
77
+ export type { MockChatBusOptions } from './testing';
78
+ export type { ChatEventBase, ChatEvents, ChatCommands, ChatBus, ChatEventEmitter, ChatCommandHandler, EventSubscribeOptions, ChatPromptConfig, ChatPromptResponse, ChoicePromptConfig, ConfirmPromptConfig, FormPromptConfig, SuggestionItem, AgentContext, BriefingEvent, BriefingSection, ScratchpadState, ScratchpadSection, ScratchpadEvent, StreamDoneMetadata, ChatError, Citation, ToolCallEvent, ClarificationEvent, DataValidation, LLMNumber, HallucinatedNumber, DataValidationOptions, VerifiedTextContent, DataPreviewColumn, DataPreviewContent, MapSectionContent, AgentCardContent, SplitStepperContent, AgentHandoffContent, BriefingDiffContent, } from './types/chat-bus';
76
79
  //# sourceMappingURL=index.d.ts.map
package/dist/index.d.ts CHANGED
@@ -72,5 +72,8 @@ export type { AutocompleteContextValue, AutocompleteProviderProps, } from './con
72
72
  export { createGroqPlugin, createSupabasePlugin, createRestPlugin, } from './plugins';
73
73
  export type { UIComponent, UILayout, GridPosition, ComponentType, RendererError, ChartComponentParams, TableComponentParams, MetricComponentParams, TextComponentParams, ActionComponentParams, GridComponentParams, FormFieldOption, FormFieldType, FormFieldParams, FormComponentParams, ShowWhenOperator, ShowWhenCondition, ActionRequestBase, ActionResultBase, ActionLifecycleCallbacks, ModalSize, ModalComponentParams, ActionGroupLayout, ActionGroupGap, ActionGroupParams, GalleryImage, ImageGalleryParams, VideoComponentParams, CodeComponentParams, MapMarker, MapComponentParams, MapPopupConfig, MapGeoJSONStyle, MapLayer, MapPMTilesConfig, IframePolicy, ValidationOptions, ResizeConstraints, DragDropConfig, DragEventData, DraggableGridItemProps as DraggableGridItemPropsType, AutocompleteResultType, AutocompleteOption, AutocompleteResult, AutocompleteContext, AutocompletePlugin, GroqPluginConfig, SupabasePluginConfig, DuckDBPluginConfig, RestPluginConfig, FieldAutocompleteConfig, AutocompleteProviderConfig, } from './types';
74
74
  export { validateComponent, validateLayout, validateIframeDomain, getIframeSandbox, DEFAULT_RESOURCE_LIMITS, DEFAULT_IFRAME_DOMAINS, TRUSTED_IFRAME_DOMAINS, ComponentRegistry, createEventEmitter, createCommandHandler, createChatBus, mergeScratchpadSections, validateAgainstSource, } from './services';
75
- export type { ChatEventBase, ChatEvents, ChatCommands, ChatBus, ChatEventEmitter, ChatCommandHandler, EventSubscribeOptions, ChatPromptConfig, ChatPromptResponse, ChoicePromptConfig, ConfirmPromptConfig, FormPromptConfig, SelectPromptConfig, SuggestionItem, AgentContext, BriefingEvent, BriefingSection, ScratchpadState, ScratchpadSection, ScratchpadEvent, StreamDoneMetadata, ChatError, Citation, ToolCallEvent, ClarificationEvent, DataValidation, LLMNumber, HallucinatedNumber, DataValidationOptions, VerifiedTextContent, DataPreviewColumn, DataPreviewContent, MapSectionContent, AgentCardContent, SplitStepperContent, AgentHandoffContent, BriefingDiffContent, } from './types/chat-bus';
75
+ export { clarificationToPromptConfig } from './services/chat-bus';
76
+ export { createMockChatBus } from './testing';
77
+ export type { MockChatBusOptions } from './testing';
78
+ export type { ChatEventBase, ChatEvents, ChatCommands, ChatBus, ChatEventEmitter, ChatCommandHandler, EventSubscribeOptions, ChatPromptConfig, ChatPromptResponse, ChoicePromptConfig, ConfirmPromptConfig, FormPromptConfig, SuggestionItem, AgentContext, BriefingEvent, BriefingSection, ScratchpadState, ScratchpadSection, ScratchpadEvent, StreamDoneMetadata, ChatError, Citation, ToolCallEvent, ClarificationEvent, DataValidation, LLMNumber, HallucinatedNumber, DataValidationOptions, VerifiedTextContent, DataPreviewColumn, DataPreviewContent, MapSectionContent, AgentCardContent, SplitStepperContent, AgentHandoffContent, BriefingDiffContent, } from './types/chat-bus';
76
79
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -48,8 +48,9 @@ import { createSupabasePlugin } from "./plugins/supabase.js";
48
48
  import { createRestPlugin } from "./plugins/rest.js";
49
49
  import { DEFAULT_IFRAME_DOMAINS, DEFAULT_RESOURCE_LIMITS, TRUSTED_IFRAME_DOMAINS, getIframeSandbox, validateComponent, validateIframeDomain, validateLayout } from "./services/validation.js";
50
50
  import { ComponentRegistry } from "./services/component-registry.js";
51
- import { createChatBus, createCommandHandler, createEventEmitter, mergeScratchpadSections } from "./services/chat-bus.js";
51
+ import { clarificationToPromptConfig, createChatBus, createCommandHandler, createEventEmitter, mergeScratchpadSections } from "./services/chat-bus.js";
52
52
  import { validateAgainstSource } from "./services/data-validator.js";
53
+ import { createMockChatBus } from "./testing/index.js";
53
54
  export {
54
55
  AgentCard,
55
56
  AgentHandoff,
@@ -80,10 +81,12 @@ export {
80
81
  TRUSTED_IFRAME_DOMAINS,
81
82
  UIResourceRenderer,
82
83
  VerifiedText,
84
+ clarificationToPromptConfig,
83
85
  createChatBus,
84
86
  createCommandHandler,
85
87
  createEventEmitter,
86
88
  createGroqPlugin,
89
+ createMockChatBus,
87
90
  createRestPlugin,
88
91
  createSupabasePlugin,
89
92
  dispatchScratchpad,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -134,6 +134,29 @@ function mergeScratchpadSections(existing, incoming, mode = "replace") {
134
134
  return incoming;
135
135
  }
136
136
  }
137
+ function clarificationToPromptConfig(event) {
138
+ return {
139
+ type: "choice",
140
+ title: event.question,
141
+ config: {
142
+ options: event.options.map((opt) => {
143
+ const merged = { ...opt.metadata ?? {} };
144
+ const legacyFileId = opt.file_id;
145
+ if (legacyFileId !== void 0 && merged.file_id === void 0) {
146
+ merged.file_id = legacyFileId;
147
+ }
148
+ return {
149
+ value: opt.value,
150
+ label: opt.label,
151
+ // Only include metadata if non-empty (keeps payloads clean)
152
+ ...Object.keys(merged).length > 0 ? { metadata: merged } : {}
153
+ };
154
+ }),
155
+ layout: "vertical"
156
+ }
157
+ };
158
+ }
159
+ exports.clarificationToPromptConfig = clarificationToPromptConfig;
137
160
  exports.createChatBus = createChatBus;
138
161
  exports.createCommandHandler = createCommandHandler;
139
162
  exports.createEventEmitter = createEventEmitter;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-bus.cjs","sources":["../../src/services/chat-bus.ts"],"sourcesContent":["/**\n * Chat Bus — Event Emitter + Command Handler\n * v2.4.0: Core primitives for the chat event/command bus\n *\n * @experimental — This API may change without major bump until v2.5.0.\n */\n\nimport type {\n ChatEvents,\n ChatCommands,\n ChatEventEmitter,\n ChatCommandHandler,\n ChatBus,\n EventSubscribeOptions,\n ScratchpadSection,\n} from '../types/chat-bus'\n\n// ─── Event Emitter ───────────────────────────────────────────\n\ninterface Listener<F extends (...args: any[]) => any> {\n handler: F\n options?: EventSubscribeOptions\n throttledHandler?: F\n}\n\n/**\n * Create a typed event emitter with throttle and streamKey filtering support.\n *\n * @experimental\n *\n * @example\n * const emitter = createEventEmitter<ChatEvents>()\n * const unsub = emitter.on('onToken', (event) => console.log(event.token), { throttle: 100 })\n * emitter.emit('onToken', { streamKey: 'abc', token: 'hello' })\n * unsub()\n */\nexport function createEventEmitter(): ChatEventEmitter {\n const listeners = new Map<string, Set<Listener<any>>>()\n\n interface ThrottledFn<F> {\n fn: F\n cancel: () => void\n }\n\n function createThrottled<F extends (...args: any[]) => void>(fn: F, ms: number): ThrottledFn<F> {\n let lastCall = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let lastArgs: any[] | null = null\n let cancelled = false\n\n const throttled = ((...args: any[]) => {\n if (cancelled) return\n lastArgs = args\n const now = Date.now()\n const remaining = ms - (now - lastCall)\n\n if (remaining <= 0) {\n if (timer) { clearTimeout(timer); timer = null }\n lastCall = now\n fn(...args)\n } else if (!timer) {\n timer = setTimeout(() => {\n lastCall = Date.now()\n timer = null\n if (lastArgs && !cancelled) {\n try { fn(...lastArgs) } catch (err) { console.error('[ChatBus] Error in throttled handler:', err) }\n }\n }, remaining)\n }\n }) as F\n\n return {\n fn: throttled,\n cancel: () => { cancelled = true; if (timer) { clearTimeout(timer); timer = null } },\n }\n }\n\n return {\n on(event, handler, options) {\n if (!listeners.has(event as string)) {\n listeners.set(event as string, new Set())\n }\n\n const listener: Listener<typeof handler> = { handler, options }\n\n // Apply throttle if requested\n let throttleHandle: ThrottledFn<typeof handler> | null = null\n if (options?.throttle && options.throttle > 0) {\n throttleHandle = createThrottled(handler, options.throttle)\n listener.throttledHandler = throttleHandle.fn\n }\n\n listeners.get(event as string)!.add(listener)\n\n // Return unsubscribe function — cancels pending throttle timers\n return () => {\n throttleHandle?.cancel()\n listeners.get(event as string)?.delete(listener)\n }\n },\n\n emit(event, ...args) {\n const set = listeners.get(event as string)\n if (!set) return\n\n for (const listener of set) {\n // StreamKey filtering: skip if listener wants a specific streamKey\n // For most events args[0] has streamKey; for onCustomEvent args[1] has it\n if (listener.options?.streamKey) {\n let streamKeyArg: unknown\n for (const arg of args) {\n if (arg && typeof arg === 'object' && 'streamKey' in (arg as any)) {\n streamKeyArg = (arg as any).streamKey\n break\n }\n }\n if (streamKeyArg !== undefined && streamKeyArg !== listener.options.streamKey) continue\n }\n\n const fn = listener.throttledHandler || listener.handler\n try {\n fn(...args)\n } catch (err) {\n console.error(`[ChatBus] Error in ${event as string} handler:`, err)\n }\n }\n },\n\n clear() {\n listeners.clear()\n },\n } as ChatEventEmitter\n}\n\n// ─── Command Handler ─────────────────────────────────────────\n\n/**\n * Create a typed command handler. The host app registers handlers,\n * agents execute commands.\n *\n * @experimental\n *\n * @example\n * const commands = createCommandHandler<ChatCommands>()\n * commands.handle('injectPrompt', (text) => setInputValue(text))\n * commands.exec('injectPrompt', 'Hello world')\n */\nexport function createCommandHandler(): ChatCommandHandler {\n const handlers = new Map<string, (...args: any[]) => any>()\n\n return {\n handle(command, handler) {\n handlers.set(command as string, handler)\n },\n\n exec(command, ...args) {\n const handler = handlers.get(command as string)\n if (!handler) {\n console.warn(`[ChatBus] No handler registered for command: ${command as string}`)\n return undefined as any\n }\n return handler(...args)\n },\n } as ChatCommandHandler\n}\n\n// ─── Chat Bus Factory ────────────────────────────────────────\n\n/**\n * Create a complete ChatBus with events + commands.\n *\n * @experimental\n *\n * @example\n * const bus = createChatBus()\n * bus.events.on('onStreamEnd', (event) => { ... })\n * bus.commands.handle('sendPrompt', (text) => { ... })\n */\nexport function createChatBus(): ChatBus {\n return {\n events: createEventEmitter(),\n commands: createCommandHandler(),\n }\n}\n\n// ─── Scratchpad Section Merge Helper ─────────────────────────\n\n/**\n * Merge sections from a ScratchpadEvent into existing state sections.\n * Handles replace/append/upsert modes.\n *\n * @example\n * const newSections = mergeScratchpadSections(\n * currentState.sections,\n * event.sections,\n * event.sectionMode\n * )\n */\nexport function mergeScratchpadSections(\n existing: ScratchpadSection[],\n incoming: ScratchpadSection[] | undefined,\n mode: 'replace' | 'append' | 'upsert' = 'replace'\n): ScratchpadSection[] {\n if (!incoming) return existing\n\n switch (mode) {\n case 'append':\n return [...existing, ...incoming]\n\n case 'upsert': {\n const result = [...existing]\n for (const section of incoming) {\n const idx = result.findIndex((s) => s.id === section.id)\n if (idx >= 0) {\n result[idx] = section\n } else {\n result.push(section)\n }\n }\n return result\n }\n\n case 'replace':\n default:\n return incoming\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-bus.cjs","sources":["../../src/services/chat-bus.ts"],"sourcesContent":["/**\n * Chat Bus — Event Emitter + Command Handler\n * v2.4.0: Core primitives for the chat event/command bus\n *\n * @experimental — This API may change without major bump until v2.5.0.\n */\n\nimport type {\n ChatEvents,\n ChatCommands,\n ChatEventEmitter,\n ChatCommandHandler,\n ChatBus,\n EventSubscribeOptions,\n ScratchpadSection,\n ClarificationEvent,\n ChatPromptConfig,\n} from '../types/chat-bus'\n\n// ─── Event Emitter ───────────────────────────────────────────\n\ninterface Listener<F extends (...args: any[]) => any> {\n handler: F\n options?: EventSubscribeOptions\n throttledHandler?: F\n}\n\n/**\n * Create a typed event emitter with throttle and streamKey filtering support.\n *\n * @experimental\n *\n * @example\n * const emitter = createEventEmitter<ChatEvents>()\n * const unsub = emitter.on('onToken', (event) => console.log(event.token), { throttle: 100 })\n * emitter.emit('onToken', { streamKey: 'abc', token: 'hello' })\n * unsub()\n */\nexport function createEventEmitter(): ChatEventEmitter {\n const listeners = new Map<string, Set<Listener<any>>>()\n\n interface ThrottledFn<F> {\n fn: F\n cancel: () => void\n }\n\n function createThrottled<F extends (...args: any[]) => void>(fn: F, ms: number): ThrottledFn<F> {\n let lastCall = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let lastArgs: any[] | null = null\n let cancelled = false\n\n const throttled = ((...args: any[]) => {\n if (cancelled) return\n lastArgs = args\n const now = Date.now()\n const remaining = ms - (now - lastCall)\n\n if (remaining <= 0) {\n if (timer) { clearTimeout(timer); timer = null }\n lastCall = now\n fn(...args)\n } else if (!timer) {\n timer = setTimeout(() => {\n lastCall = Date.now()\n timer = null\n if (lastArgs && !cancelled) {\n try { fn(...lastArgs) } catch (err) { console.error('[ChatBus] Error in throttled handler:', err) }\n }\n }, remaining)\n }\n }) as F\n\n return {\n fn: throttled,\n cancel: () => { cancelled = true; if (timer) { clearTimeout(timer); timer = null } },\n }\n }\n\n return {\n on(event, handler, options) {\n if (!listeners.has(event as string)) {\n listeners.set(event as string, new Set())\n }\n\n const listener: Listener<typeof handler> = { handler, options }\n\n // Apply throttle if requested\n let throttleHandle: ThrottledFn<typeof handler> | null = null\n if (options?.throttle && options.throttle > 0) {\n throttleHandle = createThrottled(handler, options.throttle)\n listener.throttledHandler = throttleHandle.fn\n }\n\n listeners.get(event as string)!.add(listener)\n\n // Return unsubscribe function — cancels pending throttle timers\n return () => {\n throttleHandle?.cancel()\n listeners.get(event as string)?.delete(listener)\n }\n },\n\n emit(event, ...args) {\n const set = listeners.get(event as string)\n if (!set) return\n\n for (const listener of set) {\n // StreamKey filtering: skip if listener wants a specific streamKey\n // For most events args[0] has streamKey; for onCustomEvent args[1] has it\n if (listener.options?.streamKey) {\n let streamKeyArg: unknown\n for (const arg of args) {\n if (arg && typeof arg === 'object' && 'streamKey' in (arg as any)) {\n streamKeyArg = (arg as any).streamKey\n break\n }\n }\n if (streamKeyArg !== undefined && streamKeyArg !== listener.options.streamKey) continue\n }\n\n const fn = listener.throttledHandler || listener.handler\n try {\n fn(...args)\n } catch (err) {\n console.error(`[ChatBus] Error in ${event as string} handler:`, err)\n }\n }\n },\n\n clear() {\n listeners.clear()\n },\n } as ChatEventEmitter\n}\n\n// ─── Command Handler ─────────────────────────────────────────\n\n/**\n * Create a typed command handler. The host app registers handlers,\n * agents execute commands.\n *\n * @experimental\n *\n * @example\n * const commands = createCommandHandler<ChatCommands>()\n * commands.handle('injectPrompt', (text) => setInputValue(text))\n * commands.exec('injectPrompt', 'Hello world')\n */\nexport function createCommandHandler(): ChatCommandHandler {\n const handlers = new Map<string, (...args: any[]) => any>()\n\n return {\n handle(command, handler) {\n handlers.set(command as string, handler)\n },\n\n exec(command, ...args) {\n const handler = handlers.get(command as string)\n if (!handler) {\n console.warn(`[ChatBus] No handler registered for command: ${command as string}`)\n return undefined as any\n }\n return handler(...args)\n },\n } as ChatCommandHandler\n}\n\n// ─── Chat Bus Factory ────────────────────────────────────────\n\n/**\n * Create a complete ChatBus with events + commands.\n *\n * @experimental\n *\n * @example\n * const bus = createChatBus()\n * bus.events.on('onStreamEnd', (event) => { ... })\n * bus.commands.handle('sendPrompt', (text) => { ... })\n */\nexport function createChatBus(): ChatBus {\n return {\n events: createEventEmitter(),\n commands: createCommandHandler(),\n }\n}\n\n// ─── Scratchpad Section Merge Helper ─────────────────────────\n\n/**\n * Merge sections from a ScratchpadEvent into existing state sections.\n * Handles replace/append/upsert modes.\n *\n * @example\n * const newSections = mergeScratchpadSections(\n * currentState.sections,\n * event.sections,\n * event.sectionMode\n * )\n */\nexport function mergeScratchpadSections(\n existing: ScratchpadSection[],\n incoming: ScratchpadSection[] | undefined,\n mode: 'replace' | 'append' | 'upsert' = 'replace'\n): ScratchpadSection[] {\n if (!incoming) return existing\n\n switch (mode) {\n case 'append':\n return [...existing, ...incoming]\n\n case 'upsert': {\n const result = [...existing]\n for (const section of incoming) {\n const idx = result.findIndex((s) => s.id === section.id)\n if (idx >= 0) {\n result[idx] = section\n } else {\n result.push(section)\n }\n }\n return result\n }\n\n case 'replace':\n default:\n return incoming\n }\n}\n\n// ─── Clarification → Prompt Helper (v4.3.9) ──────────────────\n\n/**\n * Convert a ClarificationEvent into a ChatPromptConfig.\n * Universal bridge for apps receiving clarification events via SSE.\n *\n * Legacy runtime `file_id` (removed from the type in v5.0.0) is still\n * transparently migrated into `metadata.file_id` when present, so payloads\n * from older servers continue to work without upgrade pressure.\n *\n * @experimental\n * @since v4.3.9\n * @example\n * bus.events.on('onClarificationNeeded', ({ clarification }) => {\n * bus.commands.exec('showChatPrompt', clarificationToPromptConfig(clarification))\n * })\n */\nexport function clarificationToPromptConfig(\n event: ClarificationEvent\n): ChatPromptConfig {\n return {\n type: 'choice',\n title: event.question,\n config: {\n options: event.options.map((opt) => {\n const merged: Record<string, unknown> = { ...(opt.metadata ?? {}) }\n // Runtime fallback for legacy payloads that still carry file_id at the top level.\n const legacyFileId = (opt as { file_id?: number }).file_id\n if (legacyFileId !== undefined && merged.file_id === undefined) {\n merged.file_id = legacyFileId\n }\n return {\n value: opt.value,\n label: opt.label,\n // Only include metadata if non-empty (keeps payloads clean)\n ...(Object.keys(merged).length > 0 ? { metadata: merged } : {}),\n }\n }),\n layout: 'vertical',\n },\n }\n}\n"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * @experimental — This API may change without major bump until v2.5.0.
6
6
  */
7
- import type { ChatEventEmitter, ChatCommandHandler, ChatBus, ScratchpadSection } from '../types/chat-bus';
7
+ import type { ChatEventEmitter, ChatCommandHandler, ChatBus, ScratchpadSection, ClarificationEvent, ChatPromptConfig } from '../types/chat-bus';
8
8
  /**
9
9
  * Create a typed event emitter with throttle and streamKey filtering support.
10
10
  *
@@ -52,4 +52,20 @@ export declare function createChatBus(): ChatBus;
52
52
  * )
53
53
  */
54
54
  export declare function mergeScratchpadSections(existing: ScratchpadSection[], incoming: ScratchpadSection[] | undefined, mode?: 'replace' | 'append' | 'upsert'): ScratchpadSection[];
55
+ /**
56
+ * Convert a ClarificationEvent into a ChatPromptConfig.
57
+ * Universal bridge for apps receiving clarification events via SSE.
58
+ *
59
+ * Legacy runtime `file_id` (removed from the type in v5.0.0) is still
60
+ * transparently migrated into `metadata.file_id` when present, so payloads
61
+ * from older servers continue to work without upgrade pressure.
62
+ *
63
+ * @experimental
64
+ * @since v4.3.9
65
+ * @example
66
+ * bus.events.on('onClarificationNeeded', ({ clarification }) => {
67
+ * bus.commands.exec('showChatPrompt', clarificationToPromptConfig(clarification))
68
+ * })
69
+ */
70
+ export declare function clarificationToPromptConfig(event: ClarificationEvent): ChatPromptConfig;
55
71
  //# sourceMappingURL=chat-bus.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-bus.d.ts","sourceRoot":"","sources":["../../src/services/chat-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAGV,gBAAgB,EAChB,kBAAkB,EAClB,OAAO,EAEP,iBAAiB,EAClB,MAAM,mBAAmB,CAAA;AAU1B;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,IAAI,gBAAgB,CAgGrD;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,IAAI,kBAAkB,CAiBzD;AAID;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAKvC;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,QAAQ,EAAE,iBAAiB,EAAE,GAAG,SAAS,EACzC,IAAI,GAAE,SAAS,GAAG,QAAQ,GAAG,QAAoB,GAChD,iBAAiB,EAAE,CAwBrB"}
1
+ {"version":3,"file":"chat-bus.d.ts","sourceRoot":"","sources":["../../src/services/chat-bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAGV,gBAAgB,EAChB,kBAAkB,EAClB,OAAO,EAEP,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,mBAAmB,CAAA;AAU1B;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,IAAI,gBAAgB,CAgGrD;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,IAAI,kBAAkB,CAiBzD;AAID;;;;;;;;;GASG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAKvC;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,QAAQ,EAAE,iBAAiB,EAAE,GAAG,SAAS,EACzC,IAAI,GAAE,SAAS,GAAG,QAAQ,GAAG,QAAoB,GAChD,iBAAiB,EAAE,CAwBrB;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,kBAAkB,GACxB,gBAAgB,CAsBlB"}
@@ -132,7 +132,30 @@ function mergeScratchpadSections(existing, incoming, mode = "replace") {
132
132
  return incoming;
133
133
  }
134
134
  }
135
+ function clarificationToPromptConfig(event) {
136
+ return {
137
+ type: "choice",
138
+ title: event.question,
139
+ config: {
140
+ options: event.options.map((opt) => {
141
+ const merged = { ...opt.metadata ?? {} };
142
+ const legacyFileId = opt.file_id;
143
+ if (legacyFileId !== void 0 && merged.file_id === void 0) {
144
+ merged.file_id = legacyFileId;
145
+ }
146
+ return {
147
+ value: opt.value,
148
+ label: opt.label,
149
+ // Only include metadata if non-empty (keeps payloads clean)
150
+ ...Object.keys(merged).length > 0 ? { metadata: merged } : {}
151
+ };
152
+ }),
153
+ layout: "vertical"
154
+ }
155
+ };
156
+ }
135
157
  export {
158
+ clarificationToPromptConfig,
136
159
  createChatBus,
137
160
  createCommandHandler,
138
161
  createEventEmitter,
@@ -1 +1 @@
1
- {"version":3,"file":"chat-bus.js","sources":["../../src/services/chat-bus.ts"],"sourcesContent":["/**\n * Chat Bus — Event Emitter + Command Handler\n * v2.4.0: Core primitives for the chat event/command bus\n *\n * @experimental — This API may change without major bump until v2.5.0.\n */\n\nimport type {\n ChatEvents,\n ChatCommands,\n ChatEventEmitter,\n ChatCommandHandler,\n ChatBus,\n EventSubscribeOptions,\n ScratchpadSection,\n} from '../types/chat-bus'\n\n// ─── Event Emitter ───────────────────────────────────────────\n\ninterface Listener<F extends (...args: any[]) => any> {\n handler: F\n options?: EventSubscribeOptions\n throttledHandler?: F\n}\n\n/**\n * Create a typed event emitter with throttle and streamKey filtering support.\n *\n * @experimental\n *\n * @example\n * const emitter = createEventEmitter<ChatEvents>()\n * const unsub = emitter.on('onToken', (event) => console.log(event.token), { throttle: 100 })\n * emitter.emit('onToken', { streamKey: 'abc', token: 'hello' })\n * unsub()\n */\nexport function createEventEmitter(): ChatEventEmitter {\n const listeners = new Map<string, Set<Listener<any>>>()\n\n interface ThrottledFn<F> {\n fn: F\n cancel: () => void\n }\n\n function createThrottled<F extends (...args: any[]) => void>(fn: F, ms: number): ThrottledFn<F> {\n let lastCall = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let lastArgs: any[] | null = null\n let cancelled = false\n\n const throttled = ((...args: any[]) => {\n if (cancelled) return\n lastArgs = args\n const now = Date.now()\n const remaining = ms - (now - lastCall)\n\n if (remaining <= 0) {\n if (timer) { clearTimeout(timer); timer = null }\n lastCall = now\n fn(...args)\n } else if (!timer) {\n timer = setTimeout(() => {\n lastCall = Date.now()\n timer = null\n if (lastArgs && !cancelled) {\n try { fn(...lastArgs) } catch (err) { console.error('[ChatBus] Error in throttled handler:', err) }\n }\n }, remaining)\n }\n }) as F\n\n return {\n fn: throttled,\n cancel: () => { cancelled = true; if (timer) { clearTimeout(timer); timer = null } },\n }\n }\n\n return {\n on(event, handler, options) {\n if (!listeners.has(event as string)) {\n listeners.set(event as string, new Set())\n }\n\n const listener: Listener<typeof handler> = { handler, options }\n\n // Apply throttle if requested\n let throttleHandle: ThrottledFn<typeof handler> | null = null\n if (options?.throttle && options.throttle > 0) {\n throttleHandle = createThrottled(handler, options.throttle)\n listener.throttledHandler = throttleHandle.fn\n }\n\n listeners.get(event as string)!.add(listener)\n\n // Return unsubscribe function — cancels pending throttle timers\n return () => {\n throttleHandle?.cancel()\n listeners.get(event as string)?.delete(listener)\n }\n },\n\n emit(event, ...args) {\n const set = listeners.get(event as string)\n if (!set) return\n\n for (const listener of set) {\n // StreamKey filtering: skip if listener wants a specific streamKey\n // For most events args[0] has streamKey; for onCustomEvent args[1] has it\n if (listener.options?.streamKey) {\n let streamKeyArg: unknown\n for (const arg of args) {\n if (arg && typeof arg === 'object' && 'streamKey' in (arg as any)) {\n streamKeyArg = (arg as any).streamKey\n break\n }\n }\n if (streamKeyArg !== undefined && streamKeyArg !== listener.options.streamKey) continue\n }\n\n const fn = listener.throttledHandler || listener.handler\n try {\n fn(...args)\n } catch (err) {\n console.error(`[ChatBus] Error in ${event as string} handler:`, err)\n }\n }\n },\n\n clear() {\n listeners.clear()\n },\n } as ChatEventEmitter\n}\n\n// ─── Command Handler ─────────────────────────────────────────\n\n/**\n * Create a typed command handler. The host app registers handlers,\n * agents execute commands.\n *\n * @experimental\n *\n * @example\n * const commands = createCommandHandler<ChatCommands>()\n * commands.handle('injectPrompt', (text) => setInputValue(text))\n * commands.exec('injectPrompt', 'Hello world')\n */\nexport function createCommandHandler(): ChatCommandHandler {\n const handlers = new Map<string, (...args: any[]) => any>()\n\n return {\n handle(command, handler) {\n handlers.set(command as string, handler)\n },\n\n exec(command, ...args) {\n const handler = handlers.get(command as string)\n if (!handler) {\n console.warn(`[ChatBus] No handler registered for command: ${command as string}`)\n return undefined as any\n }\n return handler(...args)\n },\n } as ChatCommandHandler\n}\n\n// ─── Chat Bus Factory ────────────────────────────────────────\n\n/**\n * Create a complete ChatBus with events + commands.\n *\n * @experimental\n *\n * @example\n * const bus = createChatBus()\n * bus.events.on('onStreamEnd', (event) => { ... })\n * bus.commands.handle('sendPrompt', (text) => { ... })\n */\nexport function createChatBus(): ChatBus {\n return {\n events: createEventEmitter(),\n commands: createCommandHandler(),\n }\n}\n\n// ─── Scratchpad Section Merge Helper ─────────────────────────\n\n/**\n * Merge sections from a ScratchpadEvent into existing state sections.\n * Handles replace/append/upsert modes.\n *\n * @example\n * const newSections = mergeScratchpadSections(\n * currentState.sections,\n * event.sections,\n * event.sectionMode\n * )\n */\nexport function mergeScratchpadSections(\n existing: ScratchpadSection[],\n incoming: ScratchpadSection[] | undefined,\n mode: 'replace' | 'append' | 'upsert' = 'replace'\n): ScratchpadSection[] {\n if (!incoming) return existing\n\n switch (mode) {\n case 'append':\n return [...existing, ...incoming]\n\n case 'upsert': {\n const result = [...existing]\n for (const section of incoming) {\n const idx = result.findIndex((s) => s.id === section.id)\n if (idx >= 0) {\n result[idx] = section\n } else {\n result.push(section)\n }\n }\n return result\n }\n\n case 'replace':\n default:\n return incoming\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chat-bus.js","sources":["../../src/services/chat-bus.ts"],"sourcesContent":["/**\n * Chat Bus — Event Emitter + Command Handler\n * v2.4.0: Core primitives for the chat event/command bus\n *\n * @experimental — This API may change without major bump until v2.5.0.\n */\n\nimport type {\n ChatEvents,\n ChatCommands,\n ChatEventEmitter,\n ChatCommandHandler,\n ChatBus,\n EventSubscribeOptions,\n ScratchpadSection,\n ClarificationEvent,\n ChatPromptConfig,\n} from '../types/chat-bus'\n\n// ─── Event Emitter ───────────────────────────────────────────\n\ninterface Listener<F extends (...args: any[]) => any> {\n handler: F\n options?: EventSubscribeOptions\n throttledHandler?: F\n}\n\n/**\n * Create a typed event emitter with throttle and streamKey filtering support.\n *\n * @experimental\n *\n * @example\n * const emitter = createEventEmitter<ChatEvents>()\n * const unsub = emitter.on('onToken', (event) => console.log(event.token), { throttle: 100 })\n * emitter.emit('onToken', { streamKey: 'abc', token: 'hello' })\n * unsub()\n */\nexport function createEventEmitter(): ChatEventEmitter {\n const listeners = new Map<string, Set<Listener<any>>>()\n\n interface ThrottledFn<F> {\n fn: F\n cancel: () => void\n }\n\n function createThrottled<F extends (...args: any[]) => void>(fn: F, ms: number): ThrottledFn<F> {\n let lastCall = 0\n let timer: ReturnType<typeof setTimeout> | null = null\n let lastArgs: any[] | null = null\n let cancelled = false\n\n const throttled = ((...args: any[]) => {\n if (cancelled) return\n lastArgs = args\n const now = Date.now()\n const remaining = ms - (now - lastCall)\n\n if (remaining <= 0) {\n if (timer) { clearTimeout(timer); timer = null }\n lastCall = now\n fn(...args)\n } else if (!timer) {\n timer = setTimeout(() => {\n lastCall = Date.now()\n timer = null\n if (lastArgs && !cancelled) {\n try { fn(...lastArgs) } catch (err) { console.error('[ChatBus] Error in throttled handler:', err) }\n }\n }, remaining)\n }\n }) as F\n\n return {\n fn: throttled,\n cancel: () => { cancelled = true; if (timer) { clearTimeout(timer); timer = null } },\n }\n }\n\n return {\n on(event, handler, options) {\n if (!listeners.has(event as string)) {\n listeners.set(event as string, new Set())\n }\n\n const listener: Listener<typeof handler> = { handler, options }\n\n // Apply throttle if requested\n let throttleHandle: ThrottledFn<typeof handler> | null = null\n if (options?.throttle && options.throttle > 0) {\n throttleHandle = createThrottled(handler, options.throttle)\n listener.throttledHandler = throttleHandle.fn\n }\n\n listeners.get(event as string)!.add(listener)\n\n // Return unsubscribe function — cancels pending throttle timers\n return () => {\n throttleHandle?.cancel()\n listeners.get(event as string)?.delete(listener)\n }\n },\n\n emit(event, ...args) {\n const set = listeners.get(event as string)\n if (!set) return\n\n for (const listener of set) {\n // StreamKey filtering: skip if listener wants a specific streamKey\n // For most events args[0] has streamKey; for onCustomEvent args[1] has it\n if (listener.options?.streamKey) {\n let streamKeyArg: unknown\n for (const arg of args) {\n if (arg && typeof arg === 'object' && 'streamKey' in (arg as any)) {\n streamKeyArg = (arg as any).streamKey\n break\n }\n }\n if (streamKeyArg !== undefined && streamKeyArg !== listener.options.streamKey) continue\n }\n\n const fn = listener.throttledHandler || listener.handler\n try {\n fn(...args)\n } catch (err) {\n console.error(`[ChatBus] Error in ${event as string} handler:`, err)\n }\n }\n },\n\n clear() {\n listeners.clear()\n },\n } as ChatEventEmitter\n}\n\n// ─── Command Handler ─────────────────────────────────────────\n\n/**\n * Create a typed command handler. The host app registers handlers,\n * agents execute commands.\n *\n * @experimental\n *\n * @example\n * const commands = createCommandHandler<ChatCommands>()\n * commands.handle('injectPrompt', (text) => setInputValue(text))\n * commands.exec('injectPrompt', 'Hello world')\n */\nexport function createCommandHandler(): ChatCommandHandler {\n const handlers = new Map<string, (...args: any[]) => any>()\n\n return {\n handle(command, handler) {\n handlers.set(command as string, handler)\n },\n\n exec(command, ...args) {\n const handler = handlers.get(command as string)\n if (!handler) {\n console.warn(`[ChatBus] No handler registered for command: ${command as string}`)\n return undefined as any\n }\n return handler(...args)\n },\n } as ChatCommandHandler\n}\n\n// ─── Chat Bus Factory ────────────────────────────────────────\n\n/**\n * Create a complete ChatBus with events + commands.\n *\n * @experimental\n *\n * @example\n * const bus = createChatBus()\n * bus.events.on('onStreamEnd', (event) => { ... })\n * bus.commands.handle('sendPrompt', (text) => { ... })\n */\nexport function createChatBus(): ChatBus {\n return {\n events: createEventEmitter(),\n commands: createCommandHandler(),\n }\n}\n\n// ─── Scratchpad Section Merge Helper ─────────────────────────\n\n/**\n * Merge sections from a ScratchpadEvent into existing state sections.\n * Handles replace/append/upsert modes.\n *\n * @example\n * const newSections = mergeScratchpadSections(\n * currentState.sections,\n * event.sections,\n * event.sectionMode\n * )\n */\nexport function mergeScratchpadSections(\n existing: ScratchpadSection[],\n incoming: ScratchpadSection[] | undefined,\n mode: 'replace' | 'append' | 'upsert' = 'replace'\n): ScratchpadSection[] {\n if (!incoming) return existing\n\n switch (mode) {\n case 'append':\n return [...existing, ...incoming]\n\n case 'upsert': {\n const result = [...existing]\n for (const section of incoming) {\n const idx = result.findIndex((s) => s.id === section.id)\n if (idx >= 0) {\n result[idx] = section\n } else {\n result.push(section)\n }\n }\n return result\n }\n\n case 'replace':\n default:\n return incoming\n }\n}\n\n// ─── Clarification → Prompt Helper (v4.3.9) ──────────────────\n\n/**\n * Convert a ClarificationEvent into a ChatPromptConfig.\n * Universal bridge for apps receiving clarification events via SSE.\n *\n * Legacy runtime `file_id` (removed from the type in v5.0.0) is still\n * transparently migrated into `metadata.file_id` when present, so payloads\n * from older servers continue to work without upgrade pressure.\n *\n * @experimental\n * @since v4.3.9\n * @example\n * bus.events.on('onClarificationNeeded', ({ clarification }) => {\n * bus.commands.exec('showChatPrompt', clarificationToPromptConfig(clarification))\n * })\n */\nexport function clarificationToPromptConfig(\n event: ClarificationEvent\n): ChatPromptConfig {\n return {\n type: 'choice',\n title: event.question,\n config: {\n options: event.options.map((opt) => {\n const merged: Record<string, unknown> = { ...(opt.metadata ?? {}) }\n // Runtime fallback for legacy payloads that still carry file_id at the top level.\n const legacyFileId = (opt as { file_id?: number }).file_id\n if (legacyFileId !== undefined && merged.file_id === undefined) {\n merged.file_id = legacyFileId\n }\n return {\n value: opt.value,\n label: opt.label,\n // Only include metadata if non-empty (keeps payloads clean)\n ...(Object.keys(merged).length > 0 ? { metadata: merged } : {}),\n }\n }),\n layout: 'vertical',\n },\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"scratchpad-store.cjs","sources":["../../src/stores/scratchpad-store.ts"],"sourcesContent":["/**\n * Scratchpad Store — singleton reactive state for HITL scratchpad\n * v3.0.3: Eliminates ChatBus relay chain race condition\n *\n * @experimental\n *\n * Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.\n * Zero bus, zero relay, zero race condition.\n */\n\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\nconst [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n}>({ current: null, pinned: false })\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState() {\n return {\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n/**\n * Function for the PARSER/STORE — mutates the scratchpad state.\n * Called from the SSE callback, no bus needed.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n // DX1: lifecycle logging\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore(produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`)\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex((sec: ScratchpadSection) => sec.id === incoming.id)\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n }))\n } else if (event.action === 'close') {\n console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, 'color: #6b7280; font-weight: bold', 'color: inherit')\n setScratchpadStore({ current: null, pinned: false })\n }\n}\n"],"names":["createStore","produce","_a","s"],"mappings":"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"}
1
+ {"version":3,"file":"scratchpad-store.cjs","sources":["../../src/stores/scratchpad-store.ts"],"sourcesContent":["/**\n * Scratchpad Store — singleton reactive state for HITL scratchpad\n * v3.0.3: Eliminates ChatBus relay chain race condition\n *\n * @experimental\n *\n * Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.\n * Zero bus, zero relay, zero race condition.\n *\n * **Known limitation (v4.3.9):** This store is a module-level singleton, not\n * a context-scoped factory. Two `ScratchpadPanel` instances in the same app\n * will share the same state. Multi-panel scenarios (e.g. chat + admin dashboard\n * both showing scratchpads simultaneously) are unsupported. Host apps that need\n * isolated scratchpads should not reuse this store — wait for v4.4.0 which\n * will expose `createScratchpadStore()` factory for per-panel instances.\n */\n\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\nconst [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n}>({ current: null, pinned: false })\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState() {\n return {\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n/**\n * Function for the PARSER/STORE — mutates the scratchpad state.\n * Called from the SSE callback, no bus needed.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n // DX1: lifecycle logging\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore(produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`)\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex((sec: ScratchpadSection) => sec.id === incoming.id)\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n }))\n } else if (event.action === 'close') {\n console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, 'color: #6b7280; font-weight: bold', 'color: inherit')\n setScratchpadStore({ current: null, pinned: false })\n }\n}\n"],"names":["createStore","produce","_a","s"],"mappings":"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"}
@@ -6,6 +6,13 @@
6
6
  *
7
7
  * Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.
8
8
  * Zero bus, zero relay, zero race condition.
9
+ *
10
+ * **Known limitation (v4.3.9):** This store is a module-level singleton, not
11
+ * a context-scoped factory. Two `ScratchpadPanel` instances in the same app
12
+ * will share the same state. Multi-panel scenarios (e.g. chat + admin dashboard
13
+ * both showing scratchpads simultaneously) are unsupported. Host apps that need
14
+ * isolated scratchpads should not reuse this store — wait for v4.4.0 which
15
+ * will expose `createScratchpadStore()` factory for per-panel instances.
9
16
  */
10
17
  import type { ScratchpadState, ScratchpadEvent } from '../types/chat-bus';
11
18
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scratchpad-store.d.ts","sourceRoot":"","sources":["../../src/stores/scratchpad-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAqB,MAAM,mBAAmB,CAAA;AAO5F;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB;;;;EAMjC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CA2E/D"}
1
+ {"version":3,"file":"scratchpad-store.d.ts","sourceRoot":"","sources":["../../src/stores/scratchpad-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAqB,MAAM,mBAAmB,CAAA;AAO5F;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB;;;;EAMjC;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CA2E/D"}
@@ -1 +1 @@
1
- {"version":3,"file":"scratchpad-store.js","sources":["../../src/stores/scratchpad-store.ts"],"sourcesContent":["/**\n * Scratchpad Store — singleton reactive state for HITL scratchpad\n * v3.0.3: Eliminates ChatBus relay chain race condition\n *\n * @experimental\n *\n * Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.\n * Zero bus, zero relay, zero race condition.\n */\n\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\nconst [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n}>({ current: null, pinned: false })\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState() {\n return {\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n/**\n * Function for the PARSER/STORE — mutates the scratchpad state.\n * Called from the SSE callback, no bus needed.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n // DX1: lifecycle logging\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore(produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`)\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex((sec: ScratchpadSection) => sec.id === incoming.id)\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n }))\n } else if (event.action === 'close') {\n console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, 'color: #6b7280; font-weight: bold', 'color: inherit')\n setScratchpadStore({ current: null, pinned: false })\n }\n}\n"],"names":["_a","s"],"mappings":"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"}
1
+ {"version":3,"file":"scratchpad-store.js","sources":["../../src/stores/scratchpad-store.ts"],"sourcesContent":["/**\n * Scratchpad Store — singleton reactive state for HITL scratchpad\n * v3.0.3: Eliminates ChatBus relay chain race condition\n *\n * @experimental\n *\n * Parser calls dispatchScratchpad() → store updates → ScratchpadPanel reads reactively.\n * Zero bus, zero relay, zero race condition.\n *\n * **Known limitation (v4.3.9):** This store is a module-level singleton, not\n * a context-scoped factory. Two `ScratchpadPanel` instances in the same app\n * will share the same state. Multi-panel scenarios (e.g. chat + admin dashboard\n * both showing scratchpads simultaneously) are unsupported. Host apps that need\n * isolated scratchpads should not reuse this store — wait for v4.4.0 which\n * will expose `createScratchpadStore()` factory for per-panel instances.\n */\n\nimport { createStore, produce } from 'solid-js/store'\nimport type { ScratchpadState, ScratchpadEvent, ScratchpadSection } from '../types/chat-bus'\n\nconst [scratchpadStore, setScratchpadStore] = createStore<{\n current: ScratchpadState | null\n pinned: boolean\n}>({ current: null, pinned: false })\n\n/**\n * Hook for the COMPONENT — reads the scratchpad state reactively.\n *\n * @example\n * const { state, pinned, close } = useScratchpadState()\n * <Show when={state()}>\n * <ScratchpadPanel state={state()!} pinned={pinned()} onClose={close} />\n * </Show>\n */\nexport function useScratchpadState() {\n return {\n state: () => scratchpadStore.current,\n pinned: () => scratchpadStore.pinned,\n close: () => setScratchpadStore({ current: null, pinned: false }),\n }\n}\n\n/**\n * Function for the PARSER/STORE — mutates the scratchpad state.\n * Called from the SSE callback, no bus needed.\n *\n * @example\n * // In your SSE parser callback — ONE LINE\n * onScratchpad: (data) => dispatchScratchpad(data as ScratchpadEvent)\n */\nexport function dispatchScratchpad(event: ScratchpadEvent): void {\n // DX1: lifecycle logging\n if (event.action === 'create') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c create id=${event.id} sections=${event.sections?.length || 0} status=${event.status || 'loading'}${event.pinned ? ' pinned' : ''}`,\n 'color: #10b981; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore({\n current: {\n id: event.id,\n title: event.title || '',\n sections: event.sections || [],\n filters: event.filters || {},\n preview: event.preview,\n agentMessages: event.agentMessages || [],\n status: event.status || 'loading',\n previewEndpoint: (event as any).previewEndpoint,\n previewDebounce: (event as any).previewDebounce,\n previewMethod: (event as any).previewMethod,\n previewHeaders: (event as any).previewHeaders,\n turn: (event as any).turn,\n totalTurns: (event as any).totalTurns,\n turnHistory: (event as any).turnHistory,\n },\n pinned: event.pinned || false,\n })\n } else if (event.action === 'update') {\n console.info(\n `%c[MCP-UI] dispatchScratchpad%c update id=${event.id} sectionMode=${event.sectionMode || 'replace'} sections=${event.sections?.length || 0} status=${event.status || '-'}`,\n 'color: #3b82f6; font-weight: bold', 'color: inherit'\n )\n setScratchpadStore(produce((s) => {\n if (!s.current || s.current.id !== event.id) {\n console.warn(`[MCP-UI] dispatchScratchpad: update for id=${event.id} but current is ${s.current?.id || 'null'}. Ignoring.`)\n return\n }\n\n if (event.sections) {\n const mode = event.sectionMode || 'replace'\n if (mode === 'replace') {\n s.current.sections = event.sections\n } else if (mode === 'append') {\n s.current.sections = [...s.current.sections, ...event.sections]\n } else if (mode === 'upsert') {\n let matchCount = 0\n for (const incoming of event.sections) {\n const idx = s.current.sections.findIndex((sec: ScratchpadSection) => sec.id === incoming.id)\n if (idx >= 0) {\n s.current.sections[idx] = incoming\n matchCount++\n } else {\n s.current.sections.push(incoming)\n }\n }\n if (matchCount === 0 && event.sections.length > 0) {\n console.warn(\n `[MCP-UI] dispatchScratchpad: sectionMode='upsert' but no IDs matched. ` +\n `Incoming: [${event.sections.map((s: ScratchpadSection) => s.id).join(', ')}] ` +\n `Existing: [${s.current.sections.map((s: ScratchpadSection) => s.id).join(', ')}]. All appended.`\n )\n }\n }\n }\n if (event.agentMessages) s.current.agentMessages = event.agentMessages\n if (event.status) s.current.status = event.status\n if (event.filters) s.current.filters = event.filters\n if (event.preview) s.current.preview = event.preview\n if (event.pinned != null) s.pinned = event.pinned\n if ((event as any).turnHistory) s.current.turnHistory = (event as any).turnHistory\n if ((event as any).turn != null) s.current.turn = (event as any).turn\n }))\n } else if (event.action === 'close') {\n console.info(`%c[MCP-UI] dispatchScratchpad%c close id=${event.id}`, 'color: #6b7280; font-weight: bold', 'color: inherit')\n setScratchpadStore({ current: null, pinned: false })\n }\n}\n"],"names":["_a","s"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const chatBus = require("../services/chat-bus.cjs");
4
+ function createMockChatBus(options = {}) {
5
+ const bus = chatBus.createChatBus();
6
+ const queue = [...options.promptResponses ?? []];
7
+ bus.commands.handle("showChatPrompt", async (config) => {
8
+ var _a;
9
+ (_a = options.onShowChatPrompt) == null ? void 0 : _a.call(options, config);
10
+ const response = queue.shift();
11
+ if (!response) {
12
+ throw new Error("createMockChatBus: no more pre-programmed responses");
13
+ }
14
+ return response;
15
+ });
16
+ return bus;
17
+ }
18
+ exports.createMockChatBus = createMockChatBus;
19
+ //# sourceMappingURL=index.cjs.map