@ebowwa/coder 0.7.64 → 0.7.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +36168 -32
- package/dist/interfaces/ui/terminal/cli/index.js +34253 -158
- package/dist/interfaces/ui/terminal/native/README.md +53 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
- package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
- package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
- package/dist/interfaces/ui/terminal/native/index.js +43 -0
- package/dist/interfaces/ui/terminal/native/index.node +0 -0
- package/dist/interfaces/ui/terminal/native/package.json +34 -0
- package/dist/native/README.md +53 -0
- package/dist/native/claude_code_native.darwin-x64.node +0 -0
- package/dist/native/claude_code_native.dylib +0 -0
- package/dist/native/index.d.ts +0 -480
- package/dist/native/index.darwin-arm64.node +0 -0
- package/dist/native/index.js +43 -1625
- package/dist/native/index.node +0 -0
- package/dist/native/package.json +34 -0
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.js +33 -19
- package/package.json +3 -2
- package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
- package/packages/src/core/agent-loop/compaction.ts +6 -2
- package/packages/src/core/agent-loop/index.ts +2 -0
- package/packages/src/core/agent-loop/loop-state.ts +1 -1
- package/packages/src/core/agent-loop/turn-executor.ts +4 -0
- package/packages/src/core/agent-loop/types.ts +4 -0
- package/packages/src/core/api-client-impl.ts +283 -173
- package/packages/src/core/cognitive-security/hooks.ts +2 -1
- package/packages/src/core/config/todo +7 -0
- package/packages/src/core/context/__tests__/integration.test.ts +334 -0
- package/packages/src/core/context/compaction.ts +170 -0
- package/packages/src/core/context/constants.ts +58 -0
- package/packages/src/core/context/extraction.ts +85 -0
- package/packages/src/core/context/index.ts +66 -0
- package/packages/src/core/context/summarization.ts +251 -0
- package/packages/src/core/context/token-estimation.ts +98 -0
- package/packages/src/core/context/types.ts +59 -0
- package/packages/src/core/models.ts +81 -4
- package/packages/src/core/normalizers/todo +5 -1
- package/packages/src/core/providers/README.md +230 -0
- package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
- package/packages/src/core/providers/index.ts +419 -0
- package/packages/src/core/providers/types.ts +132 -0
- package/packages/src/core/retry.ts +10 -0
- package/packages/src/ecosystem/tools/index.ts +174 -0
- package/packages/src/index.ts +23 -2
- package/packages/src/interfaces/ui/index.ts +17 -20
- package/packages/src/interfaces/ui/spinner.ts +2 -2
- package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
- package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
- package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
- package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
- package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
- package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
- package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
- package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +393 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
- package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
- package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
- package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
- package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
- package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
- package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
- package/packages/src/native/index.ts +404 -27
- package/packages/src/native/tui_v2_types.ts +39 -0
- package/packages/src/teammates/coordination.test.ts +279 -0
- package/packages/src/teammates/coordination.ts +646 -0
- package/packages/src/teammates/index.ts +95 -25
- package/packages/src/teammates/integration.test.ts +272 -0
- package/packages/src/teammates/runner.test.ts +235 -0
- package/packages/src/teammates/runner.ts +750 -0
- package/packages/src/teammates/schemas.ts +673 -0
- package/packages/src/types/index.ts +1 -0
- package/packages/src/core/context-compaction.ts +0 -578
- package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
- package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
- package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
- package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
- package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
- package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
- package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
- package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
- package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
- package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
- package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
- package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
- package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
- package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
- package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
- package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
- package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
- package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
- package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
- package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
- package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
- package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
- package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
- package/packages/src/interfaces/ui/terminal/tui/useNativeInput.ts +0 -239
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Store - Non-React Implementation
|
|
3
|
+
*
|
|
4
|
+
* Centralized message state management extracted from v1 TUI patterns.
|
|
5
|
+
* This is a pure TypeScript class without React dependency.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Single source of truth for messages
|
|
9
|
+
* - Separate UI and API message tracking
|
|
10
|
+
* - Token count management
|
|
11
|
+
* - Change notification system
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Message as ApiMessage } from "../../../../../types/index.js";
|
|
15
|
+
import type { MessageStore, UIMessage, MessageSubType } from "./types.js";
|
|
16
|
+
|
|
17
|
+
// ============================================
|
|
18
|
+
// UTILITIES
|
|
19
|
+
// ============================================
|
|
20
|
+
|
|
21
|
+
let globalMessageId = 0;
|
|
22
|
+
|
|
23
|
+
function generateId(): string {
|
|
24
|
+
globalMessageId += 1;
|
|
25
|
+
return `msg-${globalMessageId}-${Date.now()}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Extract text content from API message content blocks
|
|
30
|
+
*/
|
|
31
|
+
function extractTextContent(content: ApiMessage["content"]): string {
|
|
32
|
+
if (typeof content === "string") {
|
|
33
|
+
return content;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return content.map((block) => {
|
|
37
|
+
switch (block.type) {
|
|
38
|
+
case "text":
|
|
39
|
+
return block.text;
|
|
40
|
+
case "tool_use":
|
|
41
|
+
return `[Tool: ${block.name}]\n${JSON.stringify(block.input, null, 2)}`;
|
|
42
|
+
case "tool_result": {
|
|
43
|
+
const resultContent = typeof block.content === "string"
|
|
44
|
+
? block.content
|
|
45
|
+
: extractTextContent(block.content);
|
|
46
|
+
const prefix = block.is_error ? "[Tool Error]" : "[Tool Result]";
|
|
47
|
+
const truncated = resultContent.length > 500
|
|
48
|
+
? resultContent.slice(0, 500) + "..."
|
|
49
|
+
: resultContent;
|
|
50
|
+
return `${prefix}\n${truncated}`;
|
|
51
|
+
}
|
|
52
|
+
case "thinking":
|
|
53
|
+
return `[Thinking]\n${block.thinking}`;
|
|
54
|
+
case "redacted_thinking":
|
|
55
|
+
return "[Redacted Thinking]";
|
|
56
|
+
case "image":
|
|
57
|
+
return "[Image]";
|
|
58
|
+
default:
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
}).join("\n\n");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Convert API message to UI message
|
|
66
|
+
*/
|
|
67
|
+
function apiToUi(msg: ApiMessage): UIMessage | null {
|
|
68
|
+
const content = extractTextContent(msg.content);
|
|
69
|
+
if (!content) return null;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
id: generateId(),
|
|
73
|
+
role: msg.role,
|
|
74
|
+
content,
|
|
75
|
+
timestamp: Date.now(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Estimate token count from text (rough: ~4 chars per token)
|
|
81
|
+
*/
|
|
82
|
+
function estimateTokens(text: string): number {
|
|
83
|
+
if (!text) return 0;
|
|
84
|
+
return Math.ceil(text.length / 4);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Estimate total tokens from messages
|
|
89
|
+
*/
|
|
90
|
+
function estimateMessagesTokens(messages: ApiMessage[]): number {
|
|
91
|
+
let total = 0;
|
|
92
|
+
for (const msg of messages) {
|
|
93
|
+
if (typeof msg.content === "string") {
|
|
94
|
+
total += estimateTokens(msg.content);
|
|
95
|
+
} else if (Array.isArray(msg.content)) {
|
|
96
|
+
for (const block of msg.content) {
|
|
97
|
+
if (block.type === "text") {
|
|
98
|
+
total += estimateTokens(block.text);
|
|
99
|
+
} else if (block.type === "tool_use") {
|
|
100
|
+
total += estimateTokens(JSON.stringify(block.input));
|
|
101
|
+
} else if (block.type === "tool_result") {
|
|
102
|
+
if (typeof block.content === "string") {
|
|
103
|
+
total += estimateTokens(block.content);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return total;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ============================================
|
|
113
|
+
// MESSAGE STORE CLASS
|
|
114
|
+
// ============================================
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Non-React message store implementation
|
|
118
|
+
*
|
|
119
|
+
* Usage:
|
|
120
|
+
* ```ts
|
|
121
|
+
* const store = new MessageStoreImpl();
|
|
122
|
+
* store.addMessage({ role: "user", content: "Hello" });
|
|
123
|
+
* console.log(store.messages);
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export class MessageStoreImpl implements MessageStore {
|
|
127
|
+
private _messages: UIMessage[] = [];
|
|
128
|
+
private _apiMessages: ApiMessage[] = [];
|
|
129
|
+
private _tokenCount = 0;
|
|
130
|
+
private _listeners: Set<() => void> = new Set();
|
|
131
|
+
private _pendingUserMessages: Set<string> = new Set();
|
|
132
|
+
|
|
133
|
+
/** UI messages for display */
|
|
134
|
+
get messages(): UIMessage[] {
|
|
135
|
+
return [...this._messages];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** API messages for context */
|
|
139
|
+
get apiMessages(): ApiMessage[] {
|
|
140
|
+
return [...this._apiMessages];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Current token count */
|
|
144
|
+
get tokenCount(): number {
|
|
145
|
+
return this._tokenCount;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Update token count */
|
|
149
|
+
setTokenCount(count: number): void {
|
|
150
|
+
this._tokenCount = count;
|
|
151
|
+
this._notify();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Add a UI message
|
|
156
|
+
* Returns the message ID
|
|
157
|
+
*/
|
|
158
|
+
addMessage(msg: Omit<UIMessage, "id" | "timestamp">): string {
|
|
159
|
+
const id = generateId();
|
|
160
|
+
const fullMsg: UIMessage = {
|
|
161
|
+
...msg,
|
|
162
|
+
id,
|
|
163
|
+
timestamp: Date.now(),
|
|
164
|
+
};
|
|
165
|
+
this._messages = [...this._messages, fullMsg];
|
|
166
|
+
|
|
167
|
+
// Track user messages added manually (to skip in addApiMessages)
|
|
168
|
+
if (msg.role === "user") {
|
|
169
|
+
this._pendingUserMessages.add(msg.content);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this._notify();
|
|
173
|
+
return id;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Add multiple messages from API response
|
|
178
|
+
*/
|
|
179
|
+
addApiMessages(msgs: ApiMessage[]): void {
|
|
180
|
+
if (msgs.length === 0) return;
|
|
181
|
+
|
|
182
|
+
// Update API messages
|
|
183
|
+
this._apiMessages = [...this._apiMessages, ...msgs];
|
|
184
|
+
|
|
185
|
+
// Convert to UI messages, skipping user messages already added manually
|
|
186
|
+
const newUiMsgs = msgs
|
|
187
|
+
.map(apiToUi)
|
|
188
|
+
.filter((m): m is UIMessage => m !== null)
|
|
189
|
+
.filter((m) => {
|
|
190
|
+
if (m.role === "user" && this._pendingUserMessages.has(m.content)) {
|
|
191
|
+
this._pendingUserMessages.delete(m.content);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
return true;
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
if (newUiMsgs.length > 0) {
|
|
198
|
+
this._messages = [...this._messages, ...newUiMsgs];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Update token estimate
|
|
202
|
+
this._tokenCount = estimateMessagesTokens(this._apiMessages);
|
|
203
|
+
|
|
204
|
+
this._notify();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Add system message (convenience method)
|
|
209
|
+
*/
|
|
210
|
+
addSystem(
|
|
211
|
+
content: string,
|
|
212
|
+
subType?: MessageSubType,
|
|
213
|
+
toolName?: string,
|
|
214
|
+
isError?: boolean
|
|
215
|
+
): string {
|
|
216
|
+
return this.addMessage({
|
|
217
|
+
role: "system",
|
|
218
|
+
content,
|
|
219
|
+
subType,
|
|
220
|
+
toolName,
|
|
221
|
+
isError,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Clear all messages
|
|
227
|
+
*/
|
|
228
|
+
clear(): void {
|
|
229
|
+
this._messages = [];
|
|
230
|
+
this._apiMessages = [];
|
|
231
|
+
this._pendingUserMessages.clear();
|
|
232
|
+
this._tokenCount = 0;
|
|
233
|
+
this._notify();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Replace all messages (for undo/redo)
|
|
238
|
+
*/
|
|
239
|
+
replace(ui: UIMessage[], api: ApiMessage[]): void {
|
|
240
|
+
this._messages = [...ui];
|
|
241
|
+
this._apiMessages = [...api];
|
|
242
|
+
this._pendingUserMessages.clear();
|
|
243
|
+
this._tokenCount = estimateMessagesTokens(this._apiMessages);
|
|
244
|
+
this._notify();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Subscribe to store changes
|
|
249
|
+
* Returns unsubscribe function
|
|
250
|
+
*/
|
|
251
|
+
subscribe(listener: () => void): () => void {
|
|
252
|
+
this._listeners.add(listener);
|
|
253
|
+
return () => {
|
|
254
|
+
this._listeners.delete(listener);
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Notify all listeners of a change
|
|
260
|
+
*/
|
|
261
|
+
private _notify(): void {
|
|
262
|
+
for (const listener of this._listeners) {
|
|
263
|
+
try {
|
|
264
|
+
listener();
|
|
265
|
+
} catch (err) {
|
|
266
|
+
console.error("MessageStore listener error:", err);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ============================================
|
|
273
|
+
// SINGLETON (optional convenience)
|
|
274
|
+
// ============================================
|
|
275
|
+
|
|
276
|
+
let globalStore: MessageStoreImpl | null = null;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Get or create the global message store
|
|
280
|
+
*/
|
|
281
|
+
export function getMessageStore(): MessageStoreImpl {
|
|
282
|
+
if (!globalStore) {
|
|
283
|
+
globalStore = new MessageStoreImpl();
|
|
284
|
+
}
|
|
285
|
+
return globalStore;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Reset the global message store (for testing)
|
|
290
|
+
*/
|
|
291
|
+
export function resetMessageStore(): void {
|
|
292
|
+
globalStore = null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ============================================
|
|
296
|
+
// EXPORTS
|
|
297
|
+
// ============================================
|
|
298
|
+
|
|
299
|
+
export type { MessageStore, UIMessage, MessageSubType } from "./types.js";
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Interactive Types
|
|
3
|
+
* Type definitions for the non-React interactive mode
|
|
4
|
+
*
|
|
5
|
+
* Extracted from v1 TUI patterns without Ink dependency
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { PermissionMode, ClaudeModel, Message as ApiMessage, ToolDefinition } from "../../../../../types/index.js";
|
|
9
|
+
import type { HookManager } from "../../../../../ecosystem/hooks/index.js";
|
|
10
|
+
import type { LoadedSession, SessionSummary } from "../../../../../core/sessions/types.js";
|
|
11
|
+
import type { TeammateModeRunner } from "../../../../../teammates/runner.js";
|
|
12
|
+
|
|
13
|
+
// ============================================
|
|
14
|
+
// MESSAGE TYPES
|
|
15
|
+
// ============================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Message sub-type for granular categorization
|
|
19
|
+
*/
|
|
20
|
+
export type MessageSubType =
|
|
21
|
+
| "tool_call"
|
|
22
|
+
| "tool_result"
|
|
23
|
+
| "hook"
|
|
24
|
+
| "info"
|
|
25
|
+
| "error"
|
|
26
|
+
| "thinking";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* UI-specific message representation
|
|
30
|
+
*/
|
|
31
|
+
export interface UIMessage {
|
|
32
|
+
id: string;
|
|
33
|
+
role: "user" | "assistant" | "system";
|
|
34
|
+
content: string;
|
|
35
|
+
timestamp: number;
|
|
36
|
+
subType?: MessageSubType;
|
|
37
|
+
toolName?: string;
|
|
38
|
+
isError?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ============================================
|
|
42
|
+
// MESSAGE STORE TYPES
|
|
43
|
+
// ============================================
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Message store interface for non-React usage
|
|
47
|
+
*/
|
|
48
|
+
export interface MessageStore {
|
|
49
|
+
/** UI messages for display */
|
|
50
|
+
messages: UIMessage[];
|
|
51
|
+
/** API messages for context */
|
|
52
|
+
apiMessages: ApiMessage[];
|
|
53
|
+
/** Add a UI message */
|
|
54
|
+
addMessage(msg: Omit<UIMessage, "id" | "timestamp">): string;
|
|
55
|
+
/** Add multiple messages from API response */
|
|
56
|
+
addApiMessages(msgs: ApiMessage[]): void;
|
|
57
|
+
/** Add system message (convenience) */
|
|
58
|
+
addSystem(content: string, subType?: MessageSubType, toolName?: string, isError?: boolean): string;
|
|
59
|
+
/** Clear all messages */
|
|
60
|
+
clear(): void;
|
|
61
|
+
/** Replace messages (for undo/redo) */
|
|
62
|
+
replace(ui: UIMessage[], api: ApiMessage[]): void;
|
|
63
|
+
/** Total token count */
|
|
64
|
+
tokenCount: number;
|
|
65
|
+
/** Update token count */
|
|
66
|
+
setTokenCount(count: number): void;
|
|
67
|
+
/** Subscribe to store changes */
|
|
68
|
+
subscribe(listener: () => void): () => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================
|
|
72
|
+
// INPUT HANDLER TYPES
|
|
73
|
+
// ============================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Native key event representation for input handling
|
|
77
|
+
*
|
|
78
|
+
* This is the TypeScript interface used by input handlers.
|
|
79
|
+
* It's converted from the native module's InputEvent format.
|
|
80
|
+
*/
|
|
81
|
+
export interface NativeKeyEvent {
|
|
82
|
+
/** Key code or character */
|
|
83
|
+
code: string;
|
|
84
|
+
/** Whether this is a special/control key */
|
|
85
|
+
is_special: boolean;
|
|
86
|
+
/** Ctrl modifier state */
|
|
87
|
+
ctrl: boolean;
|
|
88
|
+
/** Alt modifier state */
|
|
89
|
+
alt: boolean;
|
|
90
|
+
/** Shift modifier state */
|
|
91
|
+
shift: boolean;
|
|
92
|
+
/** Key event kind */
|
|
93
|
+
kind: "press" | "release" | "repeat";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Input handler function type
|
|
98
|
+
*/
|
|
99
|
+
export type InputHandler = (event: NativeKeyEvent) => boolean;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Input handler registration options
|
|
103
|
+
*/
|
|
104
|
+
export interface InputHandlerOptions {
|
|
105
|
+
/** Unique ID for this handler */
|
|
106
|
+
id: string;
|
|
107
|
+
/** Priority (higher = receives input first) */
|
|
108
|
+
priority?: number;
|
|
109
|
+
/** Handler function - return true to consume, false to pass through */
|
|
110
|
+
handler: InputHandler;
|
|
111
|
+
/** Whether this handler is currently active */
|
|
112
|
+
isActive?: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Input manager interface for non-React usage
|
|
117
|
+
*/
|
|
118
|
+
export interface InputManager {
|
|
119
|
+
/** Register an input handler */
|
|
120
|
+
register(options: InputHandlerOptions): () => void;
|
|
121
|
+
/** Set focus to a specific handler */
|
|
122
|
+
focus(handlerId: string): void;
|
|
123
|
+
/** Get currently focused handler ID */
|
|
124
|
+
focusedId: string | null;
|
|
125
|
+
/** Dispatch a key event to handlers */
|
|
126
|
+
dispatch(event: NativeKeyEvent): boolean;
|
|
127
|
+
/** Whether input is currently blocked */
|
|
128
|
+
isBlocked: boolean;
|
|
129
|
+
/** Block/unblock input (for loading states) */
|
|
130
|
+
setBlocked(blocked: boolean): void;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ============================================
|
|
134
|
+
// INTERACTIVE RUNNER TYPES
|
|
135
|
+
// ============================================
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Props for the interactive runner
|
|
139
|
+
*/
|
|
140
|
+
export interface InteractiveRunnerProps {
|
|
141
|
+
apiKey: string;
|
|
142
|
+
model: ClaudeModel;
|
|
143
|
+
permissionMode: PermissionMode;
|
|
144
|
+
maxTokens: number;
|
|
145
|
+
systemPrompt: string;
|
|
146
|
+
tools: ToolDefinition[];
|
|
147
|
+
hookManager: HookManager;
|
|
148
|
+
sessionStore: SessionStore;
|
|
149
|
+
sessionId: string;
|
|
150
|
+
setSessionId: (id: string) => void;
|
|
151
|
+
initialMessages: ApiMessage[];
|
|
152
|
+
workingDirectory: string;
|
|
153
|
+
teammateRunner?: TeammateModeRunner | null;
|
|
154
|
+
onExit?: () => Promise<void> | void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Session store interface (subset used by interactive mode)
|
|
159
|
+
*/
|
|
160
|
+
export interface SessionStore {
|
|
161
|
+
saveMessage(message: ApiMessage): Promise<void>;
|
|
162
|
+
saveMetrics(metrics: unknown): Promise<void>;
|
|
163
|
+
exportSession(sessionId: string, format: "jsonl" | "json" | "markdown"): Promise<string>;
|
|
164
|
+
listSessions(limit?: number): Promise<SessionSummary[]>;
|
|
165
|
+
resumeSession(sessionId: string): Promise<LoadedSession | null>;
|
|
166
|
+
createSession(options: {
|
|
167
|
+
model: string;
|
|
168
|
+
workingDirectory: string;
|
|
169
|
+
agentName?: string;
|
|
170
|
+
agentColor?: string;
|
|
171
|
+
teamName?: string;
|
|
172
|
+
}): Promise<string>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Session info for listing
|
|
177
|
+
*/
|
|
178
|
+
export interface SessionInfo {
|
|
179
|
+
id: string;
|
|
180
|
+
messageCount: number;
|
|
181
|
+
lastActivity?: number;
|
|
182
|
+
metadata?: Record<string, unknown>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Interactive runner state
|
|
187
|
+
*/
|
|
188
|
+
export interface InteractiveState {
|
|
189
|
+
isLoading: boolean;
|
|
190
|
+
inputValue: string;
|
|
191
|
+
cursorPos: number;
|
|
192
|
+
scrollOffset: number;
|
|
193
|
+
totalCost: number;
|
|
194
|
+
spinnerFrame: string;
|
|
195
|
+
streamingText: string;
|
|
196
|
+
inputHistory: string[];
|
|
197
|
+
historyIndex: number;
|
|
198
|
+
sessionSelectMode: boolean;
|
|
199
|
+
selectableSessions: SessionInfo[];
|
|
200
|
+
helpMode: boolean;
|
|
201
|
+
helpSection: number;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================
|
|
205
|
+
// COMMAND TYPES
|
|
206
|
+
// ============================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Command handler context
|
|
210
|
+
*/
|
|
211
|
+
export interface CommandContext {
|
|
212
|
+
sessionId: string;
|
|
213
|
+
setSessionId: (id: string) => void;
|
|
214
|
+
model: ClaudeModel;
|
|
215
|
+
setModel: (model: ClaudeModel) => void;
|
|
216
|
+
messageStore: MessageStore;
|
|
217
|
+
totalCost: number;
|
|
218
|
+
setTotalCost: (cost: number) => void;
|
|
219
|
+
permissionMode: PermissionMode;
|
|
220
|
+
tools: ToolDefinition[];
|
|
221
|
+
workingDirectory: string;
|
|
222
|
+
sessionStore: SessionStore;
|
|
223
|
+
messagesLength: number;
|
|
224
|
+
onExit: () => void;
|
|
225
|
+
exit: () => void;
|
|
226
|
+
state: InteractiveState;
|
|
227
|
+
setState: (state: Partial<InteractiveState>) => void;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ============================================
|
|
231
|
+
// CONTEXT INFO TYPES
|
|
232
|
+
// ============================================
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Context info from status-line calculations
|
|
236
|
+
*/
|
|
237
|
+
export interface ContextInfo {
|
|
238
|
+
percentRemaining: number;
|
|
239
|
+
isLow: boolean;
|
|
240
|
+
isCritical: boolean;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ============================================
|
|
244
|
+
// PRIORITY CONSTANTS
|
|
245
|
+
// ============================================
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Input priority levels
|
|
249
|
+
*/
|
|
250
|
+
export const InputPriority = {
|
|
251
|
+
/** Normal components */
|
|
252
|
+
DEFAULT: 0,
|
|
253
|
+
/** Focused input fields */
|
|
254
|
+
INPUT: 10,
|
|
255
|
+
/** Selectable lists */
|
|
256
|
+
LIST: 20,
|
|
257
|
+
/** Modal dialogs */
|
|
258
|
+
MODAL: 100,
|
|
259
|
+
/** System-level (Ctrl+C, etc.) */
|
|
260
|
+
SYSTEM: 1000,
|
|
261
|
+
} as const;
|
|
262
|
+
|
|
263
|
+
// ============================================
|
|
264
|
+
// RE-EXPORTS
|
|
265
|
+
// ============================================
|
|
266
|
+
|
|
267
|
+
export type {
|
|
268
|
+
PermissionMode,
|
|
269
|
+
ClaudeModel,
|
|
270
|
+
Message as ApiMessage,
|
|
271
|
+
ToolDefinition,
|
|
272
|
+
} from "../../../../../types/index.js";
|
|
273
|
+
export type { HookManager } from "../../../../../ecosystem/hooks/index.js";
|
|
274
|
+
export type { SkillManager } from "../../../../../ecosystem/skills/index.js";
|
|
@@ -69,3 +69,16 @@ export {
|
|
|
69
69
|
type StatusLineOptions,
|
|
70
70
|
type ContextInfo,
|
|
71
71
|
} from "./status-line.js";
|
|
72
|
+
|
|
73
|
+
// Spinner Frames
|
|
74
|
+
export {
|
|
75
|
+
spinnerFrames,
|
|
76
|
+
dotSpinnerFrames,
|
|
77
|
+
asciiSpinnerFrames,
|
|
78
|
+
arrowSpinnerFrames,
|
|
79
|
+
simpleDotFrames,
|
|
80
|
+
toolSpinnerFrames,
|
|
81
|
+
nextFrame,
|
|
82
|
+
createSpinnerIterator,
|
|
83
|
+
getFrame,
|
|
84
|
+
} from "./spinner-frames.js";
|
|
@@ -30,6 +30,8 @@ export interface QueryOptions {
|
|
|
30
30
|
hookManager: HookManager;
|
|
31
31
|
workingDirectory: string;
|
|
32
32
|
gitStatus?: GitStatus | null;
|
|
33
|
+
/** Resolved permission mode (from config, may differ from args.permissionMode) */
|
|
34
|
+
permissionMode?: import("../../../../types/index.js").PermissionMode;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
// ============================================
|
|
@@ -48,11 +50,15 @@ export async function runSingleQuery(options: QueryOptions): Promise<void> {
|
|
|
48
50
|
hookManager,
|
|
49
51
|
workingDirectory,
|
|
50
52
|
gitStatus,
|
|
53
|
+
permissionMode: resolvedPermissionMode,
|
|
51
54
|
} = options;
|
|
52
55
|
|
|
56
|
+
// Use resolved permission mode from config if provided, otherwise fall back to args
|
|
57
|
+
const permissionMode = resolvedPermissionMode ?? args.permissionMode;
|
|
58
|
+
|
|
53
59
|
// Show initial status line
|
|
54
60
|
const initialStatusOptions: StatusLineOptions = {
|
|
55
|
-
permissionMode
|
|
61
|
+
permissionMode,
|
|
56
62
|
tokensUsed: 0,
|
|
57
63
|
maxTokens: getContextWindow(args.model),
|
|
58
64
|
model: args.model,
|
|
@@ -92,7 +98,7 @@ export async function runSingleQuery(options: QueryOptions): Promise<void> {
|
|
|
92
98
|
maxTokens: args.maxTokens,
|
|
93
99
|
systemPrompt,
|
|
94
100
|
tools,
|
|
95
|
-
permissionMode
|
|
101
|
+
permissionMode, // Use resolved permission mode
|
|
96
102
|
workingDirectory,
|
|
97
103
|
gitStatus: gitStatus ?? undefined,
|
|
98
104
|
extendedThinking: extendedThinkingConfig,
|
|
@@ -129,7 +135,7 @@ export async function runSingleQuery(options: QueryOptions): Promise<void> {
|
|
|
129
135
|
|
|
130
136
|
// Show final status line
|
|
131
137
|
const finalStatusOptions: StatusLineOptions = {
|
|
132
|
-
permissionMode
|
|
138
|
+
permissionMode,
|
|
133
139
|
tokensUsed: totalTokens,
|
|
134
140
|
maxTokens: getContextWindow(args.model),
|
|
135
141
|
model: args.model,
|
|
@@ -45,6 +45,8 @@ export interface SetupOptions {
|
|
|
45
45
|
apiKey: string;
|
|
46
46
|
workingDirectory: string;
|
|
47
47
|
onProgress?: (message: string) => void;
|
|
48
|
+
/** When true, disable console logging for security hooks (prevents TUI corruption) */
|
|
49
|
+
isTuiMode?: boolean;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
// ============================================
|
|
@@ -196,14 +198,16 @@ export async function setupSession(options: SetupOptions): Promise<SessionSetup>
|
|
|
196
198
|
|
|
197
199
|
// Register cognitive security hooks (in-process handlers)
|
|
198
200
|
// When bypassPermissions is set, disable all security checks
|
|
201
|
+
// In TUI mode, disable console logging to prevent corrupting the display
|
|
199
202
|
const isBypassMode = permissionMode === "bypassPermissions";
|
|
203
|
+
const isTuiMode = options.isTuiMode ?? false;
|
|
200
204
|
const securityHandlers = createSecurityHookHandlers({
|
|
201
205
|
enabled: !isBypassMode, // Disable entirely in bypass mode
|
|
202
206
|
checkIntentAlignment: !isBypassMode,
|
|
203
207
|
enforceFlowPolicies: !isBypassMode,
|
|
204
208
|
preventLeaks: !isBypassMode,
|
|
205
209
|
trackTaints: !isBypassMode,
|
|
206
|
-
logEvents:
|
|
210
|
+
logEvents: !isTuiMode, // Disable console logging in TUI mode to prevent display corruption
|
|
207
211
|
blockOnViolation: false, // Never block - log only
|
|
208
212
|
minAlignmentScore: 0.3,
|
|
209
213
|
approvalRequiredSensitivities: ["secret", "top_secret"],
|