@hydraharness/harness-client-ui-conversation 0.1.1-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/lib/client.js +11683 -0
  4. package/lib/index.js +28 -0
  5. package/lib/invariant.js +25 -0
  6. package/lib/types/client/apply.d.ts +16 -0
  7. package/lib/types/client/chat/ActivityGroup.d.ts +22 -0
  8. package/lib/types/client/chat/AssistantMarkdown.d.ts +20 -0
  9. package/lib/types/client/chat/AssistantNodeView.d.ts +4 -0
  10. package/lib/types/client/chat/ChatNodeSeat.d.ts +11 -0
  11. package/lib/types/client/chat/ChatView.d.ts +7 -0
  12. package/lib/types/client/chat/CommandNodeView.d.ts +9 -0
  13. package/lib/types/client/chat/CompactionCommandCard.d.ts +8 -0
  14. package/lib/types/client/chat/CompactionItem.d.ts +19 -0
  15. package/lib/types/client/chat/ContextBody.d.ts +130 -0
  16. package/lib/types/client/chat/ContextInjectionRow.d.ts +26 -0
  17. package/lib/types/client/chat/GenericCommandCard.d.ts +9 -0
  18. package/lib/types/client/chat/MessageIconActions.d.ts +44 -0
  19. package/lib/types/client/chat/MessageItem.d.ts +46 -0
  20. package/lib/types/client/chat/PromptVersionMenu.d.ts +21 -0
  21. package/lib/types/client/chat/ReasoningRow.d.ts +14 -0
  22. package/lib/types/client/chat/StatsLine.d.ts +87 -0
  23. package/lib/types/client/chat/TurnTailNodeView.d.ts +9 -0
  24. package/lib/types/client/chat/message-chrome.d.ts +49 -0
  25. package/lib/types/client/chat/register-node-renderers.d.ts +11 -0
  26. package/lib/types/client/chat/tool-node-reader.d.ts +16 -0
  27. package/lib/types/client/chat/turn-assistant.d.ts +8 -0
  28. package/lib/types/client/chat/turn-metrics.d.ts +38 -0
  29. package/lib/types/client/chat/use-calendar-day.d.ts +6 -0
  30. package/lib/types/client/chat/use-throttled-visual-update.d.ts +8 -0
  31. package/lib/types/client/chat/useVersionNavigation.d.ts +12 -0
  32. package/lib/types/client/chat/web-citations.d.ts +11 -0
  33. package/lib/types/client/contract/chat-nodes.d.ts +68 -0
  34. package/lib/types/client/contract/composer-submission.d.ts +8 -0
  35. package/lib/types/client/contract/prompt-edit.d.ts +6 -0
  36. package/lib/types/client/contract/queue.d.ts +9 -0
  37. package/lib/types/client/contract/slots.d.ts +819 -0
  38. package/lib/types/client/contract/views.d.ts +39 -0
  39. package/lib/types/client/conversation-nodes/assistant.d.ts +35 -0
  40. package/lib/types/client/conversation-nodes/chat-snapshot-builder.d.ts +29 -0
  41. package/lib/types/client/conversation-nodes/command.d.ts +54 -0
  42. package/lib/types/client/conversation-nodes/common.d.ts +40 -0
  43. package/lib/types/client/conversation-nodes/compaction.d.ts +21 -0
  44. package/lib/types/client/conversation-nodes/fallback.d.ts +16 -0
  45. package/lib/types/client/conversation-nodes/inbox.d.ts +21 -0
  46. package/lib/types/client/conversation-nodes/message.d.ts +30 -0
  47. package/lib/types/client/conversation-nodes/register.d.ts +7 -0
  48. package/lib/types/client/conversation-nodes/retry.d.ts +23 -0
  49. package/lib/types/client/conversation-nodes/tool.d.ts +25 -0
  50. package/lib/types/client/conversation-nodes/turn-error.d.ts +30 -0
  51. package/lib/types/client/conversation-nodes/turn-max-tokens.d.ts +22 -0
  52. package/lib/types/client/conversation-nodes/turn-tail.d.ts +28 -0
  53. package/lib/types/client/image-labels.d.ts +22 -0
  54. package/lib/types/client/index.d.ts +30 -0
  55. package/lib/types/client/input/blocks.d.ts +57 -0
  56. package/lib/types/client/input/contract.d.ts +340 -0
  57. package/lib/types/client/input/decorations.d.ts +71 -0
  58. package/lib/types/client/input/facade.d.ts +261 -0
  59. package/lib/types/client/input/hub.d.ts +88 -0
  60. package/lib/types/client/input/machine.d.ts +135 -0
  61. package/lib/types/client/input/submission-policy.d.ts +46 -0
  62. package/lib/types/client/locales.d.ts +242 -0
  63. package/lib/types/client/queue/QueueDock.d.ts +29 -0
  64. package/lib/types/client/queue/store.d.ts +19 -0
  65. package/lib/types/client/reference/ReferenceIcon.d.ts +16 -0
  66. package/lib/types/client/service.d.ts +220 -0
  67. package/lib/types/client/settings/EnterBehaviorRow.d.ts +21 -0
  68. package/lib/types/client/settings/UsageSection.d.ts +21 -0
  69. package/lib/types/client/skeleton/ApprovalPanel.d.ts +13 -0
  70. package/lib/types/client/skeleton/ContextMeter.d.ts +14 -0
  71. package/lib/types/client/skeleton/ConversationRoot.d.ts +5 -0
  72. package/lib/types/client/skeleton/ConversationSession.d.ts +20 -0
  73. package/lib/types/client/skeleton/DetailsPanel.d.ts +5 -0
  74. package/lib/types/client/skeleton/EmptyHero.d.ts +58 -0
  75. package/lib/types/client/skeleton/InputBar.d.ts +11 -0
  76. package/lib/types/client/skeleton/PermissionSelect.d.ts +11 -0
  77. package/lib/types/client/skeleton/TodoPanel.d.ts +28 -0
  78. package/lib/types/client/skeleton/safari.d.ts +18 -0
  79. package/lib/types/client/stores.d.ts +22 -0
  80. package/lib/types/index.d.ts +9 -0
  81. package/lib/types/invariant.d.ts +16 -0
  82. package/lib/types/submission-settings.d.ts +20 -0
  83. package/package.json +121 -0
@@ -0,0 +1,819 @@
1
+ /** Conversation slot declarations and their composed component props. */
2
+ import type { ReactNode, RefObject } from 'react';
3
+ import type { ImageAttachmentRef, VideoAttachmentRef } from '@hydraharness/harness-attachment';
4
+ import type { InjectFace, MaybeSnapshotSelectorHook, PropsLocale, PropsRenderSlots, PropsRuntime, PropsStore, SlotHookFactory, SnapshotSelectorHook } from '@hydraharness/harness-client-ui-slots';
5
+ import type { CommandNode, CompactionSummaryNode, ConversationSnapshot, ConversationTurnDataMap, ObservableSnapshot, PendingInteraction, PendingWait, SessionId, ToolCallBlock, TurnLocation, WorkspaceId } from '@hydraharness/harness-client-runtime/client';
6
+ import type { MarkdownFileMentions } from '@hydraharness/harness-client-ui-primitives';
7
+ import type { MessageId } from '@hydraharness/harness-client-connection/client';
8
+ import type { ComposerBlock } from '../input/blocks.ts';
9
+ import type { ComposerKeyboard, DraftAttachmentId, EditSelection, InputActions, InputNotice, InputState } from '../input/contract.ts';
10
+ import type { createChatStore } from '../stores.ts';
11
+ import type { ComposerSubmitGesture, InputSubmitMode } from './composer-submission.ts';
12
+ import type { ChatNode, ChatNodeKind } from './chat-nodes.ts';
13
+ import type { CallId, SelectionTarget, ViewTab } from './views.ts';
14
+ /** Browser-owned image that has not crossed the durable host boundary. */
15
+ export interface ComposerAttachment {
16
+ kind: 'image';
17
+ id: DraftAttachmentId;
18
+ file: File;
19
+ previewUrl: string;
20
+ }
21
+ /** Browser-selected text context that is queued beside the composer draft. */
22
+ export interface BrowserAnnotationAttachment {
23
+ kind: 'browser-annotation';
24
+ id: DraftAttachmentId;
25
+ file: File;
26
+ comment: string;
27
+ /** Optional screenshot image kept paired with this annotation. */
28
+ screenshotId?: DraftAttachmentId;
29
+ }
30
+ /** Input state handed to the optional attachment presentation plugin. */
31
+ export interface ComposerAttachmentsOwnerProps {
32
+ /** Browser-owned draft images in input order. */
33
+ attachments: readonly ComposerAttachment[];
34
+ /** Browser-selected text files waiting beside the composer draft. */
35
+ browserAnnotations?: readonly BrowserAnnotationAttachment[];
36
+ /** Whether a document-level file drop may add images now. */
37
+ canAcceptDrop: boolean;
38
+ /** Add one dropped batch through the composer's validation path. */
39
+ onAddImages: (files: readonly File[]) => void;
40
+ /** Remove one draft image through the conversation service. */
41
+ onRemoveImage: (id: DraftAttachmentId) => void;
42
+ /** Remove one browser-selected text file from the draft. */
43
+ onRemoveBrowserAnnotation?: (id: DraftAttachmentId) => void;
44
+ /** Update the user comment attached to one browser-selected text file. */
45
+ onUpdateBrowserAnnotationComment?: (id: DraftAttachmentId, comment: string) => void;
46
+ /** Display-ready limits for the drop invitation. */
47
+ dropLimits?: {
48
+ readonly count: number;
49
+ readonly size: string;
50
+ } | undefined;
51
+ }
52
+ /** Historical image group handed to the optional attachment presentation plugin. */
53
+ export interface MessageImagesOwnerProps {
54
+ /** Consecutive image blocks rendered as one gallery. */
55
+ images: readonly {
56
+ readonly attachment: ImageAttachmentRef;
57
+ }[];
58
+ /** Session-authorized durable image loader. */
59
+ loadImage: (attachment: ImageAttachmentRef) => Promise<string>;
60
+ /** Message-side alignment. */
61
+ align: 'start' | 'end';
62
+ /** Generated output preserves each image's dimensions instead of using message thumbnails. */
63
+ presentation?: 'media' | undefined;
64
+ }
65
+ /** Slot-backed renderer used by chat nodes without importing an attachment implementation. */
66
+ export type RenderMessageImages = (owner: Omit<MessageImagesOwnerProps, 'loadImage'>) => ReactNode;
67
+ declare module '@hydraharness/harness-client-ui-slots' {
68
+ interface SlotMap {
69
+ /**
70
+ * The entire body of one session: taking this seat means rendering that
71
+ * session's conversation yourself. The occupant also owns the per-session
72
+ * draft mirror and the active view ring, so a replacement inherits both
73
+ * duties and an empty one leaves a blank session pane — nothing here
74
+ * degrades gracefully. To ADD rather than replace, take a seat inside the
75
+ * flow instead: `conversation.view` for a whole tab, the input regions for
76
+ * composer chrome.
77
+ */
78
+ 'conversation.session': {
79
+ kind: 'single';
80
+ scope: 'session';
81
+ };
82
+ /**
83
+ * The strip above the session's scrollport: title, view tabs, and the
84
+ * action row. Taking this seat means rendering all three yourself, and it
85
+ * also collapses `conversation.session.header.actions` — that additive
86
+ * seat is declared by whoever occupies this one, so replacing the header
87
+ * takes every action entry down with it.
88
+ */
89
+ 'conversation.session.header': {
90
+ kind: 'single';
91
+ scope: 'session';
92
+ };
93
+ /**
94
+ * One breadcrumb title and its lineage controls. The render site keeps
95
+ * the ordinary title as fallback; an occupant receives plain title data
96
+ * and may replace a subagent title with one combined navigation control.
97
+ */
98
+ 'conversation.session.header.lineage': {
99
+ kind: 'single';
100
+ scope: 'session';
101
+ owner: ConversationHeaderLineageOwnerProps;
102
+ };
103
+ /**
104
+ * One button in the session header's action row — the additive way to put
105
+ * a per-session control beside the title without replacing the header.
106
+ * Entries render by ascending `order`; negative values are reserved for
107
+ * static session context that precedes interactive actions. The owner
108
+ * passes nothing: everything a control needs comes from the framework
109
+ * session kit (`sessionId`, `useSession`, `useInput`, `inputActions`) and
110
+ * from the registrant's own inject face, so an empty owner share means
111
+ * self-sufficient, not starved.
112
+ */
113
+ 'conversation.session.header.actions': {
114
+ kind: 'list';
115
+ scope: 'session';
116
+ owner: ConversationHeaderActionOwnerProps;
117
+ };
118
+ /**
119
+ * Right-aligned Session utilities kept outside the title-adjacent action
120
+ * group, so an optional utility cannot reorder session context or lineage.
121
+ */
122
+ 'conversation.session.header.utilities': {
123
+ kind: 'list';
124
+ scope: 'session';
125
+ owner: ConversationHeaderActionOwnerProps;
126
+ };
127
+ /**
128
+ * The conversation view ring: one list entry per view tab (chat here;
129
+ * trajectory/waterfall from ui-trajectory), rendered one-at-a-time by
130
+ * the session body via `only: <active id>`. Declared by this package's
131
+ * body entry (declaring is claiming). Session scope: views read the
132
+ * conversation snapshot through the standard kit.
133
+ */
134
+ 'conversation.view': {
135
+ kind: 'list';
136
+ scope: 'session';
137
+ owner: ConvViewOwnerProps;
138
+ };
139
+ /** Final business node renderer, dispatched by `ChatConversationViewNode.kind`. */
140
+ 'conversation.chat.node': {
141
+ kind: 'keyed';
142
+ scope: 'session';
143
+ owner: ChatNodeOwnerProps;
144
+ keyProps: {
145
+ [Kind in ChatNodeKind]: {
146
+ node: ChatNode<Kind>;
147
+ };
148
+ };
149
+ hookContext: string;
150
+ inject: ChatNodeTurnDataInjected;
151
+ };
152
+ /** Optional renderer for one consecutive group of durable message images. */
153
+ 'conversation.message.images': {
154
+ kind: 'single';
155
+ scope: 'session';
156
+ owner: MessageImagesOwnerProps;
157
+ };
158
+ /**
159
+ * The chat view's per-command row hole: keyed dispatch on the command
160
+ * name (`command/run.name`; a run-less cross-window node has none and
161
+ * always lands on the fallback). Declared by the chat view entry; the
162
+ * render site dispatches via `entryKey: name` with GenericCommandCard as
163
+ * the `fallback` — a slash command renders durably with zero
164
+ * registration, and a domain upgrades by registering one row component.
165
+ */
166
+ 'conversation.chat.commandview': {
167
+ kind: 'keyed';
168
+ scope: 'session';
169
+ owner: CommandRowOwnerProps;
170
+ };
171
+ /**
172
+ * The completed Turn Node's extension chain, rendered before that Node's
173
+ * IconActions. Entries derive a match from the engine-owned Turn and
174
+ * closing seq before mounting, so presentation components never mount
175
+ * only to return null; an all-declined chain renders nothing.
176
+ */
177
+ 'conversation.chat.turnTail': {
178
+ kind: 'chain';
179
+ scope: 'session';
180
+ owner: TurnTailOwnerProps;
181
+ };
182
+ /**
183
+ * Action strip attached to one finalized assistant message, rendered
184
+ * inside that message's IconActions row. The chat entry owns the render
185
+ * site and passes the addressed message identity; contributors add
186
+ * per-message actions without importing the conversation implementation.
187
+ * Entries render by ascending `order`.
188
+ */
189
+ 'conversation.chat.assistant-actions': {
190
+ kind: 'list';
191
+ scope: 'session';
192
+ owner: AssistantActionOwnerProps;
193
+ };
194
+ /**
195
+ * The body of the details panel for the tool call the user selected —
196
+ * one occupant, so taking it means rendering every tool's output, not just
197
+ * the ones you know. The owner passes a frozen `block` whose two lifecycle
198
+ * forms must both be handled: branch on `'kind' in block` (a settled
199
+ * `ToolResultNode` has it, a still-running call does not), and treat
200
+ * `cwd` as display-only, for shortening workspace-rooted paths.
201
+ * A per-tool renderer belongs in the keyed `tool.call.toolview` seat
202
+ * instead; this one is the whole panel.
203
+ */
204
+ 'conversation.details.tool': {
205
+ kind: 'single';
206
+ scope: 'session';
207
+ owner: DetailsToolOwnerProps;
208
+ };
209
+ /**
210
+ * The composer takeover chain: entries are selector-routed replacements
211
+ * of the default InputBar. Declared by this package's 'conversation'
212
+ * entry; the owner dispatches the {@link ComposerChainProps} currency and
213
+ * routing lives in entry selectors — new takeover kinds register with
214
+ * zero owner changes.
215
+ */
216
+ 'conversation.composer': {
217
+ kind: 'chain';
218
+ scope: 'session';
219
+ owner: ComposerChainProps;
220
+ };
221
+ /**
222
+ * The hero-phase Workspace picker hole: rendered by ConversationRoot
223
+ * while the session is blank (picking another workspace switches to that
224
+ * workspace's blank session, draft carried). Root scope: the picker
225
+ * reads the global workspace list.
226
+ */
227
+ 'conversation.hero.workspace': {
228
+ kind: 'single';
229
+ scope: 'root';
230
+ owner: EmptyWorkspaceOwnerProps;
231
+ };
232
+ /**
233
+ * Brand mark leading the blank-session headline. Declared by this
234
+ * package's `conversation` entry; the shell supplies a Hydra fallback.
235
+ */
236
+ 'conversation.hero.brand.mark': {
237
+ kind: 'single';
238
+ scope: 'root';
239
+ owner: HeroBrandMarkOwnerProps;
240
+ };
241
+ /**
242
+ * The agent-preset chip beside the workspace picker on the new-session
243
+ * screen. Root scope: no session exists yet, so the choice is staged for
244
+ * the next one rather than applied to a current one.
245
+ */
246
+ 'conversation.hero.agentPreset': {
247
+ kind: 'single';
248
+ scope: 'root';
249
+ owner: HeroAgentPresetOwnerProps;
250
+ };
251
+ /**
252
+ * A full-width row of its own, stacked above the composer card — the seat
253
+ * for anything that needs a line to itself (queue rows, a todo strip, a
254
+ * goal bar). Pick this over the three seats below when your content wraps
255
+ * or carries prose; pick `conversation.composer.dock` for an ambient
256
+ * readout under the card, and `conversation.input.left` /
257
+ * `.right` for a small control INSIDE the card's tool row.
258
+ * Read only `session`/`input` off the owner share ({@link InputZone}) —
259
+ * both are point-in-time snapshots re-rendered for you, never subscribe.
260
+ */
261
+ 'conversation.input.dock': {
262
+ kind: 'list';
263
+ scope: 'session';
264
+ owner: InputZone;
265
+ };
266
+ /**
267
+ * The band under the composer card, inside the bar's width column — the
268
+ * seat for an ambient readout about the conversation (the shipped stats
269
+ * line lives here). Same {@link InputZone} owner share as the other
270
+ * regions. Anything the user must click belongs in the tool row instead
271
+ * (`conversation.input.left` / `.right`); anything needing its own line
272
+ * above the card belongs in `conversation.input.dock`.
273
+ */
274
+ 'conversation.composer.dock': {
275
+ kind: 'list';
276
+ scope: 'session';
277
+ owner: InputZone;
278
+ };
279
+ /**
280
+ * The left end of the tool row INSIDE the composer card, after the
281
+ * resident chrome (access mode, plan, attach) — the seat for a small
282
+ * always-visible control. Entries sit beside that chrome, never replace
283
+ * it. Same {@link InputZone} owner share; use `.right` for a control that
284
+ * belongs next to the send button, and the docks for anything taller than
285
+ * one row.
286
+ */
287
+ 'conversation.input.left': {
288
+ kind: 'list';
289
+ scope: 'session';
290
+ owner: InputZone;
291
+ };
292
+ /**
293
+ * The right end of the same tool row, before the primary send button —
294
+ * the seat for a control the user reaches on the way to sending (the
295
+ * model select sits in its own named seat just left of here). Same
296
+ * {@link InputZone} owner share and the same one-row height budget as
297
+ * `conversation.input.left`.
298
+ */
299
+ 'conversation.input.right': {
300
+ kind: 'list';
301
+ scope: 'session';
302
+ owner: InputZone;
303
+ };
304
+ /**
305
+ * The default composer body: a single slot rendered as the composer
306
+ * chain's fallback (a real entry, not a chain rider, so a
307
+ * takeover election hides rather than unmounts it and the textarea DOM
308
+ * survives). Session-maybe: the bar stays mounted across the
309
+ * no-session/session transition — the no-workspace hero renders the SAME
310
+ * textarea DOM as a read-only Workspace-picker trigger instead of a
311
+ * parallel inert tree — with the machine hooks absent until a session is
312
+ * current. InputBar registers
313
+ * here from this package's apply; its machine state arrives through the
314
+ * standard provide channel (useInput + inputActions), the keyboard
315
+ * command face through its own inject.
316
+ */
317
+ 'conversation.composer.bar': {
318
+ kind: 'single';
319
+ scope: 'session-maybe';
320
+ owner: ComposerBarOwnerProps;
321
+ };
322
+ /** Optional draft-image rail, drop target, and preview surface inside the composer. */
323
+ 'conversation.input.attachments': {
324
+ kind: 'single';
325
+ scope: 'session-maybe';
326
+ owner: ComposerAttachmentsOwnerProps;
327
+ };
328
+ /**
329
+ * The named plan-status seat in the composer tool row, immediately right
330
+ * of the access-mode control — one occupant, so taking it means rendering
331
+ * the plan affordance yourself. The owner passes only `locked` (see
332
+ * {@link InputControlOwnerProps}): honour it by refusing interaction, and
333
+ * take everything else from the framework session kit or your own inject.
334
+ * Unoccupied, the seat renders nothing at all — the bar paints no
335
+ * placeholder, so an absent plan plugin costs no layout.
336
+ */
337
+ 'conversation.input.plan': {
338
+ kind: 'single';
339
+ scope: 'session';
340
+ owner: InputControlOwnerProps;
341
+ };
342
+ /**
343
+ * The named model-select seat at the right end of the composer tool row,
344
+ * left of the send button — one occupant, so taking it means rendering the
345
+ * whole model affordance yourself. Same `locked`-only owner share and same
346
+ * renders-nothing-while-empty contract as the plan seat. Note the composer
347
+ * deliberately keeps this seat LIVE while it refuses text for a
348
+ * model-related block: every such block is one the user clears by picking
349
+ * a model here.
350
+ */
351
+ 'conversation.input.model': {
352
+ kind: 'single';
353
+ scope: 'session';
354
+ owner: InputControlOwnerProps;
355
+ };
356
+ }
357
+ /**
358
+ * ui-conversation's members of the session standard kit, provided through
359
+ * `sessions.provide`: every session-scope slot component
360
+ * receives the input machine's state hook and the two public actions.
361
+ */
362
+ interface SessionStandardProps {
363
+ /** Selector hook over the session's live input machine state. */
364
+ useInput: SnapshotSelectorHook<InputState>;
365
+ /** The public input action face (stable identity per session). */
366
+ inputActions: InputActions;
367
+ }
368
+ /** Input members for the resident composer while current session is optional. */
369
+ interface SessionMaybeStandardProps {
370
+ useInput: MaybeSnapshotSelectorHook<InputState>;
371
+ inputActions: InputActions | undefined;
372
+ }
373
+ }
374
+ /** Owner share of the hero agent-preset chip: the shell supplies nothing. */
375
+ export interface HeroAgentPresetOwnerProps {
376
+ /** Marker field: the chip owns its own roster, staging, and menu state. */
377
+ children?: never;
378
+ }
379
+ /** Owner share of the strict session content seat. */
380
+ export interface ConversationSessionOwnerProps {
381
+ /**
382
+ * Wrap the view ring in the transcript scrollport that also hosts the
383
+ * sticky composer seat (whole `'conversation.composer'` chain output).
384
+ * Supplied for every real session (hero/settling/active) so the composer
385
+ * keeps one tree seat across the blank → active flip; the header stays
386
+ * outside that wrapper as ordinary column chrome (`flex: none`), while
387
+ * active CSS sticks the seat to the bottom of the same scrollport so wheel
388
+ * over the footer scrolls the flow.
389
+ * @param view - the session view-ring content (null while blank chrome is hidden).
390
+ * @returns the scrollport containing `view` and the sticky composer seat.
391
+ */
392
+ wrapActiveBody?: (view: ReactNode) => ReactNode;
393
+ }
394
+ /** Header actions derive their state from the standard session/global kit. */
395
+ export interface ConversationHeaderActionOwnerProps {
396
+ }
397
+ /** Plain breadcrumb data handed to the optional lineage renderer. */
398
+ export interface ConversationHeaderLineageOwnerProps {
399
+ /** Session represented by this breadcrumb title. */
400
+ lineageSessionId: SessionId;
401
+ /** Display title available to a renderer that combines the title with a control. */
402
+ displayTitle: string;
403
+ /** Navigate to an ancestor title when its combined control is clicked. */
404
+ openTitle?: () => void;
405
+ }
406
+ /**
407
+ * The input-region slot currency: dock/left/right entries read
408
+ * the conversation snapshot and the live input state as owner props (both
409
+ * are point-in-time snapshots — the dispatching skeleton re-renders on
410
+ * either store's change, so entries stay current without subscribing).
411
+ */
412
+ export interface InputZone {
413
+ readonly session: ConversationSnapshot;
414
+ readonly input: InputState;
415
+ }
416
+ /**
417
+ * View-slot owner share: the cross-view inspect handoff (otherwise views need
418
+ * nothing from the render site — sessionId and the snapshot hook arrive as
419
+ * framework-standard props; tool rows go through each view's own declared
420
+ * toolview hole).
421
+ */
422
+ export interface ConvViewOwnerProps {
423
+ /** One-shot inspect request from another view (chat's Inspect button); null when idle. */
424
+ inspect?: {
425
+ callId: CallId;
426
+ } | null;
427
+ /** Acknowledge the inspect request once applied (clears the store field). */
428
+ onInspectDone?: () => void;
429
+ }
430
+ /**
431
+ * Optional prose file-mention provider, consumed via `ctx.get('chatFileMentions')`
432
+ * (optional-service convention): the chat view asks it for a closing message's
433
+ * inline-code vocabulary and threads the result into MarkdownText. Absent
434
+ * service — the providing plugin composed out of cordis.yml — turns the
435
+ * surface off; the prose renders inert code.
436
+ */
437
+ export interface ChatFileMentions {
438
+ /**
439
+ * Mention vocabulary for the closing message the owner currency names.
440
+ * @param owner - Turn-tail owner currency (Turn data, closing seq, opener).
441
+ * @returns The resolver MarkdownText consumes, or undefined when the turn
442
+ * produced nothing worth linking.
443
+ */
444
+ forClosing(owner: TurnTailOwnerProps): MarkdownFileMentions | undefined;
445
+ }
446
+ declare module '@hydraharness/cordis' {
447
+ interface Context {
448
+ /** Prose file-mention provider (ui-deliverables); reach via ctx.get — optional. */
449
+ chatFileMentions: ChatFileMentions;
450
+ }
451
+ }
452
+ /**
453
+ * Owner currency of the chat view's turn-tail hole: the engine-owned Turn and
454
+ * the closing assistant's anchor. Registrants read their own typed Turn data
455
+ * and open files through the same opener the tool rows use.
456
+ */
457
+ export interface TurnTailOwnerProps {
458
+ /** Engine-owned closing Turn boundary. */
459
+ turn: TurnLocation;
460
+ /** The closing assistant's seq — the anchor the tail renders under. */
461
+ seq: number;
462
+ /**
463
+ * Open a filesystem path through the Host (tool-row semantics; the chat
464
+ * view resolves relative paths against the session cwd).
465
+ */
466
+ openFile: (path: string) => void;
467
+ }
468
+ /**
469
+ * Owner currency of the assistant-message action strip: the durable identity
470
+ * of the one finalized message the contributed actions address. Only finalized
471
+ * messages reach this slot, so the id is always present.
472
+ */
473
+ export interface AssistantActionOwnerProps {
474
+ /** Stable identity carried from the `assistant/message` event. */
475
+ messageId: MessageId;
476
+ }
477
+ /** Hook constrained to business data published on the current Chat Node's Turn. */
478
+ export type UseChatNodeTurnData = <Key extends Extract<keyof ConversationTurnDataMap, string>>(key: Key) => Readonly<ConversationTurnDataMap[Key]> | undefined;
479
+ /** Slot-level Hook factory used by renderers reading their Node's Turn data. */
480
+ export interface ChatNodeTurnDataInjected {
481
+ hooks: {
482
+ turnData: SlotHookFactory<'conversation.chat.node', UseChatNodeTurnData>;
483
+ };
484
+ }
485
+ /** Stable owner currency delivered to one keyed Chat business renderer. */
486
+ export interface ChatNodeOwnerProps {
487
+ /** Resolve session-authorized media for a generated output. */
488
+ loadMedia: (attachment: ImageAttachmentRef | VideoAttachmentRef) => Promise<string>;
489
+ /** Selected Tool call, when the shared details store names one. */
490
+ selectedCallId?: CallId | undefined;
491
+ /** Session workspace root; Tool summaries display paths relative to it. */
492
+ cwd?: string | undefined;
493
+ openFile: (path: string) => void;
494
+ inspectCall: (callId: CallId) => void;
495
+ forkAt: (seq: number) => void;
496
+ /** Render a historical image group through the attachment slot. */
497
+ renderMessageImages: RenderMessageImages;
498
+ fileMentions: (owner: TurnTailOwnerProps) => MarkdownFileMentions | undefined;
499
+ }
500
+ /** Full props of one registered keyed Chat business renderer. */
501
+ export type ChatNodeViewProps<Kind extends ChatNodeKind = ChatNodeKind> = PropsRuntime<'conversation.chat.node', Kind> & PropsLocale<'conversation'>;
502
+ /** Owner currency of the details panel's Tool output renderer. */
503
+ export interface DetailsToolOwnerProps {
504
+ /** Frozen selected call slice. */
505
+ block: ToolCallBlock;
506
+ /** Session workspace root for card cwd and relative-path display. */
507
+ cwd?: string | undefined;
508
+ }
509
+ /**
510
+ * Owner share of the per-command row slot: the frozen {@link CommandNode}
511
+ * slice off the snapshot (cache-stable reference — memo premise). The node
512
+ * carries the whole lifecycle (structured name/args, pairing id, and
513
+ * outcome-or-executing). A successful domain command may also carry the
514
+ * explicitly linked projection node needed to fold two log records into one
515
+ * presentation row.
516
+ */
517
+ export interface CommandRowOwnerProps {
518
+ /** Folded command lifecycle node (run + optional done). */
519
+ node: CommandNode;
520
+ /** Explicitly linked compaction checkpoint for the settled `/compact` presentation. */
521
+ compaction?: CompactionSummaryNode;
522
+ }
523
+ /** Full props of a registered command-row component. */
524
+ export type CommandRowProps = PropsRuntime<'conversation.chat.commandview'>;
525
+ /**
526
+ * Base props of a conversation view entry: the framework standard kit for the
527
+ * session-scope 'conversation.view' slot (useSession narrowed to the
528
+ * conversation snapshot by the runtime merge, sessionId, useSessions).
529
+ * Entries declaring the shared store or an inject face compose their shares
530
+ * on top (the chat entry's {@link ChatViewSlotProps}); store-less pure
531
+ * readers (ui-trajectory) take this base alone.
532
+ */
533
+ export type ConvViewProps = PropsRuntime<'conversation.view'>;
534
+ /** The shared chat store handle type declared by the Session header/body, details, and chat-view registrations. */
535
+ export type ChatStore = ReturnType<typeof createChatStore>;
536
+ /** Business callbacks injected into the conversation slot. */
537
+ export interface ConversationInjected {
538
+ /**
539
+ * Connect the selected Workspace and open its reusable/new blank session.
540
+ * When a blank session is already current, carry its draft to the target.
541
+ */
542
+ selectWorkspace: (workspaceId: WorkspaceId) => Promise<void>;
543
+ /**
544
+ * Framework-bound sources. `composerBlock` is this session's block when a
545
+ * plugin raised one; the reason is the blocker's own localized copy, which
546
+ * the root renders as the inert composer's placeholder.
547
+ */
548
+ hooks: {
549
+ composerBlock: ObservableSnapshot<ComposerBlock | undefined>;
550
+ };
551
+ }
552
+ /** Business callbacks injected into the strict Session body seat. */
553
+ export interface ConversationSessionInjected {
554
+ /** Views projected from the `conversation.view` slot ledger. */
555
+ views: {
556
+ list: () => readonly ViewTab[];
557
+ subscribe: (fn: () => void) => () => void;
558
+ version: () => number;
559
+ };
560
+ /** Release historical image URLs when this rendered session scope unmounts. */
561
+ releaseSessionImages: (sessionId: SessionId) => void;
562
+ /** Bind the input machine's draft persistence mirror to the session store. */
563
+ bindDraftMirror: (write: (text: string) => void) => () => void;
564
+ }
565
+ /** Business callbacks injected into the strict session header seat. */
566
+ export interface ConversationSessionHeaderInjected {
567
+ /** Views projected from the `conversation.view` slot ledger. */
568
+ views: {
569
+ list: () => readonly ViewTab[];
570
+ subscribe: (fn: () => void) => () => void;
571
+ version: () => number;
572
+ };
573
+ /** Select a real Session through the runtime navigation owner. */
574
+ open: (sessionId: SessionId) => void;
575
+ /** Select a stored transcript path without changing Session identity. */
576
+ openVersion?: ((id: SessionId | import('@hydraharness/harness-session/types').SessionVersionId) => Promise<void> | void) | undefined;
577
+ /** Add a stored transcript path as logged model reference context. */
578
+ referenceVersion?: ((id: SessionId | import('@hydraharness/harness-session/types').SessionVersionId) => Promise<void> | void) | undefined;
579
+ }
580
+ /**
581
+ * Owner share of the composer-bar slot: ConversationRoot's layout-phase
582
+ * inputs plus the input-region child-slot content it renders (the region
583
+ * slots stay declared/rendered by the conversation entry; the bar hosts the
584
+ * results as chrome).
585
+ */
586
+ export interface ComposerBarOwnerProps {
587
+ /** Hero = empty-state centered card; composer = resident bottom bar. */
588
+ variant: 'hero' | 'composer';
589
+ /**
590
+ * A block another plugin raised for this session: the bar refuses input and
591
+ * shows the blocker's reason as the placeholder, but — unlike `disabled` —
592
+ * keeps the model seat live. Every block this contract has is one the user
593
+ * clears by choosing a model, so locking that seat too would leave the
594
+ * composer telling them to do the one thing it prevents.
595
+ */
596
+ blocked?: {
597
+ readonly reason: string;
598
+ };
599
+ /**
600
+ * Inert no-workspace state: the bar locks message actions while preserving
601
+ * its normal DOM so the Workspace pick transitions in place.
602
+ */
603
+ disabled?: boolean;
604
+ /** Whether the shared Workspace picker menu is expanded, regardless of which trigger opened it. */
605
+ workspacePickerOpen?: boolean;
606
+ /** Open the existing Workspace picker from the inert textarea. */
607
+ onRequestWorkspace?: () => void;
608
+ placeholder?: string;
609
+ /** Optional content rendered above the textarea. */
610
+ accessory?: ReactNode;
611
+ /** Floating overlay anchor content (menu / popup shell entries), rendered inside the card. */
612
+ overlay?: ReactNode;
613
+ /** input.left slot entries (tool row, beside the resident chrome). */
614
+ leftItems?: ReactNode;
615
+ /** input.right slot entries (tool row, before the primary button). */
616
+ rightItems?: ReactNode;
617
+ /** composer.dock entries (stats line), rendered under the card inside the bar's width column. */
618
+ footer?: ReactNode;
619
+ }
620
+ /** Injected share of the composer-bar entry (package-internal faces). */
621
+ export interface ComposerBarInjected {
622
+ /** The InputBar-exclusive keyboard/DOM command face (private plane); absent with the session. */
623
+ keyboard: ComposerKeyboard | undefined;
624
+ /** Create previews and append image ids to the session input. */
625
+ addImages: ((files: readonly File[]) => string | null) | undefined;
626
+ /** Release one preview and remove its id from session input. */
627
+ removeImage: ((id: DraftAttachmentId) => void) | undefined;
628
+ /** Resolve ordered input ids to browser-owned draft images. */
629
+ draftImages: ((ids: readonly DraftAttachmentId[]) => readonly ComposerAttachment[]) | undefined;
630
+ /** Resolve ordered input ids to browser-selected text-file attachments. */
631
+ draftBrowserAnnotations?: ((ids: readonly DraftAttachmentId[]) => readonly BrowserAnnotationAttachment[]) | undefined;
632
+ /** Release one browser-selected text-file attachment. */
633
+ removeBrowserAnnotation?: ((id: DraftAttachmentId) => void) | undefined;
634
+ /** Update the comment carried with one browser-selected text-file attachment. */
635
+ updateBrowserAnnotationComment?: ((id: DraftAttachmentId, comment: string) => void) | undefined;
636
+ /** Resolve one keyboard submission gesture against the current running state and persisted preference. */
637
+ resolveSubmitMode: (running: boolean, gesture: ComposerSubmitGesture, steeringAvailable: boolean) => InputSubmitMode;
638
+ /** Toggle the shared slash menu with only its command source; absent without ui-input-trigger or a session. */
639
+ toggleCommandMenu: ((selection: EditSelection) => void) | undefined;
640
+ /** Cancel the in-flight turn; absent with the session. */
641
+ stop: (() => void) | undefined;
642
+ /**
643
+ * Submit one slash-command line against this session's agent (the chrome
644
+ * controls' write path — the permission chip submits `/permission <preset>`);
645
+ * absent with the session.
646
+ * Resolves admission: false = rejected/unmatched/transport failure.
647
+ */
648
+ command: ((line: string) => Promise<boolean>) | undefined;
649
+ /**
650
+ * Registrant hooks compartment: the renderer binds these to
651
+ * useNotices/useLexicon (static absent sources without a session — hook
652
+ * order stays constant).
653
+ */
654
+ hooks: {
655
+ /** Latest surfaced notice (null after none; seq keys re-render of repeats). */
656
+ notices: ObservableSnapshot<InputNotice | null>;
657
+ /** Hot plain-text reference lexicon for the decoration scan (plain-text-reference decision;
658
+ * see .agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md). */
659
+ lexicon: ObservableSnapshot<ReadonlyMap<'/' | '@', readonly string[]>>;
660
+ /** Source name opened by the programmatic menu launcher, or null. */
661
+ menuLauncher: ObservableSnapshot<string | null>;
662
+ };
663
+ }
664
+ /**
665
+ * Owner share of the two named composer control seats (plan / model): the
666
+ * bar passes its disable state; the filling entry owns everything else.
667
+ */
668
+ export interface InputControlOwnerProps {
669
+ /** Session-removed lock (the bar's chrome disable state). */
670
+ locked: boolean;
671
+ }
672
+ /** Full composer-bar props: standard kit & owner share & control-seat render share & injected share (hooks bound) & locale seat. */
673
+ export type ComposerBarProps = PropsRuntime<'conversation.composer.bar'> & PropsRenderSlots<'conversation.input.attachments' | 'conversation.input.plan' | 'conversation.input.model'> & InjectFace<ComposerBarInjected> & PropsLocale<'conversation'>;
674
+ /**
675
+ * Composer chain currency: what ConversationRoot dispatches at its
676
+ * renderSlotChain site. The owner declares the currency only — never a
677
+ * per-entry contract; takeover packages narrow it in their own selectors
678
+ * (`interactions.find(i => i.kind === ...)`), so new takeover kinds register
679
+ * with zero owner changes.
680
+ */
681
+ export interface ComposerChainProps {
682
+ interactions: readonly PendingInteraction[];
683
+ /** Current conversation facts for feature-owned takeover selectors. */
684
+ session: ConversationSnapshot | undefined;
685
+ }
686
+ /** Presentation props supplied to the blank-session brand-mark occupant. */
687
+ export interface HeroBrandMarkOwnerProps {
688
+ /** Requested square edge in pixels. */
689
+ size: number;
690
+ /** Host CSS class for preserving the default hero mark color and hover motion. */
691
+ className?: string | undefined;
692
+ }
693
+ /**
694
+ * Full conversation-slot component props: runtime & child-render (view ring
695
+ * + composer chain/bar + input-region + hero picker slots) & store & injected
696
+ * shares & the locale seat.
697
+ */
698
+ export type ConversationSlotProps = PropsRuntime<'conversation'> & PropsRenderSlots<'conversation.session' | 'conversation.session.header' | 'conversation.composer' | 'conversation.composer.bar' | 'conversation.input.overlay' | 'conversation.input.dock' | 'conversation.composer.dock' | 'conversation.input.left' | 'conversation.input.right' | 'conversation.hero.brand.mark' | 'conversation.hero.workspace' | 'conversation.hero.agentPreset'> & InjectFace<ConversationInjected> & PropsLocale<'conversation'>;
699
+ /** Full strict-session body props: per-session store, view ring, and draft mirror. */
700
+ export type ConversationSessionSlotProps = PropsRuntime<'conversation.session'> & PropsRenderSlots<'conversation.view'> & PropsStore<ChatStore> & ConversationSessionInjected;
701
+ /** Full strict-session header props: shared store, tabs/actions render shares, navigation, and locale. */
702
+ export type ConversationSessionHeaderSlotProps = PropsRuntime<'conversation.session.header'> & PropsRenderSlots<'conversation.session.header.lineage' | 'conversation.session.header.actions' | 'conversation.session.header.utilities'> & PropsStore<ChatStore> & ConversationSessionHeaderInjected & PropsLocale<'conversation'>;
703
+ /** The pending approval carrier the owner dispatches into the composer chain. */
704
+ export type ApprovalWait = PendingWait<'approval'>;
705
+ /**
706
+ * Approval domain face over the carrier (the ui-user-questions PendingQuestion
707
+ * pattern): render identity and question material forwarded transparently;
708
+ * answer owns the wire encoding — the ApprovalResponsePayload value shape
709
+ * with the audit correlation the host reconciles — and turns a rejected
710
+ * carrier receipt into a thrown error. Minted per carrier via useMemo.
711
+ */
712
+ export declare class PendingApproval {
713
+ private readonly wait;
714
+ /**
715
+ * @param wait - the runtime carrier for one pending approval question.
716
+ */
717
+ constructor(wait: ApprovalWait);
718
+ /** Opaque render identity (React key / one-shot latch remount axis), forwarded from the carrier. */
719
+ get key(): string;
720
+ /** The tool the question is about (headline fallback), forwarded from the carrier payload. */
721
+ get toolName(): string;
722
+ /** The asker's human-readable WHY (headline when present), forwarded from the carrier payload. */
723
+ get reason(): string | undefined;
724
+ /** The paired tool call's id when the ask names one (command-line lookup key), forwarded from the carrier payload. */
725
+ get callId(): string | undefined;
726
+ /**
727
+ * Deliver the user's decision; a rejected carrier receipt throws. Panel
728
+ * removal stays frame-driven: the broadcast `approval/resolved` settles the
729
+ * wait and drops it from the pending list.
730
+ * @param outcome - the only two client-answerable outcomes.
731
+ */
732
+ answer(outcome: 'allowed-once' | 'rejected'): Promise<void>;
733
+ }
734
+ /**
735
+ * Full approval-composer props: the framework runtime share (chain currency +
736
+ * session/global standard kit) plus the chain `matched` share — the entry's
737
+ * selector result, already narrowed to the approval carrier — plus the
738
+ * standard locale seat. No injected share: the carrier plus the domain face
739
+ * above carry the whole behavior surface; the paired command line derives
740
+ * from useSession in-component.
741
+ */
742
+ export type ApprovalComposerProps = PropsRuntime<'conversation.composer'> & {
743
+ matched: ApprovalWait;
744
+ } & PropsLocale<'conversation'>;
745
+ /** In-memory reader position resilient to transcript width reflow. */
746
+ export interface ChatScrollPosition {
747
+ /** Stable rendered node/call identity nearest the visible reading edge. */
748
+ readonly anchorKey: string;
749
+ /** Anchor top relative to the transcript scrollport when saved. */
750
+ readonly anchorTop: number;
751
+ /** Approximate offset used before the semantic anchor is measured. */
752
+ readonly scrollTop: number;
753
+ }
754
+ /**
755
+ * Injected share of the chat view entry: the two callbacks whose targets live
756
+ * outside the view (layout orchestration; the session object layer).
757
+ */
758
+ export interface ChatViewInjected {
759
+ /** Selection write + details panel opening in one gesture (store action + layout orchestration). */
760
+ openDetails: (target: SelectionTarget) => void;
761
+ /**
762
+ * Open a tool-arg filesystem path with the host OS default application
763
+ * (relative paths resolve against the session cwd). Always returns a
764
+ * promise: fulfills when the Host opens the path, rejects when it cannot
765
+ * hand the path off (the chat view shows that reason and a retry).
766
+ */
767
+ openFile: (path: string) => Promise<void>;
768
+ loadOlder: () => void;
769
+ /** Resolve session-authorized historical media for inline display. */
770
+ loadMedia: (attachment: ImageAttachmentRef | VideoAttachmentRef) => Promise<string>;
771
+ /** Hand a call off to the trajectory view: write the one-shot inspect target and switch tabs. */
772
+ inspectCall: (callId: CallId) => void;
773
+ /**
774
+ * Per-session scroll memory surviving view switches (in-memory, never
775
+ * persisted): the view saves on every scroll and restores on remount; a
776
+ * fresh page load starts empty and keeps the open-jump-to-bottom default.
777
+ */
778
+ chatScroll: {
779
+ /** Record a semantic reader position; null clears it when pinned. */
780
+ save: (position: ChatScrollPosition | null) => void;
781
+ /** Last reader position, or null when pinned or never recorded. */
782
+ read: () => ChatScrollPosition | null;
783
+ };
784
+ /** Fork through the completed turn ending at the eligible message `seq`, then open the child. */
785
+ forkAt: (seq: number) => void;
786
+ /**
787
+ * Prose file-mention vocabulary for one closing message, from the optional
788
+ * {@link ChatFileMentions} service (resolved lazily per call, so composing
789
+ * the provider in or out takes effect live). Undefined when the service is
790
+ * absent or the turn produced nothing worth linking.
791
+ */
792
+ fileMentions: (owner: TurnTailOwnerProps) => MarkdownFileMentions | undefined;
793
+ }
794
+ /** Full chat-view component props: runtime & its Tool/command/tail render shares & store & injected & locale seat. */
795
+ export type ChatViewSlotProps = PropsRuntime<'conversation.view'> & PropsRenderSlots<'conversation.chat.node' | 'conversation.message.images'> & PropsStore<ChatStore> & ChatViewInjected & PropsLocale<'conversation'>;
796
+ /** Full props of the attachment plugin's composer entry. */
797
+ export type ComposerAttachmentsProps = PropsRuntime<'conversation.input.attachments'> & PropsLocale<'conversation'>;
798
+ /** Full props of the attachment plugin's message-gallery entry. */
799
+ export type MessageImagesProps = PropsRuntime<'conversation.message.images'> & PropsLocale<'conversation'>;
800
+ /**
801
+ * Injected share of the details slot: the panel is otherwise a pure reader of
802
+ * the shared chat store, but its close button is a layout orchestration call.
803
+ */
804
+ export interface DetailsInjected {
805
+ /** Close the details panel (layout geometry stays with ctx.layout). */
806
+ closeDetails: () => void;
807
+ }
808
+ /** Full details-slot props: selection store, Tool output seat, injected close callback, and locale. */
809
+ export type DetailsSlotProps = PropsRuntime<'details'> & PropsRenderSlots<'conversation.details.tool'> & PropsStore<ChatStore> & DetailsInjected & PropsLocale<'conversation'>;
810
+ /** Owner share common to the hero / New-Session Workspace pickers. */
811
+ export interface EmptyWorkspaceOwnerProps {
812
+ open: boolean;
813
+ anchorRef?: RefObject<HTMLElement>;
814
+ /** Currently active workspace (renders a trailing check in the picker list). */
815
+ selectedId?: WorkspaceId | undefined;
816
+ onPick: (workspaceId: WorkspaceId) => void;
817
+ onClose: () => void;
818
+ }
819
+ //# sourceMappingURL=slots.d.ts.map