joonecli 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +12 -12
  2. package/dist/__tests__/optimizations.test.js.map +1 -1
  3. package/dist/__tests__/promptBuilder.test.js +14 -20
  4. package/dist/__tests__/promptBuilder.test.js.map +1 -1
  5. package/dist/agents/agentRegistry.d.ts +37 -0
  6. package/dist/agents/agentRegistry.js +58 -0
  7. package/dist/agents/agentRegistry.js.map +1 -0
  8. package/dist/agents/agentSpec.d.ts +54 -0
  9. package/dist/agents/agentSpec.js +9 -0
  10. package/dist/agents/agentSpec.js.map +1 -0
  11. package/dist/agents/builtinAgents.d.ts +20 -0
  12. package/{src/agents/builtinAgents.ts → dist/agents/builtinAgents.js} +84 -101
  13. package/dist/agents/builtinAgents.js.map +1 -0
  14. package/dist/cli/config.d.ts +4 -0
  15. package/dist/cli/config.js.map +1 -1
  16. package/dist/cli/index.js +29 -2
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/postinstall.d.ts +2 -0
  19. package/dist/cli/postinstall.js +25 -0
  20. package/dist/cli/postinstall.js.map +1 -0
  21. package/dist/commands/builtinCommands.d.ts +21 -0
  22. package/dist/commands/builtinCommands.js +241 -0
  23. package/dist/commands/builtinCommands.js.map +1 -0
  24. package/dist/commands/commandRegistry.d.ts +92 -0
  25. package/dist/commands/commandRegistry.js +128 -0
  26. package/dist/commands/commandRegistry.js.map +1 -0
  27. package/dist/core/agentLoop.d.ts +7 -2
  28. package/dist/core/agentLoop.js +35 -13
  29. package/dist/core/agentLoop.js.map +1 -1
  30. package/dist/core/autoSave.d.ts +41 -0
  31. package/dist/core/autoSave.js +69 -0
  32. package/dist/core/autoSave.js.map +1 -0
  33. package/dist/core/compactor.d.ts +66 -0
  34. package/dist/core/compactor.js +170 -0
  35. package/dist/core/compactor.js.map +1 -0
  36. package/dist/core/contextGuard.d.ts +38 -0
  37. package/dist/core/contextGuard.js +122 -0
  38. package/dist/core/contextGuard.js.map +1 -0
  39. package/dist/core/events.d.ts +45 -0
  40. package/dist/core/events.js +8 -0
  41. package/dist/core/events.js.map +1 -0
  42. package/dist/core/promptBuilder.d.ts +16 -1
  43. package/dist/core/promptBuilder.js +27 -14
  44. package/dist/core/promptBuilder.js.map +1 -1
  45. package/dist/core/sessionResumer.js +3 -3
  46. package/dist/core/sessionResumer.js.map +1 -1
  47. package/dist/core/sessionStore.js +3 -2
  48. package/dist/core/sessionStore.js.map +1 -1
  49. package/dist/core/subAgent.d.ts +56 -0
  50. package/dist/core/subAgent.js +240 -0
  51. package/dist/core/subAgent.js.map +1 -0
  52. package/dist/core/tokenCounter.d.ts +8 -1
  53. package/dist/core/tokenCounter.js +28 -0
  54. package/dist/core/tokenCounter.js.map +1 -1
  55. package/dist/debug_google.d.ts +1 -0
  56. package/dist/debug_google.js +23 -0
  57. package/dist/debug_google.js.map +1 -0
  58. package/dist/middleware/permission.js +1 -0
  59. package/dist/middleware/permission.js.map +1 -1
  60. package/dist/test_google.d.ts +1 -0
  61. package/dist/test_google.js +32 -89
  62. package/dist/test_google.js.map +1 -0
  63. package/dist/tools/browser.js +4 -1
  64. package/dist/tools/browser.js.map +1 -1
  65. package/dist/tools/index.d.ts +2 -1
  66. package/dist/tools/index.js +11 -3
  67. package/dist/tools/index.js.map +1 -1
  68. package/dist/tools/installHostDeps.d.ts +2 -0
  69. package/dist/tools/installHostDeps.js +37 -0
  70. package/dist/tools/installHostDeps.js.map +1 -0
  71. package/dist/tools/router.js +3 -0
  72. package/dist/tools/router.js.map +1 -1
  73. package/dist/tools/spawnAgent.d.ts +19 -0
  74. package/dist/tools/spawnAgent.js +132 -0
  75. package/dist/tools/spawnAgent.js.map +1 -0
  76. package/dist/tracing/sessionTracer.d.ts +1 -0
  77. package/dist/tracing/sessionTracer.js +4 -1
  78. package/dist/tracing/sessionTracer.js.map +1 -1
  79. package/dist/ui/App.js +94 -6
  80. package/dist/ui/App.js.map +1 -1
  81. package/dist/ui/components/ActionLog.d.ts +7 -0
  82. package/dist/ui/components/ActionLog.js +63 -0
  83. package/dist/ui/components/ActionLog.js.map +1 -0
  84. package/dist/ui/components/FileBrowser.d.ts +2 -0
  85. package/dist/ui/components/FileBrowser.js +41 -0
  86. package/dist/ui/components/FileBrowser.js.map +1 -0
  87. package/package.json +5 -6
  88. package/AGENTS.md +0 -56
  89. package/Handover.md +0 -115
  90. package/PROGRESS.md +0 -160
  91. package/docs/01_insights_and_patterns.md +0 -27
  92. package/docs/02_edge_cases_and_mitigations.md +0 -143
  93. package/docs/03_initial_implementation_plan.md +0 -66
  94. package/docs/04_tech_stack_proposal.md +0 -20
  95. package/docs/05_prd.md +0 -87
  96. package/docs/06_user_stories.md +0 -72
  97. package/docs/07_system_architecture.md +0 -138
  98. package/docs/08_roadmap.md +0 -200
  99. package/e2b/Dockerfile +0 -26
  100. package/src/__tests__/bootstrap.test.ts +0 -111
  101. package/src/__tests__/config.test.ts +0 -97
  102. package/src/__tests__/m55.test.ts +0 -238
  103. package/src/__tests__/middleware.test.ts +0 -219
  104. package/src/__tests__/modelFactory.test.ts +0 -63
  105. package/src/__tests__/optimizations.test.ts +0 -201
  106. package/src/__tests__/promptBuilder.test.ts +0 -141
  107. package/src/__tests__/sandbox.test.ts +0 -102
  108. package/src/__tests__/security.test.ts +0 -122
  109. package/src/__tests__/streaming.test.ts +0 -82
  110. package/src/__tests__/toolRouter.test.ts +0 -52
  111. package/src/__tests__/tools.test.ts +0 -146
  112. package/src/__tests__/tracing.test.ts +0 -196
  113. package/src/agents/agentRegistry.ts +0 -69
  114. package/src/agents/agentSpec.ts +0 -67
  115. package/src/cli/config.ts +0 -124
  116. package/src/cli/index.ts +0 -730
  117. package/src/cli/modelFactory.ts +0 -174
  118. package/src/cli/providers.ts +0 -107
  119. package/src/commands/builtinCommands.ts +0 -293
  120. package/src/commands/commandRegistry.ts +0 -194
  121. package/src/core/agentLoop.d.ts.map +0 -1
  122. package/src/core/agentLoop.ts +0 -312
  123. package/src/core/autoSave.ts +0 -95
  124. package/src/core/compactor.ts +0 -252
  125. package/src/core/contextGuard.ts +0 -129
  126. package/src/core/errors.ts +0 -202
  127. package/src/core/promptBuilder.d.ts.map +0 -1
  128. package/src/core/promptBuilder.ts +0 -139
  129. package/src/core/reasoningRouter.ts +0 -121
  130. package/src/core/retry.ts +0 -75
  131. package/src/core/sessionResumer.ts +0 -90
  132. package/src/core/sessionStore.ts +0 -215
  133. package/src/core/subAgent.ts +0 -339
  134. package/src/core/tokenCounter.ts +0 -64
  135. package/src/evals/dataset.ts +0 -67
  136. package/src/evals/evaluator.ts +0 -81
  137. package/src/hitl/bridge.ts +0 -160
  138. package/src/middleware/commandSanitizer.ts +0 -60
  139. package/src/middleware/loopDetection.ts +0 -63
  140. package/src/middleware/permission.ts +0 -72
  141. package/src/middleware/pipeline.ts +0 -75
  142. package/src/middleware/preCompletion.ts +0 -94
  143. package/src/middleware/types.ts +0 -45
  144. package/src/sandbox/bootstrap.ts +0 -121
  145. package/src/sandbox/manager.ts +0 -239
  146. package/src/sandbox/sync.ts +0 -157
  147. package/src/skills/loader.ts +0 -143
  148. package/src/skills/tools.ts +0 -99
  149. package/src/skills/types.ts +0 -13
  150. package/src/test_cache.ts +0 -72
  151. package/src/test_google.js +0 -40
  152. package/src/test_google.ts +0 -40
  153. package/src/tools/askUser.ts +0 -47
  154. package/src/tools/browser.ts +0 -137
  155. package/src/tools/index.d.ts.map +0 -1
  156. package/src/tools/index.ts +0 -237
  157. package/src/tools/registry.ts +0 -198
  158. package/src/tools/router.ts +0 -78
  159. package/src/tools/security.ts +0 -220
  160. package/src/tools/spawnAgent.ts +0 -158
  161. package/src/tools/webSearch.ts +0 -142
  162. package/src/tracing/analyzer.ts +0 -265
  163. package/src/tracing/langsmith.ts +0 -63
  164. package/src/tracing/sessionTracer.ts +0 -202
  165. package/src/tracing/types.ts +0 -49
  166. package/src/types/valyu.d.ts +0 -37
  167. package/src/ui/App.tsx +0 -404
  168. package/src/ui/components/HITLPrompt.tsx +0 -119
  169. package/src/ui/components/Header.tsx +0 -51
  170. package/src/ui/components/MessageBubble.tsx +0 -46
  171. package/src/ui/components/StatusBar.tsx +0 -138
  172. package/src/ui/components/StreamingText.tsx +0 -48
  173. package/src/ui/components/ToolCallPanel.tsx +0 -80
  174. package/tests/commands/commands.test.ts +0 -356
  175. package/tests/core/compactor.test.ts +0 -217
  176. package/tests/core/retryAndErrors.test.ts +0 -164
  177. package/tests/core/sessionResumer.test.ts +0 -95
  178. package/tests/core/sessionStore.test.ts +0 -84
  179. package/tests/core/stability.test.ts +0 -165
  180. package/tests/core/subAgent.test.ts +0 -238
  181. package/tests/hitl/hitlBridge.test.ts +0 -115
  182. package/tsconfig.json +0 -16
  183. package/vitest.config.ts +0 -10
  184. package/vitest.out +0 -48
@@ -1,49 +0,0 @@
1
- /**
2
- * Core tracing data types for session instrumentation.
3
- */
4
-
5
- /**
6
- * A single traced event during agent execution.
7
- */
8
- export interface TraceEvent {
9
- /** Event category. */
10
- type: "llm_call" | "tool_call" | "error" | "compaction";
11
- /** Unix timestamp (ms) when the event started. */
12
- timestamp: number;
13
- /** Duration in milliseconds (if applicable). */
14
- duration?: number;
15
- /** Event-specific payload. */
16
- data: Record<string, any>;
17
- }
18
-
19
- /**
20
- * Aggregated metrics for a complete session.
21
- */
22
- export interface TraceSummary {
23
- totalTokens: number;
24
- promptTokens: number;
25
- completionTokens: number;
26
- /** Estimated cost in USD. */
27
- totalCost: number;
28
- /** Cache hit rate: 0-1 (ratio of cached prompt tokens to total prompt tokens). */
29
- cacheHitRate: number;
30
- /** Number of tool calls made during the session. */
31
- toolCallCount: number;
32
- /** Number of errors encountered. */
33
- errorCount: number;
34
- /** Total session duration in ms. */
35
- totalDuration: number;
36
- /** Number of LLM turns. */
37
- turnCount: number;
38
- }
39
-
40
- /**
41
- * Full trace for a single agent session, suitable for persistence and analysis.
42
- */
43
- export interface SessionTrace {
44
- sessionId: string;
45
- startedAt: number;
46
- endedAt?: number;
47
- events: TraceEvent[];
48
- summary: TraceSummary;
49
- }
@@ -1,37 +0,0 @@
1
- /**
2
- * Type declaration for @valyu/ai-sdk.
3
- *
4
- * This is a minimal stub so TypeScript compiles without requiring the
5
- * package to be installed. The actual SDK is dynamically imported at runtime.
6
- */
7
- declare module "@valyu/ai-sdk" {
8
- interface ValyuOptions {
9
- apiKey: string;
10
- }
11
-
12
- interface SearchParams {
13
- query: string;
14
- maxResults?: number;
15
- }
16
-
17
- interface SearchResult {
18
- title?: string;
19
- url?: string;
20
- snippet?: string;
21
- content?: string;
22
- }
23
-
24
- interface SearchResponse {
25
- results: SearchResult[];
26
- }
27
-
28
- export class Valyu {
29
- constructor(options: ValyuOptions);
30
- search(params: SearchParams): Promise<SearchResponse>;
31
- paperSearch(params: SearchParams): Promise<SearchResponse>;
32
- financeSearch(params: SearchParams): Promise<SearchResponse>;
33
- patentSearch(params: SearchParams): Promise<SearchResponse>;
34
- secSearch(params: SearchParams): Promise<SearchResponse>;
35
- companyResearch(params: SearchParams): Promise<SearchResponse>;
36
- }
37
- }
package/src/ui/App.tsx DELETED
@@ -1,404 +0,0 @@
1
- import React, { useState, useEffect, useMemo } from "react";
2
- import { Box, Text, useInput, useApp } from "ink";
3
- import TextInput from "ink-text-input";
4
- import { Header } from "./components/Header.js";
5
- import { StatusBar } from "./components/StatusBar.js";
6
- import { MessageBubble } from "./components/MessageBubble.js";
7
- import { StreamingText } from "./components/StreamingText.js";
8
- import { ToolCallPanel, ToolCallStatus } from "./components/ToolCallPanel.js";
9
- import { HITLPrompt } from "./components/HITLPrompt.js";
10
- import { ExecutionHarness } from "../core/agentLoop.js";
11
- import { ContextState } from "../core/promptBuilder.js";
12
- import { countMessageTokens } from "../core/tokenCounter.js";
13
- import { HumanMessage, AIMessage, BaseMessage } from "@langchain/core/messages";
14
- import {
15
- HITLBridge,
16
- HITLQuestion,
17
- HITLPermissionRequest,
18
- } from "../hitl/bridge.js";
19
- import { createDefaultRegistry } from "../commands/builtinCommands.js";
20
- import { CommandRegistry } from "../commands/commandRegistry.js";
21
-
22
- export interface Message {
23
- role: "user" | "agent" | "system";
24
- content: string;
25
- }
26
-
27
- export interface ActiveToolCall {
28
- name: string;
29
- args: Record<string, unknown>;
30
- status: ToolCallStatus;
31
- result?: string;
32
- }
33
-
34
- interface AppProps {
35
- provider: string;
36
- model: string;
37
- streaming: boolean;
38
- harness: ExecutionHarness;
39
- initialState: ContextState;
40
- maxTokens: number;
41
- }
42
-
43
- export const App: React.FC<AppProps> = ({
44
- provider,
45
- model,
46
- streaming,
47
- harness,
48
- initialState,
49
- maxTokens,
50
- }) => {
51
- const { exit } = useApp();
52
-
53
- // Slash Command Registry — initialized once, stable across renders
54
- const commandRegistry = useMemo(() => createDefaultRegistry(), []);
55
-
56
- // UI State
57
- const [messages, setMessages] = useState<Message[]>([
58
- { role: "system", content: "Session started. Type your request below." },
59
- ]);
60
- const [inputValue, setInputValue] = useState("");
61
- const [isProcessing, setIsProcessing] = useState(false);
62
-
63
- // Streaming & Tool State
64
- const [streamingTokens, setStreamingTokens] = useState<string[]>([]);
65
- const [isStreaming, setIsStreaming] = useState(false);
66
- const [activeToolCall, setActiveToolCall] = useState<ActiveToolCall | null>(
67
- null,
68
- );
69
-
70
- // Core Engine State
71
- const [contextState, setContextState] = useState<ContextState>(initialState);
72
-
73
- // HITL State
74
- const [hitlQuestion, setHitlQuestion] = useState<HITLQuestion | undefined>(
75
- undefined,
76
- );
77
- const [hitlPermission, setHitlPermission] = useState<
78
- HITLPermissionRequest | undefined
79
- >(undefined);
80
-
81
- // Listen for HITL events from the bridge
82
- useEffect(() => {
83
- const bridge = HITLBridge.getInstance();
84
-
85
- const onQuestion = (q: HITLQuestion) => setHitlQuestion(q);
86
- const onPermission = (p: HITLPermissionRequest) => setHitlPermission(p);
87
-
88
- bridge.on("question", onQuestion);
89
- bridge.on("permission", onPermission);
90
-
91
- // Clear prompts when resolved
92
- const origResolve = bridge.resolveAnswer.bind(bridge);
93
- bridge.resolveAnswer = (id: string, answer: string) => {
94
- origResolve(id, answer);
95
- setHitlQuestion(undefined);
96
- setHitlPermission(undefined);
97
- };
98
-
99
- return () => {
100
- bridge.off("question", onQuestion);
101
- bridge.off("permission", onPermission);
102
- };
103
- }, []);
104
-
105
- // StatusBar Metrics
106
- const [startTime] = useState(Date.now());
107
- const [elapsed, setElapsed] = useState("0s");
108
-
109
- // Update elapsed time
110
- useEffect(() => {
111
- const interval = setInterval(() => {
112
- const seconds = Math.floor((Date.now() - startTime) / 1000);
113
- if (seconds < 60) {
114
- setElapsed(`${seconds}s`);
115
- } else {
116
- const mins = Math.floor(seconds / 60);
117
- const secs = seconds % 60;
118
- setElapsed(`${mins}m ${secs}s`);
119
- }
120
- }, 1000);
121
- return () => clearInterval(interval);
122
- }, [startTime]);
123
-
124
- // Hold the latest state in a ref for the unmount/signal handlers
125
- const stateRef = React.useRef(contextState);
126
- useEffect(() => {
127
- stateRef.current = contextState;
128
- }, [contextState]);
129
-
130
- const performGracefulExit = async () => {
131
- try {
132
- await harness.autoSave.forceSave({
133
- config: { provider, model },
134
- state: stateRef.current,
135
- });
136
- } catch (e) {
137
- // Ignore errors during exit
138
- } finally {
139
- exit();
140
- process.exit(0);
141
- }
142
- };
143
-
144
- useEffect(() => {
145
- const handleSignal = () => {
146
- performGracefulExit();
147
- };
148
-
149
- process.on("SIGINT", handleSignal);
150
- process.on("SIGTERM", handleSignal);
151
-
152
- return () => {
153
- process.off("SIGINT", handleSignal);
154
- process.off("SIGTERM", handleSignal);
155
- };
156
- }, [provider, model, harness, exit]);
157
-
158
- // Handle Ctrl+C (Keyboard)
159
- useInput((input, key) => {
160
- if (key.ctrl && input === "c") {
161
- performGracefulExit();
162
- }
163
- });
164
-
165
- const runAgentLoop = async (currentState: ContextState) => {
166
- try {
167
- setIsStreaming(true);
168
- setStreamingTokens([]);
169
-
170
- let aiResponse: AIMessage;
171
-
172
- if (streaming) {
173
- aiResponse = await harness.streamStep(currentState, {
174
- onToken: (token) => {
175
- setStreamingTokens((prev) => [...prev, token]);
176
- },
177
- });
178
- } else {
179
- aiResponse = await harness.step(currentState);
180
- }
181
-
182
- setIsStreaming(false);
183
-
184
- // Add AI text to UI if any
185
- if (
186
- aiResponse.content &&
187
- typeof aiResponse.content === "string" &&
188
- aiResponse.content.trim() !== ""
189
- ) {
190
- setMessages((prev) => [
191
- ...prev,
192
- { role: "agent", content: aiResponse.content as string },
193
- ]);
194
- }
195
-
196
- // Add AI message to memory for following tools
197
- let nextHistory = [...currentState.conversationHistory, aiResponse];
198
-
199
- // Handle Tools
200
- if (aiResponse.tool_calls && aiResponse.tool_calls.length > 0) {
201
- // Execute all tool calls once
202
- const toolMessages = await harness.executeToolCalls(
203
- aiResponse,
204
- currentState,
205
- );
206
-
207
- // Update UI sequentially for each tool call
208
- for (const call of aiResponse.tool_calls) {
209
- setActiveToolCall({
210
- name: call.name,
211
- args: call.args,
212
- status: "running",
213
- });
214
-
215
- // Brief delay to show running state
216
- await new Promise((resolve) => setTimeout(resolve, 300));
217
-
218
- setActiveToolCall({
219
- name: call.name,
220
- args: call.args,
221
- status: "success",
222
- result:
223
- toolMessages.length > 0
224
- ? "Tool execution completed."
225
- : "No output.",
226
- });
227
- }
228
-
229
- // Add tool results to history once
230
- nextHistory = [...nextHistory, ...toolMessages];
231
-
232
- // Wait a sec so user sees the success state
233
- await new Promise((resolve) => setTimeout(resolve, 800));
234
- setActiveToolCall(null);
235
-
236
- // Recurse: Agent needs to react to the tool output
237
- const nextState = { ...currentState, conversationHistory: nextHistory };
238
- setContextState(nextState);
239
- await runAgentLoop(nextState);
240
- } else {
241
- // Turn complete
242
- setContextState({ ...currentState, conversationHistory: nextHistory });
243
- setIsProcessing(false);
244
- }
245
- } catch (error: any) {
246
- setIsStreaming(false);
247
- setActiveToolCall(null);
248
- setIsProcessing(false);
249
- setMessages((prev) => [
250
- ...prev,
251
- { role: "system", content: `Error: ${error.message}` },
252
- ]);
253
- }
254
- };
255
-
256
- const handleSubmit = async (query: string) => {
257
- if (!query.trim() || isProcessing) return;
258
-
259
- const userText = query.trim();
260
- setInputValue("");
261
-
262
- // ── Slash Command Interception ──
263
- // Commands starting with "/" are handled locally at zero LLM cost.
264
- if (commandRegistry.isCommand(userText)) {
265
- setMessages((prev) => [...prev, { role: "user", content: userText }]);
266
-
267
- const commandContext = {
268
- config: {
269
- provider,
270
- model,
271
- maxTokens,
272
- streaming,
273
- temperature: 0,
274
- } as any,
275
- configPath: "",
276
- harness,
277
- contextState,
278
- setContextState,
279
- addSystemMessage: (content: string) => {
280
- setMessages((prev) => [...prev, { role: "system", content }]);
281
- },
282
- provider,
283
- model,
284
- maxTokens,
285
- };
286
-
287
- try {
288
- const result = await commandRegistry.execute(userText, commandContext);
289
-
290
- // Handle /exit signal
291
- if (result === "__EXIT__") {
292
- setMessages((prev) => [
293
- ...prev,
294
- { role: "system", content: "Goodbye! 👋" },
295
- ]);
296
- setTimeout(() => {
297
- exit();
298
- process.exit(0);
299
- }, 500);
300
- return;
301
- }
302
-
303
- if (result) {
304
- setMessages((prev) => [...prev, { role: "system", content: result }]);
305
- }
306
- } catch (err: any) {
307
- setMessages((prev) => [
308
- ...prev,
309
- { role: "system", content: `Command error: ${err.message}` },
310
- ]);
311
- }
312
- return;
313
- }
314
-
315
- // ── Normal Agent Message ──
316
- setIsProcessing(true);
317
-
318
- // 1. Update UI
319
- setMessages((prev) => [...prev, { role: "user", content: userText }]);
320
-
321
- // 2. Update Engine State
322
- const humanMsg = new HumanMessage(userText);
323
- const updatedState = {
324
- ...contextState,
325
- conversationHistory: [...contextState.conversationHistory, humanMsg],
326
- };
327
- setContextState(updatedState);
328
-
329
- // 3. Start Turn
330
- runAgentLoop(updatedState);
331
- };
332
-
333
- const summary = harness.tracer.getSummary();
334
- const contextTokens = countMessageTokens(contextState.conversationHistory);
335
-
336
- return (
337
- <Box flexDirection="column" minHeight={15}>
338
- <Header provider={provider} model={model} streaming={streaming} />
339
-
340
- <Box flexDirection="column" paddingY={1}>
341
- {messages.map((msg, i) => (
342
- <MessageBubble key={i} role={msg.role} content={msg.content} />
343
- ))}
344
- </Box>
345
-
346
- {isStreaming && (
347
- <Box paddingX={1} marginBottom={1}>
348
- <Box marginLeft={2}>
349
- <StreamingText tokens={streamingTokens} isStreaming={isStreaming} />
350
- </Box>
351
- </Box>
352
- )}
353
-
354
- {activeToolCall && (
355
- <Box paddingX={1} marginBottom={1}>
356
- <ToolCallPanel
357
- toolName={activeToolCall.name}
358
- args={activeToolCall.args}
359
- status={activeToolCall.status}
360
- result={activeToolCall.result}
361
- />
362
- </Box>
363
- )}
364
-
365
- {/* Interactive Prompt Area */}
366
- {(hitlQuestion || hitlPermission) && (
367
- <HITLPrompt question={hitlQuestion} permission={hitlPermission} />
368
- )}
369
-
370
- {!isProcessing && !hitlQuestion && !hitlPermission && (
371
- <Box paddingX={1} marginBottom={1}>
372
- <Box marginRight={1}>
373
- <Text color="green" bold>
374
-
375
- </Text>
376
- </Box>
377
- <TextInput
378
- value={inputValue}
379
- onChange={setInputValue}
380
- onSubmit={handleSubmit}
381
- placeholder="What should we build today?"
382
- />
383
- </Box>
384
- )}
385
-
386
- {isProcessing && !isStreaming && !activeToolCall && (
387
- <Box paddingX={1} marginBottom={1}>
388
- <Text dimColor>Thinking...</Text>
389
- </Box>
390
- )}
391
-
392
- <StatusBar
393
- contextTokens={contextTokens}
394
- maxContextTokens={maxTokens}
395
- totalTokens={summary.totalTokens}
396
- cacheHitRate={summary.cacheHitRate}
397
- toolCalls={summary.toolCallCount}
398
- turns={summary.turnCount}
399
- cost={summary.totalCost}
400
- elapsed={elapsed}
401
- />
402
- </Box>
403
- );
404
- };
@@ -1,119 +0,0 @@
1
- import React, { useState } from "react";
2
- import { Box, Text } from "ink";
3
- import TextInput from "ink-text-input";
4
- import {
5
- HITLQuestion,
6
- HITLPermissionRequest,
7
- HITLBridge,
8
- } from "../../hitl/bridge.js";
9
-
10
- interface HITLPromptProps {
11
- /** The active HITL question or permission request. */
12
- question?: HITLQuestion;
13
- permission?: HITLPermissionRequest;
14
- }
15
-
16
- /**
17
- * HITLPrompt — renders a blocking question/permission prompt in the TUI.
18
- *
19
- * When a tool calls `ask_user_question` or the PermissionMiddleware fires,
20
- * this component takes over the input area to capture the user's response.
21
- */
22
- export const HITLPrompt: React.FC<HITLPromptProps> = ({
23
- question,
24
- permission,
25
- }) => {
26
- const [inputValue, setInputValue] = useState("");
27
-
28
- const handleSubmit = (value: string) => {
29
- const bridge = HITLBridge.getInstance();
30
- if (question) {
31
- bridge.resolveAnswer(question.id, value);
32
- } else if (permission) {
33
- bridge.resolveAnswer(permission.id, value);
34
- }
35
- setInputValue("");
36
- };
37
-
38
- if (question) {
39
- return (
40
- <Box flexDirection="column" paddingX={1} marginBottom={1}>
41
- <Box marginBottom={1}>
42
- <Text color="yellow" bold>
43
- ❓ Agent is asking:
44
- </Text>
45
- </Box>
46
- <Box marginLeft={2} marginBottom={1}>
47
- <Text>{question.question}</Text>
48
- </Box>
49
- {question.options && question.options.length > 0 && (
50
- <Box flexDirection="column" marginLeft={2} marginBottom={1}>
51
- {question.options.map((opt, i) => (
52
- <Text key={i} dimColor>
53
- {` ${i + 1}. ${opt}`}
54
- </Text>
55
- ))}
56
- </Box>
57
- )}
58
- <Box>
59
- <Box marginRight={1}>
60
- <Text color="yellow" bold>
61
-
62
- </Text>
63
- </Box>
64
- <TextInput
65
- value={inputValue}
66
- onChange={setInputValue}
67
- onSubmit={handleSubmit}
68
- placeholder="Type your answer..."
69
- />
70
- </Box>
71
- </Box>
72
- );
73
- }
74
-
75
- if (permission) {
76
- const argsSummary = Object.entries(permission.args)
77
- .map(
78
- ([k, v]) =>
79
- `${k}: ${typeof v === "string" ? v.substring(0, 60) : JSON.stringify(v)}`,
80
- )
81
- .join(", ");
82
-
83
- return (
84
- <Box flexDirection="column" paddingX={1} marginBottom={1}>
85
- <Box marginBottom={1}>
86
- <Text color="red" bold>
87
- ⚠ Permission Required:
88
- </Text>
89
- </Box>
90
- <Box marginLeft={2} marginBottom={1}>
91
- <Text>
92
- The agent wants to execute{" "}
93
- <Text bold color="white">
94
- {permission.toolName}
95
- </Text>
96
- </Text>
97
- </Box>
98
- <Box marginLeft={2} marginBottom={1}>
99
- <Text dimColor>{argsSummary}</Text>
100
- </Box>
101
- <Box>
102
- <Box marginRight={1}>
103
- <Text color="red" bold>
104
- Allow? (y/n):
105
- </Text>
106
- </Box>
107
- <TextInput
108
- value={inputValue}
109
- onChange={setInputValue}
110
- onSubmit={handleSubmit}
111
- placeholder="y or n"
112
- />
113
- </Box>
114
- </Box>
115
- );
116
- }
117
-
118
- return null;
119
- };
@@ -1,51 +0,0 @@
1
- import React from "react";
2
- import { Box, Text } from "ink";
3
- import chalk from "chalk";
4
-
5
- interface HeaderProps {
6
- provider: string;
7
- model: string;
8
- streaming: boolean;
9
- }
10
-
11
- export const Header: React.FC<HeaderProps> = ({
12
- provider,
13
- model,
14
- streaming,
15
- }) => {
16
- return (
17
- <Box
18
- flexDirection="column"
19
- paddingX={1}
20
- borderStyle="round"
21
- borderColor="cyan"
22
- >
23
- <Box justifyContent="space-between">
24
- <Text bold color="cyan">
25
- ◆ joone
26
- </Text>
27
- <Text dimColor>v0.1.0</Text>
28
- </Box>
29
- <Box marginTop={0} gap={2}>
30
- <Text>
31
- <Text dimColor>provider</Text>{" "}
32
- <Text color="white" bold>
33
- {provider}
34
- </Text>
35
- </Text>
36
- <Text>
37
- <Text dimColor>model</Text>{" "}
38
- <Text color="white" bold>
39
- {model}
40
- </Text>
41
- </Text>
42
- <Text>
43
- <Text dimColor>stream</Text>{" "}
44
- <Text color={streaming ? "green" : "yellow"}>
45
- {streaming ? "on" : "off"}
46
- </Text>
47
- </Text>
48
- </Box>
49
- </Box>
50
- );
51
- };
@@ -1,46 +0,0 @@
1
- import React from "react";
2
- import { Box, Text } from "ink";
3
-
4
- type MessageRole = "user" | "agent" | "system";
5
-
6
- interface MessageBubbleProps {
7
- role: MessageRole;
8
- content: string;
9
- }
10
-
11
- /**
12
- * Renders a single conversation message with role-based styling.
13
- * - User messages: cyan accent, labeled "you"
14
- * - Agent messages: green accent, labeled "joone"
15
- * - System messages: centered, dim yellow
16
- */ export const MessageBubble: React.FC<MessageBubbleProps> = ({
17
- role,
18
- content,
19
- }) => {
20
- if (role === "system") {
21
- return (
22
- <Box paddingX={1} justifyContent="center">
23
- <Text dimColor italic color="yellow">
24
- ⚙ {content}
25
- </Text>
26
- </Box>
27
- );
28
- }
29
-
30
- const isUser = role === "user";
31
- const accentColor = isUser ? "cyan" : "green";
32
- const label = isUser ? "you" : "joone";
33
-
34
- return (
35
- <Box flexDirection="column" paddingX={1}>
36
- <Text bold color={accentColor}>
37
- {label}
38
- </Text>
39
- <Box marginLeft={2}>
40
- <Text color="white" wrap="wrap">
41
- {content}
42
- </Text>
43
- </Box>
44
- </Box>
45
- );
46
- };