@geoffai/coder 1.0.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.
@@ -0,0 +1,1019 @@
1
+ import { ChildProcess } from 'child_process';
2
+ import OpenAI from 'openai';
3
+
4
+ interface Message {
5
+ role: "system" | "user" | "assistant" | "tool";
6
+ content: string | ContentPart[];
7
+ tool_calls?: ToolCall[];
8
+ tool_call_id?: string;
9
+ name?: string;
10
+ }
11
+ interface ContentPart {
12
+ type: "text" | "image_url";
13
+ text?: string;
14
+ image_url?: {
15
+ url: string;
16
+ };
17
+ }
18
+ interface ToolCall {
19
+ id: string;
20
+ type: "function";
21
+ function: {
22
+ name: string;
23
+ arguments: string;
24
+ };
25
+ }
26
+ interface ToolDefinition {
27
+ type: "function";
28
+ function: {
29
+ name: string;
30
+ description: string;
31
+ parameters: {
32
+ type: "object";
33
+ properties: Record<string, PropertyDefinition>;
34
+ required: string[];
35
+ };
36
+ };
37
+ }
38
+ interface PropertyDefinition {
39
+ type: string;
40
+ description: string;
41
+ enum?: string[];
42
+ }
43
+ interface CompletionRequest {
44
+ model: string;
45
+ messages: Message[];
46
+ tools?: ToolDefinition[];
47
+ tool_choice?: "auto" | "none" | {
48
+ type: "function";
49
+ function: {
50
+ name: string;
51
+ };
52
+ };
53
+ stream?: boolean;
54
+ max_tokens?: number;
55
+ temperature?: number;
56
+ }
57
+ interface CompletionResponse {
58
+ id: string;
59
+ object: string;
60
+ created: number;
61
+ model: string;
62
+ choices: Choice[];
63
+ usage?: {
64
+ prompt_tokens: number;
65
+ completion_tokens: number;
66
+ total_tokens: number;
67
+ };
68
+ }
69
+ interface Choice {
70
+ index: number;
71
+ message: Message;
72
+ finish_reason: "stop" | "tool_calls" | "length" | "content_filter" | null;
73
+ }
74
+ interface StreamChunk {
75
+ id: string;
76
+ object: string;
77
+ created: number;
78
+ model: string;
79
+ choices: StreamChoice[];
80
+ }
81
+ interface StreamChoice {
82
+ index: number;
83
+ delta: Partial<Message> & {
84
+ tool_calls?: Partial<ToolCall>[];
85
+ };
86
+ finish_reason: string | null;
87
+ }
88
+ interface ToolResult {
89
+ success: boolean;
90
+ output: string;
91
+ error?: string;
92
+ }
93
+ type AgentEventType = 'iteration_start' | 'llm_call' | 'llm_response' | 'tool_call' | 'tool_result' | 'file_written' | 'file_modified' | 'command_executed' | 'task_complete' | 'error' | 'thinking';
94
+ interface AgentEvent {
95
+ type: AgentEventType;
96
+ timestamp: number;
97
+ data: Record<string, unknown>;
98
+ }
99
+ type AgentEventCallback = (event: AgentEvent) => void;
100
+ interface AgentConfig {
101
+ apiKey: string;
102
+ baseUrl: string;
103
+ model: string;
104
+ maxIterations: number;
105
+ maxTokens: number;
106
+ temperature: number;
107
+ workingDirectory: string;
108
+ autoApprove: boolean;
109
+ verbose: boolean;
110
+ onEvent?: AgentEventCallback;
111
+ sandboxId?: string;
112
+ }
113
+ interface AgentState {
114
+ messages: Message[];
115
+ iteration: number;
116
+ isRunning: boolean;
117
+ lastToolCalls: ToolCall[];
118
+ tokensUsed: number;
119
+ metrics: AgentMetrics;
120
+ }
121
+ interface AgentMetrics {
122
+ tokensUsed: number;
123
+ promptTokens: number;
124
+ completionTokens: number;
125
+ contextSize: number;
126
+ filesCreated: number;
127
+ filesModified: number;
128
+ linesAdded: number;
129
+ linesRemoved: number;
130
+ commandsExecuted: number;
131
+ toolCallsTotal: number;
132
+ apiCalls: number;
133
+ startTime: number;
134
+ modifiedFiles: Set<string>;
135
+ createdFiles: Set<string>;
136
+ }
137
+ type ToolHandler = (args: Record<string, unknown>) => Promise<ToolResult>;
138
+ interface ToolRegistry {
139
+ definitions: ToolDefinition[];
140
+ handlers: Map<string, ToolHandler>;
141
+ }
142
+
143
+ declare class AgentLoop {
144
+ private client;
145
+ private config;
146
+ private state;
147
+ private tools;
148
+ private logger;
149
+ private aborted;
150
+ constructor(config: AgentConfig);
151
+ /**
152
+ * Emit an event to the callback if configured
153
+ */
154
+ private emit;
155
+ private createInitialMetrics;
156
+ private createInitialState;
157
+ private getSystemMessage;
158
+ private estimateContextSize;
159
+ private manageContextWindow;
160
+ private checkContextLimits;
161
+ run(userPrompt: string): Promise<string>;
162
+ private callLLM;
163
+ /**
164
+ * Process XML-style tool calls from LLM response
165
+ *
166
+ * This method checks if the response contains XML-style tool calls
167
+ * (e.g., <function=name><parameter=...>...</parameter></function>)
168
+ * and converts them to OpenAI-compatible JSON format.
169
+ *
170
+ * If regex parsing fails and content contains XML patterns,
171
+ * it optionally uses an AI inference to convert them.
172
+ */
173
+ private processXmlToolCalls;
174
+ /**
175
+ * AI-assisted tool call parsing
176
+ *
177
+ * Uses a lightweight LLM call to convert XML tool calls to JSON format.
178
+ * This is a fallback when regex parsing fails.
179
+ */
180
+ private aiAssistedToolParsing;
181
+ private handleToolCalls;
182
+ private trackToolMetrics;
183
+ private addToolResultMessage;
184
+ private looksLikeCompletion;
185
+ /**
186
+ * Check if we should stop the loop based on state
187
+ * Returns true if we should stop
188
+ */
189
+ private shouldStopLoop;
190
+ abort(): void;
191
+ getState(): Readonly<AgentState>;
192
+ getMetrics(): Readonly<AgentMetrics>;
193
+ getTokensUsed(): number;
194
+ }
195
+
196
+ /**
197
+ * ContentGenerator interface for GeoffNet Qwen integration
198
+ * Simplified from qwen-code to focus on OpenAI-compatible generation
199
+ */
200
+ /**
201
+ * Gemini-compatible types for internal use
202
+ * These mirror the @google/genai types but are simplified for our use case
203
+ */
204
+ interface Part {
205
+ text?: string;
206
+ thought?: boolean;
207
+ functionCall?: FunctionCall;
208
+ functionResponse?: FunctionResponse;
209
+ inlineData?: {
210
+ data: string;
211
+ mimeType: string;
212
+ };
213
+ fileData?: {
214
+ fileUri: string;
215
+ mimeType: string;
216
+ };
217
+ }
218
+ interface Content {
219
+ role: 'user' | 'model';
220
+ parts: Part[];
221
+ }
222
+ interface FunctionCall {
223
+ id?: string;
224
+ name?: string;
225
+ args?: Record<string, unknown>;
226
+ }
227
+ interface FunctionResponse {
228
+ id?: string;
229
+ name?: string;
230
+ response?: unknown;
231
+ }
232
+ interface FunctionDeclaration {
233
+ name: string;
234
+ description: string;
235
+ parameters?: Record<string, unknown>;
236
+ parametersJsonSchema?: Record<string, unknown>;
237
+ }
238
+ interface Tool {
239
+ functionDeclarations?: FunctionDeclaration[];
240
+ }
241
+ type ToolListUnion = (Tool | CallableTool)[];
242
+ interface CallableTool {
243
+ tool: () => Promise<Tool>;
244
+ }
245
+ interface UsageMetadata {
246
+ promptTokenCount?: number;
247
+ candidatesTokenCount?: number;
248
+ totalTokenCount?: number;
249
+ cachedContentTokenCount?: number;
250
+ thoughtsTokenCount?: number;
251
+ }
252
+ interface Candidate {
253
+ content: Content;
254
+ finishReason?: FinishReason;
255
+ index: number;
256
+ safetyRatings?: unknown[];
257
+ }
258
+ declare enum FinishReason {
259
+ FINISH_REASON_UNSPECIFIED = 0,
260
+ STOP = 1,
261
+ MAX_TOKENS = 2,
262
+ SAFETY = 3,
263
+ RECITATION = 4,
264
+ OTHER = 5
265
+ }
266
+ /**
267
+ * Gemini-style response wrapper
268
+ */
269
+ declare class GenerateContentResponse {
270
+ candidates?: Candidate[];
271
+ usageMetadata?: UsageMetadata;
272
+ responseId?: string;
273
+ createTime?: string;
274
+ modelVersion?: string;
275
+ promptFeedback?: {
276
+ safetyRatings: unknown[];
277
+ };
278
+ }
279
+ interface GenerateContentParameters {
280
+ contents: Content[] | string;
281
+ config?: {
282
+ systemInstruction?: string | Content;
283
+ tools?: ToolListUnion;
284
+ temperature?: number;
285
+ topP?: number;
286
+ maxOutputTokens?: number;
287
+ abortSignal?: AbortSignal;
288
+ };
289
+ }
290
+ interface CountTokensParameters {
291
+ contents: Content[] | string;
292
+ }
293
+ interface CountTokensResponse {
294
+ totalTokens: number;
295
+ }
296
+ interface EmbedContentParameters {
297
+ contents: Content[] | string;
298
+ }
299
+ interface EmbedContentResponse {
300
+ embeddings: {
301
+ values: number[];
302
+ }[];
303
+ }
304
+ /**
305
+ * Authentication types supported
306
+ */
307
+ declare enum AuthType {
308
+ USE_OPENAI = "openai",
309
+ USE_OLLAMA = "ollama"
310
+ }
311
+ /**
312
+ * Configuration for content generation
313
+ */
314
+ interface ContentGeneratorConfig {
315
+ model: string;
316
+ apiKey?: string;
317
+ baseUrl?: string;
318
+ authType?: AuthType;
319
+ enableOpenAILogging?: boolean;
320
+ openAILoggingDir?: string;
321
+ timeout?: number;
322
+ maxRetries?: number;
323
+ enableTextToolCallParsing?: boolean;
324
+ samplingParams?: {
325
+ top_p?: number;
326
+ top_k?: number;
327
+ repetition_penalty?: number;
328
+ presence_penalty?: number;
329
+ frequency_penalty?: number;
330
+ temperature?: number;
331
+ max_tokens?: number;
332
+ };
333
+ }
334
+ /**
335
+ * Interface for content generation implementations
336
+ */
337
+ interface ContentGenerator {
338
+ generateContent(request: GenerateContentParameters, userPromptId: string): Promise<GenerateContentResponse>;
339
+ generateContentStream(request: GenerateContentParameters, userPromptId: string): Promise<AsyncGenerator<GenerateContentResponse>>;
340
+ countTokens(request: CountTokensParameters): Promise<CountTokensResponse>;
341
+ embedContent(request: EmbedContentParameters): Promise<EmbedContentResponse>;
342
+ setAvailableTools?(toolNames: string[]): void;
343
+ }
344
+ declare const DEFAULT_QWEN_MODEL = "qwen3-coder:30b";
345
+
346
+ /**
347
+ * Interface for OpenAI-compatible providers
348
+ */
349
+ interface OpenAICompatibleProvider {
350
+ buildHeaders(): Record<string, string | undefined>;
351
+ buildClient(): OpenAI;
352
+ buildRequest(request: OpenAI.Chat.ChatCompletionCreateParams, userPromptId: string): OpenAI.Chat.ChatCompletionCreateParams;
353
+ }
354
+
355
+ /**
356
+ * Content Generation Pipeline
357
+ * Handles the complete request/response flow for OpenAI-compatible APIs
358
+ */
359
+
360
+ interface PipelineConfig {
361
+ provider: OpenAICompatibleProvider;
362
+ contentGeneratorConfig: ContentGeneratorConfig;
363
+ }
364
+ declare class ContentGenerationPipeline {
365
+ private config;
366
+ client: OpenAI;
367
+ private converter;
368
+ private contentGeneratorConfig;
369
+ constructor(config: PipelineConfig);
370
+ /**
371
+ * Set available tool names for text-based tool call parsing
372
+ */
373
+ setAvailableTools(toolNames: string[]): void;
374
+ /**
375
+ * Execute a non-streaming request
376
+ */
377
+ execute(request: GenerateContentParameters, userPromptId: string): Promise<GenerateContentResponse>;
378
+ /**
379
+ * Execute a streaming request
380
+ */
381
+ executeStream(request: GenerateContentParameters, userPromptId: string): Promise<AsyncGenerator<GenerateContentResponse>>;
382
+ /**
383
+ * Process the OpenAI stream and convert to Gemini format
384
+ */
385
+ private processStream;
386
+ private buildRequest;
387
+ private buildSamplingParameters;
388
+ private handleError;
389
+ }
390
+
391
+ /**
392
+ * OpenAI Content Generator
393
+ * Main implementation of ContentGenerator for OpenAI-compatible APIs
394
+ */
395
+
396
+ declare class OpenAIContentGenerator implements ContentGenerator {
397
+ protected pipeline: ContentGenerationPipeline;
398
+ constructor(contentGeneratorConfig: ContentGeneratorConfig, provider: OpenAICompatibleProvider);
399
+ /**
400
+ * Set available tool names for text-based tool call parsing
401
+ */
402
+ setAvailableTools(toolNames: string[]): void;
403
+ generateContent(request: GenerateContentParameters, userPromptId: string): Promise<GenerateContentResponse>;
404
+ generateContentStream(request: GenerateContentParameters, userPromptId: string): Promise<AsyncGenerator<GenerateContentResponse>>;
405
+ countTokens(request: CountTokensParameters): Promise<CountTokensResponse>;
406
+ embedContent(request: EmbedContentParameters): Promise<EmbedContentResponse>;
407
+ }
408
+
409
+ /**
410
+ * OpenAI <-> Gemini format converter
411
+ * Handles bidirectional conversion between OpenAI Chat Completion format
412
+ * and internal Gemini-style format, including streaming tool call parsing.
413
+ */
414
+
415
+ /**
416
+ * Converter class for transforming data between Gemini and OpenAI formats
417
+ */
418
+ declare class OpenAIContentConverter {
419
+ private model;
420
+ private streamingToolCallParser;
421
+ private textToolCallParser;
422
+ private enableTextToolCallParsing;
423
+ private streamingTextAccumulator;
424
+ private streamingHasNativeToolCalls;
425
+ constructor(model: string, enableTextToolCallParsing?: boolean);
426
+ /**
427
+ * Set available tool names for text-based tool call parsing
428
+ */
429
+ setAvailableTools(toolNames: string[]): void;
430
+ /**
431
+ * Enable or disable text-based tool call parsing
432
+ */
433
+ setTextToolCallParsing(enabled: boolean): void;
434
+ /**
435
+ * Reset streaming state for new stream processing
436
+ */
437
+ resetStreamingToolCalls(): void;
438
+ /**
439
+ * Convert Gemini tools to OpenAI format
440
+ */
441
+ convertGeminiToolsToOpenAI(geminiTools: ToolListUnion): Promise<OpenAI.Chat.ChatCompletionTool[]>;
442
+ /**
443
+ * Convert Gemini tool parameters to OpenAI JSON Schema format
444
+ */
445
+ convertGeminiToolParametersToOpenAI(parameters: Record<string, unknown>): Record<string, unknown> | undefined;
446
+ /**
447
+ * Convert Gemini request to OpenAI message format
448
+ */
449
+ convertGeminiRequestToOpenAI(request: GenerateContentParameters): OpenAI.Chat.ChatCompletionMessageParam[];
450
+ private addSystemInstructionMessage;
451
+ private processContents;
452
+ private processContent;
453
+ private parseParts;
454
+ private extractFunctionResponseContent;
455
+ private getMediaType;
456
+ private createMultimodalMessage;
457
+ private extractTextFromContentUnion;
458
+ /**
459
+ * Convert OpenAI response to Gemini format
460
+ */
461
+ convertOpenAIResponseToGemini(openaiResponse: OpenAI.Chat.ChatCompletion): GenerateContentResponse;
462
+ /**
463
+ * Convert OpenAI stream chunk to Gemini format
464
+ */
465
+ convertOpenAIChunkToGemini(chunk: OpenAI.Chat.ChatCompletionChunk): GenerateContentResponse;
466
+ private mapOpenAIFinishReasonToGemini;
467
+ private cleanOrphanedToolCalls;
468
+ private mergeConsecutiveAssistantMessages;
469
+ }
470
+
471
+ /**
472
+ * StreamingToolCallParser - Handles streaming tool call objects with inconsistent chunk formats
473
+ *
474
+ * Problems this parser addresses:
475
+ * - Tool calls arrive with varying chunk shapes (empty strings, partial JSON, complete objects)
476
+ * - Tool calls may lack IDs, names, or have inconsistent indices
477
+ * - Multiple tool calls can be processed simultaneously with interleaved chunks
478
+ * - Index collisions occur when the same index is reused for different tool calls
479
+ * - JSON arguments are fragmented across multiple chunks and need reconstruction
480
+ */
481
+ /**
482
+ * Type definition for the result of parsing a JSON chunk in tool calls
483
+ */
484
+ interface ToolCallParseResult {
485
+ /** Whether the JSON parsing is complete */
486
+ complete: boolean;
487
+ /** The parsed JSON value (only present when complete is true) */
488
+ value?: Record<string, unknown>;
489
+ /** Error information if parsing failed */
490
+ error?: Error;
491
+ /** Whether the JSON was repaired (e.g., auto-closed unclosed strings) */
492
+ repaired?: boolean;
493
+ }
494
+ declare class StreamingToolCallParser {
495
+ /** Accumulated buffer containing all received chunks for each tool call index */
496
+ private buffers;
497
+ /** Current nesting depth in JSON structure for each tool call index */
498
+ private depths;
499
+ /** Whether we're currently inside a string literal for each tool call index */
500
+ private inStrings;
501
+ /** Whether the next character should be treated as escaped for each tool call index */
502
+ private escapes;
503
+ /** Metadata for each tool call index */
504
+ private toolCallMeta;
505
+ /** Map from tool call ID to actual index used for storage */
506
+ private idToIndexMap;
507
+ /** Counter for generating new indices when collisions occur */
508
+ private nextAvailableIndex;
509
+ /**
510
+ * Processes a new chunk of tool call data and attempts to parse complete JSON objects
511
+ */
512
+ addChunk(index: number, chunk: string, id?: string, name?: string): ToolCallParseResult;
513
+ /**
514
+ * Gets the current tool call metadata for a specific index
515
+ */
516
+ getToolCallMeta(index: number): {
517
+ id?: string;
518
+ name?: string;
519
+ };
520
+ /**
521
+ * Gets all completed tool calls that are ready to be emitted
522
+ */
523
+ getCompletedToolCalls(): Array<{
524
+ id?: string;
525
+ name?: string;
526
+ args: Record<string, unknown>;
527
+ index: number;
528
+ }>;
529
+ private findNextAvailableIndex;
530
+ private findMostRecentIncompleteIndex;
531
+ /**
532
+ * Resets the parser state for a specific tool call index
533
+ */
534
+ resetIndex(index: number): void;
535
+ /**
536
+ * Resets the entire parser state for processing a new stream
537
+ */
538
+ reset(): void;
539
+ /**
540
+ * Gets the current accumulated buffer content for a specific index
541
+ */
542
+ getBuffer(index: number): string;
543
+ /**
544
+ * Gets the current parsing state information for a specific index
545
+ */
546
+ getState(index: number): {
547
+ depth: number;
548
+ inString: boolean;
549
+ escape: boolean;
550
+ };
551
+ }
552
+
553
+ /**
554
+ * Text-based tool call parser for models that don't support native OpenAI tool_calls.
555
+ * Supports multiple XML and JSON formats used by Qwen and other models.
556
+ */
557
+ /**
558
+ * Parsed tool call from text content
559
+ */
560
+ interface ParsedTextToolCall {
561
+ id: string;
562
+ name: string;
563
+ args: Record<string, unknown>;
564
+ }
565
+ /**
566
+ * Result of text tool call parsing
567
+ */
568
+ interface TextToolCallParseResult {
569
+ toolCalls: ParsedTextToolCall[];
570
+ remainingText: string;
571
+ /** Extracted thinking/reasoning content from <think> tags */
572
+ thinkingContent?: string;
573
+ }
574
+ /**
575
+ * Parses tool calls from text content for models that don't support native OpenAI tool_calls.
576
+ *
577
+ * Supports multiple formats:
578
+ * 1. XML-style: <tool_call>{"name": "tool_name", "arguments": {...}}</tool_call>
579
+ * 2. Function notation: tool_name({"arg": "value"})
580
+ * 3. JSON block: ```json\n{"tool": "name", "args": {...}}\n```
581
+ * 4. Bracket notation: [tool_call: tool_name(args)]
582
+ * 5. Llama/Qwen style: <function=name><parameter=key>value</parameter></function>
583
+ */
584
+ declare class TextToolCallParser {
585
+ private toolCallCounter;
586
+ private availableToolNames;
587
+ constructor(availableToolNames?: string[]);
588
+ /**
589
+ * Set the available tool names for validation
590
+ */
591
+ setAvailableTools(toolNames: string[]): void;
592
+ /**
593
+ * Generate a unique tool call ID
594
+ */
595
+ private generateId;
596
+ /**
597
+ * Check if a tool name is valid
598
+ */
599
+ private isValidTool;
600
+ /**
601
+ * Parse tool calls from text content
602
+ */
603
+ parse(content: string): TextToolCallParseResult;
604
+ /**
605
+ * Parse thinking/reasoning content from <think> tags (Qwen models)
606
+ * Supports: <think>...</think>, <thinking>...</thinking>, <reasoning>...</reasoning>
607
+ */
608
+ private parseThinkTags;
609
+ /**
610
+ * Parse XML-style tool calls: <tool_call>{"name": "...", "arguments": {...}}</tool_call>
611
+ */
612
+ private parseXmlStyle;
613
+ /**
614
+ * Parse function call XML: <function_call>{"name": "...", "parameters": {...}}</function_call>
615
+ */
616
+ private parseFunctionCallXml;
617
+ /**
618
+ * Parse Llama/Qwen style tool calls: <function=name><parameter=key>value</parameter></function>
619
+ * Also handles variations like </tool_call> at the end
620
+ */
621
+ private parseLlamaStyleXml;
622
+ /**
623
+ * Parse JSON code blocks that contain tool call structures
624
+ */
625
+ private parseJsonBlocks;
626
+ /**
627
+ * Parse bracket notation: [tool_call: name(args)] or [name: args]
628
+ */
629
+ private parseBracketNotation;
630
+ /**
631
+ * Parse inline function calls: tool_name({...})
632
+ */
633
+ private parseInlineFunctionCalls;
634
+ /**
635
+ * Parse various JSON formats for tool calls
636
+ */
637
+ private parseToolCallJson;
638
+ /**
639
+ * Reset the parser state
640
+ */
641
+ reset(): void;
642
+ }
643
+
644
+ /**
645
+ * OpenAI Content Generator exports
646
+ */
647
+
648
+ /**
649
+ * Create an OpenAI-compatible content generator with the default provider
650
+ */
651
+ declare function createOpenAIContentGenerator(contentGeneratorConfig: ContentGeneratorConfig): ContentGenerator;
652
+
653
+ /**
654
+ * QwenAgentAdapter
655
+ *
656
+ * Adapter layer that wraps the Qwen-style content generator and provides
657
+ * the same interface as AgentLoop. This enables:
658
+ * - Text-based XML tool call parsing for Qwen models
659
+ * - Streaming tool call reconstruction
660
+ * - Same event emission interface for frontend compatibility
661
+ */
662
+
663
+ /**
664
+ * Extended configuration for QwenAgentAdapter
665
+ */
666
+ interface QwenAgentConfig extends AgentConfig {
667
+ /** Enable text-based tool call parsing (XML format support) */
668
+ enableTextToolCallParsing?: boolean;
669
+ /** Sampling parameters for the model */
670
+ samplingParams?: ContentGeneratorConfig['samplingParams'];
671
+ }
672
+ /**
673
+ * QwenAgentAdapter provides an AgentLoop-compatible interface
674
+ * while using the Qwen content generator with XML tool call parsing
675
+ */
676
+ declare class QwenAgentAdapter {
677
+ private config;
678
+ private state;
679
+ private tools;
680
+ private logger;
681
+ private aborted;
682
+ private contentGenerator;
683
+ constructor(config: QwenAgentConfig);
684
+ /**
685
+ * Emit an event to the callback if configured
686
+ */
687
+ private emit;
688
+ private createInitialMetrics;
689
+ private createInitialState;
690
+ private getSystemMessage;
691
+ private estimateContextSize;
692
+ private manageContextWindow;
693
+ private checkContextLimits;
694
+ /**
695
+ * Convert internal messages to Gemini-style Content format
696
+ */
697
+ private messagesToContents;
698
+ /**
699
+ * Convert tool definitions to Gemini Tool format
700
+ */
701
+ private toolsToGeminiFormat;
702
+ /**
703
+ * Extract tool calls from Gemini response
704
+ */
705
+ private extractToolCalls;
706
+ /**
707
+ * Extract text content from Gemini response
708
+ */
709
+ private extractTextContent;
710
+ /**
711
+ * Main agent loop - runs until task completion or max iterations
712
+ */
713
+ run(userPrompt: string): Promise<string>;
714
+ private callLLM;
715
+ private handleToolCalls;
716
+ private trackToolMetrics;
717
+ private addToolResultMessage;
718
+ private looksLikeCompletion;
719
+ private shouldStopLoop;
720
+ private serializeMetrics;
721
+ abort(): void;
722
+ getState(): Readonly<AgentState>;
723
+ getMetrics(): Readonly<AgentMetrics>;
724
+ getTokensUsed(): number;
725
+ }
726
+
727
+ interface RetryConfig {
728
+ maxRetries: number;
729
+ baseDelayMs: number;
730
+ maxDelayMs: number;
731
+ retryableStatuses: number[];
732
+ }
733
+ declare class APIClient {
734
+ private apiKey;
735
+ private baseUrl;
736
+ private model;
737
+ private retryConfig;
738
+ constructor(config: {
739
+ apiKey: string;
740
+ baseUrl: string;
741
+ model: string;
742
+ retryConfig?: Partial<RetryConfig>;
743
+ });
744
+ private sleep;
745
+ private calculateBackoff;
746
+ private shouldRetry;
747
+ createCompletion(messages: Message[], tools?: ToolDefinition[], options?: {
748
+ stream?: boolean;
749
+ maxTokens?: number;
750
+ temperature?: number;
751
+ }): Promise<CompletionResponse>;
752
+ createStreamingCompletion(messages: Message[], tools?: ToolDefinition[], options?: {
753
+ maxTokens?: number;
754
+ temperature?: number;
755
+ }): AsyncGenerator<StreamChunk, void, unknown>;
756
+ accumulateStream(messages: Message[], tools?: ToolDefinition[], options?: {
757
+ maxTokens?: number;
758
+ temperature?: number;
759
+ onToken?: (token: string) => void;
760
+ }): Promise<Message>;
761
+ }
762
+
763
+ interface ToolRegistryConfig {
764
+ workingDir: string;
765
+ ignorePatterns?: string[];
766
+ }
767
+ declare function createToolRegistry(config: ToolRegistryConfig | string): ToolRegistry;
768
+
769
+ /**
770
+ * TodoWrite Tool
771
+ *
772
+ * Creates and manages a structured task list for coding sessions.
773
+ * This helps track progress, organize complex tasks, and demonstrates
774
+ * thoroughness to the user.
775
+ */
776
+
777
+ /**
778
+ * Todo item structure
779
+ */
780
+ interface TodoItem {
781
+ id: string;
782
+ content: string;
783
+ status: 'pending' | 'in_progress' | 'completed';
784
+ /** Active form description for UI display during execution */
785
+ activeForm?: string;
786
+ }
787
+ /**
788
+ * Read todos from file
789
+ */
790
+ declare function readTodos(sessionId?: string): Promise<TodoItem[]>;
791
+ /**
792
+ * Write todos to file
793
+ */
794
+ declare function writeTodos(todos: TodoItem[], sessionId?: string): Promise<void>;
795
+ /**
796
+ * Create the todo_write tool handler
797
+ */
798
+ declare function createTodoWriteHandler(sessionId?: string): ToolHandler;
799
+ /**
800
+ * Todo write tool definition
801
+ */
802
+ declare const todoWriteDefinition: ToolDefinition;
803
+ /**
804
+ * List all todo sessions
805
+ */
806
+ declare function listTodoSessions(): Promise<string[]>;
807
+
808
+ /**
809
+ * Plan Mode Tools
810
+ *
811
+ * Tools for entering/exiting plan mode where the agent plans before executing.
812
+ * Plan mode ensures user approval before the agent starts writing code.
813
+ */
814
+
815
+ /**
816
+ * Plan mode state interface
817
+ */
818
+ interface PlanModeState {
819
+ enabled: boolean;
820
+ plan?: string;
821
+ awaitingApproval: boolean;
822
+ approved: boolean;
823
+ }
824
+ /**
825
+ * Get or create plan mode state for a session
826
+ */
827
+ declare function getPlanModeState(sessionId?: string): PlanModeState;
828
+ /**
829
+ * Set plan mode approval status (called by frontend)
830
+ */
831
+ declare function setPlanApproval(sessionId: string, approved: boolean): void;
832
+ /**
833
+ * Enter plan mode for a session
834
+ */
835
+ declare function enterPlanMode(sessionId?: string): void;
836
+ /**
837
+ * Check if plan mode is enabled
838
+ */
839
+ declare function isPlanModeEnabled(sessionId?: string): boolean;
840
+ /**
841
+ * Create exit_plan_mode tool handler
842
+ */
843
+ declare function createExitPlanModeHandler(sessionId?: string, onEvent?: AgentEventCallback): ToolHandler;
844
+ /**
845
+ * exit_plan_mode tool definition
846
+ */
847
+ declare const exitPlanModeDefinition: ToolDefinition;
848
+ /**
849
+ * Create enter_plan_mode tool handler (for explicit plan mode entry)
850
+ */
851
+ declare function createEnterPlanModeHandler(sessionId?: string, onEvent?: AgentEventCallback): ToolHandler;
852
+ /**
853
+ * enter_plan_mode tool definition
854
+ */
855
+ declare const enterPlanModeDefinition: ToolDefinition;
856
+
857
+ /**
858
+ * Background Process Tools
859
+ *
860
+ * Tools for running processes in the background and managing them.
861
+ * Enables long-running tasks like dev servers without blocking the agent.
862
+ */
863
+
864
+ /**
865
+ * Background process info
866
+ */
867
+ interface BackgroundProcess {
868
+ id: string;
869
+ pid: number;
870
+ command: string;
871
+ startTime: number;
872
+ output: string[];
873
+ isComplete: boolean;
874
+ exitCode: number | null;
875
+ process: ChildProcess;
876
+ }
877
+ /**
878
+ * Background process manager
879
+ */
880
+ declare class BackgroundProcessManager {
881
+ private processes;
882
+ private counter;
883
+ /**
884
+ * Start a background process
885
+ */
886
+ start(command: string, workingDir: string, onEvent?: AgentEventCallback): BackgroundProcess;
887
+ /**
888
+ * Get a background process by ID
889
+ */
890
+ get(id: string): BackgroundProcess | undefined;
891
+ /**
892
+ * Get all background processes
893
+ */
894
+ getAll(): BackgroundProcess[];
895
+ /**
896
+ * Kill a background process
897
+ */
898
+ kill(id: string): boolean;
899
+ /**
900
+ * Get output from a background process
901
+ */
902
+ getOutput(id: string, tail?: number): string;
903
+ /**
904
+ * Clean up completed processes
905
+ */
906
+ cleanup(): void;
907
+ }
908
+ declare const manager: BackgroundProcessManager;
909
+ /**
910
+ * Create run_background handler
911
+ */
912
+ declare function createRunBackgroundHandler(workingDir: string, onEvent?: AgentEventCallback): ToolHandler;
913
+ /**
914
+ * Create get_process_output handler
915
+ */
916
+ declare function createGetProcessOutputHandler(): ToolHandler;
917
+ /**
918
+ * Create kill_process handler
919
+ */
920
+ declare function createKillProcessHandler(): ToolHandler;
921
+ /**
922
+ * Create list_processes handler
923
+ */
924
+ declare function createListProcessesHandler(): ToolHandler;
925
+ declare const runBackgroundDefinition: ToolDefinition;
926
+ declare const getProcessOutputDefinition: ToolDefinition;
927
+ declare const killProcessDefinition: ToolDefinition;
928
+ declare const listProcessesDefinition: ToolDefinition;
929
+
930
+ type LogEventType = 'tool_call' | 'tool_result' | 'context_truncated' | 'assistant_message' | 'user_message' | 'info' | 'warn' | 'error' | 'success' | 'metrics_update' | 'iteration_start' | 'task_complete';
931
+ interface LogEvent {
932
+ type: LogEventType;
933
+ timestamp: number;
934
+ data: {
935
+ message?: string;
936
+ toolName?: string;
937
+ toolArgs?: string;
938
+ success?: boolean;
939
+ output?: string;
940
+ error?: string;
941
+ iteration?: number;
942
+ maxIterations?: number;
943
+ metrics?: AgentMetrics;
944
+ removedCount?: number;
945
+ tokenLimit?: number;
946
+ };
947
+ }
948
+ type LogEventCallback = (event: LogEvent) => void;
949
+ declare class Logger {
950
+ private verbose;
951
+ private currentMetricsLine;
952
+ private onLogEvent?;
953
+ constructor(verbose?: boolean, onLogEvent?: LogEventCallback);
954
+ setLogEventCallback(callback: LogEventCallback | undefined): void;
955
+ private emit;
956
+ private timestamp;
957
+ private num;
958
+ private truncate;
959
+ private colorize;
960
+ private statusIcon;
961
+ private line;
962
+ private label;
963
+ private section;
964
+ private clearStatusLine;
965
+ private restoreStatusLine;
966
+ private print;
967
+ info(message: string): void;
968
+ debug(message: string): void;
969
+ warn(message: string): void;
970
+ error(message: string): void;
971
+ success(message: string): void;
972
+ tool(name: string, args: string): void;
973
+ toolResult(name: string, result: ToolResult): void;
974
+ assistant(message: string): void;
975
+ user(message: string): void;
976
+ separator(): void;
977
+ banner(): void;
978
+ config(config: Record<string, unknown>): void;
979
+ metricsUpdate(metrics: AgentMetrics, iteration: number): void;
980
+ clearMetricsLine(): void;
981
+ metricsSummary(metrics: AgentMetrics, iteration: number): void;
982
+ iterationHeader(iteration: number, maxIterations: number): void;
983
+ }
984
+
985
+ interface GeoffConfig {
986
+ model?: string;
987
+ baseUrl?: string;
988
+ apiKey?: string;
989
+ maxIterations?: number;
990
+ maxTokens?: number;
991
+ temperature?: number;
992
+ verbose?: boolean;
993
+ ignorePatterns?: string[];
994
+ blockedCommands?: string[];
995
+ retry?: {
996
+ maxRetries?: number;
997
+ baseDelayMs?: number;
998
+ maxDelayMs?: number;
999
+ };
1000
+ }
1001
+ /**
1002
+ * Load configuration from .geoff.json files only.
1003
+ * Priority (highest to lowest):
1004
+ * 1. Project config (.geoff.json in working directory)
1005
+ * 2. User config (~/.geoff.json)
1006
+ * 3. Default config
1007
+ *
1008
+ * Model configuration (model, baseUrl, apiKey) comes exclusively from .geoff.json
1009
+ */
1010
+ declare function loadConfig(workingDirectory: string): GeoffConfig;
1011
+ /**
1012
+ * Validate configuration values
1013
+ */
1014
+ declare function validateConfig(config: GeoffConfig): {
1015
+ valid: boolean;
1016
+ errors: string[];
1017
+ };
1018
+
1019
+ export { APIClient, type AgentConfig, type AgentEvent, type AgentEventCallback, type AgentEventType, AgentLoop, type AgentMetrics, type AgentState, type BackgroundProcess, type CompletionRequest, type CompletionResponse, ContentGenerationPipeline, type ContentGenerator, type ContentGeneratorConfig, DEFAULT_QWEN_MODEL, type GenerateContentParameters, GenerateContentResponse, type GeoffConfig, Logger, type Message, OpenAIContentConverter, OpenAIContentGenerator, type PlanModeState, QwenAgentAdapter, type QwenAgentConfig, StreamingToolCallParser, TextToolCallParser, type TodoItem, type ToolCall, type ToolDefinition, type ToolHandler, type ToolRegistry, type ToolResult, manager as backgroundProcessManager, createEnterPlanModeHandler, createExitPlanModeHandler, createGetProcessOutputHandler, createKillProcessHandler, createListProcessesHandler, createOpenAIContentGenerator, createRunBackgroundHandler, createTodoWriteHandler, createToolRegistry, enterPlanMode, enterPlanModeDefinition, exitPlanModeDefinition, getPlanModeState, getProcessOutputDefinition, isPlanModeEnabled, killProcessDefinition, listProcessesDefinition, listTodoSessions, loadConfig, readTodos, runBackgroundDefinition, setPlanApproval, todoWriteDefinition, validateConfig, writeTodos };