@stackmemoryai/stackmemory 0.5.66 → 0.6.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.
- package/README.md +139 -45
- package/bin/codex-sm +6 -0
- package/bin/opencode-sm +1 -1
- package/dist/src/cli/claude-sm.js +162 -25
- package/dist/src/cli/claude-sm.js.map +2 -2
- package/dist/src/cli/commands/ping.js +14 -0
- package/dist/src/cli/commands/ping.js.map +7 -0
- package/dist/src/cli/commands/ralph.js +103 -1
- package/dist/src/cli/commands/ralph.js.map +2 -2
- package/dist/src/cli/commands/retrieval.js +1 -1
- package/dist/src/cli/commands/retrieval.js.map +2 -2
- package/dist/src/cli/commands/skills.js +201 -6
- package/dist/src/cli/commands/skills.js.map +2 -2
- package/dist/src/cli/index.js +66 -27
- package/dist/src/cli/index.js.map +2 -2
- package/dist/src/core/digest/types.js +1 -1
- package/dist/src/core/digest/types.js.map +1 -1
- package/dist/src/core/extensions/provider-adapter.js +2 -5
- package/dist/src/core/extensions/provider-adapter.js.map +2 -2
- package/dist/src/core/retrieval/llm-provider.js +2 -2
- package/dist/src/core/retrieval/llm-provider.js.map +1 -1
- package/dist/src/core/retrieval/types.js +1 -1
- package/dist/src/core/retrieval/types.js.map +1 -1
- package/dist/src/features/sweep/pty-wrapper.js +15 -5
- package/dist/src/features/sweep/pty-wrapper.js.map +2 -2
- package/dist/src/features/workers/tmux-manager.js +71 -0
- package/dist/src/features/workers/tmux-manager.js.map +7 -0
- package/dist/src/features/workers/worker-registry.js +52 -0
- package/dist/src/features/workers/worker-registry.js.map +7 -0
- package/dist/src/integrations/linear/webhook-handler.js +82 -0
- package/dist/src/integrations/linear/webhook-handler.js.map +2 -2
- package/dist/src/integrations/mcp/server.js +16 -10
- package/dist/src/integrations/mcp/server.js.map +2 -2
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +2 -2
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js.map +2 -2
- package/dist/src/orchestrators/multimodal/constants.js +1 -1
- package/dist/src/orchestrators/multimodal/constants.js.map +1 -1
- package/dist/src/orchestrators/multimodal/harness.js +28 -29
- package/dist/src/orchestrators/multimodal/harness.js.map +2 -2
- package/dist/src/orchestrators/multimodal/providers.js +35 -22
- package/dist/src/orchestrators/multimodal/providers.js.map +2 -2
- package/dist/src/skills/claude-skills.js +116 -1
- package/dist/src/skills/claude-skills.js.map +2 -2
- package/dist/src/skills/linear-task-runner.js +262 -0
- package/dist/src/skills/linear-task-runner.js.map +7 -0
- package/dist/src/skills/recursive-agent-orchestrator.js +114 -85
- package/dist/src/skills/recursive-agent-orchestrator.js.map +2 -2
- package/dist/src/skills/spec-generator-skill.js +441 -0
- package/dist/src/skills/spec-generator-skill.js.map +7 -0
- package/package.json +12 -5
- package/scripts/install-claude-hooks-auto.js +23 -9
- package/scripts/install-claude-hooks.sh +2 -2
- package/templates/claude-hooks/hooks.json +4 -2
- package/templates/claude-hooks/on-task-complete.js +91 -0
- package/templates/claude-hooks/post-edit-sweep.js +7 -10
- package/templates/claude-hooks/skill-eval.cjs +411 -0
- package/templates/claude-hooks/skill-eval.sh +31 -0
- package/templates/claude-hooks/skill-rules.json +274 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/core/extensions/provider-adapter.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Provider Adapter Interface for StackMemory\n *\n * Philosophy: \"Standardize the intersection; expose the union\"\n * - Portable core stream API with shared semantics across all providers\n * - Provider-specific extensions available through explicit opt-in capabilities\n */\n\n// =============================================================================\n// Core Types - Portable across all providers\n// =============================================================================\n\n/**\n * Message role - intersection of all providers\n */\nexport type MessageRole = 'user' | 'assistant' | 'system';\n\n/**\n * Content block types - intersection of all providers\n */\nexport type ContentBlockType = 'text' | 'image' | 'tool_use' | 'tool_result';\n\n/**\n * Base content block\n */\nexport interface ContentBlock {\n type: ContentBlockType;\n}\n\nexport interface TextBlock extends ContentBlock {\n type: 'text';\n text: string;\n}\n\nexport interface ImageBlock extends ContentBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n mediaType: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';\n data: string;\n };\n}\n\nexport interface ToolUseBlock extends ContentBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock extends ContentBlock {\n type: 'tool_result';\n toolUseId: string;\n content: string | ContentBlock[];\n isError?: boolean;\n}\n\nexport type AnyContentBlock =\n | TextBlock\n | ImageBlock\n | ToolUseBlock\n | ToolResultBlock;\n\n/**\n * Message - portable message format\n */\nexport interface Message {\n role: MessageRole;\n content: string | AnyContentBlock[];\n}\n\n/**\n * Tool definition - portable tool schema\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: 'object';\n properties: Record<string, unknown>;\n required?: string[];\n };\n}\n\n/**\n * Stream options - core options for all providers\n */\nexport interface StreamOptions {\n model: string;\n maxTokens: number;\n temperature?: number;\n topP?: number;\n stopSequences?: string[];\n system?: string;\n tools?: ToolDefinition[];\n}\n\n// =============================================================================\n// Stream Events - Portable event types\n// =============================================================================\n\nexport type StreamEventType =\n | 'message_start'\n | 'content_block_start'\n | 'content_block_delta'\n | 'content_block_stop'\n | 'message_delta'\n | 'message_stop'\n | 'error';\n\nexport interface StreamEventBase {\n type: StreamEventType;\n}\n\nexport interface MessageStartEvent extends StreamEventBase {\n type: 'message_start';\n message: {\n id: string;\n model: string;\n role: 'assistant';\n usage?: {\n inputTokens: number;\n outputTokens: number;\n };\n };\n}\n\nexport interface ContentBlockStartEvent extends StreamEventBase {\n type: 'content_block_start';\n index: number;\n contentBlock: AnyContentBlock;\n}\n\nexport interface ContentBlockDeltaEvent extends StreamEventBase {\n type: 'content_block_delta';\n index: number;\n delta: {\n type: 'text_delta' | 'input_json_delta';\n text?: string;\n partialJson?: string;\n };\n}\n\nexport interface ContentBlockStopEvent extends StreamEventBase {\n type: 'content_block_stop';\n index: number;\n}\n\nexport interface MessageDeltaEvent extends StreamEventBase {\n type: 'message_delta';\n delta: {\n stopReason?: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use';\n };\n usage?: {\n outputTokens: number;\n };\n}\n\nexport interface MessageStopEvent extends StreamEventBase {\n type: 'message_stop';\n}\n\nexport interface ErrorEvent extends StreamEventBase {\n type: 'error';\n error: {\n type: string;\n message: string;\n };\n}\n\nexport type StreamEvent =\n | MessageStartEvent\n | ContentBlockStartEvent\n | ContentBlockDeltaEvent\n | ContentBlockStopEvent\n | MessageDeltaEvent\n | MessageStopEvent\n | ErrorEvent;\n\n// =============================================================================\n// Provider Capabilities - Union of provider-specific features\n// =============================================================================\n\n/**\n * Claude-specific extensions\n */\nexport interface ClaudeExtensions {\n /**\n * Extended thinking - deep reasoning capability\n */\n extendedThinking?: {\n enabled: boolean;\n budgetTokens?: number;\n };\n\n /**\n * XML-structured output preference\n */\n xmlOutput?: {\n enabled: boolean;\n rootElement?: string;\n };\n\n /**\n * Computer use - desktop automation\n */\n computerUse?: {\n enabled: boolean;\n displaySize?: { width: number; height: number };\n };\n\n /**\n * PDF/document support\n */\n documentSupport?: {\n enabled: boolean;\n maxPages?: number;\n };\n}\n\n/**\n * OpenAI GPT-specific extensions\n */\nexport interface GPTExtensions {\n /**\n * Code interpreter - execute Python code\n */\n codeInterpreter?: {\n enabled: boolean;\n fileIds?: string[];\n };\n\n /**\n * Web browsing capability\n */\n browsing?: {\n enabled: boolean;\n };\n\n /**\n * DALL-E image generation\n */\n imageGeneration?: {\n enabled: boolean;\n size?: '256x256' | '512x512' | '1024x1024' | '1792x1024' | '1024x1792';\n quality?: 'standard' | 'hd';\n };\n\n /**\n * Function calling mode\n */\n functionCalling?: {\n mode: 'auto' | 'none' | 'required';\n };\n\n /**\n * JSON mode output\n */\n jsonMode?: {\n enabled: boolean;\n schema?: Record<string, unknown>;\n };\n}\n\n/**\n * Google Gemini-specific extensions\n */\nexport interface GeminiExtensions {\n /**\n * Grounding with Google Search\n */\n grounding?: {\n enabled: boolean;\n dynamicThreshold?: number;\n };\n\n /**\n * Native multimodal - video/audio support\n */\n multimodal?: {\n videoEnabled: boolean;\n audioEnabled: boolean;\n maxVideoDurationSec?: number;\n };\n\n /**\n * Code execution\n */\n codeExecution?: {\n enabled: boolean;\n };\n\n /**\n * Safety settings\n */\n safetySettings?: Array<{\n category: string;\n threshold: 'BLOCK_NONE' | 'BLOCK_LOW' | 'BLOCK_MEDIUM' | 'BLOCK_HIGH';\n }>;\n}\n\n/**\n * All provider extensions - union type\n */\nexport interface ProviderExtensions {\n claude?: ClaudeExtensions;\n gpt?: GPTExtensions;\n gemini?: GeminiExtensions;\n}\n\n// =============================================================================\n// Provider Adapter Interface\n// =============================================================================\n\n/**\n * Provider adapter interface - core contract for all providers\n */\nexport interface ProviderAdapter {\n /**\n * Unique provider identifier\n */\n readonly id: string;\n\n /**\n * Human-readable provider name\n */\n readonly name: string;\n\n /**\n * Provider version\n */\n readonly version: string;\n\n /**\n * Available extensions for this provider\n */\n readonly extensions: Partial<ProviderExtensions>;\n\n /**\n * Check if provider supports a specific extension\n */\n supportsExtension(extension: keyof ProviderExtensions): boolean;\n\n /**\n * Core streaming API - portable across all providers\n */\n stream(\n messages: Message[],\n options: StreamOptions\n ): AsyncIterable<StreamEvent>;\n\n /**\n * Non-streaming completion\n */\n complete(\n messages: Message[],\n options: StreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }>;\n\n /**\n * Validate API key / connection\n */\n validateConnection(): Promise<boolean>;\n\n /**\n * Get available models for this provider\n */\n listModels(): Promise<string[]>;\n}\n\n// =============================================================================\n// Claude Adapter Implementation\n// =============================================================================\n\n/**\n * Extended stream options for Claude\n */\nexport interface ClaudeStreamOptions extends StreamOptions {\n extensions?: ClaudeExtensions;\n}\n\n/**\n * Claude-specific stream events\n */\nexport interface ThinkingBlockStartEvent extends StreamEventBase {\n type: 'content_block_start';\n index: number;\n contentBlock: {\n type: 'thinking';\n thinking: string;\n };\n}\n\nexport interface ThinkingBlockDeltaEvent extends StreamEventBase {\n type: 'content_block_delta';\n index: number;\n delta: {\n type: 'thinking_delta';\n thinking: string;\n };\n}\n\n/**\n * Claude adapter - full implementation with extensions\n */\nexport class ClaudeAdapter implements ProviderAdapter {\n readonly id = 'claude';\n readonly name = 'Anthropic Claude';\n readonly version = '1.0.0';\n\n readonly extensions: ProviderExtensions = {\n claude: {\n extendedThinking: { enabled: true, budgetTokens: 10000 },\n xmlOutput: { enabled: true },\n computerUse: { enabled: true },\n documentSupport: { enabled: true, maxPages: 100 },\n },\n };\n\n private apiKey: string;\n private baseUrl: string;\n\n constructor(config: { apiKey: string; baseUrl?: string }) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || 'https://api.anthropic.com';\n }\n\n supportsExtension(extension: keyof ProviderExtensions): boolean {\n return extension === 'claude';\n }\n\n async *stream(\n messages: Message[],\n options: ClaudeStreamOptions\n ): AsyncIterable<StreamEvent> {\n const body = this.buildRequestBody(messages, options);\n\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n 'anthropic-beta': this.getBetaFlags(options.extensions),\n },\n body: JSON.stringify({ ...body, stream: true }),\n });\n\n if (!response.ok) {\n yield {\n type: 'error',\n error: {\n type: 'api_error',\n message: `Claude API error: ${response.status} ${response.statusText}`,\n },\n };\n return;\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n yield {\n type: 'error',\n error: { type: 'stream_error', message: 'No response body' },\n };\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') return;\n\n try {\n const event = JSON.parse(data) as StreamEvent;\n yield this.normalizeEvent(event);\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n }\n\n async complete(\n messages: Message[],\n options: ClaudeStreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }> {\n const body = this.buildRequestBody(messages, options);\n\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n 'anthropic-beta': this.getBetaFlags(options.extensions),\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(\n `Claude API error: ${response.status} ${response.statusText}`\n );\n }\n\n const data = await response.json();\n\n return {\n content: data.content as AnyContentBlock[],\n usage: {\n inputTokens: data.usage?.input_tokens ?? 0,\n outputTokens: data.usage?.output_tokens ?? 0,\n },\n stopReason: data.stop_reason ?? 'end_turn',\n };\n }\n\n async validateConnection(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: 'claude-3-haiku-20240307',\n max_tokens: 1,\n messages: [{ role: 'user', content: 'hi' }],\n }),\n });\n return response.ok || response.status === 400;\n } catch {\n return false;\n }\n }\n\n async listModels(): Promise<string[]> {\n return [\n 'claude-opus-4-20250514',\n 'claude-sonnet-4-20250514',\n 'claude-3-5-sonnet-20241022',\n 'claude-3-5-haiku-20241022',\n 'claude-3-opus-20240229',\n 'claude-3-sonnet-20240229',\n 'claude-3-haiku-20240307',\n ];\n }\n\n private buildRequestBody(\n messages: Message[],\n options: ClaudeStreamOptions\n ): Record<string, unknown> {\n const body: Record<string, unknown> = {\n model: options.model,\n max_tokens: options.maxTokens,\n messages: messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n };\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n if (options.topP !== undefined) {\n body.top_p = options.topP;\n }\n if (options.stopSequences?.length) {\n body.stop_sequences = options.stopSequences;\n }\n if (options.system) {\n body.system = options.system;\n }\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n }));\n }\n\n // Claude extensions\n if (options.extensions?.extendedThinking?.enabled) {\n body.thinking = {\n type: 'enabled',\n budget_tokens:\n options.extensions.extendedThinking.budgetTokens || 10000,\n };\n // Extended thinking requires temperature = 1\n body.temperature = 1;\n }\n\n return body;\n }\n\n private getBetaFlags(extensions?: ClaudeExtensions): string {\n const flags: string[] = [];\n\n if (extensions?.extendedThinking?.enabled) {\n flags.push('interleaved-thinking-2025-05-14');\n }\n if (extensions?.computerUse?.enabled) {\n flags.push('computer-use-2024-10-22');\n }\n if (extensions?.documentSupport?.enabled) {\n flags.push('pdfs-2024-09-25');\n }\n\n return flags.join(',');\n }\n\n private normalizeEvent(event: StreamEvent): StreamEvent {\n // Normalize Claude-specific events to portable format\n return event;\n }\n}\n\n// =============================================================================\n// GPT Adapter Stub\n// =============================================================================\n\n/**\n * Extended stream options for GPT\n */\nexport interface GPTStreamOptions extends StreamOptions {\n extensions?: GPTExtensions;\n}\n\n/**\n * GPT adapter - stub implementation\n */\nexport class GPTAdapter implements ProviderAdapter {\n readonly id = 'gpt';\n readonly name = 'OpenAI GPT';\n readonly version = '1.0.0';\n\n readonly extensions: ProviderExtensions = {\n gpt: {\n codeInterpreter: { enabled: true },\n browsing: { enabled: true },\n imageGeneration: {\n enabled: true,\n size: '1024x1024',\n quality: 'standard',\n },\n functionCalling: { mode: 'auto' },\n jsonMode: { enabled: true },\n },\n };\n\n private apiKey: string;\n private baseUrl: string;\n\n constructor(config: { apiKey: string; baseUrl?: string }) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || 'https://api.openai.com';\n }\n\n supportsExtension(extension: keyof ProviderExtensions): boolean {\n return extension === 'gpt';\n }\n\n async *stream(\n messages: Message[],\n options: GPTStreamOptions\n ): AsyncIterable<StreamEvent> {\n // Convert messages to OpenAI format\n const openaiMessages = messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n }));\n\n const body: Record<string, unknown> = {\n model: options.model,\n max_tokens: options.maxTokens,\n messages: openaiMessages,\n stream: true,\n };\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n if (options.topP !== undefined) {\n body.top_p = options.topP;\n }\n\n // GPT extensions\n if (options.extensions?.jsonMode?.enabled) {\n body.response_format = { type: 'json_object' };\n }\n\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n type: 'function',\n function: {\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n },\n }));\n\n if (options.extensions?.functionCalling?.mode) {\n body.tool_choice = options.extensions.functionCalling.mode;\n }\n }\n\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n yield {\n type: 'error',\n error: {\n type: 'api_error',\n message: `GPT API error: ${response.status} ${response.statusText}`,\n },\n };\n return;\n }\n\n // Yield message start\n yield {\n type: 'message_start',\n message: {\n id: `msg_${Date.now()}`,\n model: options.model,\n role: 'assistant',\n },\n };\n\n const reader = response.body?.getReader();\n if (!reader) return;\n\n const decoder = new TextDecoder();\n let buffer = '';\n const blockIndex = 0;\n let blockStarted = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') {\n if (blockStarted) {\n yield { type: 'content_block_stop', index: blockIndex };\n }\n yield { type: 'message_stop' };\n return;\n }\n\n try {\n const parsed = JSON.parse(data);\n const delta = parsed.choices?.[0]?.delta;\n\n if (delta?.content && !blockStarted) {\n blockStarted = true;\n yield {\n type: 'content_block_start',\n index: blockIndex,\n contentBlock: { type: 'text', text: '' },\n };\n }\n\n if (delta?.content) {\n yield {\n type: 'content_block_delta',\n index: blockIndex,\n delta: { type: 'text_delta', text: delta.content },\n };\n }\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n }\n\n async complete(\n messages: Message[],\n options: GPTStreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }> {\n const openaiMessages = messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n }));\n\n const body: Record<string, unknown> = {\n model: options.model,\n max_tokens: options.maxTokens,\n messages: openaiMessages,\n };\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(\n `GPT API error: ${response.status} ${response.statusText}`\n );\n }\n\n const data = await response.json();\n const choice = data.choices?.[0];\n\n return {\n content: [{ type: 'text', text: choice?.message?.content ?? '' }],\n usage: {\n inputTokens: data.usage?.prompt_tokens ?? 0,\n outputTokens: data.usage?.completion_tokens ?? 0,\n },\n stopReason: choice?.finish_reason ?? 'stop',\n };\n }\n\n async validateConnection(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/v1/models`, {\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async listModels(): Promise<string[]> {\n return [\n 'gpt-4o',\n 'gpt-4o-mini',\n 'gpt-4-turbo',\n 'gpt-4',\n 'gpt-3.5-turbo',\n 'o1',\n 'o1-mini',\n 'o1-preview',\n ];\n }\n}\n\n// =============================================================================\n// Gemini Adapter Stub\n// =============================================================================\n\n/**\n * Extended stream options for Gemini\n */\nexport interface GeminiStreamOptions extends StreamOptions {\n extensions?: GeminiExtensions;\n}\n\n/**\n * Gemini adapter - stub implementation\n */\nexport class GeminiAdapter implements ProviderAdapter {\n readonly id = 'gemini';\n readonly name = 'Google Gemini';\n readonly version = '1.0.0';\n\n readonly extensions: ProviderExtensions = {\n gemini: {\n grounding: { enabled: true, dynamicThreshold: 0.3 },\n multimodal: {\n videoEnabled: true,\n audioEnabled: true,\n maxVideoDurationSec: 60,\n },\n codeExecution: { enabled: true },\n },\n };\n\n private apiKey: string;\n private baseUrl: string;\n\n constructor(config: { apiKey: string; baseUrl?: string }) {\n this.apiKey = config.apiKey;\n this.baseUrl =\n config.baseUrl || 'https://generativelanguage.googleapis.com/v1beta';\n }\n\n supportsExtension(extension: keyof ProviderExtensions): boolean {\n return extension === 'gemini';\n }\n\n async *stream(\n messages: Message[],\n options: GeminiStreamOptions\n ): AsyncIterable<StreamEvent> {\n // Convert messages to Gemini format\n const contents = messages\n .filter((m) => m.role !== 'system')\n .map((m) => ({\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: [\n {\n text:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n },\n ],\n }));\n\n const body: Record<string, unknown> = {\n contents,\n generationConfig: {\n maxOutputTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n },\n };\n\n // System instruction\n const systemMsg = messages.find((m) => m.role === 'system');\n if (systemMsg || options.system) {\n body.systemInstruction = {\n parts: [\n {\n text:\n options.system ||\n (typeof systemMsg?.content === 'string' ? systemMsg.content : ''),\n },\n ],\n };\n }\n\n // Gemini extensions\n if (options.extensions?.grounding?.enabled) {\n body.tools = [\n {\n googleSearchRetrieval: {\n dynamicRetrievalConfig: {\n mode: 'MODE_DYNAMIC',\n dynamicThreshold: options.extensions.grounding.dynamicThreshold,\n },\n },\n },\n ];\n }\n\n if (options.extensions?.codeExecution?.enabled) {\n body.tools = [\n ...((body.tools as unknown[]) || []),\n { codeExecution: {} },\n ];\n }\n\n if (options.extensions?.safetySettings) {\n body.safetySettings = options.extensions.safetySettings;\n }\n\n const url = `${this.baseUrl}/models/${options.model}:streamGenerateContent?key=${this.apiKey}&alt=sse`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n yield {\n type: 'error',\n error: {\n type: 'api_error',\n message: `Gemini API error: ${response.status} ${response.statusText}`,\n },\n };\n return;\n }\n\n yield {\n type: 'message_start',\n message: {\n id: `msg_${Date.now()}`,\n model: options.model,\n role: 'assistant',\n },\n };\n\n const reader = response.body?.getReader();\n if (!reader) return;\n\n const decoder = new TextDecoder();\n let buffer = '';\n const blockIndex = 0;\n let blockStarted = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n try {\n const parsed = JSON.parse(line.slice(6));\n const text = parsed.candidates?.[0]?.content?.parts?.[0]?.text;\n\n if (text && !blockStarted) {\n blockStarted = true;\n yield {\n type: 'content_block_start',\n index: blockIndex,\n contentBlock: { type: 'text', text: '' },\n };\n }\n\n if (text) {\n yield {\n type: 'content_block_delta',\n index: blockIndex,\n delta: { type: 'text_delta', text },\n };\n }\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n\n if (blockStarted) {\n yield { type: 'content_block_stop', index: blockIndex };\n }\n yield { type: 'message_stop' };\n }\n\n async complete(\n messages: Message[],\n options: GeminiStreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }> {\n const contents = messages\n .filter((m) => m.role !== 'system')\n .map((m) => ({\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: [\n {\n text:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n },\n ],\n }));\n\n const body: Record<string, unknown> = {\n contents,\n generationConfig: {\n maxOutputTokens: options.maxTokens,\n temperature: options.temperature,\n },\n };\n\n const url = `${this.baseUrl}/models/${options.model}:generateContent?key=${this.apiKey}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(\n `Gemini API error: ${response.status} ${response.statusText}`\n );\n }\n\n const data = await response.json();\n const candidate = data.candidates?.[0];\n const text = candidate?.content?.parts?.[0]?.text ?? '';\n\n return {\n content: [{ type: 'text', text }],\n usage: {\n inputTokens: data.usageMetadata?.promptTokenCount ?? 0,\n outputTokens: data.usageMetadata?.candidatesTokenCount ?? 0,\n },\n stopReason: candidate?.finishReason ?? 'STOP',\n };\n }\n\n async validateConnection(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/models?key=${this.apiKey}`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async listModels(): Promise<string[]> {\n return [\n 'gemini-2.0-flash-exp',\n 'gemini-1.5-pro',\n 'gemini-1.5-flash',\n 'gemini-1.5-flash-8b',\n 'gemini-1.0-pro',\n ];\n }\n}\n\n// =============================================================================\n// Provider Registry\n// =============================================================================\n\nexport type ProviderId = 'claude' | 'gpt' | 'gemini';\n\n/**\n * Provider configuration\n */\nexport interface ProviderConfig {\n apiKey: string;\n baseUrl?: string;\n}\n\n/**\n * Create a provider adapter\n */\nexport function createProvider(\n id: ProviderId,\n config: ProviderConfig\n): ProviderAdapter {\n switch (id) {\n case 'claude':\n return new ClaudeAdapter(config);\n case 'gpt':\n return new GPTAdapter(config);\n case 'gemini':\n return new GeminiAdapter(config);\n default:\n throw new Error(`Unknown provider: ${id}`);\n }\n}\n\n/**\n * Provider registry for managing multiple providers\n */\nexport class ProviderRegistry {\n private providers = new Map<string, ProviderAdapter>();\n\n register(adapter: ProviderAdapter): void {\n this.providers.set(adapter.id, adapter);\n }\n\n get(id: string): ProviderAdapter | undefined {\n return this.providers.get(id);\n }\n\n list(): ProviderAdapter[] {\n return Array.from(this.providers.values());\n }\n\n has(id: string): boolean {\n return this.providers.has(id);\n }\n\n /**\n * Find providers that support a specific extension\n */\n findByExtension(extension: keyof ProviderExtensions): ProviderAdapter[] {\n return this.list().filter((p) => p.supportsExtension(extension));\n }\n}\n\n/**\n * Global provider registry\n */\nexport const providerRegistry = new ProviderRegistry();\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Provider Adapter Interface for StackMemory\n *\n * Philosophy: \"Standardize the intersection; expose the union\"\n * - Portable core stream API with shared semantics across all providers\n * - Provider-specific extensions available through explicit opt-in capabilities\n */\n\n// =============================================================================\n// Core Types - Portable across all providers\n// =============================================================================\n\n/**\n * Message role - intersection of all providers\n */\nexport type MessageRole = 'user' | 'assistant' | 'system';\n\n/**\n * Content block types - intersection of all providers\n */\nexport type ContentBlockType = 'text' | 'image' | 'tool_use' | 'tool_result';\n\n/**\n * Base content block\n */\nexport interface ContentBlock {\n type: ContentBlockType;\n}\n\nexport interface TextBlock extends ContentBlock {\n type: 'text';\n text: string;\n}\n\nexport interface ImageBlock extends ContentBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n mediaType: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp';\n data: string;\n };\n}\n\nexport interface ToolUseBlock extends ContentBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: Record<string, unknown>;\n}\n\nexport interface ToolResultBlock extends ContentBlock {\n type: 'tool_result';\n toolUseId: string;\n content: string | ContentBlock[];\n isError?: boolean;\n}\n\nexport type AnyContentBlock =\n | TextBlock\n | ImageBlock\n | ToolUseBlock\n | ToolResultBlock;\n\n/**\n * Message - portable message format\n */\nexport interface Message {\n role: MessageRole;\n content: string | AnyContentBlock[];\n}\n\n/**\n * Tool definition - portable tool schema\n */\nexport interface ToolDefinition {\n name: string;\n description: string;\n inputSchema: {\n type: 'object';\n properties: Record<string, unknown>;\n required?: string[];\n };\n}\n\n/**\n * Stream options - core options for all providers\n */\nexport interface StreamOptions {\n model: string;\n maxTokens: number;\n temperature?: number;\n topP?: number;\n stopSequences?: string[];\n system?: string;\n tools?: ToolDefinition[];\n}\n\n// =============================================================================\n// Stream Events - Portable event types\n// =============================================================================\n\nexport type StreamEventType =\n | 'message_start'\n | 'content_block_start'\n | 'content_block_delta'\n | 'content_block_stop'\n | 'message_delta'\n | 'message_stop'\n | 'error';\n\nexport interface StreamEventBase {\n type: StreamEventType;\n}\n\nexport interface MessageStartEvent extends StreamEventBase {\n type: 'message_start';\n message: {\n id: string;\n model: string;\n role: 'assistant';\n usage?: {\n inputTokens: number;\n outputTokens: number;\n };\n };\n}\n\nexport interface ContentBlockStartEvent extends StreamEventBase {\n type: 'content_block_start';\n index: number;\n contentBlock: AnyContentBlock;\n}\n\nexport interface ContentBlockDeltaEvent extends StreamEventBase {\n type: 'content_block_delta';\n index: number;\n delta: {\n type: 'text_delta' | 'input_json_delta';\n text?: string;\n partialJson?: string;\n };\n}\n\nexport interface ContentBlockStopEvent extends StreamEventBase {\n type: 'content_block_stop';\n index: number;\n}\n\nexport interface MessageDeltaEvent extends StreamEventBase {\n type: 'message_delta';\n delta: {\n stopReason?: 'end_turn' | 'max_tokens' | 'stop_sequence' | 'tool_use';\n };\n usage?: {\n outputTokens: number;\n };\n}\n\nexport interface MessageStopEvent extends StreamEventBase {\n type: 'message_stop';\n}\n\nexport interface ErrorEvent extends StreamEventBase {\n type: 'error';\n error: {\n type: string;\n message: string;\n };\n}\n\nexport type StreamEvent =\n | MessageStartEvent\n | ContentBlockStartEvent\n | ContentBlockDeltaEvent\n | ContentBlockStopEvent\n | MessageDeltaEvent\n | MessageStopEvent\n | ErrorEvent;\n\n// =============================================================================\n// Provider Capabilities - Union of provider-specific features\n// =============================================================================\n\n/**\n * Claude-specific extensions\n */\nexport interface ClaudeExtensions {\n /**\n * Extended thinking - deep reasoning capability\n */\n extendedThinking?: {\n enabled: boolean;\n budgetTokens?: number;\n };\n\n /**\n * XML-structured output preference\n */\n xmlOutput?: {\n enabled: boolean;\n rootElement?: string;\n };\n\n /**\n * Computer use - desktop automation\n */\n computerUse?: {\n enabled: boolean;\n displaySize?: { width: number; height: number };\n };\n\n /**\n * PDF/document support\n */\n documentSupport?: {\n enabled: boolean;\n maxPages?: number;\n };\n}\n\n/**\n * OpenAI GPT-specific extensions\n */\nexport interface GPTExtensions {\n /**\n * Code interpreter - execute Python code\n */\n codeInterpreter?: {\n enabled: boolean;\n fileIds?: string[];\n };\n\n /**\n * Web browsing capability\n */\n browsing?: {\n enabled: boolean;\n };\n\n /**\n * DALL-E image generation\n */\n imageGeneration?: {\n enabled: boolean;\n size?: '256x256' | '512x512' | '1024x1024' | '1792x1024' | '1024x1792';\n quality?: 'standard' | 'hd';\n };\n\n /**\n * Function calling mode\n */\n functionCalling?: {\n mode: 'auto' | 'none' | 'required';\n };\n\n /**\n * JSON mode output\n */\n jsonMode?: {\n enabled: boolean;\n schema?: Record<string, unknown>;\n };\n}\n\n/**\n * Google Gemini-specific extensions\n */\nexport interface GeminiExtensions {\n /**\n * Grounding with Google Search\n */\n grounding?: {\n enabled: boolean;\n dynamicThreshold?: number;\n };\n\n /**\n * Native multimodal - video/audio support\n */\n multimodal?: {\n videoEnabled: boolean;\n audioEnabled: boolean;\n maxVideoDurationSec?: number;\n };\n\n /**\n * Code execution\n */\n codeExecution?: {\n enabled: boolean;\n };\n\n /**\n * Safety settings\n */\n safetySettings?: Array<{\n category: string;\n threshold: 'BLOCK_NONE' | 'BLOCK_LOW' | 'BLOCK_MEDIUM' | 'BLOCK_HIGH';\n }>;\n}\n\n/**\n * All provider extensions - union type\n */\nexport interface ProviderExtensions {\n claude?: ClaudeExtensions;\n gpt?: GPTExtensions;\n gemini?: GeminiExtensions;\n}\n\n// =============================================================================\n// Provider Adapter Interface\n// =============================================================================\n\n/**\n * Provider adapter interface - core contract for all providers\n */\nexport interface ProviderAdapter {\n /**\n * Unique provider identifier\n */\n readonly id: string;\n\n /**\n * Human-readable provider name\n */\n readonly name: string;\n\n /**\n * Provider version\n */\n readonly version: string;\n\n /**\n * Available extensions for this provider\n */\n readonly extensions: Partial<ProviderExtensions>;\n\n /**\n * Check if provider supports a specific extension\n */\n supportsExtension(extension: keyof ProviderExtensions): boolean;\n\n /**\n * Core streaming API - portable across all providers\n */\n stream(\n messages: Message[],\n options: StreamOptions\n ): AsyncIterable<StreamEvent>;\n\n /**\n * Non-streaming completion\n */\n complete(\n messages: Message[],\n options: StreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }>;\n\n /**\n * Validate API key / connection\n */\n validateConnection(): Promise<boolean>;\n\n /**\n * Get available models for this provider\n */\n listModels(): Promise<string[]>;\n}\n\n// =============================================================================\n// Claude Adapter Implementation\n// =============================================================================\n\n/**\n * Extended stream options for Claude\n */\nexport interface ClaudeStreamOptions extends StreamOptions {\n extensions?: ClaudeExtensions;\n}\n\n/**\n * Claude-specific stream events\n */\nexport interface ThinkingBlockStartEvent extends StreamEventBase {\n type: 'content_block_start';\n index: number;\n contentBlock: {\n type: 'thinking';\n thinking: string;\n };\n}\n\nexport interface ThinkingBlockDeltaEvent extends StreamEventBase {\n type: 'content_block_delta';\n index: number;\n delta: {\n type: 'thinking_delta';\n thinking: string;\n };\n}\n\n/**\n * Claude adapter - full implementation with extensions\n */\nexport class ClaudeAdapter implements ProviderAdapter {\n readonly id = 'claude';\n readonly name = 'Anthropic Claude';\n readonly version = '1.0.0';\n\n readonly extensions: ProviderExtensions = {\n claude: {\n extendedThinking: { enabled: true, budgetTokens: 10000 },\n xmlOutput: { enabled: true },\n computerUse: { enabled: true },\n documentSupport: { enabled: true, maxPages: 100 },\n },\n };\n\n private apiKey: string;\n private baseUrl: string;\n\n constructor(config: { apiKey: string; baseUrl?: string }) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || 'https://api.anthropic.com';\n }\n\n supportsExtension(extension: keyof ProviderExtensions): boolean {\n return extension === 'claude';\n }\n\n async *stream(\n messages: Message[],\n options: ClaudeStreamOptions\n ): AsyncIterable<StreamEvent> {\n const body = this.buildRequestBody(messages, options);\n\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n 'anthropic-beta': this.getBetaFlags(options.extensions),\n },\n body: JSON.stringify({ ...body, stream: true }),\n });\n\n if (!response.ok) {\n yield {\n type: 'error',\n error: {\n type: 'api_error',\n message: `Claude API error: ${response.status} ${response.statusText}`,\n },\n };\n return;\n }\n\n const reader = response.body?.getReader();\n if (!reader) {\n yield {\n type: 'error',\n error: { type: 'stream_error', message: 'No response body' },\n };\n return;\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') return;\n\n try {\n const event = JSON.parse(data) as StreamEvent;\n yield this.normalizeEvent(event);\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n }\n\n async complete(\n messages: Message[],\n options: ClaudeStreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }> {\n const body = this.buildRequestBody(messages, options);\n\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n 'anthropic-beta': this.getBetaFlags(options.extensions),\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(\n `Claude API error: ${response.status} ${response.statusText}`\n );\n }\n\n const data = await response.json();\n\n return {\n content: data.content as AnyContentBlock[],\n usage: {\n inputTokens: data.usage?.input_tokens ?? 0,\n outputTokens: data.usage?.output_tokens ?? 0,\n },\n stopReason: data.stop_reason ?? 'end_turn',\n };\n }\n\n async validateConnection(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({\n model: 'claude-3-5-haiku-20241022',\n max_tokens: 1,\n messages: [{ role: 'user', content: 'hi' }],\n }),\n });\n return response.ok || response.status === 400;\n } catch {\n return false;\n }\n }\n\n async listModels(): Promise<string[]> {\n return [\n 'claude-opus-4-20250514',\n 'claude-sonnet-4-20250514',\n 'claude-3-5-haiku-20241022',\n 'claude-3-opus-20240229',\n ];\n }\n\n private buildRequestBody(\n messages: Message[],\n options: ClaudeStreamOptions\n ): Record<string, unknown> {\n const body: Record<string, unknown> = {\n model: options.model,\n max_tokens: options.maxTokens,\n messages: messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n };\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n if (options.topP !== undefined) {\n body.top_p = options.topP;\n }\n if (options.stopSequences?.length) {\n body.stop_sequences = options.stopSequences;\n }\n if (options.system) {\n body.system = options.system;\n }\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n }));\n }\n\n // Claude extensions\n if (options.extensions?.extendedThinking?.enabled) {\n body.thinking = {\n type: 'enabled',\n budget_tokens:\n options.extensions.extendedThinking.budgetTokens || 10000,\n };\n // Extended thinking requires temperature = 1\n body.temperature = 1;\n }\n\n return body;\n }\n\n private getBetaFlags(extensions?: ClaudeExtensions): string {\n const flags: string[] = [];\n\n if (extensions?.extendedThinking?.enabled) {\n flags.push('interleaved-thinking-2025-05-14');\n }\n if (extensions?.computerUse?.enabled) {\n flags.push('computer-use-2024-10-22');\n }\n if (extensions?.documentSupport?.enabled) {\n flags.push('pdfs-2024-09-25');\n }\n\n return flags.join(',');\n }\n\n private normalizeEvent(event: StreamEvent): StreamEvent {\n // Normalize Claude-specific events to portable format\n return event;\n }\n}\n\n// =============================================================================\n// GPT Adapter Stub\n// =============================================================================\n\n/**\n * Extended stream options for GPT\n */\nexport interface GPTStreamOptions extends StreamOptions {\n extensions?: GPTExtensions;\n}\n\n/**\n * GPT adapter - stub implementation\n */\nexport class GPTAdapter implements ProviderAdapter {\n readonly id = 'gpt';\n readonly name = 'OpenAI GPT';\n readonly version = '1.0.0';\n\n readonly extensions: ProviderExtensions = {\n gpt: {\n codeInterpreter: { enabled: true },\n browsing: { enabled: true },\n imageGeneration: {\n enabled: true,\n size: '1024x1024',\n quality: 'standard',\n },\n functionCalling: { mode: 'auto' },\n jsonMode: { enabled: true },\n },\n };\n\n private apiKey: string;\n private baseUrl: string;\n\n constructor(config: { apiKey: string; baseUrl?: string }) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || 'https://api.openai.com';\n }\n\n supportsExtension(extension: keyof ProviderExtensions): boolean {\n return extension === 'gpt';\n }\n\n async *stream(\n messages: Message[],\n options: GPTStreamOptions\n ): AsyncIterable<StreamEvent> {\n // Convert messages to OpenAI format\n const openaiMessages = messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n }));\n\n const body: Record<string, unknown> = {\n model: options.model,\n max_tokens: options.maxTokens,\n messages: openaiMessages,\n stream: true,\n };\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n if (options.topP !== undefined) {\n body.top_p = options.topP;\n }\n\n // GPT extensions\n if (options.extensions?.jsonMode?.enabled) {\n body.response_format = { type: 'json_object' };\n }\n\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n type: 'function',\n function: {\n name: t.name,\n description: t.description,\n parameters: t.inputSchema,\n },\n }));\n\n if (options.extensions?.functionCalling?.mode) {\n body.tool_choice = options.extensions.functionCalling.mode;\n }\n }\n\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n yield {\n type: 'error',\n error: {\n type: 'api_error',\n message: `GPT API error: ${response.status} ${response.statusText}`,\n },\n };\n return;\n }\n\n // Yield message start\n yield {\n type: 'message_start',\n message: {\n id: `msg_${Date.now()}`,\n model: options.model,\n role: 'assistant',\n },\n };\n\n const reader = response.body?.getReader();\n if (!reader) return;\n\n const decoder = new TextDecoder();\n let buffer = '';\n const blockIndex = 0;\n let blockStarted = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6);\n if (data === '[DONE]') {\n if (blockStarted) {\n yield { type: 'content_block_stop', index: blockIndex };\n }\n yield { type: 'message_stop' };\n return;\n }\n\n try {\n const parsed = JSON.parse(data);\n const delta = parsed.choices?.[0]?.delta;\n\n if (delta?.content && !blockStarted) {\n blockStarted = true;\n yield {\n type: 'content_block_start',\n index: blockIndex,\n contentBlock: { type: 'text', text: '' },\n };\n }\n\n if (delta?.content) {\n yield {\n type: 'content_block_delta',\n index: blockIndex,\n delta: { type: 'text_delta', text: delta.content },\n };\n }\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n }\n\n async complete(\n messages: Message[],\n options: GPTStreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }> {\n const openaiMessages = messages.map((m) => ({\n role: m.role,\n content:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n }));\n\n const body: Record<string, unknown> = {\n model: options.model,\n max_tokens: options.maxTokens,\n messages: openaiMessages,\n };\n\n if (options.temperature !== undefined) {\n body.temperature = options.temperature;\n }\n\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(\n `GPT API error: ${response.status} ${response.statusText}`\n );\n }\n\n const data = await response.json();\n const choice = data.choices?.[0];\n\n return {\n content: [{ type: 'text', text: choice?.message?.content ?? '' }],\n usage: {\n inputTokens: data.usage?.prompt_tokens ?? 0,\n outputTokens: data.usage?.completion_tokens ?? 0,\n },\n stopReason: choice?.finish_reason ?? 'stop',\n };\n }\n\n async validateConnection(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/v1/models`, {\n headers: { Authorization: `Bearer ${this.apiKey}` },\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async listModels(): Promise<string[]> {\n return [\n 'gpt-4o',\n 'gpt-4o-mini',\n 'gpt-4-turbo',\n 'gpt-4',\n 'gpt-3.5-turbo',\n 'o1',\n 'o1-mini',\n 'o1-preview',\n ];\n }\n}\n\n// =============================================================================\n// Gemini Adapter Stub\n// =============================================================================\n\n/**\n * Extended stream options for Gemini\n */\nexport interface GeminiStreamOptions extends StreamOptions {\n extensions?: GeminiExtensions;\n}\n\n/**\n * Gemini adapter - stub implementation\n */\nexport class GeminiAdapter implements ProviderAdapter {\n readonly id = 'gemini';\n readonly name = 'Google Gemini';\n readonly version = '1.0.0';\n\n readonly extensions: ProviderExtensions = {\n gemini: {\n grounding: { enabled: true, dynamicThreshold: 0.3 },\n multimodal: {\n videoEnabled: true,\n audioEnabled: true,\n maxVideoDurationSec: 60,\n },\n codeExecution: { enabled: true },\n },\n };\n\n private apiKey: string;\n private baseUrl: string;\n\n constructor(config: { apiKey: string; baseUrl?: string }) {\n this.apiKey = config.apiKey;\n this.baseUrl =\n config.baseUrl || 'https://generativelanguage.googleapis.com/v1beta';\n }\n\n supportsExtension(extension: keyof ProviderExtensions): boolean {\n return extension === 'gemini';\n }\n\n async *stream(\n messages: Message[],\n options: GeminiStreamOptions\n ): AsyncIterable<StreamEvent> {\n // Convert messages to Gemini format\n const contents = messages\n .filter((m) => m.role !== 'system')\n .map((m) => ({\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: [\n {\n text:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n },\n ],\n }));\n\n const body: Record<string, unknown> = {\n contents,\n generationConfig: {\n maxOutputTokens: options.maxTokens,\n temperature: options.temperature,\n topP: options.topP,\n },\n };\n\n // System instruction\n const systemMsg = messages.find((m) => m.role === 'system');\n if (systemMsg || options.system) {\n body.systemInstruction = {\n parts: [\n {\n text:\n options.system ||\n (typeof systemMsg?.content === 'string' ? systemMsg.content : ''),\n },\n ],\n };\n }\n\n // Gemini extensions\n if (options.extensions?.grounding?.enabled) {\n body.tools = [\n {\n googleSearchRetrieval: {\n dynamicRetrievalConfig: {\n mode: 'MODE_DYNAMIC',\n dynamicThreshold: options.extensions.grounding.dynamicThreshold,\n },\n },\n },\n ];\n }\n\n if (options.extensions?.codeExecution?.enabled) {\n body.tools = [\n ...((body.tools as unknown[]) || []),\n { codeExecution: {} },\n ];\n }\n\n if (options.extensions?.safetySettings) {\n body.safetySettings = options.extensions.safetySettings;\n }\n\n const url = `${this.baseUrl}/models/${options.model}:streamGenerateContent?key=${this.apiKey}&alt=sse`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n yield {\n type: 'error',\n error: {\n type: 'api_error',\n message: `Gemini API error: ${response.status} ${response.statusText}`,\n },\n };\n return;\n }\n\n yield {\n type: 'message_start',\n message: {\n id: `msg_${Date.now()}`,\n model: options.model,\n role: 'assistant',\n },\n };\n\n const reader = response.body?.getReader();\n if (!reader) return;\n\n const decoder = new TextDecoder();\n let buffer = '';\n const blockIndex = 0;\n let blockStarted = false;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n try {\n const parsed = JSON.parse(line.slice(6));\n const text = parsed.candidates?.[0]?.content?.parts?.[0]?.text;\n\n if (text && !blockStarted) {\n blockStarted = true;\n yield {\n type: 'content_block_start',\n index: blockIndex,\n contentBlock: { type: 'text', text: '' },\n };\n }\n\n if (text) {\n yield {\n type: 'content_block_delta',\n index: blockIndex,\n delta: { type: 'text_delta', text },\n };\n }\n } catch {\n // Skip malformed events\n }\n }\n }\n }\n\n if (blockStarted) {\n yield { type: 'content_block_stop', index: blockIndex };\n }\n yield { type: 'message_stop' };\n }\n\n async complete(\n messages: Message[],\n options: GeminiStreamOptions\n ): Promise<{\n content: AnyContentBlock[];\n usage: { inputTokens: number; outputTokens: number };\n stopReason: string;\n }> {\n const contents = messages\n .filter((m) => m.role !== 'system')\n .map((m) => ({\n role: m.role === 'assistant' ? 'model' : 'user',\n parts: [\n {\n text:\n typeof m.content === 'string'\n ? m.content\n : m.content\n .filter((c): c is TextBlock => c.type === 'text')\n .map((c) => c.text)\n .join(''),\n },\n ],\n }));\n\n const body: Record<string, unknown> = {\n contents,\n generationConfig: {\n maxOutputTokens: options.maxTokens,\n temperature: options.temperature,\n },\n };\n\n const url = `${this.baseUrl}/models/${options.model}:generateContent?key=${this.apiKey}`;\n\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw new Error(\n `Gemini API error: ${response.status} ${response.statusText}`\n );\n }\n\n const data = await response.json();\n const candidate = data.candidates?.[0];\n const text = candidate?.content?.parts?.[0]?.text ?? '';\n\n return {\n content: [{ type: 'text', text }],\n usage: {\n inputTokens: data.usageMetadata?.promptTokenCount ?? 0,\n outputTokens: data.usageMetadata?.candidatesTokenCount ?? 0,\n },\n stopReason: candidate?.finishReason ?? 'STOP',\n };\n }\n\n async validateConnection(): Promise<boolean> {\n try {\n const response = await fetch(`${this.baseUrl}/models?key=${this.apiKey}`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async listModels(): Promise<string[]> {\n return [\n 'gemini-2.0-flash-exp',\n 'gemini-1.5-pro',\n 'gemini-1.5-flash',\n 'gemini-1.5-flash-8b',\n 'gemini-1.0-pro',\n ];\n }\n}\n\n// =============================================================================\n// Provider Registry\n// =============================================================================\n\nexport type ProviderId = 'claude' | 'gpt' | 'gemini';\n\n/**\n * Provider configuration\n */\nexport interface ProviderConfig {\n apiKey: string;\n baseUrl?: string;\n}\n\n/**\n * Create a provider adapter\n */\nexport function createProvider(\n id: ProviderId,\n config: ProviderConfig\n): ProviderAdapter {\n switch (id) {\n case 'claude':\n return new ClaudeAdapter(config);\n case 'gpt':\n return new GPTAdapter(config);\n case 'gemini':\n return new GeminiAdapter(config);\n default:\n throw new Error(`Unknown provider: ${id}`);\n }\n}\n\n/**\n * Provider registry for managing multiple providers\n */\nexport class ProviderRegistry {\n private providers = new Map<string, ProviderAdapter>();\n\n register(adapter: ProviderAdapter): void {\n this.providers.set(adapter.id, adapter);\n }\n\n get(id: string): ProviderAdapter | undefined {\n return this.providers.get(id);\n }\n\n list(): ProviderAdapter[] {\n return Array.from(this.providers.values());\n }\n\n has(id: string): boolean {\n return this.providers.has(id);\n }\n\n /**\n * Find providers that support a specific extension\n */\n findByExtension(extension: keyof ProviderExtensions): ProviderAdapter[] {\n return this.list().filter((p) => p.supportsExtension(extension));\n }\n}\n\n/**\n * Global provider registry\n */\nexport const providerRegistry = new ProviderRegistry();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -14,7 +14,7 @@ class AnthropicLLMProvider {
|
|
|
14
14
|
this.client = new Anthropic({
|
|
15
15
|
apiKey: config.apiKey
|
|
16
16
|
});
|
|
17
|
-
this.model = config.model || "claude-3-haiku-
|
|
17
|
+
this.model = config.model || "claude-3-5-haiku-20241022";
|
|
18
18
|
this.temperature = config.temperature ?? 0.3;
|
|
19
19
|
this.maxRetries = config.maxRetries ?? 2;
|
|
20
20
|
this.timeout = config.timeout ?? 3e4;
|
|
@@ -139,7 +139,7 @@ function createLLMProvider() {
|
|
|
139
139
|
}
|
|
140
140
|
return new AnthropicLLMProvider({
|
|
141
141
|
apiKey,
|
|
142
|
-
model: process.env["ANTHROPIC_MODEL"] || "claude-3-haiku-
|
|
142
|
+
model: process.env["ANTHROPIC_MODEL"] || "claude-3-5-haiku-20241022",
|
|
143
143
|
temperature: parseFloat(process.env["ANTHROPIC_TEMPERATURE"] || "0.3")
|
|
144
144
|
});
|
|
145
145
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/core/retrieval/llm-provider.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * LLM Provider Implementation for Context Retrieval\n * Provides real Anthropic API integration for intelligent context analysis\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport { logger } from '../monitoring/logger.js';\n\n/**\n * LLM provider interface for context analysis\n */\nexport interface LLMProvider {\n analyze(prompt: string, maxTokens: number): Promise<string>;\n}\n\n/**\n * Configuration for Anthropic LLM provider\n */\nexport interface AnthropicProviderConfig {\n apiKey: string;\n model?: string;\n temperature?: number;\n maxRetries?: number;\n timeout?: number;\n}\n\n/**\n * Real Anthropic LLM provider using the official SDK\n */\nexport class AnthropicLLMProvider implements LLMProvider {\n private client: Anthropic;\n private model: string;\n private temperature: number;\n private maxRetries: number;\n private timeout: number;\n\n constructor(config: AnthropicProviderConfig) {\n this.client = new Anthropic({\n apiKey: config.apiKey,\n });\n this.model = config.model || 'claude-3-haiku-
|
|
4
|
+
"sourcesContent": ["/**\n * LLM Provider Implementation for Context Retrieval\n * Provides real Anthropic API integration for intelligent context analysis\n */\n\nimport Anthropic from '@anthropic-ai/sdk';\nimport { logger } from '../monitoring/logger.js';\n\n/**\n * LLM provider interface for context analysis\n */\nexport interface LLMProvider {\n analyze(prompt: string, maxTokens: number): Promise<string>;\n}\n\n/**\n * Configuration for Anthropic LLM provider\n */\nexport interface AnthropicProviderConfig {\n apiKey: string;\n model?: string;\n temperature?: number;\n maxRetries?: number;\n timeout?: number;\n}\n\n/**\n * Real Anthropic LLM provider using the official SDK\n */\nexport class AnthropicLLMProvider implements LLMProvider {\n private client: Anthropic;\n private model: string;\n private temperature: number;\n private maxRetries: number;\n private timeout: number;\n\n constructor(config: AnthropicProviderConfig) {\n this.client = new Anthropic({\n apiKey: config.apiKey,\n });\n this.model = config.model || 'claude-3-5-haiku-20241022';\n this.temperature = config.temperature ?? 0.3;\n this.maxRetries = config.maxRetries ?? 2;\n this.timeout = config.timeout ?? 30000;\n\n logger.info('AnthropicLLMProvider initialized', {\n model: this.model,\n temperature: this.temperature,\n });\n }\n\n /**\n * Analyze a prompt using the Anthropic API\n */\n async analyze(prompt: string, maxTokens: number): Promise<string> {\n const startTime = Date.now();\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const response = await this.makeRequest(prompt, maxTokens);\n\n logger.debug('LLM analysis completed', {\n model: this.model,\n promptLength: prompt.length,\n responseLength: response.length,\n durationMs: Date.now() - startTime,\n attempt,\n });\n\n return response;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Check if retryable\n if (this.isRetryableError(error) && attempt < this.maxRetries) {\n const backoffMs = Math.pow(2, attempt) * 1000;\n logger.warn('LLM request failed, retrying', {\n attempt,\n backoffMs,\n error: lastError.message,\n });\n await this.sleep(backoffMs);\n continue;\n }\n\n break;\n }\n }\n\n logger.error('LLM analysis failed after retries', lastError!);\n throw lastError;\n }\n\n /**\n * Make the actual API request\n */\n private async makeRequest(\n prompt: string,\n maxTokens: number\n ): Promise<string> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await this.client.messages.create({\n model: this.model,\n max_tokens: maxTokens,\n temperature: this.temperature,\n messages: [\n {\n role: 'user',\n content: prompt,\n },\n ],\n });\n\n // Extract text from response\n const textContent = response.content.find((c) => c.type === 'text');\n if (!textContent || textContent.type !== 'text') {\n throw new Error('No text content in response');\n }\n\n return textContent.text;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Check if an error is retryable\n */\n private isRetryableError(error: unknown): boolean {\n if (error instanceof Anthropic.RateLimitError) {\n return true;\n }\n if (error instanceof Anthropic.APIConnectionError) {\n return true;\n }\n if (error instanceof Anthropic.InternalServerError) {\n return true;\n }\n // Timeout errors are retryable\n if (error instanceof Error && error.name === 'AbortError') {\n return true;\n }\n return false;\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/**\n * Local fallback LLM provider - uses heuristic summarization without external APIs\n * This ensures StackMemory works in LOCAL_ONLY mode\n */\nexport class LocalFallbackProvider implements LLMProvider {\n async analyze(prompt: string, maxTokens: number): Promise<string> {\n // Extract content from prompt and create a heuristic summary\n const lines = prompt.split('\\n').filter((l) => l.trim());\n const contentStart = lines.findIndex((l) => l.includes('Content:'));\n\n if (contentStart === -1 || lines.length < 3) {\n return 'Context summary not available (local mode)';\n }\n\n // Extract key information heuristically\n const content = lines.slice(contentStart + 1).join('\\n');\n const sentences = content\n .split(/[.!?]+/)\n .filter((s) => s.trim().length > 10);\n\n // Take first few sentences up to maxTokens (rough approximation: 4 chars = 1 token)\n const maxChars = maxTokens * 4;\n let summary = '';\n for (const sentence of sentences.slice(0, 5)) {\n if (summary.length + sentence.length > maxChars) break;\n summary += sentence.trim() + '. ';\n }\n\n return (\n summary.trim() || 'Context available (use LLM API for detailed analysis)'\n );\n }\n}\n\n/**\n * Factory function to create an LLM provider based on environment\n */\nexport function createLLMProvider(): LLMProvider | undefined {\n // Check for local-only mode\n if (\n process.env['STACKMEMORY_LOCAL'] === 'true' ||\n process.env['LOCAL_ONLY'] === 'true'\n ) {\n logger.info('LOCAL mode - using heuristic summarization');\n return new LocalFallbackProvider();\n }\n\n const apiKey = process.env['ANTHROPIC_API_KEY'];\n\n if (!apiKey) {\n logger.info(\n 'No ANTHROPIC_API_KEY found, LLM retrieval will use heuristics'\n );\n return new LocalFallbackProvider();\n }\n\n return new AnthropicLLMProvider({\n apiKey,\n model: process.env['ANTHROPIC_MODEL'] || 'claude-3-5-haiku-20241022',\n temperature: parseFloat(process.env['ANTHROPIC_TEMPERATURE'] || '0.3'),\n });\n}\n"],
|
|
5
5
|
"mappings": ";;;;AAKA,OAAO,eAAe;AACtB,SAAS,cAAc;AAuBhB,MAAM,qBAA4C;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAAiC;AAC3C,SAAK,SAAS,IAAI,UAAU;AAAA,MAC1B,QAAQ,OAAO;AAAA,IACjB,CAAC;AACD,SAAK,QAAQ,OAAO,SAAS;AAC7B,SAAK,cAAc,OAAO,eAAe;AACzC,SAAK,aAAa,OAAO,cAAc;AACvC,SAAK,UAAU,OAAO,WAAW;AAEjC,WAAO,KAAK,oCAAoC;AAAA,MAC9C,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,IACpB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,QAAgB,WAAoC;AAChE,UAAM,YAAY,KAAK,IAAI;AAC3B,QAAI,YAA0B;AAE9B,aAAS,UAAU,GAAG,WAAW,KAAK,YAAY,WAAW;AAC3D,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,YAAY,QAAQ,SAAS;AAEzD,eAAO,MAAM,0BAA0B;AAAA,UACrC,OAAO,KAAK;AAAA,UACZ,cAAc,OAAO;AAAA,UACrB,gBAAgB,SAAS;AAAA,UACzB,YAAY,KAAK,IAAI,IAAI;AAAA,UACzB;AAAA,QACF,CAAC;AAED,eAAO;AAAA,MACT,SAAS,OAAO;AACd,oBAAY,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAGpE,YAAI,KAAK,iBAAiB,KAAK,KAAK,UAAU,KAAK,YAAY;AAC7D,gBAAM,YAAY,KAAK,IAAI,GAAG,OAAO,IAAI;AACzC,iBAAO,KAAK,gCAAgC;AAAA,YAC1C;AAAA,YACA;AAAA,YACA,OAAO,UAAU;AAAA,UACnB,CAAC;AACD,gBAAM,KAAK,MAAM,SAAS;AAC1B;AAAA,QACF;AAEA;AAAA,MACF;AAAA,IACF;AAEA,WAAO,MAAM,qCAAqC,SAAU;AAC5D,UAAM;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YACZ,QACA,WACiB;AACjB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,KAAK,OAAO;AAEnE,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,OAAO,SAAS,OAAO;AAAA,QACjD,OAAO,KAAK;AAAA,QACZ,YAAY;AAAA,QACZ,aAAa,KAAK;AAAA,QAClB,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF,CAAC;AAGD,YAAM,cAAc,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AAClE,UAAI,CAAC,eAAe,YAAY,SAAS,QAAQ;AAC/C,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC/C;AAEA,aAAO,YAAY;AAAA,IACrB,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,OAAyB;AAChD,QAAI,iBAAiB,UAAU,gBAAgB;AAC7C,aAAO;AAAA,IACT;AACA,QAAI,iBAAiB,UAAU,oBAAoB;AACjD,aAAO;AAAA,IACT;AACA,QAAI,iBAAiB,UAAU,qBAAqB;AAClD,aAAO;AAAA,IACT;AAEA,QAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;AACzD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,MAAM,IAA2B;AACvC,WAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAAA,EACzD;AACF;AAMO,MAAM,sBAA6C;AAAA,EACxD,MAAM,QAAQ,QAAgB,WAAoC;AAEhE,UAAM,QAAQ,OAAO,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC;AACvD,UAAM,eAAe,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC;AAElE,QAAI,iBAAiB,MAAM,MAAM,SAAS,GAAG;AAC3C,aAAO;AAAA,IACT;AAGA,UAAM,UAAU,MAAM,MAAM,eAAe,CAAC,EAAE,KAAK,IAAI;AACvD,UAAM,YAAY,QACf,MAAM,QAAQ,EACd,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE;AAGrC,UAAM,WAAW,YAAY;AAC7B,QAAI,UAAU;AACd,eAAW,YAAY,UAAU,MAAM,GAAG,CAAC,GAAG;AAC5C,UAAI,QAAQ,SAAS,SAAS,SAAS,SAAU;AACjD,iBAAW,SAAS,KAAK,IAAI;AAAA,IAC/B;AAEA,WACE,QAAQ,KAAK,KAAK;AAAA,EAEtB;AACF;AAKO,SAAS,oBAA6C;AAE3D,MACE,QAAQ,IAAI,mBAAmB,MAAM,UACrC,QAAQ,IAAI,YAAY,MAAM,QAC9B;AACA,WAAO,KAAK,4CAA4C;AACxD,WAAO,IAAI,sBAAsB;AAAA,EACnC;AAEA,QAAM,SAAS,QAAQ,IAAI,mBAAmB;AAE9C,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MACL;AAAA,IACF;AACA,WAAO,IAAI,sBAAsB;AAAA,EACnC;AAEA,SAAO,IAAI,qBAAqB;AAAA,IAC9B;AAAA,IACA,OAAO,QAAQ,IAAI,iBAAiB,KAAK;AAAA,IACzC,aAAa,WAAW,QAAQ,IAAI,uBAAuB,KAAK,KAAK;AAAA,EACvE,CAAC;AACH;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/core/retrieval/types.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Types for LLM-Driven Context Retrieval System\n * Implements intelligent context selection based on compressed summaries\n */\n\nimport { Frame, Anchor, Event } from '../context/index.js';\nimport { StackMemoryQuery } from '../query/query-parser.js';\n\n/**\n * Compressed summary of recent session activity\n */\nexport interface RecentSessionSummary {\n /** Recent frames with their key attributes */\n frames: FrameSummary[];\n /** Dominant operations performed */\n dominantOperations: OperationSummary[];\n /** Files that were touched */\n filesTouched: FileSummary[];\n /** Errors encountered */\n errorsEncountered: ErrorSummary[];\n /** Time range covered */\n timeRange: {\n start: number;\n end: number;\n durationMs: number;\n };\n}\n\nexport interface FrameSummary {\n frameId: string;\n name: string;\n type: string;\n depth: number;\n eventCount: number;\n anchorCount: number;\n score: number;\n createdAt: number;\n closedAt?: number;\n digestPreview?: string;\n}\n\nexport interface OperationSummary {\n operation: string;\n count: number;\n lastOccurrence: number;\n successRate: number;\n}\n\nexport interface FileSummary {\n path: string;\n operationCount: number;\n lastModified: number;\n operations: string[];\n}\n\nexport interface ErrorSummary {\n errorType: string;\n message: string;\n count: number;\n lastOccurrence: number;\n resolved: boolean;\n}\n\n/**\n * Historical patterns extracted from memory\n */\nexport interface HistoricalPatterns {\n /** Frame counts by topic */\n topicFrameCounts: Record<string, number>;\n /** Key decisions made */\n keyDecisions: DecisionSummary[];\n /** Recurring issues */\n recurringIssues: IssueSummary[];\n /** Common tool sequences */\n commonToolSequences: ToolSequence[];\n /** Time-based activity patterns */\n activityPatterns: ActivityPattern[];\n}\n\nexport interface DecisionSummary {\n id: string;\n text: string;\n frameId: string;\n timestamp: number;\n impact: 'low' | 'medium' | 'high';\n relatedFiles?: string[];\n}\n\nexport interface IssueSummary {\n issueType: string;\n occurrenceCount: number;\n lastSeen: number;\n resolutionRate: number;\n commonFixes?: string[];\n}\n\nexport interface ToolSequence {\n pattern: string;\n frequency: number;\n avgDuration: number;\n successRate: number;\n}\n\nexport interface ActivityPattern {\n periodType: 'hourly' | 'daily' | 'weekly';\n peakPeriods: string[];\n avgEventsPerPeriod: number;\n}\n\n/**\n * Queryable indices for fast retrieval\n */\nexport interface QueryableIndices {\n /** Index by error type */\n byErrorType: Record<string, string[]>; // errorType -> frameIds\n /** Index by timeframe */\n byTimeframe: Record<string, string[]>; // timeKey -> frameIds\n /** Index by contributor */\n byContributor: Record<string, string[]>; // userId -> frameIds\n /** Index by topic */\n byTopic: Record<string, string[]>; // topic -> frameIds\n /** Index by file */\n byFile: Record<string, string[]>; // filePath -> frameIds\n}\n\n/**\n * Complete compressed summary for LLM analysis\n */\nexport interface CompressedSummary {\n /** Project identifier */\n projectId: string;\n /** Generation timestamp */\n generatedAt: number;\n /** Recent session summary */\n recentSession: RecentSessionSummary;\n /** Historical patterns */\n historicalPatterns: HistoricalPatterns;\n /** Queryable indices */\n queryableIndices: QueryableIndices;\n /** Summary statistics */\n stats: SummaryStats;\n}\n\nexport interface SummaryStats {\n totalFrames: number;\n totalEvents: number;\n totalAnchors: number;\n totalDecisions: number;\n oldestFrame: number;\n newestFrame: number;\n avgFrameDepth: number;\n avgEventsPerFrame: number;\n}\n\n/**\n * LLM analysis request\n */\nexport interface LLMAnalysisRequest {\n /** Current user query */\n currentQuery: string;\n /** Parsed structured query */\n parsedQuery?: StackMemoryQuery;\n /** Compressed summary */\n compressedSummary: CompressedSummary;\n /** Token budget for context */\n tokenBudget: number;\n /** Optional hints for retrieval */\n hints?: RetrievalHints;\n}\n\nexport interface RetrievalHints {\n /** Prefer recent frames */\n preferRecent?: boolean;\n /** Focus on specific topics */\n focusTopics?: string[];\n /** Include error context */\n includeErrors?: boolean;\n /** Include decision history */\n includeDecisions?: boolean;\n /** Minimum relevance score */\n minRelevance?: number;\n}\n\n/**\n * LLM analysis response\n */\nexport interface LLMAnalysisResponse {\n /** Reasoning for the retrieval decision (auditable) */\n reasoning: string;\n /** Frames to retrieve with priority order */\n framesToRetrieve: FrameRetrievalPlan[];\n /** Confidence score (0.0 - 1.0) */\n confidenceScore: number;\n /** Additional context recommendations */\n recommendations: ContextRecommendation[];\n /** Analysis metadata */\n metadata: AnalysisMetadata;\n}\n\nexport interface FrameRetrievalPlan {\n frameId: string;\n priority: number; // 1-10, higher = more important\n reason: string;\n includeEvents: boolean;\n includeAnchors: boolean;\n includeDigest: boolean;\n estimatedTokens: number;\n}\n\nexport interface ContextRecommendation {\n type: 'include' | 'exclude' | 'summarize';\n target: string; // frameId, anchorId, or description\n reason: string;\n impact: 'low' | 'medium' | 'high';\n}\n\nexport interface AnalysisMetadata {\n analysisTimeMs: number;\n summaryTokens: number;\n queryComplexity: 'simple' | 'moderate' | 'complex';\n matchedPatterns: string[];\n fallbackUsed: boolean;\n}\n\n/**\n * Retrieved context result\n */\nexport interface RetrievedContext {\n /** Assembled context string */\n context: string;\n /** Frames included */\n frames: Frame[];\n /** Anchors included */\n anchors: Anchor[];\n /** Events included */\n events: Event[];\n /** LLM analysis that drove retrieval */\n analysis: LLMAnalysisResponse;\n /** Token usage */\n tokenUsage: {\n budget: number;\n used: number;\n remaining: number;\n };\n /** Retrieval metadata */\n metadata: RetrievalMetadata;\n}\n\nexport interface RetrievalMetadata {\n retrievalTimeMs: number;\n cacheHit: boolean;\n framesScanned: number;\n framesIncluded: number;\n compressionRatio: number;\n}\n\n/**\n * Configuration for the retrieval system\n */\nexport interface RetrievalConfig {\n /** Maximum frames to include in summary */\n maxSummaryFrames: number;\n /** Default token budget */\n defaultTokenBudget: number;\n /** Cache TTL in seconds */\n cacheTtlSeconds: number;\n /** Minimum confidence to use LLM suggestions */\n minConfidenceThreshold: number;\n /** Enable fallback to heuristic retrieval */\n enableFallback: boolean;\n /** LLM provider configuration */\n llmConfig: {\n provider: 'anthropic' | 'openai' | 'local';\n model: string;\n maxTokens: number;\n temperature: number;\n };\n}\n\nexport const DEFAULT_RETRIEVAL_CONFIG: RetrievalConfig = {\n maxSummaryFrames: 15,\n defaultTokenBudget: 8000,\n cacheTtlSeconds: 300,\n minConfidenceThreshold: 0.6,\n enableFallback: true,\n llmConfig: {\n provider: 'anthropic',\n model: 'claude-3-haiku-
|
|
4
|
+
"sourcesContent": ["/**\n * Types for LLM-Driven Context Retrieval System\n * Implements intelligent context selection based on compressed summaries\n */\n\nimport { Frame, Anchor, Event } from '../context/index.js';\nimport { StackMemoryQuery } from '../query/query-parser.js';\n\n/**\n * Compressed summary of recent session activity\n */\nexport interface RecentSessionSummary {\n /** Recent frames with their key attributes */\n frames: FrameSummary[];\n /** Dominant operations performed */\n dominantOperations: OperationSummary[];\n /** Files that were touched */\n filesTouched: FileSummary[];\n /** Errors encountered */\n errorsEncountered: ErrorSummary[];\n /** Time range covered */\n timeRange: {\n start: number;\n end: number;\n durationMs: number;\n };\n}\n\nexport interface FrameSummary {\n frameId: string;\n name: string;\n type: string;\n depth: number;\n eventCount: number;\n anchorCount: number;\n score: number;\n createdAt: number;\n closedAt?: number;\n digestPreview?: string;\n}\n\nexport interface OperationSummary {\n operation: string;\n count: number;\n lastOccurrence: number;\n successRate: number;\n}\n\nexport interface FileSummary {\n path: string;\n operationCount: number;\n lastModified: number;\n operations: string[];\n}\n\nexport interface ErrorSummary {\n errorType: string;\n message: string;\n count: number;\n lastOccurrence: number;\n resolved: boolean;\n}\n\n/**\n * Historical patterns extracted from memory\n */\nexport interface HistoricalPatterns {\n /** Frame counts by topic */\n topicFrameCounts: Record<string, number>;\n /** Key decisions made */\n keyDecisions: DecisionSummary[];\n /** Recurring issues */\n recurringIssues: IssueSummary[];\n /** Common tool sequences */\n commonToolSequences: ToolSequence[];\n /** Time-based activity patterns */\n activityPatterns: ActivityPattern[];\n}\n\nexport interface DecisionSummary {\n id: string;\n text: string;\n frameId: string;\n timestamp: number;\n impact: 'low' | 'medium' | 'high';\n relatedFiles?: string[];\n}\n\nexport interface IssueSummary {\n issueType: string;\n occurrenceCount: number;\n lastSeen: number;\n resolutionRate: number;\n commonFixes?: string[];\n}\n\nexport interface ToolSequence {\n pattern: string;\n frequency: number;\n avgDuration: number;\n successRate: number;\n}\n\nexport interface ActivityPattern {\n periodType: 'hourly' | 'daily' | 'weekly';\n peakPeriods: string[];\n avgEventsPerPeriod: number;\n}\n\n/**\n * Queryable indices for fast retrieval\n */\nexport interface QueryableIndices {\n /** Index by error type */\n byErrorType: Record<string, string[]>; // errorType -> frameIds\n /** Index by timeframe */\n byTimeframe: Record<string, string[]>; // timeKey -> frameIds\n /** Index by contributor */\n byContributor: Record<string, string[]>; // userId -> frameIds\n /** Index by topic */\n byTopic: Record<string, string[]>; // topic -> frameIds\n /** Index by file */\n byFile: Record<string, string[]>; // filePath -> frameIds\n}\n\n/**\n * Complete compressed summary for LLM analysis\n */\nexport interface CompressedSummary {\n /** Project identifier */\n projectId: string;\n /** Generation timestamp */\n generatedAt: number;\n /** Recent session summary */\n recentSession: RecentSessionSummary;\n /** Historical patterns */\n historicalPatterns: HistoricalPatterns;\n /** Queryable indices */\n queryableIndices: QueryableIndices;\n /** Summary statistics */\n stats: SummaryStats;\n}\n\nexport interface SummaryStats {\n totalFrames: number;\n totalEvents: number;\n totalAnchors: number;\n totalDecisions: number;\n oldestFrame: number;\n newestFrame: number;\n avgFrameDepth: number;\n avgEventsPerFrame: number;\n}\n\n/**\n * LLM analysis request\n */\nexport interface LLMAnalysisRequest {\n /** Current user query */\n currentQuery: string;\n /** Parsed structured query */\n parsedQuery?: StackMemoryQuery;\n /** Compressed summary */\n compressedSummary: CompressedSummary;\n /** Token budget for context */\n tokenBudget: number;\n /** Optional hints for retrieval */\n hints?: RetrievalHints;\n}\n\nexport interface RetrievalHints {\n /** Prefer recent frames */\n preferRecent?: boolean;\n /** Focus on specific topics */\n focusTopics?: string[];\n /** Include error context */\n includeErrors?: boolean;\n /** Include decision history */\n includeDecisions?: boolean;\n /** Minimum relevance score */\n minRelevance?: number;\n}\n\n/**\n * LLM analysis response\n */\nexport interface LLMAnalysisResponse {\n /** Reasoning for the retrieval decision (auditable) */\n reasoning: string;\n /** Frames to retrieve with priority order */\n framesToRetrieve: FrameRetrievalPlan[];\n /** Confidence score (0.0 - 1.0) */\n confidenceScore: number;\n /** Additional context recommendations */\n recommendations: ContextRecommendation[];\n /** Analysis metadata */\n metadata: AnalysisMetadata;\n}\n\nexport interface FrameRetrievalPlan {\n frameId: string;\n priority: number; // 1-10, higher = more important\n reason: string;\n includeEvents: boolean;\n includeAnchors: boolean;\n includeDigest: boolean;\n estimatedTokens: number;\n}\n\nexport interface ContextRecommendation {\n type: 'include' | 'exclude' | 'summarize';\n target: string; // frameId, anchorId, or description\n reason: string;\n impact: 'low' | 'medium' | 'high';\n}\n\nexport interface AnalysisMetadata {\n analysisTimeMs: number;\n summaryTokens: number;\n queryComplexity: 'simple' | 'moderate' | 'complex';\n matchedPatterns: string[];\n fallbackUsed: boolean;\n}\n\n/**\n * Retrieved context result\n */\nexport interface RetrievedContext {\n /** Assembled context string */\n context: string;\n /** Frames included */\n frames: Frame[];\n /** Anchors included */\n anchors: Anchor[];\n /** Events included */\n events: Event[];\n /** LLM analysis that drove retrieval */\n analysis: LLMAnalysisResponse;\n /** Token usage */\n tokenUsage: {\n budget: number;\n used: number;\n remaining: number;\n };\n /** Retrieval metadata */\n metadata: RetrievalMetadata;\n}\n\nexport interface RetrievalMetadata {\n retrievalTimeMs: number;\n cacheHit: boolean;\n framesScanned: number;\n framesIncluded: number;\n compressionRatio: number;\n}\n\n/**\n * Configuration for the retrieval system\n */\nexport interface RetrievalConfig {\n /** Maximum frames to include in summary */\n maxSummaryFrames: number;\n /** Default token budget */\n defaultTokenBudget: number;\n /** Cache TTL in seconds */\n cacheTtlSeconds: number;\n /** Minimum confidence to use LLM suggestions */\n minConfidenceThreshold: number;\n /** Enable fallback to heuristic retrieval */\n enableFallback: boolean;\n /** LLM provider configuration */\n llmConfig: {\n provider: 'anthropic' | 'openai' | 'local';\n model: string;\n maxTokens: number;\n temperature: number;\n };\n}\n\nexport const DEFAULT_RETRIEVAL_CONFIG: RetrievalConfig = {\n maxSummaryFrames: 15,\n defaultTokenBudget: 8000,\n cacheTtlSeconds: 300,\n minConfidenceThreshold: 0.6,\n enableFallback: true,\n llmConfig: {\n provider: 'anthropic',\n model: 'claude-3-5-haiku-20241022',\n maxTokens: 1024,\n temperature: 0.3,\n },\n};\n"],
|
|
5
5
|
"mappings": ";;;;AAuRO,MAAM,2BAA4C;AAAA,EACvD,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,wBAAwB;AAAA,EACxB,gBAAgB;AAAA,EAChB,WAAW;AAAA,IACT,UAAU;AAAA,IACV,OAAO;AAAA,IACP,WAAW;AAAA,IACX,aAAa;AAAA,EACf;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,7 +9,12 @@ import { SweepStateWatcher } from "./state-watcher.js";
|
|
|
9
9
|
import { StatusBar } from "./status-bar.js";
|
|
10
10
|
import { TabInterceptor } from "./tab-interceptor.js";
|
|
11
11
|
const HOME = process.env["HOME"] || "/tmp";
|
|
12
|
-
|
|
12
|
+
function getSweepDir() {
|
|
13
|
+
return process.env["SWEEP_STATE_DIR"] || join(HOME, ".stackmemory");
|
|
14
|
+
}
|
|
15
|
+
function getSweepPath(filename) {
|
|
16
|
+
return join(getSweepDir(), filename);
|
|
17
|
+
}
|
|
13
18
|
const ALT_SCREEN_ENTER = "\x1B[?1049h";
|
|
14
19
|
const ALT_SCREEN_EXIT = "\x1B[?1049l";
|
|
15
20
|
class PtyWrapper {
|
|
@@ -24,7 +29,7 @@ class PtyWrapper {
|
|
|
24
29
|
this.config = {
|
|
25
30
|
claudeBin: config.claudeBin || this.findClaude(),
|
|
26
31
|
claudeArgs: config.claudeArgs || [],
|
|
27
|
-
stateFile: config.stateFile ||
|
|
32
|
+
stateFile: config.stateFile || getSweepPath("sweep-state.json")
|
|
28
33
|
};
|
|
29
34
|
this.stateWatcher = new SweepStateWatcher(this.config.stateFile);
|
|
30
35
|
this.statusBar = new StatusBar();
|
|
@@ -35,6 +40,10 @@ class PtyWrapper {
|
|
|
35
40
|
});
|
|
36
41
|
}
|
|
37
42
|
async start() {
|
|
43
|
+
const sweepDir = getSweepDir();
|
|
44
|
+
if (!existsSync(sweepDir)) {
|
|
45
|
+
mkdirSync(sweepDir, { recursive: true });
|
|
46
|
+
}
|
|
38
47
|
let pty;
|
|
39
48
|
try {
|
|
40
49
|
pty = await import("node-pty");
|
|
@@ -109,12 +118,13 @@ class PtyWrapper {
|
|
|
109
118
|
}
|
|
110
119
|
acceptPrediction() {
|
|
111
120
|
if (!this.currentPrediction || !this.ptyProcess) return;
|
|
112
|
-
const dir =
|
|
121
|
+
const dir = getSweepDir();
|
|
113
122
|
if (!existsSync(dir)) {
|
|
114
123
|
mkdirSync(dir, { recursive: true });
|
|
115
124
|
}
|
|
125
|
+
const pendingFile = getSweepPath("sweep-pending.json");
|
|
116
126
|
writeFileSync(
|
|
117
|
-
|
|
127
|
+
pendingFile,
|
|
118
128
|
JSON.stringify(
|
|
119
129
|
{
|
|
120
130
|
file_path: this.currentPrediction.file_path,
|
|
@@ -125,7 +135,7 @@ class PtyWrapper {
|
|
|
125
135
|
2
|
|
126
136
|
)
|
|
127
137
|
);
|
|
128
|
-
const prompt = `Apply the Sweep prediction from ${
|
|
138
|
+
const prompt = `Apply the Sweep prediction from ${pendingFile}
|
|
129
139
|
`;
|
|
130
140
|
this.ptyProcess.write(prompt);
|
|
131
141
|
this.dismissPrediction();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/features/sweep/pty-wrapper.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Sweep PTY Wrapper\n *\n * Wraps Claude Code in a pseudo-terminal to add a Sweep prediction\n * status bar at the bottom of the terminal. Predictions from the\n * PostToolUse hook are displayed via the status bar. Tab to accept,\n * Esc to dismiss.\n */\n\nimport { join } from 'path';\nimport { writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { SweepStateWatcher, type PredictionEvent } from './state-watcher.js';\nimport { StatusBar } from './status-bar.js';\nimport { TabInterceptor } from './tab-interceptor.js';\n\nconst HOME = process.env['HOME'] || '/tmp';\
|
|
5
|
-
"mappings": ";;;;AASA,SAAS,YAAY;AACrB,SAAS,eAAe,YAAY,iBAAiB;AACrD,SAAS,gBAAgB;AACzB,SAAS,yBAA+C;AACxD,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAE/B,MAAM,OAAO,QAAQ,IAAI,MAAM,KAAK;
|
|
4
|
+
"sourcesContent": ["/**\n * Sweep PTY Wrapper\n *\n * Wraps Claude Code in a pseudo-terminal to add a Sweep prediction\n * status bar at the bottom of the terminal. Predictions from the\n * PostToolUse hook are displayed via the status bar. Tab to accept,\n * Esc to dismiss.\n */\n\nimport { join } from 'path';\nimport { writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { SweepStateWatcher, type PredictionEvent } from './state-watcher.js';\nimport { StatusBar } from './status-bar.js';\nimport { TabInterceptor } from './tab-interceptor.js';\n\nconst HOME = process.env['HOME'] || '/tmp';\n\nfunction getSweepDir(): string {\n return process.env['SWEEP_STATE_DIR'] || join(HOME, '.stackmemory');\n}\n\nfunction getSweepPath(filename: string): string {\n return join(getSweepDir(), filename);\n}\n\n// Alt screen buffer detection\nconst ALT_SCREEN_ENTER = '\\x1b[?1049h';\nconst ALT_SCREEN_EXIT = '\\x1b[?1049l';\n\nexport interface PtyWrapperConfig {\n claudeBin?: string;\n claudeArgs?: string[];\n stateFile?: string;\n}\n\n// Minimal interface for node-pty process to avoid compile-time dep\ninterface PtyProcess {\n write(data: string): void;\n resize(cols: number, rows: number): void;\n onData(cb: (data: string) => void): void;\n onExit(cb: (e: { exitCode: number }) => void): void;\n kill(): void;\n}\n\nexport class PtyWrapper {\n private config: Required<PtyWrapperConfig>;\n private stateWatcher: SweepStateWatcher;\n private statusBar: StatusBar;\n private tabInterceptor: TabInterceptor;\n private currentPrediction: PredictionEvent | null = null;\n private inAltScreen = false;\n private ptyProcess: PtyProcess | null = null;\n\n constructor(config: PtyWrapperConfig = {}) {\n this.config = {\n claudeBin: config.claudeBin || this.findClaude(),\n claudeArgs: config.claudeArgs || [],\n stateFile: config.stateFile || getSweepPath('sweep-state.json'),\n };\n\n this.stateWatcher = new SweepStateWatcher(this.config.stateFile);\n this.statusBar = new StatusBar();\n this.tabInterceptor = new TabInterceptor({\n onAccept: () => this.acceptPrediction(),\n onDismiss: () => this.dismissPrediction(),\n onPassthrough: (data) => this.ptyProcess?.write(data.toString('utf-8')),\n });\n }\n\n async start(): Promise<void> {\n // Ensure the sweep state directory exists\n const sweepDir = getSweepDir();\n if (!existsSync(sweepDir)) {\n mkdirSync(sweepDir, { recursive: true });\n }\n\n // Dynamic import for optional dependency\n let pty: typeof import('node-pty');\n try {\n pty = await import('node-pty');\n } catch {\n throw new Error(\n 'node-pty is required for the PTY wrapper.\\n' +\n 'Install with: npm install node-pty'\n );\n }\n\n const cols = process.stdout.columns || 80;\n const rows = process.stdout.rows || 24;\n\n // Filter undefined values from env\n const env: Record<string, string> = {};\n for (const [k, v] of Object.entries(process.env)) {\n if (v !== undefined) env[k] = v;\n }\n\n // Spawn Claude Code in a PTY with 1 row reserved for status bar\n this.ptyProcess = pty.spawn(this.config.claudeBin, this.config.claudeArgs, {\n name: process.env['TERM'] || 'xterm-256color',\n cols,\n rows: rows - 1,\n cwd: process.cwd(),\n env,\n }) as PtyProcess;\n\n // Set raw mode on stdin\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(true);\n }\n process.stdin.resume();\n\n // PTY stdout -> parent stdout (transparent passthrough)\n this.ptyProcess.onData((data: string) => {\n // Detect alt screen buffer transitions\n if (data.includes(ALT_SCREEN_ENTER)) {\n this.inAltScreen = true;\n this.statusBar.hide();\n }\n if (data.includes(ALT_SCREEN_EXIT)) {\n this.inAltScreen = false;\n }\n\n process.stdout.write(data);\n });\n\n // Parent stdin -> tab interceptor -> PTY\n process.stdin.on('data', (data: Buffer) => {\n this.tabInterceptor.process(data);\n });\n\n // State watcher -> status bar\n this.stateWatcher.on('loading', () => {\n if (!this.inAltScreen) {\n this.statusBar.showLoading();\n }\n });\n\n this.stateWatcher.on('prediction', (event: PredictionEvent) => {\n this.currentPrediction = event;\n this.tabInterceptor.setPredictionActive(true);\n if (!this.inAltScreen) {\n this.statusBar.show(\n event.prediction,\n event.file_path,\n event.latency_ms\n );\n }\n });\n\n this.stateWatcher.start();\n\n // Handle terminal resize\n process.stdout.on('resize', () => {\n const newCols = process.stdout.columns || 80;\n const newRows = process.stdout.rows || 24;\n this.ptyProcess?.resize(newCols, newRows - 1);\n this.statusBar.resize(newRows, newCols);\n });\n\n // Handle PTY exit\n this.ptyProcess.onExit(({ exitCode }) => {\n this.cleanup();\n process.exit(exitCode);\n });\n\n // Handle signals\n const onSignal = () => {\n this.cleanup();\n process.exit(0);\n };\n process.on('SIGINT', onSignal);\n process.on('SIGTERM', onSignal);\n }\n\n private acceptPrediction(): void {\n if (!this.currentPrediction || !this.ptyProcess) return;\n\n // Write prediction to pending file for Claude to read\n const dir = getSweepDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n const pendingFile = getSweepPath('sweep-pending.json');\n writeFileSync(\n pendingFile,\n JSON.stringify(\n {\n file_path: this.currentPrediction.file_path,\n predicted_content: this.currentPrediction.prediction,\n timestamp: Date.now(),\n },\n null,\n 2\n )\n );\n\n // Inject acceptance prompt into PTY stdin.\n // SAFETY: pendingFile is derived from env or a constant path, not\n // arbitrary user input. The prompt is written to Claude Code's input,\n // which interprets it as a user message, not as a shell command.\n const prompt = `Apply the Sweep prediction from ${pendingFile}\\n`;\n this.ptyProcess.write(prompt);\n\n this.dismissPrediction();\n }\n\n private dismissPrediction(): void {\n this.currentPrediction = null;\n this.tabInterceptor.setPredictionActive(false);\n this.statusBar.hide();\n }\n\n private cleanup(): void {\n this.stateWatcher.stop();\n this.statusBar.hide();\n\n if (process.stdin.isTTY) {\n process.stdin.setRawMode(false);\n }\n process.stdin.pause();\n }\n\n private findClaude(): string {\n // Check PATH first via which\n try {\n const resolved = execSync('which claude', { encoding: 'utf-8' }).trim();\n if (resolved) return resolved;\n } catch {\n // Not on PATH\n }\n\n // Check known locations\n const candidates = [\n join(HOME, '.bun', 'bin', 'claude'),\n '/usr/local/bin/claude',\n '/opt/homebrew/bin/claude',\n ];\n\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n\n return 'claude';\n }\n}\n\n/**\n * Launch the PTY wrapper\n */\nexport async function launchWrapper(config?: PtyWrapperConfig): Promise<void> {\n const wrapper = new PtyWrapper(config);\n await wrapper.start();\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
function isTmuxAvailable() {
|
|
7
|
+
try {
|
|
8
|
+
execSync("which tmux", { stdio: "ignore" });
|
|
9
|
+
return true;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function createTmuxSession(name, paneCount) {
|
|
15
|
+
execSync(`tmux new-session -d -s ${name}`, { stdio: "ignore" });
|
|
16
|
+
for (let i = 1; i < paneCount; i++) {
|
|
17
|
+
execSync(`tmux split-window -t ${name}`, { stdio: "ignore" });
|
|
18
|
+
execSync(`tmux select-layout -t ${name} tiled`, { stdio: "ignore" });
|
|
19
|
+
}
|
|
20
|
+
execSync(`tmux select-layout -t ${name} tiled`, { stdio: "ignore" });
|
|
21
|
+
}
|
|
22
|
+
function sendToPane(session, pane, command) {
|
|
23
|
+
execSync(
|
|
24
|
+
`tmux send-keys -t ${session}:${pane} ${shellEscape(command)} Enter`,
|
|
25
|
+
{
|
|
26
|
+
stdio: "ignore"
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function killTmuxSession(name) {
|
|
31
|
+
execSync(`tmux kill-session -t ${name}`, { stdio: "ignore" });
|
|
32
|
+
}
|
|
33
|
+
function attachToSession(name) {
|
|
34
|
+
execSync(`tmux attach-session -t ${name}`, { stdio: "inherit" });
|
|
35
|
+
}
|
|
36
|
+
function listPanes(session) {
|
|
37
|
+
try {
|
|
38
|
+
const output = execSync(
|
|
39
|
+
`tmux list-panes -t ${session} -F "#{pane_index}"`,
|
|
40
|
+
{ encoding: "utf-8" }
|
|
41
|
+
);
|
|
42
|
+
return output.trim().split("\n").filter(Boolean);
|
|
43
|
+
} catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function sendCtrlC(session, pane) {
|
|
48
|
+
execSync(`tmux send-keys -t ${session}:${pane} C-c`, { stdio: "ignore" });
|
|
49
|
+
}
|
|
50
|
+
function sessionExists(name) {
|
|
51
|
+
try {
|
|
52
|
+
execSync(`tmux has-session -t ${name}`, { stdio: "ignore" });
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function shellEscape(cmd) {
|
|
59
|
+
return "'" + cmd.replace(/'/g, "'\\''") + "'";
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
attachToSession,
|
|
63
|
+
createTmuxSession,
|
|
64
|
+
isTmuxAvailable,
|
|
65
|
+
killTmuxSession,
|
|
66
|
+
listPanes,
|
|
67
|
+
sendCtrlC,
|
|
68
|
+
sendToPane,
|
|
69
|
+
sessionExists
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=tmux-manager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/features/workers/tmux-manager.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Tmux Manager\n *\n * Creates and manages tmux sessions for parallel Claude workers.\n * Each pane runs an isolated claude-sm instance.\n */\n\nimport { execSync } from 'child_process';\n\nexport function isTmuxAvailable(): boolean {\n try {\n execSync('which tmux', { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\nexport function createTmuxSession(name: string, paneCount: number): void {\n // Create a detached session with the first pane\n execSync(`tmux new-session -d -s ${name}`, { stdio: 'ignore' });\n\n // Add remaining panes\n for (let i = 1; i < paneCount; i++) {\n execSync(`tmux split-window -t ${name}`, { stdio: 'ignore' });\n // Rebalance after each split to prevent \"no space\" errors\n execSync(`tmux select-layout -t ${name} tiled`, { stdio: 'ignore' });\n }\n\n // Final tiled layout\n execSync(`tmux select-layout -t ${name} tiled`, { stdio: 'ignore' });\n}\n\nexport function sendToPane(\n session: string,\n pane: string,\n command: string\n): void {\n execSync(\n `tmux send-keys -t ${session}:${pane} ${shellEscape(command)} Enter`,\n {\n stdio: 'ignore',\n }\n );\n}\n\nexport function killTmuxSession(name: string): void {\n execSync(`tmux kill-session -t ${name}`, { stdio: 'ignore' });\n}\n\nexport function attachToSession(name: string): void {\n execSync(`tmux attach-session -t ${name}`, { stdio: 'inherit' });\n}\n\nexport function listPanes(session: string): string[] {\n try {\n const output = execSync(\n `tmux list-panes -t ${session} -F \"#{pane_index}\"`,\n { encoding: 'utf-8' }\n );\n return output.trim().split('\\n').filter(Boolean);\n } catch {\n return [];\n }\n}\n\nexport function sendCtrlC(session: string, pane: string): void {\n execSync(`tmux send-keys -t ${session}:${pane} C-c`, { stdio: 'ignore' });\n}\n\nexport function sessionExists(name: string): boolean {\n try {\n execSync(`tmux has-session -t ${name}`, { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction shellEscape(cmd: string): string {\n // Wrap in single quotes, escaping existing single quotes\n return \"'\" + cmd.replace(/'/g, \"'\\\\''\") + \"'\";\n}\n"],
|
|
5
|
+
"mappings": ";;;;AAOA,SAAS,gBAAgB;AAElB,SAAS,kBAA2B;AACzC,MAAI;AACF,aAAS,cAAc,EAAE,OAAO,SAAS,CAAC;AAC1C,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,kBAAkB,MAAc,WAAyB;AAEvE,WAAS,0BAA0B,IAAI,IAAI,EAAE,OAAO,SAAS,CAAC;AAG9D,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,aAAS,wBAAwB,IAAI,IAAI,EAAE,OAAO,SAAS,CAAC;AAE5D,aAAS,yBAAyB,IAAI,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,EACrE;AAGA,WAAS,yBAAyB,IAAI,UAAU,EAAE,OAAO,SAAS,CAAC;AACrE;AAEO,SAAS,WACd,SACA,MACA,SACM;AACN;AAAA,IACE,qBAAqB,OAAO,IAAI,IAAI,IAAI,YAAY,OAAO,CAAC;AAAA,IAC5D;AAAA,MACE,OAAO;AAAA,IACT;AAAA,EACF;AACF;AAEO,SAAS,gBAAgB,MAAoB;AAClD,WAAS,wBAAwB,IAAI,IAAI,EAAE,OAAO,SAAS,CAAC;AAC9D;AAEO,SAAS,gBAAgB,MAAoB;AAClD,WAAS,0BAA0B,IAAI,IAAI,EAAE,OAAO,UAAU,CAAC;AACjE;AAEO,SAAS,UAAU,SAA2B;AACnD,MAAI;AACF,UAAM,SAAS;AAAA,MACb,sBAAsB,OAAO;AAAA,MAC7B,EAAE,UAAU,QAAQ;AAAA,IACtB;AACA,WAAO,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AAAA,EACjD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,UAAU,SAAiB,MAAoB;AAC7D,WAAS,qBAAqB,OAAO,IAAI,IAAI,QAAQ,EAAE,OAAO,SAAS,CAAC;AAC1E;AAEO,SAAS,cAAc,MAAuB;AACnD,MAAI;AACF,aAAS,uBAAuB,IAAI,IAAI,EAAE,OAAO,SAAS,CAAC;AAC3D,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,KAAqB;AAExC,SAAO,MAAM,IAAI,QAAQ,MAAM,OAAO,IAAI;AAC5C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
function workersDir() {
|
|
9
|
+
return join(homedir(), ".stackmemory", "workers");
|
|
10
|
+
}
|
|
11
|
+
function registryFile() {
|
|
12
|
+
return join(workersDir(), "registry.json");
|
|
13
|
+
}
|
|
14
|
+
function ensureWorkerStateDir(workerId) {
|
|
15
|
+
const dir = join(workersDir(), workerId);
|
|
16
|
+
if (!existsSync(dir)) {
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
return dir;
|
|
20
|
+
}
|
|
21
|
+
function saveRegistry(session) {
|
|
22
|
+
const dir = workersDir();
|
|
23
|
+
if (!existsSync(dir)) {
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
writeFileSync(registryFile(), JSON.stringify(session, null, 2));
|
|
27
|
+
}
|
|
28
|
+
function loadRegistry() {
|
|
29
|
+
if (!existsSync(registryFile())) return null;
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(readFileSync(registryFile(), "utf-8"));
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function clearRegistry() {
|
|
37
|
+
const file = registryFile();
|
|
38
|
+
if (existsSync(file)) {
|
|
39
|
+
rmSync(file, { force: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function getWorkersDir() {
|
|
43
|
+
return workersDir();
|
|
44
|
+
}
|
|
45
|
+
export {
|
|
46
|
+
clearRegistry,
|
|
47
|
+
ensureWorkerStateDir,
|
|
48
|
+
getWorkersDir,
|
|
49
|
+
loadRegistry,
|
|
50
|
+
saveRegistry
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=worker-registry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/features/workers/worker-registry.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Worker Registry\n *\n * Tracks parallel Claude worker sessions spawned via tmux.\n * Each worker gets an isolated state directory for Sweep predictions.\n */\n\nimport { join } from 'path';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';\nimport { homedir } from 'os';\n\nfunction workersDir(): string {\n return join(homedir(), '.stackmemory', 'workers');\n}\n\nfunction registryFile(): string {\n return join(workersDir(), 'registry.json');\n}\n\nexport interface WorkerEntry {\n id: string;\n pane: string;\n pid?: number;\n task?: string;\n cwd: string;\n startedAt: string;\n stateDir: string;\n}\n\nexport interface WorkerSession {\n sessionName: string;\n workers: WorkerEntry[];\n createdAt: string;\n}\n\nexport function ensureWorkerStateDir(workerId: string): string {\n const dir = join(workersDir(), workerId);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\nexport function saveRegistry(session: WorkerSession): void {\n const dir = workersDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(registryFile(), JSON.stringify(session, null, 2));\n}\n\nexport function loadRegistry(): WorkerSession | null {\n if (!existsSync(registryFile())) return null;\n try {\n return JSON.parse(readFileSync(registryFile(), 'utf-8')) as WorkerSession;\n } catch {\n return null;\n }\n}\n\nexport function clearRegistry(): void {\n const file = registryFile();\n if (existsSync(file)) {\n rmSync(file, { force: true });\n }\n}\n\nexport function getWorkersDir(): string {\n return workersDir();\n}\n"],
|
|
5
|
+
"mappings": ";;;;AAOA,SAAS,YAAY;AACrB,SAAS,YAAY,WAAW,cAAc,eAAe,cAAc;AAC3E,SAAS,eAAe;AAExB,SAAS,aAAqB;AAC5B,SAAO,KAAK,QAAQ,GAAG,gBAAgB,SAAS;AAClD;AAEA,SAAS,eAAuB;AAC9B,SAAO,KAAK,WAAW,GAAG,eAAe;AAC3C;AAkBO,SAAS,qBAAqB,UAA0B;AAC7D,QAAM,MAAM,KAAK,WAAW,GAAG,QAAQ;AACvC,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AACA,SAAO;AACT;AAEO,SAAS,aAAa,SAA8B;AACzD,QAAM,MAAM,WAAW;AACvB,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AACA,gBAAc,aAAa,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAChE;AAEO,SAAS,eAAqC;AACnD,MAAI,CAAC,WAAW,aAAa,CAAC,EAAG,QAAO;AACxC,MAAI;AACF,WAAO,KAAK,MAAM,aAAa,aAAa,GAAG,OAAO,CAAC;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,gBAAsB;AACpC,QAAM,OAAO,aAAa;AAC1B,MAAI,WAAW,IAAI,GAAG;AACpB,WAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,EAC9B;AACF;AAEO,SAAS,gBAAwB;AACtC,SAAO,WAAW;AACpB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|