@steerable/agent-ui 0.2.3 → 0.2.5

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 (184) hide show
  1. package/dist/cards/ActionSegmentCard.d.ts +18 -0
  2. package/dist/cards/ActionSegmentCard.d.ts.map +1 -0
  3. package/dist/cards/ActionSegmentCard.js +26 -0
  4. package/dist/cards/ActionSegmentCard.js.map +1 -0
  5. package/dist/cards/AnalysisDocumentCard.d.ts +33 -0
  6. package/dist/cards/AnalysisDocumentCard.d.ts.map +1 -0
  7. package/dist/cards/AnalysisDocumentCard.js +11 -0
  8. package/dist/cards/AnalysisDocumentCard.js.map +1 -0
  9. package/dist/cards/AskUserQuestionsCard.d.ts +43 -0
  10. package/dist/cards/AskUserQuestionsCard.d.ts.map +1 -0
  11. package/dist/cards/AskUserQuestionsCard.js +171 -0
  12. package/dist/cards/AskUserQuestionsCard.js.map +1 -0
  13. package/dist/cards/CoverageReportCard.d.ts +41 -0
  14. package/dist/cards/CoverageReportCard.d.ts.map +1 -0
  15. package/dist/cards/CoverageReportCard.js +55 -0
  16. package/dist/cards/CoverageReportCard.js.map +1 -0
  17. package/dist/cards/OrchestrationPlanCard.d.ts +60 -0
  18. package/dist/cards/OrchestrationPlanCard.d.ts.map +1 -0
  19. package/dist/cards/OrchestrationPlanCard.js +103 -0
  20. package/dist/cards/OrchestrationPlanCard.js.map +1 -0
  21. package/dist/cards/PlanSelectorCard.d.ts +40 -0
  22. package/dist/cards/PlanSelectorCard.d.ts.map +1 -0
  23. package/dist/cards/PlanSelectorCard.js +106 -0
  24. package/dist/cards/PlanSelectorCard.js.map +1 -0
  25. package/dist/cards/PlanStepsCard.d.ts +18 -0
  26. package/dist/cards/PlanStepsCard.d.ts.map +1 -0
  27. package/dist/cards/PlanStepsCard.js +25 -0
  28. package/dist/cards/PlanStepsCard.js.map +1 -0
  29. package/dist/cards/QuizCard.d.ts +44 -0
  30. package/dist/cards/QuizCard.d.ts.map +1 -0
  31. package/dist/cards/QuizCard.js +113 -0
  32. package/dist/cards/QuizCard.js.map +1 -0
  33. package/dist/cards/ResearchPlanCard.d.ts +23 -0
  34. package/dist/cards/ResearchPlanCard.d.ts.map +1 -0
  35. package/dist/cards/ResearchPlanCard.js +50 -0
  36. package/dist/cards/ResearchPlanCard.js.map +1 -0
  37. package/dist/cards/SearchSourcesCard.d.ts +29 -0
  38. package/dist/cards/SearchSourcesCard.d.ts.map +1 -0
  39. package/dist/cards/SearchSourcesCard.js +83 -0
  40. package/dist/cards/SearchSourcesCard.js.map +1 -0
  41. package/dist/cards/SuggestedRepliesCard.d.ts +18 -0
  42. package/dist/cards/SuggestedRepliesCard.d.ts.map +1 -0
  43. package/dist/cards/SuggestedRepliesCard.js +14 -0
  44. package/dist/cards/SuggestedRepliesCard.js.map +1 -0
  45. package/dist/cards/SummaryMessageCard.d.ts +40 -0
  46. package/dist/cards/SummaryMessageCard.d.ts.map +1 -0
  47. package/dist/cards/SummaryMessageCard.js +56 -0
  48. package/dist/cards/SummaryMessageCard.js.map +1 -0
  49. package/dist/cards/ThinkingProcessCard.d.ts +46 -0
  50. package/dist/cards/ThinkingProcessCard.d.ts.map +1 -0
  51. package/dist/cards/ThinkingProcessCard.js +79 -0
  52. package/dist/cards/ThinkingProcessCard.js.map +1 -0
  53. package/dist/cards/ToolExecutionCard.d.ts +21 -0
  54. package/dist/cards/ToolExecutionCard.d.ts.map +1 -0
  55. package/dist/cards/ToolExecutionCard.js +65 -0
  56. package/dist/cards/ToolExecutionCard.js.map +1 -0
  57. package/dist/cards/icons.d.ts +38 -0
  58. package/dist/cards/icons.d.ts.map +1 -0
  59. package/dist/cards/icons.js +87 -0
  60. package/dist/cards/icons.js.map +1 -0
  61. package/dist/cards/index.d.ts +38 -0
  62. package/dist/cards/index.d.ts.map +1 -0
  63. package/dist/cards/index.js +24 -0
  64. package/dist/cards/index.js.map +1 -0
  65. package/dist/cards/types.d.ts +24 -0
  66. package/dist/cards/types.d.ts.map +1 -0
  67. package/dist/cards/types.js +7 -0
  68. package/dist/cards/types.js.map +1 -0
  69. package/dist/components/AgentSelector.d.ts +23 -0
  70. package/dist/components/AgentSelector.d.ts.map +1 -0
  71. package/dist/components/AgentSelector.js +63 -0
  72. package/dist/components/AgentSelector.js.map +1 -0
  73. package/dist/components/AgentSelector.stories.d.ts +12 -0
  74. package/dist/components/AgentSelector.stories.d.ts.map +1 -0
  75. package/dist/components/AgentSelector.stories.js +95 -0
  76. package/dist/components/AgentSelector.stories.js.map +1 -0
  77. package/dist/components/ChatPanel.d.ts +138 -21
  78. package/dist/components/ChatPanel.d.ts.map +1 -1
  79. package/dist/components/ChatPanel.js +181 -26
  80. package/dist/components/ChatPanel.js.map +1 -1
  81. package/dist/components/index.d.ts +3 -1
  82. package/dist/components/index.d.ts.map +1 -1
  83. package/dist/components/index.js +1 -0
  84. package/dist/components/index.js.map +1 -1
  85. package/dist/hooks/useChatStream.d.ts +15 -0
  86. package/dist/hooks/useChatStream.d.ts.map +1 -1
  87. package/dist/hooks/useChatStream.js +13 -1
  88. package/dist/hooks/useChatStream.js.map +1 -1
  89. package/dist/index.d.ts +2 -0
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +5 -0
  92. package/dist/index.js.map +1 -1
  93. package/dist/state/ChatSessionProvider.d.ts +59 -0
  94. package/dist/state/ChatSessionProvider.d.ts.map +1 -0
  95. package/dist/state/ChatSessionProvider.js +71 -0
  96. package/dist/state/ChatSessionProvider.js.map +1 -0
  97. package/dist/state/MockChatStreamTransport.d.ts +54 -0
  98. package/dist/state/MockChatStreamTransport.d.ts.map +1 -0
  99. package/dist/state/MockChatStreamTransport.js +86 -0
  100. package/dist/state/MockChatStreamTransport.js.map +1 -0
  101. package/dist/state/MockChatStreamTransport.stories.d.ts +13 -0
  102. package/dist/state/MockChatStreamTransport.stories.d.ts.map +1 -0
  103. package/dist/state/MockChatStreamTransport.stories.js +60 -0
  104. package/dist/state/MockChatStreamTransport.stories.js.map +1 -0
  105. package/dist/state/bridgeLegacySSE.d.ts +42 -0
  106. package/dist/state/bridgeLegacySSE.d.ts.map +1 -0
  107. package/dist/state/bridgeLegacySSE.js +102 -0
  108. package/dist/state/bridgeLegacySSE.js.map +1 -0
  109. package/dist/state/index.d.ts +32 -0
  110. package/dist/state/index.d.ts.map +1 -0
  111. package/dist/state/index.js +24 -0
  112. package/dist/state/index.js.map +1 -0
  113. package/dist/state/parseSSE.d.ts +50 -0
  114. package/dist/state/parseSSE.d.ts.map +1 -0
  115. package/dist/state/parseSSE.js +112 -0
  116. package/dist/state/parseSSE.js.map +1 -0
  117. package/dist/state/useChatComposer.d.ts +51 -0
  118. package/dist/state/useChatComposer.d.ts.map +1 -0
  119. package/dist/state/useChatComposer.js +82 -0
  120. package/dist/state/useChatComposer.js.map +1 -0
  121. package/dist/state/useChatList.d.ts +73 -0
  122. package/dist/state/useChatList.d.ts.map +1 -0
  123. package/dist/state/useChatList.js +161 -0
  124. package/dist/state/useChatList.js.map +1 -0
  125. package/dist/state/useChatSession.d.ts +29 -0
  126. package/dist/state/useChatSession.d.ts.map +1 -0
  127. package/dist/state/useChatSession.js +37 -0
  128. package/dist/state/useChatSession.js.map +1 -0
  129. package/dist/state/useScrollLock.d.ts +35 -0
  130. package/dist/state/useScrollLock.d.ts.map +1 -0
  131. package/dist/state/useScrollLock.js +74 -0
  132. package/dist/state/useScrollLock.js.map +1 -0
  133. package/dist/state/useToolCallStream.d.ts +38 -0
  134. package/dist/state/useToolCallStream.d.ts.map +1 -0
  135. package/dist/state/useToolCallStream.js +74 -0
  136. package/dist/state/useToolCallStream.js.map +1 -0
  137. package/package.json +11 -3
  138. package/src/cards/ActionSegmentCard.tsx +60 -0
  139. package/src/cards/AnalysisDocumentCard.tsx +81 -0
  140. package/src/cards/AskUserQuestionsCard.tsx +354 -0
  141. package/src/cards/CoverageReportCard.tsx +215 -0
  142. package/src/cards/OrchestrationPlanCard.tsx +328 -0
  143. package/src/cards/PlanSelectorCard.tsx +312 -0
  144. package/src/cards/PlanStepsCard.tsx +52 -0
  145. package/src/cards/QuizCard.tsx +309 -0
  146. package/src/cards/ResearchPlanCard.tsx +169 -0
  147. package/src/cards/SearchSourcesCard.tsx +244 -0
  148. package/src/cards/SuggestedRepliesCard.tsx +48 -0
  149. package/src/cards/SummaryMessageCard.tsx +145 -0
  150. package/src/cards/ThinkingProcessCard.tsx +155 -0
  151. package/src/cards/ToolExecutionCard.tsx +152 -0
  152. package/src/cards/cards.test.tsx +492 -0
  153. package/src/cards/icons.tsx +249 -0
  154. package/src/cards/index.ts +53 -0
  155. package/src/cards/types.ts +27 -0
  156. package/src/components/AgentSelector.stories.tsx +113 -0
  157. package/src/components/AgentSelector.test.tsx +70 -0
  158. package/src/components/AgentSelector.tsx +147 -0
  159. package/src/components/ChatPanel.test.tsx +130 -0
  160. package/src/components/ChatPanel.tsx +612 -138
  161. package/src/components/index.ts +13 -1
  162. package/src/hooks/useChatStream.test.ts +78 -0
  163. package/src/hooks/useChatStream.ts +32 -1
  164. package/src/index.ts +43 -0
  165. package/src/state/ChatSessionProvider.test.tsx +85 -0
  166. package/src/state/ChatSessionProvider.tsx +98 -0
  167. package/src/state/MockChatStreamTransport.stories.tsx +95 -0
  168. package/src/state/MockChatStreamTransport.test.ts +163 -0
  169. package/src/state/MockChatStreamTransport.ts +123 -0
  170. package/src/state/bridgeLegacySSE.test.ts +134 -0
  171. package/src/state/bridgeLegacySSE.ts +134 -0
  172. package/src/state/index.ts +82 -0
  173. package/src/state/parseSSE.test.ts +123 -0
  174. package/src/state/parseSSE.ts +123 -0
  175. package/src/state/useChatComposer.test.ts +145 -0
  176. package/src/state/useChatComposer.ts +117 -0
  177. package/src/state/useChatList.test.ts +135 -0
  178. package/src/state/useChatList.ts +220 -0
  179. package/src/state/useChatSession.test.ts +61 -0
  180. package/src/state/useChatSession.ts +64 -0
  181. package/src/state/useScrollLock.test.ts +99 -0
  182. package/src/state/useScrollLock.ts +105 -0
  183. package/src/state/useToolCallStream.test.ts +86 -0
  184. package/src/state/useToolCallStream.ts +110 -0
@@ -0,0 +1,50 @@
1
+ /**
2
+ * `parseSSE` — wire-level Server-Sent-Events parser.
3
+ *
4
+ * Feed it raw text chunks from any source (fetch ReadableStream chunks,
5
+ * Electron IPC `webContents.send` payloads, mocked fixtures, …). It buffers,
6
+ * splits on the `\r?\n\r?\n` frame terminator, and emits one
7
+ * `{ event?, data?, id?, retry? }` per complete frame. JSON parsing of `data`
8
+ * is intentionally out of scope; downstream layers like {@link bridgeLegacySSE}
9
+ * handle that.
10
+ *
11
+ * Replaces deeppath's `SSEParser` and deeppath-agent's `LocalBackendSseParser`
12
+ * with a single dependency-free implementation that compiles to both Node and
13
+ * the browser. Heartbeat-timeout watchdogs (deeppath had a 30 s gate) live on
14
+ * the transport, not here — this class is a pure parser.
15
+ */
16
+ export interface SSEFrame {
17
+ /** The `event: …` field; undefined for default `message` events. */
18
+ event?: string;
19
+ /** Concatenated `data: …` lines (newline-joined per the SSE spec). */
20
+ data?: string;
21
+ /** The `id: …` field. */
22
+ id?: string;
23
+ /** The `retry: …` field, as milliseconds. */
24
+ retry?: number;
25
+ }
26
+ export interface SSEParserOptions {
27
+ onFrame: (frame: SSEFrame) => void;
28
+ onError?: (err: Error) => void;
29
+ /** Called when the stream emits the canonical `data: [DONE]` terminator. */
30
+ onComplete?: () => void;
31
+ }
32
+ export declare class SSEParser {
33
+ private readonly options;
34
+ private buffer;
35
+ private completed;
36
+ constructor(options: SSEParserOptions);
37
+ /** Feed one chunk. Safe to call with partial UTF-8 strings (we don't decode). */
38
+ feed(chunk: string): void;
39
+ /** Call when the source signals end-of-stream. Flushes any trailing frame. */
40
+ end(): void;
41
+ /** Drop all state and stop emitting. */
42
+ cleanup(): void;
43
+ private processFrame;
44
+ }
45
+ /**
46
+ * Tolerant JSON parser for SSE `data:` payloads — returns the raw string when
47
+ * the body isn't JSON, which deeppath relies on for bare-text content deltas.
48
+ */
49
+ export declare function parseSSEData(data: string): unknown;
50
+ //# sourceMappingURL=parseSSE.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseSSE.d.ts","sourceRoot":"","sources":["../../src/state/parseSSE.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,QAAQ;IACvB,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB;AAED,qBAAa,SAAS;IAIR,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,SAAS,CAAS;gBAEG,OAAO,EAAE,gBAAgB;IAEtD,iFAAiF;IACjF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAUzB,8EAA8E;IAC9E,GAAG,IAAI,IAAI;IAMX,wCAAwC;IACxC,OAAO,IAAI,IAAI;IAKf,OAAO,CAAC,YAAY;CA8CrB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMlD"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * `parseSSE` — wire-level Server-Sent-Events parser.
3
+ *
4
+ * Feed it raw text chunks from any source (fetch ReadableStream chunks,
5
+ * Electron IPC `webContents.send` payloads, mocked fixtures, …). It buffers,
6
+ * splits on the `\r?\n\r?\n` frame terminator, and emits one
7
+ * `{ event?, data?, id?, retry? }` per complete frame. JSON parsing of `data`
8
+ * is intentionally out of scope; downstream layers like {@link bridgeLegacySSE}
9
+ * handle that.
10
+ *
11
+ * Replaces deeppath's `SSEParser` and deeppath-agent's `LocalBackendSseParser`
12
+ * with a single dependency-free implementation that compiles to both Node and
13
+ * the browser. Heartbeat-timeout watchdogs (deeppath had a 30 s gate) live on
14
+ * the transport, not here — this class is a pure parser.
15
+ */
16
+ export class SSEParser {
17
+ options;
18
+ buffer = '';
19
+ completed = false;
20
+ constructor(options) {
21
+ this.options = options;
22
+ }
23
+ /** Feed one chunk. Safe to call with partial UTF-8 strings (we don't decode). */
24
+ feed(chunk) {
25
+ if (this.completed)
26
+ return;
27
+ this.buffer += chunk;
28
+ const parts = this.buffer.split(/\r?\n\r?\n/);
29
+ this.buffer = parts.pop() ?? '';
30
+ for (const frame of parts) {
31
+ if (frame.length > 0)
32
+ this.processFrame(frame);
33
+ }
34
+ }
35
+ /** Call when the source signals end-of-stream. Flushes any trailing frame. */
36
+ end() {
37
+ if (this.completed)
38
+ return;
39
+ if (this.buffer.trim().length > 0)
40
+ this.processFrame(this.buffer);
41
+ this.buffer = '';
42
+ }
43
+ /** Drop all state and stop emitting. */
44
+ cleanup() {
45
+ this.buffer = '';
46
+ this.completed = true;
47
+ }
48
+ processFrame(raw) {
49
+ const frame = {};
50
+ const dataLines = [];
51
+ for (const line of raw.split(/\r?\n/)) {
52
+ if (line.length === 0)
53
+ continue;
54
+ // Comments per the SSE spec — leading `:` lines.
55
+ if (line.startsWith(':'))
56
+ continue;
57
+ const colon = line.indexOf(':');
58
+ const field = colon === -1 ? line : line.substring(0, colon);
59
+ let value = colon === -1 ? '' : line.substring(colon + 1);
60
+ if (value.startsWith(' '))
61
+ value = value.substring(1);
62
+ switch (field) {
63
+ case 'event':
64
+ frame.event = value;
65
+ break;
66
+ case 'data':
67
+ dataLines.push(value);
68
+ break;
69
+ case 'id':
70
+ frame.id = value;
71
+ break;
72
+ case 'retry': {
73
+ const ms = Number.parseInt(value, 10);
74
+ if (Number.isFinite(ms))
75
+ frame.retry = ms;
76
+ break;
77
+ }
78
+ default:
79
+ break;
80
+ }
81
+ }
82
+ if (dataLines.length === 0 && frame.event === undefined && frame.id === undefined) {
83
+ return;
84
+ }
85
+ if (dataLines.length > 0)
86
+ frame.data = dataLines.join('\n');
87
+ if (frame.data === '[DONE]' || frame.data === '"[DONE]"') {
88
+ this.completed = true;
89
+ this.options.onComplete?.();
90
+ return;
91
+ }
92
+ try {
93
+ this.options.onFrame(frame);
94
+ }
95
+ catch (err) {
96
+ this.options.onError?.(err instanceof Error ? err : new Error(String(err)));
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * Tolerant JSON parser for SSE `data:` payloads — returns the raw string when
102
+ * the body isn't JSON, which deeppath relies on for bare-text content deltas.
103
+ */
104
+ export function parseSSEData(data) {
105
+ try {
106
+ return JSON.parse(data);
107
+ }
108
+ catch {
109
+ return data;
110
+ }
111
+ }
112
+ //# sourceMappingURL=parseSSE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseSSE.js","sourceRoot":"","sources":["../../src/state/parseSSE.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAoBH,MAAM,OAAO,SAAS;IAIS;IAHrB,MAAM,GAAG,EAAE,CAAC;IACZ,SAAS,GAAG,KAAK,CAAC;IAE1B,YAA6B,OAAyB;QAAzB,YAAO,GAAP,OAAO,CAAkB;IAAG,CAAC;IAE1D,iFAAiF;IACjF,IAAI,CAAC,KAAa;QAChB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;QAChC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,GAAG;QACD,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;IACnB,CAAC;IAED,wCAAwC;IACxC,OAAO;QACL,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAEO,YAAY,CAAC,GAAW;QAC9B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAa,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YAChC,iDAAiD;YACjD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YACnC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAChC,MAAM,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YAC7D,IAAI,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAC1D,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YACtD,QAAQ,KAAK,EAAE,CAAC;gBACd,KAAK,OAAO;oBACV,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;oBACpB,MAAM;gBACR,KAAK,MAAM;oBACT,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACtB,MAAM;gBACR,KAAK,IAAI;oBACP,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC;oBACjB,MAAM;gBACR,KAAK,OAAO,CAAC,CAAC,CAAC;oBACb,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBACtC,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;wBAAE,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC;oBAC1C,MAAM;gBACR,CAAC;gBACD;oBACE,MAAM;YACV,CAAC;QACH,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAClF,OAAO;QACT,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE5D,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACzD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YAC5B,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * `useChatComposer` — input draft + send/cancel + keyboard glue.
3
+ *
4
+ * Lifted from deeppath-agent's `LocalChatPanel.tsx` (inline `inputValue` /
5
+ * `handleSubmit`) and deeppath's `ChatInput` (cancel handling). The hook is
6
+ * intentionally UI-free: it owns the draft string and the keyboard
7
+ * dispatcher; the visible textarea and send button live in the consuming
8
+ * component.
9
+ *
10
+ * Typical wiring:
11
+ *
12
+ * const composer = useChatComposer({ onSend: send, onCancel: cancel });
13
+ *
14
+ * <textarea
15
+ * value={composer.value}
16
+ * onChange={(e) => composer.setValue(e.target.value)}
17
+ * onKeyDown={composer.handleKeyDown}
18
+ * />
19
+ * <button onClick={composer.submit}>Send</button>
20
+ */
21
+ export interface UseChatComposerOptions {
22
+ initialValue?: string;
23
+ /** Called with the trimmed value when the user submits a non-empty draft. */
24
+ onSend: (value: string) => void | Promise<void>;
25
+ /** Called when the user wants to cancel the in-flight stream. Optional. */
26
+ onCancel?: () => void;
27
+ /**
28
+ * If true (default), Enter submits and Shift+Enter inserts a newline. Set
29
+ * to false for "Enter inserts newline, Cmd/Ctrl+Enter submits" semantics.
30
+ */
31
+ enterToSubmit?: boolean;
32
+ /** Whether the composer is currently disabled (no submit / no key handling). */
33
+ disabled?: boolean;
34
+ /**
35
+ * Whether the parent reports an in-flight stream. When true, `submit()` is
36
+ * a no-op and Enter triggers `onCancel` instead. Mirrors the cloud product.
37
+ */
38
+ isStreaming?: boolean;
39
+ }
40
+ export interface UseChatComposerReturn {
41
+ value: string;
42
+ setValue: (value: string) => void;
43
+ submit: () => Promise<void> | void;
44
+ cancel: () => void;
45
+ clear: () => void;
46
+ handleKeyDown: (e: React.KeyboardEvent<HTMLElement>) => void;
47
+ /** True if `value.trim()` is non-empty and we're not disabled. */
48
+ canSubmit: boolean;
49
+ }
50
+ export declare function useChatComposer(options: UseChatComposerOptions): UseChatComposerReturn;
51
+ //# sourceMappingURL=useChatComposer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatComposer.d.ts","sourceRoot":"","sources":["../../src/state/useChatComposer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,MAAM,WAAW,sBAAsB;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,aAAa,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAC7D,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,qBAAqB,CA8DtF"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `useChatComposer` — input draft + send/cancel + keyboard glue.
3
+ *
4
+ * Lifted from deeppath-agent's `LocalChatPanel.tsx` (inline `inputValue` /
5
+ * `handleSubmit`) and deeppath's `ChatInput` (cancel handling). The hook is
6
+ * intentionally UI-free: it owns the draft string and the keyboard
7
+ * dispatcher; the visible textarea and send button live in the consuming
8
+ * component.
9
+ *
10
+ * Typical wiring:
11
+ *
12
+ * const composer = useChatComposer({ onSend: send, onCancel: cancel });
13
+ *
14
+ * <textarea
15
+ * value={composer.value}
16
+ * onChange={(e) => composer.setValue(e.target.value)}
17
+ * onKeyDown={composer.handleKeyDown}
18
+ * />
19
+ * <button onClick={composer.submit}>Send</button>
20
+ */
21
+ import { useCallback, useRef, useState } from 'react';
22
+ export function useChatComposer(options) {
23
+ const [value, setValue] = useState(options.initialValue ?? '');
24
+ const optionsRef = useRef(options);
25
+ optionsRef.current = options;
26
+ const submit = useCallback(async () => {
27
+ const opts = optionsRef.current;
28
+ if (opts.disabled || opts.isStreaming)
29
+ return;
30
+ const trimmed = value.trim();
31
+ if (!trimmed)
32
+ return;
33
+ // Clear synchronously so the user can keep typing while the request flies.
34
+ setValue('');
35
+ await opts.onSend(trimmed);
36
+ }, [value]);
37
+ const cancel = useCallback(() => {
38
+ optionsRef.current.onCancel?.();
39
+ }, []);
40
+ const clear = useCallback(() => setValue(''), []);
41
+ const handleKeyDown = useCallback((e) => {
42
+ const opts = optionsRef.current;
43
+ if (opts.disabled)
44
+ return;
45
+ const enterSubmits = opts.enterToSubmit ?? true;
46
+ if (e.key === 'Enter') {
47
+ if (enterSubmits && !e.shiftKey && !e.metaKey && !e.ctrlKey) {
48
+ e.preventDefault();
49
+ if (opts.isStreaming) {
50
+ cancel();
51
+ }
52
+ else {
53
+ void submit();
54
+ }
55
+ return;
56
+ }
57
+ if (!enterSubmits && (e.metaKey || e.ctrlKey)) {
58
+ e.preventDefault();
59
+ if (opts.isStreaming)
60
+ cancel();
61
+ else
62
+ void submit();
63
+ return;
64
+ }
65
+ }
66
+ if (e.key === 'Escape' && opts.isStreaming) {
67
+ e.preventDefault();
68
+ cancel();
69
+ }
70
+ }, [submit, cancel]);
71
+ const canSubmit = value.trim().length > 0 && !(options.disabled || options.isStreaming);
72
+ return {
73
+ value,
74
+ setValue,
75
+ submit,
76
+ cancel,
77
+ clear,
78
+ handleKeyDown,
79
+ canSubmit,
80
+ };
81
+ }
82
+ //# sourceMappingURL=useChatComposer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatComposer.js","sourceRoot":"","sources":["../../src/state/useChatComposer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAiCtD,MAAM,UAAU,eAAe,CAAC,OAA+B;IAC7D,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACpC,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC;QAChC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,2EAA2E;QAC3E,QAAQ,CAAC,EAAE,CAAC,CAAC;QACb,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;QAC9B,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;IAClC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAElD,MAAM,aAAa,GAAG,WAAW,CAC/B,CAAC,CAAmC,EAAE,EAAE;QACtC,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC;QAChC,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;QAChD,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;YACtB,IAAI,YAAY,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;gBAC5D,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACrB,MAAM,EAAE,CAAC;gBACX,CAAC;qBAAM,CAAC;oBACN,KAAK,MAAM,EAAE,CAAC;gBAChB,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC9C,CAAC,CAAC,cAAc,EAAE,CAAC;gBACnB,IAAI,IAAI,CAAC,WAAW;oBAAE,MAAM,EAAE,CAAC;;oBAC1B,KAAK,MAAM,EAAE,CAAC;gBACnB,OAAO;YACT,CAAC;QACH,CAAC;QACD,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3C,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,MAAM,EAAE,CAAC;QACX,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,MAAM,CAAC,CACjB,CAAC;IAEF,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAExF,OAAO;QACL,KAAK;QACL,QAAQ;QACR,MAAM;QACN,MAAM;QACN,KAAK;QACL,aAAa;QACb,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `useChatList` — paginated chat-summary + agent-catalog state for the chat
3
+ * sidebar. Generic in the chat and agent shapes so cloud and local apps reuse
4
+ * the same hook (the cloud schema has extra fields on `Chat`, the local
5
+ * schema has fewer).
6
+ *
7
+ * Lifted from deeppath-agent's `useChatsAndAgents.ts` and deeppath's
8
+ * `useChatUI` chat-list subset. The hook owns:
9
+ * - paginated chat list with "load more" and refresh
10
+ * - the agent catalog (single page for now)
11
+ * - the agent picked for the next new chat
12
+ * - in-place title patching (used by `chat_title_updated` SSE events to
13
+ * avoid clobbering pagination state with a full refresh)
14
+ * - create / delete chat actions, with optimistic patching of the local
15
+ * list.
16
+ *
17
+ * The transport is provided by the consumer so the same hook serves Electron
18
+ * IPC, the cloud REST client, or any other backend.
19
+ */
20
+ import { type Dispatch, type SetStateAction } from 'react';
21
+ export interface ChatListTransport<TChat, TAgent> {
22
+ listChats: (input: {
23
+ page: number;
24
+ pageSize: number;
25
+ }) => Promise<{
26
+ chats: TChat[];
27
+ hasMore: boolean;
28
+ }>;
29
+ listAgents: () => Promise<TAgent[]>;
30
+ createChat?: (input: {
31
+ agentId?: string;
32
+ projectId?: string;
33
+ }) => Promise<TChat | string>;
34
+ deleteChat?: (chatId: string) => Promise<boolean>;
35
+ /** Tell the hook how to read a chat's id and title. */
36
+ getChatId: (chat: TChat) => string;
37
+ setChatTitle?: (chat: TChat, title: string) => TChat;
38
+ /** Tell the hook how to read an agent's id (for `selectedAgentId`). */
39
+ getAgentId: (agent: TAgent) => string;
40
+ }
41
+ export interface UseChatListOptions<TChat, TAgent> {
42
+ transport: ChatListTransport<TChat, TAgent>;
43
+ /** Defaults to 50. */
44
+ pageSize?: number;
45
+ /** Skip the mount-time fetch (e.g. SSR contexts). */
46
+ skipInitialLoad?: boolean;
47
+ }
48
+ export interface UseChatListReturn<TChat, TAgent> {
49
+ chats: TChat[];
50
+ agents: TAgent[];
51
+ isLoading: boolean;
52
+ isLoadingMoreChats: boolean;
53
+ hasMoreChats: boolean;
54
+ error: Error | null;
55
+ selectedAgentId: string | null;
56
+ /**
57
+ * Accepts either a plain value or a React-style updater function so
58
+ * adapters can patch the selection without re-reading the current value.
59
+ */
60
+ setSelectedAgentId: Dispatch<SetStateAction<string | null>>;
61
+ refreshChats: () => Promise<void>;
62
+ refreshAgents: () => Promise<void>;
63
+ loadMoreChats: () => Promise<void>;
64
+ createChat: (input?: {
65
+ agentId?: string;
66
+ projectId?: string;
67
+ }) => Promise<string | null>;
68
+ deleteChat: (chatId: string) => Promise<boolean>;
69
+ /** In-place title patcher — does not hit the server. */
70
+ patchChatTitle: (chatId: string, title: string) => void;
71
+ }
72
+ export declare function useChatList<TChat, TAgent>(options: UseChatListOptions<TChat, TAgent>): UseChatListReturn<TChat, TAgent>;
73
+ //# sourceMappingURL=useChatList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatList.d.ts","sourceRoot":"","sources":["../../src/state/useChatList.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,iBAAiB,CAAC,KAAK,EAAE,MAAM;IAC9C,SAAS,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAChE,KAAK,EAAE,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,OAAO,CAAC;KAClB,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,CAAC;IAC1F,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,uDAAuD;IACvD,SAAS,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAC;IACnC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;IACrD,uEAAuE;IACvE,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB,CAAC,KAAK,EAAE,MAAM;IAC/C,SAAS,EAAE,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC5C,sBAAsB;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB,CAAC,KAAK,EAAE,MAAM;IAC9C,KAAK,EAAE,KAAK,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B;;;OAGG;IACH,kBAAkB,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAC5D,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzF,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,wDAAwD;IACxD,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EACvC,OAAO,EAAE,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,GACzC,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CA8IlC"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * `useChatList` — paginated chat-summary + agent-catalog state for the chat
3
+ * sidebar. Generic in the chat and agent shapes so cloud and local apps reuse
4
+ * the same hook (the cloud schema has extra fields on `Chat`, the local
5
+ * schema has fewer).
6
+ *
7
+ * Lifted from deeppath-agent's `useChatsAndAgents.ts` and deeppath's
8
+ * `useChatUI` chat-list subset. The hook owns:
9
+ * - paginated chat list with "load more" and refresh
10
+ * - the agent catalog (single page for now)
11
+ * - the agent picked for the next new chat
12
+ * - in-place title patching (used by `chat_title_updated` SSE events to
13
+ * avoid clobbering pagination state with a full refresh)
14
+ * - create / delete chat actions, with optimistic patching of the local
15
+ * list.
16
+ *
17
+ * The transport is provided by the consumer so the same hook serves Electron
18
+ * IPC, the cloud REST client, or any other backend.
19
+ */
20
+ import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
21
+ export function useChatList(options) {
22
+ const pageSize = options.pageSize ?? 50;
23
+ const transportRef = useRef(options.transport);
24
+ transportRef.current = options.transport;
25
+ const [chats, setChats] = useState([]);
26
+ const [agents, setAgents] = useState([]);
27
+ const [isLoading, setLoading] = useState(!options.skipInitialLoad);
28
+ const [isLoadingMoreChats, setLoadingMore] = useState(false);
29
+ const [hasMoreChats, setHasMore] = useState(false);
30
+ const [error, setError] = useState(null);
31
+ const [selectedAgentId, setSelectedAgentId] = useState(null);
32
+ const pageRef = useRef(1);
33
+ const mountedRef = useRef(true);
34
+ useEffect(() => {
35
+ mountedRef.current = true;
36
+ return () => {
37
+ mountedRef.current = false;
38
+ };
39
+ }, []);
40
+ const refreshChats = useCallback(async () => {
41
+ setLoading(true);
42
+ try {
43
+ const res = await transportRef.current.listChats({ page: 1, pageSize });
44
+ if (!mountedRef.current)
45
+ return;
46
+ setChats(res.chats);
47
+ setHasMore(res.hasMore);
48
+ pageRef.current = 1;
49
+ setError(null);
50
+ }
51
+ catch (err) {
52
+ if (mountedRef.current)
53
+ setError(err instanceof Error ? err : new Error(String(err)));
54
+ }
55
+ finally {
56
+ if (mountedRef.current)
57
+ setLoading(false);
58
+ }
59
+ }, [pageSize]);
60
+ const refreshAgents = useCallback(async () => {
61
+ try {
62
+ const list = await transportRef.current.listAgents();
63
+ if (mountedRef.current)
64
+ setAgents(list);
65
+ }
66
+ catch (err) {
67
+ if (mountedRef.current)
68
+ setError(err instanceof Error ? err : new Error(String(err)));
69
+ }
70
+ }, []);
71
+ const loadMoreChats = useCallback(async () => {
72
+ if (!hasMoreChats || isLoadingMoreChats)
73
+ return;
74
+ setLoadingMore(true);
75
+ try {
76
+ const next = pageRef.current + 1;
77
+ const res = await transportRef.current.listChats({ page: next, pageSize });
78
+ if (!mountedRef.current)
79
+ return;
80
+ setChats((prev) => prev.concat(res.chats));
81
+ setHasMore(res.hasMore);
82
+ pageRef.current = next;
83
+ }
84
+ catch (err) {
85
+ if (mountedRef.current)
86
+ setError(err instanceof Error ? err : new Error(String(err)));
87
+ }
88
+ finally {
89
+ if (mountedRef.current)
90
+ setLoadingMore(false);
91
+ }
92
+ }, [hasMoreChats, isLoadingMoreChats, pageSize]);
93
+ const createChat = useCallback(async (input) => {
94
+ const t = transportRef.current;
95
+ if (!t.createChat)
96
+ return null;
97
+ const created = await t.createChat({
98
+ agentId: input?.agentId ?? selectedAgentId ?? undefined,
99
+ projectId: input?.projectId,
100
+ });
101
+ // Either a chat object or a bare id.
102
+ const id = typeof created === 'string' ? created : t.getChatId(created);
103
+ await refreshChats();
104
+ return id;
105
+ }, [refreshChats, selectedAgentId]);
106
+ const deleteChat = useCallback(async (chatId) => {
107
+ const t = transportRef.current;
108
+ if (!t.deleteChat)
109
+ return false;
110
+ const ok = await t.deleteChat(chatId);
111
+ if (ok && mountedRef.current) {
112
+ setChats((prev) => prev.filter((c) => t.getChatId(c) !== chatId));
113
+ }
114
+ return ok;
115
+ }, []);
116
+ const patchChatTitle = useCallback((chatId, title) => {
117
+ const t = transportRef.current;
118
+ if (!t.setChatTitle)
119
+ return;
120
+ setChats((prev) => prev.map((c) => (t.getChatId(c) === chatId ? t.setChatTitle(c, title) : c)));
121
+ }, []);
122
+ useEffect(() => {
123
+ if (options.skipInitialLoad)
124
+ return;
125
+ void refreshChats();
126
+ void refreshAgents();
127
+ // Run once on mount; downstream callers use refresh* explicitly.
128
+ // eslint-disable-next-line react-hooks/exhaustive-deps
129
+ }, []);
130
+ return useMemo(() => ({
131
+ chats,
132
+ agents,
133
+ isLoading,
134
+ isLoadingMoreChats,
135
+ hasMoreChats,
136
+ error,
137
+ selectedAgentId,
138
+ setSelectedAgentId,
139
+ refreshChats,
140
+ refreshAgents,
141
+ loadMoreChats,
142
+ createChat,
143
+ deleteChat,
144
+ patchChatTitle,
145
+ }), [
146
+ chats,
147
+ agents,
148
+ isLoading,
149
+ isLoadingMoreChats,
150
+ hasMoreChats,
151
+ error,
152
+ selectedAgentId,
153
+ refreshChats,
154
+ refreshAgents,
155
+ loadMoreChats,
156
+ createChat,
157
+ deleteChat,
158
+ patchChatTitle,
159
+ ]);
160
+ }
161
+ //# sourceMappingURL=useChatList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatList.js","sourceRoot":"","sources":["../../src/state/useChatList.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `useChatSession` — convenience wrapper that combines `useChatStream` (turn
3
+ * lifecycle) with `useChatComposer` (draft + keys + send). Most consumers want
4
+ * this single hook so the wiring is identical across web-shell, deeppath, and
5
+ * deeppath-agent. Power users still reach for the underlying primitives.
6
+ *
7
+ * const { messages, isStreaming, composer, cancel } = useChatSession({
8
+ * transport,
9
+ * initialMessages,
10
+ * });
11
+ *
12
+ * <ChatPanel>
13
+ * <MessageList messages={messages} isStreaming={isStreaming} />
14
+ * <ChatInput composer={composer} onCancel={cancel} />
15
+ * </ChatPanel>
16
+ */
17
+ import { type UseChatStreamOptions, type UseChatStreamReturn } from '../hooks/useChatStream.js';
18
+ import { type UseChatComposerOptions, type UseChatComposerReturn } from './useChatComposer.js';
19
+ export interface UseChatSessionOptions extends UseChatStreamOptions, Pick<UseChatComposerOptions, 'enterToSubmit' | 'initialValue'> {
20
+ /** Optional metadata callback when submitting a message (e.g. @mentions). */
21
+ buildMetadata?: (value: string) => Record<string, unknown> | undefined;
22
+ /** Force-disable the composer regardless of streaming state. */
23
+ composerDisabled?: boolean;
24
+ }
25
+ export interface UseChatSessionReturn extends UseChatStreamReturn {
26
+ composer: UseChatComposerReturn;
27
+ }
28
+ export declare function useChatSession(options: UseChatSessionOptions): UseChatSessionReturn;
29
+ //# sourceMappingURL=useChatSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatSession.d.ts","sourceRoot":"","sources":["../../src/state/useChatSession.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,qBACf,SAAQ,oBAAoB,EAC1B,IAAI,CAAC,sBAAsB,EAAE,eAAe,GAAG,cAAc,CAAC;IAChE,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IACvE,gEAAgE;IAChE,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAC/D,QAAQ,EAAE,qBAAqB,CAAC;CACjC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,oBAAoB,CAqBnF"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `useChatSession` — convenience wrapper that combines `useChatStream` (turn
3
+ * lifecycle) with `useChatComposer` (draft + keys + send). Most consumers want
4
+ * this single hook so the wiring is identical across web-shell, deeppath, and
5
+ * deeppath-agent. Power users still reach for the underlying primitives.
6
+ *
7
+ * const { messages, isStreaming, composer, cancel } = useChatSession({
8
+ * transport,
9
+ * initialMessages,
10
+ * });
11
+ *
12
+ * <ChatPanel>
13
+ * <MessageList messages={messages} isStreaming={isStreaming} />
14
+ * <ChatInput composer={composer} onCancel={cancel} />
15
+ * </ChatPanel>
16
+ */
17
+ import { useMemo } from 'react';
18
+ import { useChatStream, } from '../hooks/useChatStream.js';
19
+ import { useChatComposer, } from './useChatComposer.js';
20
+ export function useChatSession(options) {
21
+ const stream = useChatStream(options);
22
+ const composer = useChatComposer({
23
+ initialValue: options.initialValue,
24
+ enterToSubmit: options.enterToSubmit,
25
+ isStreaming: stream.isStreaming,
26
+ disabled: options.composerDisabled,
27
+ onSend: async (value) => {
28
+ await stream.sendUserMessage({
29
+ content: value,
30
+ metadata: options.buildMetadata?.(value),
31
+ });
32
+ },
33
+ onCancel: stream.cancel,
34
+ });
35
+ return useMemo(() => ({ ...stream, composer }), [stream, composer]);
36
+ }
37
+ //# sourceMappingURL=useChatSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatSession.js","sourceRoot":"","sources":["../../src/state/useChatSession.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EACL,aAAa,GAGd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,eAAe,GAGhB,MAAM,sBAAsB,CAAC;AAe9B,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAEtC,MAAM,QAAQ,GAAG,eAAe,CAAC;QAC/B,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,QAAQ,EAAE,OAAO,CAAC,gBAAgB;QAClC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,MAAM,MAAM,CAAC,eAAe,CAAC;gBAC3B,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC,KAAK,CAAC;aACzC,CAAC,CAAC;QACL,CAAC;QACD,QAAQ,EAAE,MAAM,CAAC,MAAM;KACxB,CAAC,CAAC;IAEH,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC,EAC/B,CAAC,MAAM,EAAE,QAAQ,CAAC,CACnB,CAAC;AACJ,CAAC"}