@ebowwa/coder 0.7.64 → 0.7.66

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 (101) hide show
  1. package/dist/index.js +36233 -32
  2. package/dist/interfaces/ui/terminal/cli/index.js +34318 -158
  3. package/dist/interfaces/ui/terminal/native/README.md +53 -0
  4. package/dist/interfaces/ui/terminal/native/claude_code_native.darwin-x64.node +0 -0
  5. package/dist/interfaces/ui/terminal/native/claude_code_native.dylib +0 -0
  6. package/dist/interfaces/ui/terminal/native/index.d.ts +0 -0
  7. package/dist/interfaces/ui/terminal/native/index.darwin-arm64.node +0 -0
  8. package/dist/interfaces/ui/terminal/native/index.js +43 -0
  9. package/dist/interfaces/ui/terminal/native/index.node +0 -0
  10. package/dist/interfaces/ui/terminal/native/package.json +34 -0
  11. package/dist/native/README.md +53 -0
  12. package/dist/native/claude_code_native.darwin-x64.node +0 -0
  13. package/dist/native/claude_code_native.dylib +0 -0
  14. package/dist/native/index.d.ts +0 -480
  15. package/dist/native/index.darwin-arm64.node +0 -0
  16. package/dist/native/index.js +43 -1625
  17. package/dist/native/index.node +0 -0
  18. package/dist/native/package.json +34 -0
  19. package/native/index.darwin-arm64.node +0 -0
  20. package/native/index.js +33 -19
  21. package/package.json +3 -2
  22. package/packages/src/core/agent-loop/__tests__/compaction.test.ts +17 -14
  23. package/packages/src/core/agent-loop/compaction.ts +6 -2
  24. package/packages/src/core/agent-loop/index.ts +2 -0
  25. package/packages/src/core/agent-loop/loop-state.ts +1 -1
  26. package/packages/src/core/agent-loop/turn-executor.ts +4 -0
  27. package/packages/src/core/agent-loop/types.ts +4 -0
  28. package/packages/src/core/api-client-impl.ts +377 -176
  29. package/packages/src/core/cognitive-security/hooks.ts +2 -1
  30. package/packages/src/core/config/todo +7 -0
  31. package/packages/src/core/context/__tests__/integration.test.ts +334 -0
  32. package/packages/src/core/context/compaction.ts +170 -0
  33. package/packages/src/core/context/constants.ts +58 -0
  34. package/packages/src/core/context/extraction.ts +85 -0
  35. package/packages/src/core/context/index.ts +66 -0
  36. package/packages/src/core/context/summarization.ts +251 -0
  37. package/packages/src/core/context/token-estimation.ts +98 -0
  38. package/packages/src/core/context/types.ts +59 -0
  39. package/packages/src/core/models.ts +81 -4
  40. package/packages/src/core/normalizers/todo +5 -1
  41. package/packages/src/core/providers/README.md +230 -0
  42. package/packages/src/core/providers/__tests__/providers.test.ts +135 -0
  43. package/packages/src/core/providers/index.ts +419 -0
  44. package/packages/src/core/providers/types.ts +132 -0
  45. package/packages/src/core/retry.ts +10 -0
  46. package/packages/src/ecosystem/tools/index.ts +174 -0
  47. package/packages/src/index.ts +23 -2
  48. package/packages/src/interfaces/ui/index.ts +17 -20
  49. package/packages/src/interfaces/ui/spinner.ts +2 -2
  50. package/packages/src/interfaces/ui/terminal/bridge/index.ts +370 -0
  51. package/packages/src/interfaces/ui/terminal/bridge/ipc.ts +829 -0
  52. package/packages/src/interfaces/ui/terminal/bridge/screen-export.ts +968 -0
  53. package/packages/src/interfaces/ui/terminal/bridge/types.ts +226 -0
  54. package/packages/src/interfaces/ui/terminal/bridge/useBridge.ts +210 -0
  55. package/packages/src/interfaces/ui/terminal/cli/bootstrap.ts +132 -0
  56. package/packages/src/interfaces/ui/terminal/cli/index.ts +200 -13
  57. package/packages/src/interfaces/ui/terminal/cli/interactive/index.ts +110 -0
  58. package/packages/src/interfaces/ui/terminal/cli/interactive/input-handler.ts +402 -0
  59. package/packages/src/interfaces/ui/terminal/cli/interactive/interactive-runner.ts +820 -0
  60. package/packages/src/interfaces/ui/terminal/cli/interactive/message-store.ts +299 -0
  61. package/packages/src/interfaces/ui/terminal/cli/interactive/types.ts +274 -0
  62. package/packages/src/interfaces/ui/terminal/shared/index.ts +13 -0
  63. package/packages/src/interfaces/ui/terminal/shared/query.ts +9 -3
  64. package/packages/src/interfaces/ui/terminal/shared/setup.ts +5 -1
  65. package/packages/src/interfaces/ui/terminal/shared/spinner-frames.ts +73 -0
  66. package/packages/src/interfaces/ui/terminal/shared/status-line.ts +10 -2
  67. package/packages/src/native/index.ts +404 -27
  68. package/packages/src/native/tui_v2_types.ts +39 -0
  69. package/packages/src/teammates/coordination.test.ts +279 -0
  70. package/packages/src/teammates/coordination.ts +646 -0
  71. package/packages/src/teammates/index.ts +95 -25
  72. package/packages/src/teammates/integration.test.ts +272 -0
  73. package/packages/src/teammates/runner.test.ts +235 -0
  74. package/packages/src/teammates/runner.ts +750 -0
  75. package/packages/src/teammates/schemas.ts +673 -0
  76. package/packages/src/types/index.ts +1 -0
  77. package/packages/src/core/context-compaction.ts +0 -578
  78. package/packages/src/interfaces/ui/Screenshot 2026-03-02 at 9.23.10/342/200/257PM.png +0 -0
  79. package/packages/src/interfaces/ui/Screenshot 2026-03-03 at 10.55.11/342/200/257AM.png +0 -0
  80. package/packages/src/interfaces/ui/terminal/tui/HelpPanel.tsx +0 -262
  81. package/packages/src/interfaces/ui/terminal/tui/InputContext.tsx +0 -232
  82. package/packages/src/interfaces/ui/terminal/tui/InputField.tsx +0 -62
  83. package/packages/src/interfaces/ui/terminal/tui/InteractiveTUI.tsx +0 -537
  84. package/packages/src/interfaces/ui/terminal/tui/MessageArea.tsx +0 -107
  85. package/packages/src/interfaces/ui/terminal/tui/MessageStore.tsx +0 -240
  86. package/packages/src/interfaces/ui/terminal/tui/StatusBar.tsx +0 -54
  87. package/packages/src/interfaces/ui/terminal/tui/commands.ts +0 -438
  88. package/packages/src/interfaces/ui/terminal/tui/components/InteractiveElements.tsx +0 -584
  89. package/packages/src/interfaces/ui/terminal/tui/components/MultilineInput.tsx +0 -614
  90. package/packages/src/interfaces/ui/terminal/tui/components/PaneManager.tsx +0 -333
  91. package/packages/src/interfaces/ui/terminal/tui/components/Sidebar.tsx +0 -604
  92. package/packages/src/interfaces/ui/terminal/tui/components/index.ts +0 -118
  93. package/packages/src/interfaces/ui/terminal/tui/console.ts +0 -49
  94. package/packages/src/interfaces/ui/terminal/tui/index.ts +0 -90
  95. package/packages/src/interfaces/ui/terminal/tui/run.tsx +0 -42
  96. package/packages/src/interfaces/ui/terminal/tui/spinner.ts +0 -69
  97. package/packages/src/interfaces/ui/terminal/tui/tui-app.tsx +0 -390
  98. package/packages/src/interfaces/ui/terminal/tui/tui-footer.ts +0 -422
  99. package/packages/src/interfaces/ui/terminal/tui/types.ts +0 -186
  100. package/packages/src/interfaces/ui/terminal/tui/useInputHandler.ts +0 -104
  101. 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: args.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: args.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: args.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: true, // Always log for audit trail
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"],