@jini-ai/chat 0.3.4 → 0.3.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 (92) hide show
  1. package/dist/react/components/AttachmentPreviewModal.d.ts +7 -0
  2. package/dist/react/components/AttachmentPreviewModal.d.ts.map +1 -0
  3. package/dist/react/components/AttachmentPreviewModal.hooks.d.ts +70 -0
  4. package/dist/react/components/AttachmentPreviewModal.hooks.d.ts.map +1 -0
  5. package/dist/react/components/AttachmentPreviewModal.hooks.js +200 -0
  6. package/dist/react/components/AttachmentPreviewModal.hooks.js.map +1 -0
  7. package/dist/react/components/AttachmentPreviewModal.js +18 -0
  8. package/dist/react/components/AttachmentPreviewModal.js.map +1 -0
  9. package/dist/react/components/Composer.d.ts.map +1 -1
  10. package/dist/react/components/Composer.js +1 -1
  11. package/dist/react/components/Composer.js.map +1 -1
  12. package/dist/react/components/MessageRow.d.ts.map +1 -1
  13. package/dist/react/components/MessageRow.js +7 -2
  14. package/dist/react/components/MessageRow.js.map +1 -1
  15. package/dist/react/components/composer-discovery.js +1 -1
  16. package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
  17. package/dist/react/features/chat-pane/components/ChatPane.d.ts.map +1 -1
  18. package/dist/react/features/chat-pane/components/ChatPane.js +4 -2
  19. package/dist/react/features/chat-pane/components/ChatPane.js.map +1 -1
  20. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts.map +1 -1
  21. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +20 -3
  22. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js.map +1 -1
  23. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.d.ts +10 -0
  24. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.d.ts.map +1 -0
  25. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.js +66 -0
  26. package/dist/react/features/chat-pane/hooks/useChatPaneComposerPlaceholder.hooks.js.map +1 -0
  27. package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.d.ts +12 -0
  28. package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.d.ts.map +1 -0
  29. package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.js +102 -0
  30. package/dist/react/features/chat-pane/react/components/AgentRuntimePicker.js.map +1 -0
  31. package/dist/react/features/chat-pane/react/components/ChatPane.d.ts +10 -0
  32. package/dist/react/features/chat-pane/react/components/ChatPane.d.ts.map +1 -0
  33. package/dist/react/features/chat-pane/react/components/ChatPane.js +137 -0
  34. package/dist/react/features/chat-pane/react/components/ChatPane.js.map +1 -0
  35. package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.d.ts +133 -0
  36. package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.d.ts.map +1 -0
  37. package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.js +258 -0
  38. package/dist/react/features/chat-pane/react/hooks/useAgentRuntimePicker.hooks.js.map +1 -0
  39. package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.d.ts +66 -0
  40. package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.d.ts.map +1 -0
  41. package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.js +223 -0
  42. package/dist/react/features/chat-pane/react/hooks/useChatPane.hooks.js.map +1 -0
  43. package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.d.ts +38 -0
  44. package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.d.ts.map +1 -0
  45. package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.js +252 -0
  46. package/dist/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.js.map +1 -0
  47. package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.d.ts +26 -0
  48. package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.d.ts.map +1 -0
  49. package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.js +60 -0
  50. package/dist/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.js.map +1 -0
  51. package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.d.ts +25 -0
  52. package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.d.ts.map +1 -0
  53. package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.js +83 -0
  54. package/dist/react/features/chat-pane/react/hooks/useChatPaneRuntimeInventory.hooks.js.map +1 -0
  55. package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.d.ts +38 -0
  56. package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.d.ts.map +1 -0
  57. package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.js +184 -0
  58. package/dist/react/features/chat-pane/react/hooks/useChatPaneWorkingDirectory.hooks.js.map +1 -0
  59. package/dist/react/features/chat-pane/react/styles.d.ts +2 -0
  60. package/dist/react/features/chat-pane/react/styles.d.ts.map +1 -0
  61. package/dist/react/features/chat-pane/react/styles.js +863 -0
  62. package/dist/react/features/chat-pane/react/styles.js.map +1 -0
  63. package/dist/react/features/chat-pane/styles.d.ts +1 -1
  64. package/dist/react/features/chat-pane/styles.d.ts.map +1 -1
  65. package/dist/react/features/chat-pane/styles.js +139 -3
  66. package/dist/react/features/chat-pane/styles.js.map +1 -1
  67. package/dist/react/features/chat-pane/types.d.ts +8 -0
  68. package/dist/react/features/chat-pane/types.d.ts.map +1 -1
  69. package/dist/react/features/model-picker/react/components/CredentialStatusBadge.d.ts +7 -0
  70. package/dist/react/features/model-picker/react/components/CredentialStatusBadge.d.ts.map +1 -0
  71. package/dist/react/features/model-picker/react/components/CredentialStatusBadge.js +20 -0
  72. package/dist/react/features/model-picker/react/components/CredentialStatusBadge.js.map +1 -0
  73. package/dist/react/features/model-picker/react/components/ModelPicker.d.ts +9 -0
  74. package/dist/react/features/model-picker/react/components/ModelPicker.d.ts.map +1 -0
  75. package/dist/react/features/model-picker/react/components/ModelPicker.js +34 -0
  76. package/dist/react/features/model-picker/react/components/ModelPicker.js.map +1 -0
  77. package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.d.ts +27 -0
  78. package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.d.ts.map +1 -0
  79. package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.js +62 -0
  80. package/dist/react/features/model-picker/react/hooks/useModelPicker.hooks.js.map +1 -0
  81. package/dist/react/hooks/attachment-preview-cache.d.ts +47 -0
  82. package/dist/react/hooks/attachment-preview-cache.d.ts.map +1 -0
  83. package/dist/react/hooks/attachment-preview-cache.js +59 -0
  84. package/dist/react/hooks/attachment-preview-cache.js.map +1 -0
  85. package/dist/react/hooks/useComposer.d.ts.map +1 -1
  86. package/dist/react/hooks/useComposer.js +12 -0
  87. package/dist/react/hooks/useComposer.js.map +1 -1
  88. package/dist/react/poc-mcp-ui-official/create-poc-resource.d.ts +1 -1
  89. package/dist/react/poc-mcp-ui-official/create-poc-resource.js +1 -1
  90. package/dist/react/slots.d.ts +8 -0
  91. package/dist/react/slots.d.ts.map +1 -1
  92. package/package.json +4 -4
@@ -0,0 +1,223 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { describeChatPaneSendBlocker, findChatPaneSendBlocker, resolveChatPaneSelection, } from '../../rules.js';
3
+ import { definedProps } from '../../../../util/defined-props.js';
4
+ import { useComposer } from '../../../../hooks/useComposer.js';
5
+ import { useConversation, } from '../../../../hooks/useConversation.js';
6
+ import { useChatPaneWorkingDirectory, } from './useChatPaneWorkingDirectory.hooks.js';
7
+ function createAttachmentBatchId() {
8
+ return globalThis.crypto?.randomUUID?.()
9
+ ?? `chat-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
10
+ }
11
+ /** Module scope so each branch costs 1 cognitive point instead of 2 (nested one level inside
12
+ * `useChatPane` where it used to live as a ternary chain). */
13
+ function resolveChatPaneActivity(selectedAgent, conversation) {
14
+ if (selectedAgent === undefined)
15
+ return 'unavailable';
16
+ if (conversation.error)
17
+ return 'failed';
18
+ if (conversation.isStreaming)
19
+ return 'streaming';
20
+ return 'ready';
21
+ }
22
+ /**
23
+ * Runs one attachment upload and, unless superseded/unmounted/aborted by the time it resolves,
24
+ * hands the results (or error) to the caller's effects. Concurrent by design — unlike
25
+ * {@link useLatestOperation}'s latest-wins model, several batches may be in flight at once
26
+ * (`addAttachments`'s `activeUploadsRef` is a `Set`, not a single slot) — so this takes an explicit
27
+ * `stillWanted()` check per attempt rather than a shared generation token.
28
+ */
29
+ async function uploadAttachmentBatch(files, effects) {
30
+ try {
31
+ const uploaded = await effects.upload(files, { signal: effects.signal, batchId: effects.batchId });
32
+ if (!effects.stillWanted())
33
+ return;
34
+ for (const attachment of uploaded)
35
+ effects.onAttachment(attachment);
36
+ }
37
+ catch (error) {
38
+ if (!effects.stillWanted())
39
+ return;
40
+ effects.onError(error instanceof Error ? error : new Error(String(error)));
41
+ }
42
+ }
43
+ export function useChatPane(options) {
44
+ const [activeUploadCount, setActiveUploadCount] = useState(0);
45
+ const [attachmentError, setAttachmentError] = useState(null);
46
+ const mountedRef = useRef(true);
47
+ const attachmentGenerationRef = useRef(0);
48
+ const attachmentBatchIdRef = useRef(createAttachmentBatchId());
49
+ const activeUploadsRef = useRef(new Set());
50
+ const [internalSelection, setInternalSelection] = useState(options.initialSelection ?? { agentId: '' });
51
+ const workingDirectoryState = useChatPaneWorkingDirectory(definedProps({
52
+ workingDirectory: options.workingDirectory,
53
+ initialWorkingDirectory: options.initialWorkingDirectory,
54
+ onChangeWorkingDirectory: options.onChangeWorkingDirectory,
55
+ workingDirectoryAccess: options.workingDirectoryAccess,
56
+ }));
57
+ const requestedSelection = options.selection ?? internalSelection;
58
+ const selection = useMemo(() => resolveChatPaneSelection(options.agents, requestedSelection), [options.agents, requestedSelection]);
59
+ const selectedAgent = options.agents.find((agent) => agent.id === selection.agentId);
60
+ const composer = useComposer(definedProps({
61
+ initialDraft: options.initialDraft,
62
+ initialAgent: selection,
63
+ }));
64
+ const conversation = useConversation(definedProps({
65
+ transport: options.transport,
66
+ initialMessages: options.initialMessages,
67
+ conversationId: options.conversationId,
68
+ // Keys off an empty string, not `undefined` — `selection.agentId` is always a string (never
69
+ // absent), so only the falsy "no agent selected" case should omit the key.
70
+ agentId: selection.agentId || undefined,
71
+ }));
72
+ const activity = resolveChatPaneActivity(selectedAgent, conversation);
73
+ useEffect(() => {
74
+ options.onActivityChange?.(activity);
75
+ }, [activity, options.onActivityChange]);
76
+ useEffect(() => {
77
+ options.onMessagesChange?.(conversation.messages);
78
+ }, [conversation.messages, options.onMessagesChange]);
79
+ useEffect(() => {
80
+ mountedRef.current = true;
81
+ return () => {
82
+ mountedRef.current = false;
83
+ attachmentGenerationRef.current += 1;
84
+ for (const controller of activeUploadsRef.current)
85
+ controller.abort();
86
+ activeUploadsRef.current.clear();
87
+ };
88
+ }, []);
89
+ useEffect(() => {
90
+ if (!selection.agentId)
91
+ return;
92
+ if (requestedSelection.agentId === selection.agentId
93
+ && requestedSelection.model === selection.model
94
+ && requestedSelection.reasoning === selection.reasoning)
95
+ return;
96
+ if (options.selection === undefined)
97
+ setInternalSelection(selection);
98
+ composer.setAgent(selection);
99
+ options.onSelectionChange?.(selection);
100
+ }, [
101
+ composer,
102
+ options.onSelectionChange,
103
+ options.selection,
104
+ requestedSelection.agentId,
105
+ requestedSelection.model,
106
+ requestedSelection.reasoning,
107
+ selection,
108
+ ]);
109
+ const setSelection = useCallback((next) => {
110
+ const validated = resolveChatPaneSelection(options.agents, next);
111
+ setInternalSelection(validated);
112
+ composer.setAgent(validated);
113
+ options.onSelectionChange?.(validated);
114
+ }, [composer, options.agents, options.onSelectionChange]);
115
+ const sendBlocker = findChatPaneSendBlocker({
116
+ selectedAgent,
117
+ isStreaming: conversation.isStreaming,
118
+ activeUploadCount,
119
+ workingDirectoryPending: workingDirectoryState.workingDirectoryPending,
120
+ workingDirectoryInvalid: workingDirectoryState.workingDirectoryInvalid,
121
+ workingDirectoryError: workingDirectoryState.workingDirectoryError,
122
+ });
123
+ const canSend = sendBlocker === null && composer.canSubmit;
124
+ const addAttachments = useCallback(async (files) => {
125
+ if (files.length === 0 || options.uploadAttachments === undefined)
126
+ return;
127
+ const generation = attachmentGenerationRef.current;
128
+ const controller = new AbortController();
129
+ activeUploadsRef.current.add(controller);
130
+ setActiveUploadCount(activeUploadsRef.current.size);
131
+ setAttachmentError(null);
132
+ try {
133
+ await uploadAttachmentBatch(files, {
134
+ upload: options.uploadAttachments,
135
+ signal: controller.signal,
136
+ batchId: attachmentBatchIdRef.current,
137
+ stillWanted: () => mountedRef.current
138
+ && !controller.signal.aborted
139
+ && generation === attachmentGenerationRef.current,
140
+ onAttachment: (attachment) => composer.addAttachment(attachment),
141
+ onError: setAttachmentError,
142
+ });
143
+ }
144
+ finally {
145
+ if (activeUploadsRef.current.delete(controller) && mountedRef.current) {
146
+ setActiveUploadCount(activeUploadsRef.current.size);
147
+ }
148
+ }
149
+ }, [composer, options.uploadAttachments]);
150
+ const sendPrompt = useCallback(async (prompt) => {
151
+ const trimmed = prompt.trim();
152
+ if (!trimmed)
153
+ throw new Error('cannot send: the prompt is empty');
154
+ if (sendBlocker !== null) {
155
+ throw new Error(`cannot send: ${describeChatPaneSendBlocker(sendBlocker)}`);
156
+ }
157
+ const attachments = composer.attachments;
158
+ const context = typeof options.runContext === 'function'
159
+ ? options.runContext({
160
+ prompt: trimmed,
161
+ selection,
162
+ workingDirectory: workingDirectoryState.workingDirectory,
163
+ })
164
+ : options.runContext;
165
+ composer.reset();
166
+ attachmentGenerationRef.current += 1;
167
+ attachmentBatchIdRef.current = createAttachmentBatchId();
168
+ options.onActivityChange?.('queued');
169
+ await conversation.sendMessage(trimmed, definedProps({
170
+ agentId: selection.agentId,
171
+ // Omitted when the array is EMPTY, not merely absent — an empty `attachments: []` would be a
172
+ // different (valid, present) value to the transport than "no attachments key at all".
173
+ attachments: attachments.length === 0 ? undefined : attachments,
174
+ context,
175
+ }));
176
+ }, [
177
+ composer,
178
+ conversation,
179
+ options.onActivityChange,
180
+ options.runContext,
181
+ selection,
182
+ sendBlocker,
183
+ workingDirectoryState.workingDirectory,
184
+ ]);
185
+ const send = useCallback(async () => {
186
+ const prompt = composer.draft.trim()
187
+ || (composer.attachments.length > 0 ? 'Review the attached file(s).' : '');
188
+ if (!prompt || !canSend)
189
+ return;
190
+ await sendPrompt(prompt);
191
+ }, [canSend, composer, sendPrompt]);
192
+ const reset = useCallback(() => {
193
+ attachmentGenerationRef.current += 1;
194
+ attachmentBatchIdRef.current = createAttachmentBatchId();
195
+ for (const controller of activeUploadsRef.current)
196
+ controller.abort();
197
+ activeUploadsRef.current.clear();
198
+ setActiveUploadCount(0);
199
+ conversation.cancel();
200
+ conversation.setMessages(options.initialMessages ?? []);
201
+ composer.reset();
202
+ setAttachmentError(null);
203
+ }, [composer, conversation, options.initialMessages]);
204
+ return {
205
+ conversation,
206
+ composer,
207
+ selection,
208
+ selectedAgent,
209
+ agents: options.agents,
210
+ activity,
211
+ canSend,
212
+ sendBlocker,
213
+ isUploadingAttachments: activeUploadCount > 0,
214
+ attachmentError,
215
+ ...workingDirectoryState,
216
+ setSelection,
217
+ addAttachments,
218
+ send,
219
+ sendPrompt,
220
+ reset,
221
+ };
222
+ }
223
+ //# sourceMappingURL=useChatPane.hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatPane.hooks.js","sourceRoot":"","sources":["../../../../../../src/react/features/chat-pane/react/hooks/useChatPane.hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { ChatPaneAgentBridgeAccess } from '../../types.js';
2
+ import type { UseChatPaneResult } from './useChatPane.hooks.js';
3
+ export interface UseChatPaneAgentControlOptions {
4
+ /** Defaults to `false` — agent control is opt-in. */
5
+ enabled?: boolean;
6
+ bridgeAccess?: ChatPaneAgentBridgeAccess;
7
+ /**
8
+ * Register these tools with the in-page WebMCP surface (`document.modelContext`). Defaults to
9
+ * `false`, and that default is a security position rather than caution.
10
+ *
11
+ * The daemon-relayed channel routes every call through `ToolExecutor` — policy, confirmation,
12
+ * timeout, audit. A WebMCP call cannot: it originates inside the page from a browser-native
13
+ * caller, with no run and no principal to authorize against, so there is nothing for the gate to
14
+ * decide on and no run to attribute the action to. Routing it through the daemon would mean
15
+ * inventing both, which is worse than not gating — an invented principal is a gate that says yes.
16
+ *
17
+ * So a host that turns this on is accepting that anything able to reach `document.modelContext`
18
+ * — a browser extension, another script on the page — can drive this pane ungated. That is a
19
+ * legitimate choice for a trusted first-party surface and a bad one for a page hosting untrusted
20
+ * content, which is exactly why it has to be the host's call and not a default.
21
+ */
22
+ webmcp?: boolean;
23
+ }
24
+ /**
25
+ * Wires the chat pane's own actions (send/draft/selection/cancel/reset/working-directory/state) up
26
+ * to the outside-caller surfaces the pane supports: a daemon-relayed action channel when
27
+ * `bridgeAccess` is supplied, so an HTTP- or MCP-driven caller can reach this SAME live pane
28
+ * instance, and — only when the host sets `webmcp` — in-page WebMCP tool registration
29
+ * (`document.modelContext.registerTool`, feature-detected). Both dispatch through one identical
30
+ * action map (`runChatPaneAction`, module scope above), so the two callers can never diverge in
31
+ * *behavior*.
32
+ *
33
+ * They do differ in *authorization*, and that difference is the reason WebMCP is off by default.
34
+ * The relayed channel arrives having already passed `ToolExecutor`; a WebMCP call has no run and
35
+ * no principal, so it cannot. See {@link UseChatPaneAgentControlOptions.webmcp}.
36
+ */
37
+ export declare function useChatPaneAgentControl(pane: UseChatPaneResult, options?: UseChatPaneAgentControlOptions): void;
38
+ //# sourceMappingURL=useChatPaneAgentControl.hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatPaneAgentControl.hooks.d.ts","sourceRoot":"","sources":["../../../../../../src/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,yBAAyB,EAA2B,MAAM,gBAAgB,CAAC;AAEzF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,WAAW,8BAA8B;IAC7C,qDAAqD;IACrD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,yBAAyB,CAAC;IACzC;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAoOD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,iBAAiB,EACvB,OAAO,GAAE,8BAAmC,GAC3C,IAAI,CA+DN"}
@@ -0,0 +1,252 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { CHAT_PANE_AGENT_TOOLS } from '../../agent-tools.js';
3
+ import { definedProps } from '../../../../util/defined-props.js';
4
+ function getModelContext() {
5
+ const doc = globalThis.document;
6
+ if (isModelContext(doc?.modelContext))
7
+ return doc.modelContext;
8
+ const nav = globalThis.navigator;
9
+ if (isModelContext(nav?.modelContext))
10
+ return nav.modelContext;
11
+ return undefined;
12
+ }
13
+ function isModelContext(value) {
14
+ return typeof value === 'object' && value !== null && typeof value.registerTool === 'function';
15
+ }
16
+ function requireStringField(input, key) {
17
+ const value = input[key];
18
+ if (typeof value !== 'string' || value.length === 0)
19
+ throw new Error(`"${key}" is required`);
20
+ return value;
21
+ }
22
+ function optionalStringField(input, key) {
23
+ const value = input[key];
24
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
25
+ }
26
+ /** Gate for capabilities whose manifest entry sets `requiresConfirmation`. */
27
+ function requireConfirmation(input, what) {
28
+ if (input['confirm'] !== true) {
29
+ throw new Error(`"confirm" must be true — ${what} is destructive and needs explicit acknowledgement`);
30
+ }
31
+ }
32
+ function errorMessage(error) {
33
+ return error instanceof Error ? error.message : String(error);
34
+ }
35
+ /** Content read off the page/transcript is untrusted input to whatever model reads it back. */
36
+ const MAX_RETURNED_CONTENT = 2000;
37
+ function summarizeContent(content) {
38
+ return {
39
+ content: content.slice(0, MAX_RETURNED_CONTENT),
40
+ truncated: content.length > MAX_RETURNED_CONTENT,
41
+ originalLength: content.length,
42
+ };
43
+ }
44
+ /**
45
+ * Routes through the pane's own guarded send (`rules.ts`'s blocker set + staged attachments +
46
+ * composer reset + batch rotation + activity notification) rather than calling
47
+ * `conversation.sendMessage` directly, so an agent-driven send cannot bypass what the composer
48
+ * enforces. Throws a describable refusal when blocked.
49
+ *
50
+ * It also does not go the other obvious route — `composer.setDraft(prompt)` followed by `send()`.
51
+ * `setDraft` is a React state update and is therefore asynchronous, so the `send()` immediately
52
+ * after it would race the re-render and read a stale draft. Passing the prompt straight to
53
+ * `sendPrompt` is what makes the agent-driven path deterministic.
54
+ */
55
+ async function sendMessageAction(pane, input) {
56
+ await pane.sendPrompt(requireStringField(input, 'prompt'));
57
+ return { sent: true };
58
+ }
59
+ async function setDraftAction(pane, input) {
60
+ pane.composer.setDraft(requireStringField(input, 'text'));
61
+ return { ok: true };
62
+ }
63
+ /**
64
+ * Only a runtime this machine actually offers.
65
+ *
66
+ * `setSelection` normalizes an unknown or unavailable agent to the first available one — correct
67
+ * for the picker, which must not break when a runtime disappears mid-session. For a caller that is
68
+ * told `{ok:true}`, that same behavior is a lie with a safety edge: asking for a constrained
69
+ * runtime and silently getting a more capable one is how a prompt intended for a sandboxed agent
70
+ * reaches a full coding agent instead. So this refuses by name rather than substituting.
71
+ */
72
+ async function selectAgentAction(pane, input) {
73
+ const agentId = requireStringField(input, 'agentId');
74
+ const agent = pane.agents.find((candidate) => candidate.id === agentId);
75
+ if (agent === undefined) {
76
+ const known = pane.agents.map((candidate) => candidate.id).join(', ');
77
+ throw new Error(`unknown agent "${agentId}" — this pane offers: ${known || 'none'}`);
78
+ }
79
+ if (agent.available === false) {
80
+ throw new Error(`agent "${agentId}" is not available on this machine`
81
+ + `${agent.diagnostic === undefined ? '' : ` (${agent.diagnostic})`}`);
82
+ }
83
+ const model = optionalStringField(input, 'model');
84
+ const reasoning = optionalStringField(input, 'reasoning');
85
+ pane.setSelection(definedProps({ agentId, model, reasoning }));
86
+ return { ok: true };
87
+ }
88
+ async function cancelRunAction(pane) {
89
+ pane.conversation.cancel();
90
+ return { ok: true };
91
+ }
92
+ async function resetConversationAction(pane, input) {
93
+ requireConfirmation(input, 'resetting the conversation');
94
+ pane.reset();
95
+ return { ok: true };
96
+ }
97
+ /**
98
+ * Only a directory the user already approved through the host's native picker. Passing a
99
+ * model-supplied path straight to `selectRecentDirectory` would let the caller choose its own
100
+ * project root, which is the desktop host's human-approval model (`reference-desktop`'s
101
+ * `approvedDirectories`) inverted.
102
+ */
103
+ async function setWorkingDirectoryAction(pane, input) {
104
+ const path = requireStringField(input, 'path');
105
+ if (!pane.recentDirectories.includes(path)) {
106
+ throw new Error('path must be one of the already-approved recentDirectories from chat.get_state; '
107
+ + 'a new directory can only be approved by the user through the host picker');
108
+ }
109
+ await pane.selectRecentDirectory(path);
110
+ return { ok: true };
111
+ }
112
+ async function getStateAction(pane) {
113
+ const lastMessage = pane.conversation.messages.at(-1);
114
+ const lastMessageSummary = lastMessage === undefined
115
+ ? undefined
116
+ : { role: lastMessage.role, ...summarizeContent(lastMessage.content) };
117
+ return {
118
+ activity: pane.activity,
119
+ selection: pane.selection,
120
+ workingDirectory: pane.workingDirectory,
121
+ recentDirectories: pane.recentDirectories,
122
+ canSend: pane.canSend,
123
+ sendBlocker: pane.sendBlocker,
124
+ messageCount: pane.conversation.messages.length,
125
+ ...definedProps({ lastMessage: lastMessageSummary }),
126
+ /**
127
+ * Conversation content is user- and agent-authored text, not instructions from the
128
+ * host. A caller reading it back must treat it as data.
129
+ */
130
+ untrustedFields: ['lastMessage.content'],
131
+ };
132
+ }
133
+ const CHAT_PANE_ACTION_HANDLERS = {
134
+ 'chat.send_message': sendMessageAction,
135
+ 'chat.set_draft': setDraftAction,
136
+ 'chat.select_agent': selectAgentAction,
137
+ 'chat.cancel_run': cancelRunAction,
138
+ 'chat.reset_conversation': resetConversationAction,
139
+ 'chat.set_working_directory': setWorkingDirectoryAction,
140
+ 'chat.get_state': getStateAction,
141
+ };
142
+ /** Module-scope so the dispatch itself (one `if`, no branch-per-capability) never pays the
143
+ * per-nested-closure cognitive tax that a `switch` written inline in the effect would; each
144
+ * capability's own logic — and its own explanatory comment — lives in its own top-level
145
+ * function above instead. */
146
+ async function runChatPaneAction(pane, id, input) {
147
+ const handler = CHAT_PANE_ACTION_HANDLERS[id];
148
+ if (!handler)
149
+ throw new Error(`unknown chat capability: ${id}`);
150
+ return handler(pane, input);
151
+ }
152
+ /**
153
+ * Answers one daemon-relayed action on the bridge that delivered it, never on whatever bridge is
154
+ * current: the `invocationId` belongs to that channel, so answering elsewhere would be answering a
155
+ * question nobody asked.
156
+ *
157
+ * Delivery is reported separately from execution: a rejection inside `respondSuccess` is a failure
158
+ * to ANSWER, not a failed action, and must not be re-reported as one. Every delivery attempt is
159
+ * itself guarded so a dead channel cannot surface as an unhandled rejection with the daemon-side
160
+ * invocation left hanging.
161
+ */
162
+ async function deliverBridgeAction(pane, bridge, action) {
163
+ let output;
164
+ try {
165
+ output = await runChatPaneAction(pane, action.capabilityId, action.input);
166
+ }
167
+ catch (error) {
168
+ await bridge.respondError(action.invocationId, errorMessage(error)).catch(() => undefined);
169
+ return;
170
+ }
171
+ try {
172
+ await bridge.respondSuccess(action.invocationId, output);
173
+ }
174
+ catch (deliveryError) {
175
+ await bridge
176
+ .respondError(action.invocationId, `action succeeded but the result could not be delivered: ${errorMessage(deliveryError)}`)
177
+ .catch(() => undefined);
178
+ }
179
+ }
180
+ /**
181
+ * Wires the chat pane's own actions (send/draft/selection/cancel/reset/working-directory/state) up
182
+ * to the outside-caller surfaces the pane supports: a daemon-relayed action channel when
183
+ * `bridgeAccess` is supplied, so an HTTP- or MCP-driven caller can reach this SAME live pane
184
+ * instance, and — only when the host sets `webmcp` — in-page WebMCP tool registration
185
+ * (`document.modelContext.registerTool`, feature-detected). Both dispatch through one identical
186
+ * action map (`runChatPaneAction`, module scope above), so the two callers can never diverge in
187
+ * *behavior*.
188
+ *
189
+ * They do differ in *authorization*, and that difference is the reason WebMCP is off by default.
190
+ * The relayed channel arrives having already passed `ToolExecutor`; a WebMCP call has no run and
191
+ * no principal, so it cannot. See {@link UseChatPaneAgentControlOptions.webmcp}.
192
+ */
193
+ export function useChatPaneAgentControl(pane, options = {}) {
194
+ const enabled = options.enabled ?? false;
195
+ const paneRef = useRef(pane);
196
+ paneRef.current = pane;
197
+ const bridgeAccessRef = useRef(options.bridgeAccess);
198
+ bridgeAccessRef.current = options.bridgeAccess;
199
+ // Presence, not identity: hosts write `agentControl={{...}}` inline in JSX (as every other prop
200
+ // here is written), so depending on the object itself would tear down and re-register all tools
201
+ // on every render, dropping in-flight actions. See the effect's closing comment for the tradeoff.
202
+ const hasBridgeAccess = options.bridgeAccess !== undefined;
203
+ useEffect(() => {
204
+ if (!enabled)
205
+ return;
206
+ const cleanups = [];
207
+ // Not registered unless the host opted in: see `webmcp`'s own doc for why this path cannot be
208
+ // gated and therefore must not be on by default.
209
+ const modelContext = options.webmcp === true ? getModelContext() : undefined;
210
+ if (modelContext) {
211
+ const controller = new AbortController();
212
+ for (const tool of CHAT_PANE_AGENT_TOOLS) {
213
+ // `registerTool` is async per the draft IDL, and a polyfill may return undefined instead.
214
+ // Normalize both, then swallow: registration is best-effort, and a rejected one must not
215
+ // surface as an unhandled rejection from a React effect.
216
+ const registered = modelContext.registerTool({
217
+ name: tool.id,
218
+ description: tool.description,
219
+ inputSchema: tool.inputSchema,
220
+ execute: (args) => runChatPaneAction(paneRef.current, tool.id, args ?? {}),
221
+ }, { signal: controller.signal });
222
+ void Promise.resolve(registered).catch(() => undefined);
223
+ }
224
+ cleanups.push(() => {
225
+ controller.abort();
226
+ if (modelContext.unregisterTool) {
227
+ for (const tool of CHAT_PANE_AGENT_TOOLS)
228
+ modelContext.unregisterTool(tool.id);
229
+ }
230
+ });
231
+ }
232
+ const subscribedBridge = bridgeAccessRef.current;
233
+ if (subscribedBridge) {
234
+ const unsubscribe = subscribedBridge.subscribe((action) => {
235
+ void deliverBridgeAction(paneRef.current, subscribedBridge, action);
236
+ });
237
+ cleanups.push(unsubscribe);
238
+ }
239
+ return () => {
240
+ for (const cleanup of cleanups)
241
+ cleanup();
242
+ };
243
+ // Deps are enablement + bridge PRESENCE, never the bridge object: hosts pass `agentControl`
244
+ // inline, so an identity dep would re-register every tool on every render (see
245
+ // `hasBridgeAccess`). Pane, run context, and bridge are all read through refs at call time.
246
+ // Tradeoff: swapping to a genuinely different bridge instance while enabled does not
247
+ // re-subscribe — responses still route to the current bridge via the ref, but the old
248
+ // subscription stays live. A host needing a true swap should toggle `enabled` or remount.
249
+ // eslint-disable-next-line react-hooks/exhaustive-deps
250
+ }, [enabled, hasBridgeAccess]);
251
+ }
252
+ //# sourceMappingURL=useChatPaneAgentControl.hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatPaneAgentControl.hooks.js","sourceRoot":"","sources":["../../../../../../src/react/features/chat-pane/react/hooks/useChatPaneAgentControl.hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { type DragEventHandler } from 'react';
2
+ export interface UseChatPaneFileDropOptions {
3
+ enabled: boolean;
4
+ onFiles: (files: File[]) => void | Promise<void>;
5
+ }
6
+ export interface ChatPaneFileDropTargetProps {
7
+ onDragEnter: DragEventHandler<HTMLDivElement>;
8
+ onDragOver: DragEventHandler<HTMLDivElement>;
9
+ onDragLeave: DragEventHandler<HTMLDivElement>;
10
+ onDrop: DragEventHandler<HTMLDivElement>;
11
+ }
12
+ export interface UseChatPaneFileDropResult {
13
+ draggingFiles: boolean;
14
+ dropReadError: string | null;
15
+ targetProps: ChatPaneFileDropTargetProps;
16
+ }
17
+ /**
18
+ * Adapts the shared nesting-safe file drop target to the chat composer.
19
+ * Non-file drags are ignored, while temporarily disabled composers still
20
+ * consume a file drop so the browser cannot navigate away to the local file.
21
+ *
22
+ * @complexity Time: O(n) for the dropped file list; space: O(n).
23
+ * @overallScore 100/100
24
+ */
25
+ export declare function useChatPaneFileDrop({ enabled, onFiles, }: UseChatPaneFileDropOptions): UseChatPaneFileDropResult;
26
+ //# sourceMappingURL=useChatPaneFileDrop.hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatPaneFileDrop.hooks.d.ts","sourceRoot":"","sources":["../../../../../../src/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,OAAO,CAAC;AAGf,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC9C,UAAU,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC7C,WAAW,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC9C,MAAM,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,2BAA2B,CAAC;CAC1C;AAQD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,GACR,EAAE,0BAA0B,GAAG,yBAAyB,CA4CxD"}
@@ -0,0 +1,60 @@
1
+ import { useCallback, } from 'react';
2
+ import { useFileDropTarget } from '@jini-ai/ui';
3
+ function carriesFiles(event) {
4
+ return Array.from(event.dataTransfer.types ?? []).includes('Files')
5
+ || Array.from(event.dataTransfer.items ?? []).some((item) => item.kind === 'file')
6
+ || (event.type === 'drop' && event.dataTransfer.files.length > 0);
7
+ }
8
+ /**
9
+ * Adapts the shared nesting-safe file drop target to the chat composer.
10
+ * Non-file drags are ignored, while temporarily disabled composers still
11
+ * consume a file drop so the browser cannot navigate away to the local file.
12
+ *
13
+ * @complexity Time: O(n) for the dropped file list; space: O(n).
14
+ * @overallScore 100/100
15
+ */
16
+ export function useChatPaneFileDrop({ enabled, onFiles, }) {
17
+ const dropTarget = useFileDropTarget((files) => {
18
+ void onFiles(files);
19
+ });
20
+ const onDragEnter = useCallback((event) => {
21
+ if (!enabled || !carriesFiles(event))
22
+ return;
23
+ dropTarget.onDragEnter(event);
24
+ }, [dropTarget.onDragEnter, enabled]);
25
+ const onDragOver = useCallback((event) => {
26
+ if (!carriesFiles(event))
27
+ return;
28
+ event.preventDefault();
29
+ if (!enabled) {
30
+ event.dataTransfer.dropEffect = 'none';
31
+ return;
32
+ }
33
+ dropTarget.onDragOver(event);
34
+ }, [dropTarget.onDragOver, enabled]);
35
+ const onDragLeave = useCallback((event) => {
36
+ if (!enabled || !carriesFiles(event))
37
+ return;
38
+ dropTarget.onDragLeave(event);
39
+ }, [dropTarget.onDragLeave, enabled]);
40
+ const onDrop = useCallback((event) => {
41
+ if (!carriesFiles(event))
42
+ return;
43
+ if (!enabled) {
44
+ event.preventDefault();
45
+ return;
46
+ }
47
+ dropTarget.onDrop(event);
48
+ }, [dropTarget.onDrop, enabled]);
49
+ return {
50
+ draggingFiles: enabled && dropTarget.draggingFiles,
51
+ dropReadError: dropTarget.dropReadError,
52
+ targetProps: {
53
+ onDragEnter,
54
+ onDragOver,
55
+ onDragLeave,
56
+ onDrop,
57
+ },
58
+ };
59
+ }
60
+ //# sourceMappingURL=useChatPaneFileDrop.hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatPaneFileDrop.hooks.js","sourceRoot":"","sources":["../../../../../../src/react/features/chat-pane/react/hooks/useChatPaneFileDrop.hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,GAGZ,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAoBhD,SAAS,YAAY,CAAC,KAAyB;IAC7C,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;WAC9D,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;WAC/E,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,OAAO,EACP,OAAO,GACoB;IAC3B,MAAM,UAAU,GAAG,iBAAiB,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7C,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,WAAW,CAAmC,CAAC,KAAK,EAAE,EAAE;QAC1E,IAAI,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;YAAE,OAAO;QAC7C,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtC,MAAM,UAAU,GAAG,WAAW,CAAmC,CAAC,KAAK,EAAE,EAAE;QACzE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;YAAE,OAAO;QACjC,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,KAAK,CAAC,YAAY,CAAC,UAAU,GAAG,MAAM,CAAC;YACvC,OAAO;QACT,CAAC;QACD,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;IAErC,MAAM,WAAW,GAAG,WAAW,CAAmC,CAAC,KAAK,EAAE,EAAE;QAC1E,IAAI,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;YAAE,OAAO;QAC7C,UAAU,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,UAAU,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtC,MAAM,MAAM,GAAG,WAAW,CAAmC,CAAC,KAAK,EAAE,EAAE;QACrE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;YAAE,OAAO;QACjC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QACD,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEjC,OAAO;QACL,aAAa,EAAE,OAAO,IAAI,UAAU,CAAC,aAAa;QAClD,aAAa,EAAE,UAAU,CAAC,aAAa;QACvC,WAAW,EAAE;YACX,WAAW;YACX,UAAU;YACV,WAAW;YACX,MAAM;SACP;KACF,CAAC;AACJ,CAAC"}