@stackmemoryai/stackmemory 0.5.31 → 0.5.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/dist/agents/core/agent-task-manager.js.map +1 -1
  2. package/dist/cli/claude-sm.js +199 -16
  3. package/dist/cli/claude-sm.js.map +2 -2
  4. package/dist/cli/commands/clear.js +1 -1
  5. package/dist/cli/commands/clear.js.map +1 -1
  6. package/dist/cli/commands/context.js +1 -12
  7. package/dist/cli/commands/context.js.map +2 -2
  8. package/dist/cli/commands/dashboard.js.map +1 -1
  9. package/dist/cli/commands/discovery.js +1 -1
  10. package/dist/cli/commands/discovery.js.map +1 -1
  11. package/dist/cli/commands/handoff.js +1 -1
  12. package/dist/cli/commands/handoff.js.map +1 -1
  13. package/dist/cli/commands/linear.js +1 -14
  14. package/dist/cli/commands/linear.js.map +2 -2
  15. package/dist/cli/commands/login.js +32 -10
  16. package/dist/cli/commands/login.js.map +2 -2
  17. package/dist/cli/commands/migrate.js +80 -22
  18. package/dist/cli/commands/migrate.js.map +2 -2
  19. package/dist/cli/commands/model.js +533 -0
  20. package/dist/cli/commands/model.js.map +7 -0
  21. package/dist/cli/commands/monitor.js +1 -1
  22. package/dist/cli/commands/monitor.js.map +1 -1
  23. package/dist/cli/commands/quality.js +1 -1
  24. package/dist/cli/commands/quality.js.map +1 -1
  25. package/dist/cli/commands/ralph.js +93 -28
  26. package/dist/cli/commands/ralph.js.map +2 -2
  27. package/dist/cli/commands/service.js +10 -3
  28. package/dist/cli/commands/service.js.map +2 -2
  29. package/dist/cli/commands/skills.js +61 -11
  30. package/dist/cli/commands/skills.js.map +2 -2
  31. package/dist/cli/commands/sms-notify.js +342 -22
  32. package/dist/cli/commands/sms-notify.js.map +3 -3
  33. package/dist/cli/commands/workflow.js +1 -1
  34. package/dist/cli/commands/workflow.js.map +1 -1
  35. package/dist/cli/commands/worktree.js +1 -1
  36. package/dist/cli/commands/worktree.js.map +1 -1
  37. package/dist/cli/index.js +3 -1
  38. package/dist/cli/index.js.map +2 -2
  39. package/dist/core/context/auto-context.js.map +1 -1
  40. package/dist/core/context/compaction-handler.js.map +2 -2
  41. package/dist/core/context/context-bridge.js.map +2 -2
  42. package/dist/core/context/dual-stack-manager.js +24 -8
  43. package/dist/core/context/dual-stack-manager.js.map +2 -2
  44. package/dist/core/context/enhanced-rehydration.js.map +1 -1
  45. package/dist/core/context/frame-database.js +41 -5
  46. package/dist/core/context/frame-database.js.map +2 -2
  47. package/dist/core/context/frame-digest.js +6 -1
  48. package/dist/core/context/frame-digest.js.map +2 -2
  49. package/dist/core/context/frame-handoff-manager.js.map +1 -1
  50. package/dist/core/context/frame-lifecycle-hooks.js +119 -0
  51. package/dist/core/context/frame-lifecycle-hooks.js.map +7 -0
  52. package/dist/core/context/frame-manager.js +56 -9
  53. package/dist/core/context/frame-manager.js.map +2 -2
  54. package/dist/core/context/frame-stack.js +29 -0
  55. package/dist/core/context/frame-stack.js.map +2 -2
  56. package/dist/core/context/incremental-gc.js.map +2 -2
  57. package/dist/core/context/index.js +4 -22
  58. package/dist/core/context/index.js.map +2 -2
  59. package/dist/core/context/permission-manager.js +0 -11
  60. package/dist/core/context/permission-manager.js.map +2 -2
  61. package/dist/core/context/recursive-context-manager.js +15 -9
  62. package/dist/core/context/recursive-context-manager.js.map +2 -2
  63. package/dist/core/context/refactored-frame-manager.js +140 -34
  64. package/dist/core/context/refactored-frame-manager.js.map +3 -3
  65. package/dist/core/context/shared-context-layer.js +0 -11
  66. package/dist/core/context/shared-context-layer.js.map +2 -2
  67. package/dist/core/context/stack-merge-resolver.js.map +1 -1
  68. package/dist/core/context/validation.js +6 -1
  69. package/dist/core/context/validation.js.map +2 -2
  70. package/dist/core/database/database-adapter.js.map +1 -1
  71. package/dist/core/database/paradedb-adapter.js.map +1 -1
  72. package/dist/core/database/query-router.js.map +1 -1
  73. package/dist/core/database/sqlite-adapter.js.map +1 -1
  74. package/dist/core/digest/frame-digest-integration.js.map +1 -1
  75. package/dist/core/digest/hybrid-digest-generator.js.map +1 -1
  76. package/dist/core/digest/types.js.map +1 -1
  77. package/dist/core/errors/index.js +249 -0
  78. package/dist/core/errors/index.js.map +2 -2
  79. package/dist/core/frame/workflow-templates.js.map +2 -2
  80. package/dist/core/merge/conflict-detector.js.map +1 -1
  81. package/dist/core/merge/resolution-engine.js.map +1 -1
  82. package/dist/core/merge/stack-diff.js.map +1 -1
  83. package/dist/core/models/fallback-monitor.js +229 -0
  84. package/dist/core/models/fallback-monitor.js.map +7 -0
  85. package/dist/core/models/model-router.js +340 -0
  86. package/dist/core/models/model-router.js.map +7 -0
  87. package/dist/core/monitoring/error-handler.js +37 -270
  88. package/dist/core/monitoring/error-handler.js.map +3 -3
  89. package/dist/core/monitoring/session-monitor.js.map +1 -1
  90. package/dist/core/performance/lazy-context-loader.js.map +1 -1
  91. package/dist/core/performance/optimized-frame-context.js.map +1 -1
  92. package/dist/core/retrieval/context-retriever.js.map +1 -1
  93. package/dist/core/retrieval/graph-retrieval.js.map +1 -1
  94. package/dist/core/retrieval/hierarchical-retrieval.js.map +1 -1
  95. package/dist/core/retrieval/llm-context-retrieval.js.map +1 -1
  96. package/dist/core/retrieval/retrieval-benchmarks.js.map +1 -1
  97. package/dist/core/retrieval/summary-generator.js.map +1 -1
  98. package/dist/core/retrieval/types.js.map +1 -1
  99. package/dist/core/storage/chromadb-adapter.js.map +1 -1
  100. package/dist/core/storage/infinite-storage.js.map +1 -1
  101. package/dist/core/storage/two-tier-storage.js.map +1 -1
  102. package/dist/features/tasks/task-aware-context.js.map +1 -1
  103. package/dist/features/web/server/index.js +1 -1
  104. package/dist/features/web/server/index.js.map +1 -1
  105. package/dist/hooks/claude-code-whatsapp-hook.js +197 -0
  106. package/dist/hooks/claude-code-whatsapp-hook.js.map +7 -0
  107. package/dist/hooks/linear-task-picker.js +1 -1
  108. package/dist/hooks/linear-task-picker.js.map +2 -2
  109. package/dist/hooks/schemas.js +105 -1
  110. package/dist/hooks/schemas.js.map +2 -2
  111. package/dist/hooks/session-summary.js +5 -1
  112. package/dist/hooks/session-summary.js.map +2 -2
  113. package/dist/hooks/sms-action-runner.js +16 -1
  114. package/dist/hooks/sms-action-runner.js.map +2 -2
  115. package/dist/hooks/sms-notify.js +4 -2
  116. package/dist/hooks/sms-notify.js.map +2 -2
  117. package/dist/hooks/sms-webhook.js +23 -2
  118. package/dist/hooks/sms-webhook.js.map +2 -2
  119. package/dist/hooks/whatsapp-commands.js +516 -0
  120. package/dist/hooks/whatsapp-commands.js.map +7 -0
  121. package/dist/hooks/whatsapp-scheduler.js +317 -0
  122. package/dist/hooks/whatsapp-scheduler.js.map +7 -0
  123. package/dist/hooks/whatsapp-sync.js +409 -0
  124. package/dist/hooks/whatsapp-sync.js.map +7 -0
  125. package/dist/index.js +1 -1
  126. package/dist/index.js.map +1 -1
  127. package/dist/integrations/mcp/handlers/context-handlers.js.map +1 -1
  128. package/dist/integrations/mcp/handlers/discovery-handlers.js.map +1 -1
  129. package/dist/integrations/mcp/server.js +1 -1
  130. package/dist/integrations/mcp/server.js.map +1 -1
  131. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js +1 -1
  132. package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +1 -1
  133. package/dist/integrations/ralph/context/stackmemory-context-loader.js +1 -1
  134. package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +1 -1
  135. package/dist/integrations/ralph/learning/pattern-learner.js +1 -1
  136. package/dist/integrations/ralph/learning/pattern-learner.js.map +1 -1
  137. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js +1 -1
  138. package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +1 -1
  139. package/dist/integrations/ralph/swarm/swarm-coordinator.js +1 -1
  140. package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +1 -1
  141. package/dist/integrations/ralph/visualization/ralph-debugger.js +1 -1
  142. package/dist/integrations/ralph/visualization/ralph-debugger.js.map +1 -1
  143. package/dist/mcp/stackmemory-mcp-server.js +1 -1
  144. package/dist/mcp/stackmemory-mcp-server.js.map +1 -1
  145. package/dist/skills/claude-skills.js.map +1 -1
  146. package/dist/skills/recursive-agent-orchestrator.js.map +1 -1
  147. package/dist/skills/unified-rlm-orchestrator.js.map +1 -1
  148. package/package.json +2 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/agents/core/agent-task-manager.ts"],
4
- "sourcesContent": ["/**\n * Agent Task Manager - Spotify-inspired task handling for StackMemory\n *\n * Integrates Spotify's background coding agent strategies:\n * - 10-turn session limits with automatic task breakdown\n * - Strong verification loops with incremental feedback\n * - Context-aware task prioritization\n * - LLM judge for semantic validation\n */\n\nimport {\n LinearTaskManager,\n PebblesTask,\n TaskStatus,\n TaskPriority,\n} from '../../features/tasks/linear-task-manager.js';\nimport { logger } from '../../core/monitoring/logger.js';\nimport { FrameManager } from '../../core/context/frame-manager.js';\nimport { TaskError, ErrorCode } from '../../core/errors/index.js';\n\nexport interface AgentTaskSession {\n id: string;\n frameId: string;\n taskId: string;\n turnCount: number;\n maxTurns: number;\n status: 'active' | 'completed' | 'failed' | 'timeout';\n startedAt: Date;\n completedAt?: Date;\n verificationResults: VerificationResult[];\n contextWindow: string[];\n feedbackLoop: FeedbackEntry[];\n}\n\nexport interface VerificationResult {\n verifierId: string;\n passed: boolean;\n message: string;\n severity: 'error' | 'warning' | 'info';\n timestamp: Date;\n autoFix?: string;\n}\n\nexport interface FeedbackEntry {\n turn: number;\n action: string;\n result: string;\n verificationPassed: boolean;\n contextAdjustment?: string;\n}\n\nexport interface TaskBreakdown {\n parentTaskId: string;\n subtasks: SubtaskDefinition[];\n dependencies: Map<string, string[]>;\n estimatedTurns: number;\n}\n\nexport interface SubtaskDefinition {\n title: string;\n description: string;\n acceptanceCriteria: string[];\n estimatedTurns: number;\n verifiers: string[];\n}\n\n/**\n * Supported agent types\n */\nexport enum AgentType {\n FORMATTER = 'formatter',\n SECURITY = 'security',\n TESTING = 'testing',\n PERFORMANCE = 'performance',\n DOCUMENTATION = 'documentation',\n REFACTORING = 'refactoring'\n}\n\n/**\n * Spotify-inspired Agent Task Manager\n */\nexport class AgentTaskManager {\n private taskStore: LinearTaskManager;\n private frameManager: FrameManager;\n private activeSessions: Map<string, AgentTaskSession> = new Map();\n private sessionTimeouts: Map<string, NodeJS.Timeout> = new Map();\n private agentRegistry: Map<AgentType, any> = new Map();\n\n // Spotify strategy constants\n private readonly MAX_TURNS_PER_SESSION = 10;\n private readonly MAX_SESSION_RETRIES = 3;\n private readonly SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n private readonly CONTEXT_WINDOW_SIZE = 5; // Last 5 significant events\n\n constructor(taskStore: LinearTaskManager, frameManager: FrameManager) {\n this.taskStore = taskStore;\n this.frameManager = frameManager;\n }\n\n /**\n * Start a new agent task session with Spotify's 10-turn limit\n */\n async startTaskSession(\n taskId: string,\n frameId: string\n ): Promise<AgentTaskSession> {\n const task = this.taskStore.getTask(taskId);\n if (!task) {\n throw new TaskError(\n `Task ${taskId} not found`,\n ErrorCode.TASK_NOT_FOUND,\n { taskId }\n );\n }\n\n // Check if task needs breakdown (Spotify strategy)\n if (this.needsBreakdown(task)) {\n const breakdown = await this.breakdownTask(task);\n return this.startMultiTaskSession(breakdown, frameId);\n }\n\n const sessionId = this.generateSessionId(taskId);\n const session: AgentTaskSession = {\n id: sessionId,\n frameId,\n taskId,\n turnCount: 0,\n maxTurns: this.MAX_TURNS_PER_SESSION,\n status: 'active',\n startedAt: new Date(),\n verificationResults: [],\n contextWindow: [],\n feedbackLoop: [],\n };\n\n this.activeSessions.set(sessionId, session);\n this.startSessionTimeout(sessionId);\n\n // Update task status\n this.taskStore.updateTaskStatus(\n taskId,\n 'in_progress',\n 'Agent session started'\n );\n\n logger.info('Started agent task session', {\n sessionId,\n taskId,\n taskTitle: task.title,\n maxTurns: this.MAX_TURNS_PER_SESSION,\n });\n\n return session;\n }\n\n /**\n * Execute a turn in the session with verification\n */\n async executeTurn(\n sessionId: string,\n action: string,\n context: Record<string, any>\n ): Promise<{\n success: boolean;\n feedback: string;\n shouldContinue: boolean;\n verificationResults: VerificationResult[];\n }> {\n const session = this.activeSessions.get(sessionId);\n if (!session || session.status !== 'active') {\n throw new TaskError(\n 'Invalid or inactive session',\n ErrorCode.TASK_INVALID_STATE,\n { sessionId }\n );\n }\n\n session.turnCount++;\n\n // Check turn limit (Spotify strategy)\n if (session.turnCount >= session.maxTurns) {\n return this.handleTurnLimitReached(session);\n }\n\n // Execute action with verification loop\n const verificationResults = await this.runVerificationLoop(\n action,\n context,\n session\n );\n\n // Update context window (keep last N significant events)\n this.updateContextWindow(session, action, verificationResults);\n\n // Generate feedback based on verification\n const feedback = this.generateFeedback(verificationResults);\n const success = verificationResults.every(\n (r) => r.passed || r.severity !== 'error'\n );\n\n // Record in feedback loop\n session.feedbackLoop.push({\n turn: session.turnCount,\n action,\n result: feedback,\n verificationPassed: success,\n contextAdjustment:\n this.suggestContextAdjustment(verificationResults) || undefined,\n });\n\n // Determine if should continue\n const shouldContinue = success && session.turnCount < session.maxTurns;\n\n if (!shouldContinue && success) {\n await this.completeSession(session);\n }\n\n return {\n success,\n feedback,\n shouldContinue,\n verificationResults,\n };\n }\n\n /**\n * Run Spotify-style verification loop\n */\n private async runVerificationLoop(\n action: string,\n context: Record<string, any>,\n session: AgentTaskSession\n ): Promise<VerificationResult[]> {\n const results: VerificationResult[] = [];\n\n // Get applicable verifiers based on context\n const verifiers = this.getApplicableVerifiers(context);\n\n for (const verifier of verifiers) {\n const result = await this.runVerifier(verifier, action, context);\n results.push(result);\n session.verificationResults.push(result);\n\n // Stop on critical errors (Spotify strategy)\n if (!result.passed && result.severity === 'error') {\n logger.warn('Verification failed, stopping execution', {\n verifier: result.verifierId,\n message: result.message,\n });\n break;\n }\n }\n\n return results;\n }\n\n /**\n * Get verifiers applicable to current context\n */\n private getApplicableVerifiers(context: Record<string, any>): string[] {\n const verifiers: string[] = [];\n\n // Add verifiers based on context (Spotify's context-aware approach)\n if (context['codeChange']) {\n verifiers.push('formatter', 'linter', 'type-checker');\n }\n if (context['testsPresent']) {\n verifiers.push('test-runner');\n }\n if (context['hasDocumentation']) {\n verifiers.push('doc-validator');\n }\n if (context['performanceCritical']) {\n verifiers.push('performance-analyzer');\n }\n\n // Always include semantic validator (LLM judge from Spotify)\n verifiers.push('semantic-validator');\n\n return verifiers;\n }\n\n /**\n * Run a single verifier\n */\n private async runVerifier(\n verifierId: string,\n action: string,\n context: Record<string, any>\n ): Promise<VerificationResult> {\n // This would integrate with actual verifiers\n // For now, return mock result\n const mockResults: Record<string, () => VerificationResult> = {\n formatter: () => ({\n verifierId: 'formatter',\n passed: Math.random() > 0.1,\n message: 'Code formatting check',\n severity: 'warning',\n timestamp: new Date(),\n autoFix: 'prettier --write',\n }),\n linter: () => ({\n verifierId: 'linter',\n passed: Math.random() > 0.2,\n message: 'Linting check',\n severity: 'error',\n timestamp: new Date(),\n }),\n 'test-runner': () => ({\n verifierId: 'test-runner',\n passed: Math.random() > 0.3,\n message: 'Test execution',\n severity: 'error',\n timestamp: new Date(),\n }),\n 'semantic-validator': () => ({\n verifierId: 'semantic-validator',\n passed: Math.random() > 0.25, // ~75% pass rate like Spotify\n message: 'Semantic validation against original requirements',\n severity: 'error',\n timestamp: new Date(),\n }),\n };\n\n const verifierFn =\n mockResults[verifierId] ||\n (() => ({\n verifierId,\n passed: true,\n message: 'Unknown verifier',\n severity: 'info' as const,\n timestamp: new Date(),\n }));\n\n return verifierFn();\n }\n\n /**\n * Check if task needs breakdown (Spotify strategy for complex tasks)\n */\n private needsBreakdown(task: PebblesTask): boolean {\n // Heuristics for determining if task is too complex\n const indicators = {\n hasMultipleComponents:\n (task.description?.match(/\\band\\b/gi)?.length || 0) > 2,\n longDescription: (task.description?.length || 0) > 500,\n highComplexityTags: task.tags.some((tag) =>\n ['refactor', 'migration', 'architecture', 'redesign'].includes(\n tag.toLowerCase()\n )\n ),\n hasManydependencies: task.depends_on.length > 3,\n };\n\n const complexityScore = Object.values(indicators).filter(Boolean).length;\n return complexityScore >= 2;\n }\n\n /**\n * Break down complex task into subtasks\n */\n private async breakdownTask(task: PebblesTask): Promise<TaskBreakdown> {\n // This would use LLM to intelligently break down the task\n // For now, return a simple breakdown\n const subtasks: SubtaskDefinition[] = [\n {\n title: `Analyze requirements for ${task.title}`,\n description: 'Understand and document requirements',\n acceptanceCriteria: [\n 'Requirements documented',\n 'Constraints identified',\n ],\n estimatedTurns: 2,\n verifiers: ['semantic-validator'],\n },\n {\n title: `Implement core functionality for ${task.title}`,\n description: 'Build the main implementation',\n acceptanceCriteria: ['Core logic implemented', 'Tests passing'],\n estimatedTurns: 5,\n verifiers: ['linter', 'test-runner'],\n },\n {\n title: `Verify and refine ${task.title}`,\n description: 'Final verification and improvements',\n acceptanceCriteria: ['All tests passing', 'Documentation complete'],\n estimatedTurns: 3,\n verifiers: ['formatter', 'linter', 'test-runner', 'semantic-validator'],\n },\n ];\n\n return {\n parentTaskId: task.id,\n subtasks,\n dependencies: new Map([\n [subtasks[1]!.title, [subtasks[0]!.title]],\n [subtasks[2]!.title, [subtasks[1]!.title]],\n ]),\n estimatedTurns: subtasks.reduce((sum, st) => sum + st.estimatedTurns, 0),\n };\n }\n\n /**\n * Start multi-task session for complex tasks\n */\n private async startMultiTaskSession(\n breakdown: TaskBreakdown,\n frameId: string\n ): Promise<AgentTaskSession> {\n // Create subtasks in task store\n const subtaskIds: string[] = [];\n\n for (const subtask of breakdown.subtasks) {\n const subtaskId = this.taskStore.createTask({\n title: subtask.title,\n description: subtask.description,\n frameId,\n parentId: breakdown.parentTaskId,\n tags: ['agent-subtask', ...subtask.verifiers],\n estimatedEffort: subtask.estimatedTurns * 5, // Rough conversion to minutes\n });\n subtaskIds.push(subtaskId);\n }\n\n // Add dependencies\n const titleToId = new Map(\n breakdown.subtasks.map((st, i) => [st.title, subtaskIds[i]])\n );\n\n for (const [title, deps] of breakdown.dependencies) {\n const taskId = titleToId.get(title);\n if (taskId) {\n for (const dep of deps) {\n const depId = titleToId.get(dep);\n if (depId) {\n this.taskStore.addDependency(taskId, depId);\n }\n }\n }\n }\n\n // Start session for first subtask\n return this.startTaskSession(subtaskIds[0]!, frameId);\n }\n\n /**\n * Update context window with significant events\n */\n private updateContextWindow(\n session: AgentTaskSession,\n action: string,\n verificationResults: VerificationResult[]\n ): void {\n const significantEvent = {\n turn: session.turnCount,\n action: action.substring(0, 100),\n verificationSummary: verificationResults.map((r) => ({\n verifier: r.verifierId,\n passed: r.passed,\n })),\n timestamp: new Date().toISOString(),\n };\n\n session.contextWindow.push(JSON.stringify(significantEvent));\n\n // Keep only last N events (Spotify's context window optimization)\n if (session.contextWindow.length > this.CONTEXT_WINDOW_SIZE) {\n session.contextWindow = session.contextWindow.slice(\n -this.CONTEXT_WINDOW_SIZE\n );\n }\n }\n\n /**\n * Generate feedback from verification results\n */\n private generateFeedback(results: VerificationResult[]): string {\n const failed = results.filter((r) => !r.passed);\n const warnings = results.filter(\n (r) => !r.passed && r.severity === 'warning'\n );\n const errors = results.filter((r) => !r.passed && r.severity === 'error');\n\n if (errors.length > 0) {\n const errorMessages = errors.map((e) => `- ${e.message}`).join('\\n');\n return `Verification failed with ${errors.length} error(s):\\n${errorMessages}`;\n }\n\n if (warnings.length > 0) {\n const warningMessages = warnings.map((w) => `- ${w.message}`).join('\\n');\n return `Verification passed with ${warnings.length} warning(s):\\n${warningMessages}`;\n }\n\n return 'All verifications passed successfully';\n }\n\n /**\n * Suggest context adjustment based on verification results\n */\n private suggestContextAdjustment(\n results: VerificationResult[]\n ): string | undefined {\n const failed = results.filter((r) => !r.passed && r.severity === 'error');\n\n if (failed.length === 0) {\n return undefined;\n }\n\n // Generate suggestions based on failure patterns\n const suggestions: string[] = [];\n\n if (failed.some((r) => r.verifierId === 'test-runner')) {\n suggestions.push('Focus on fixing failing tests');\n }\n if (failed.some((r) => r.verifierId === 'linter')) {\n suggestions.push('Address linting errors before proceeding');\n }\n if (failed.some((r) => r.verifierId === 'semantic-validator')) {\n suggestions.push('Review original requirements and adjust approach');\n }\n\n return suggestions.length > 0 ? suggestions.join('; ') : undefined;\n }\n\n /**\n * Handle turn limit reached\n */\n private async handleTurnLimitReached(session: AgentTaskSession): Promise<{\n success: boolean;\n feedback: string;\n shouldContinue: boolean;\n verificationResults: VerificationResult[];\n }> {\n logger.warn('Session reached turn limit', {\n sessionId: session.id,\n taskId: session.taskId,\n turnCount: session.turnCount,\n });\n\n // Check if task can be considered complete\n const task = this.taskStore.getTask(session.taskId);\n const isComplete = this.assessTaskCompletion(session);\n\n if (isComplete) {\n await this.completeSession(session);\n return {\n success: true,\n feedback: 'Task completed successfully within turn limit',\n shouldContinue: false,\n verificationResults: [],\n };\n }\n\n // Mark session as timeout\n session.status = 'timeout';\n this.taskStore.updateTaskStatus(\n session.taskId,\n 'blocked',\n 'Session timeout - manual review needed'\n );\n\n return {\n success: false,\n feedback: `Session reached ${this.MAX_TURNS_PER_SESSION} turn limit. Task requires manual review or retry.`,\n shouldContinue: false,\n verificationResults: [],\n };\n }\n\n /**\n * Assess if task is complete enough\n */\n private assessTaskCompletion(session: AgentTaskSession): boolean {\n // Check if recent verifications are passing\n const recentResults = session.verificationResults.slice(-5);\n const recentPassRate =\n recentResults.filter((r) => r.passed).length / recentResults.length;\n\n // Check if semantic validator passed recently (Spotify's LLM judge)\n const semanticPassed = recentResults.some(\n (r) => r.verifierId === 'semantic-validator' && r.passed\n );\n\n return recentPassRate >= 0.8 && semanticPassed;\n }\n\n /**\n * Complete a session\n */\n private async completeSession(session: AgentTaskSession): Promise<void> {\n session.status = 'completed';\n session.completedAt = new Date();\n\n // Update task status\n this.taskStore.updateTaskStatus(\n session.taskId,\n 'completed',\n 'Agent session completed'\n );\n\n // Clear timeout\n const timeout = this.sessionTimeouts.get(session.id);\n if (timeout) {\n clearTimeout(timeout);\n this.sessionTimeouts.delete(session.id);\n }\n\n // Generate and save session summary to frame\n const summary = this.generateSessionSummary(session);\n this.frameManager.addEvent('observation', {\n type: 'session_summary',\n frameId: session.frameId,\n summary,\n });\n\n logger.info('Session completed', {\n sessionId: session.id,\n taskId: session.taskId,\n turnCount: session.turnCount,\n duration: session.completedAt.getTime() - session.startedAt.getTime(),\n });\n }\n\n /**\n * Generate session summary for frame digest\n */\n private generateSessionSummary(\n session: AgentTaskSession\n ): Record<string, any> {\n const verificationStats = {\n total: session.verificationResults.length,\n passed: session.verificationResults.filter((r) => r.passed).length,\n failed: session.verificationResults.filter((r) => !r.passed).length,\n };\n\n return {\n sessionId: session.id,\n taskId: session.taskId,\n status: session.status,\n turnCount: session.turnCount,\n duration: session.completedAt\n ? session.completedAt.getTime() - session.startedAt.getTime()\n : 0,\n verificationStats,\n feedbackLoop: session.feedbackLoop.slice(-3), // Last 3 feedback entries\n contextWindow: session.contextWindow.slice(-2), // Last 2 context entries\n };\n }\n\n /**\n * Start timeout for session\n */\n private startSessionTimeout(sessionId: string): void {\n const timeout = setTimeout(() => {\n const session = this.activeSessions.get(sessionId);\n if (session && session.status === 'active') {\n session.status = 'timeout';\n this.taskStore.updateTaskStatus(\n session.taskId,\n 'blocked',\n 'Session timeout - no activity'\n );\n logger.warn('Session timed out due to inactivity', { sessionId });\n }\n }, this.SESSION_TIMEOUT_MS);\n\n this.sessionTimeouts.set(sessionId, timeout);\n }\n\n /**\n * Generate unique session ID\n */\n private generateSessionId(taskId: string): string {\n return `session-${taskId}-${Date.now()}`;\n }\n\n /**\n * Get active sessions summary\n */\n getActiveSessions(): Array<{\n sessionId: string;\n taskId: string;\n turnCount: number;\n status: string;\n startedAt: Date;\n }> {\n return Array.from(this.activeSessions.values()).map((session) => ({\n sessionId: session.id,\n taskId: session.taskId,\n turnCount: session.turnCount,\n status: session.status,\n startedAt: session.startedAt,\n }));\n }\n\n /**\n * Retry a failed session (Spotify's 3-retry strategy)\n */\n async retrySession(sessionId: string): Promise<AgentTaskSession | null> {\n const session = this.activeSessions.get(sessionId);\n if (!session || session.status === 'active') {\n return null;\n }\n\n // Count previous retries\n const retryCount = Array.from(this.activeSessions.values()).filter(\n (s) => s.taskId === session.taskId && s.status === 'failed'\n ).length;\n\n if (retryCount >= this.MAX_SESSION_RETRIES) {\n logger.warn('Max retries reached for task', {\n taskId: session.taskId,\n retries: retryCount,\n });\n return null;\n }\n\n // Start new session with learned context\n const newSession = await this.startTaskSession(\n session.taskId,\n session.frameId\n );\n\n // Transfer learned context from previous session\n newSession.contextWindow = session.contextWindow.slice(-3);\n newSession.feedbackLoop = [\n {\n turn: 0,\n action: 'Session retry with learned context',\n result: `Retrying after ${retryCount} previous attempts`,\n verificationPassed: true,\n contextAdjustment: session.feedbackLoop\n .filter((f) => f.contextAdjustment)\n .map((f) => f.contextAdjustment)\n .join('; '),\n },\n ];\n\n return newSession;\n }\n}\n"],
4
+ "sourcesContent": ["/**\n * Agent Task Manager - Spotify-inspired task handling for StackMemory\n *\n * Integrates Spotify's background coding agent strategies:\n * - 10-turn session limits with automatic task breakdown\n * - Strong verification loops with incremental feedback\n * - Context-aware task prioritization\n * - LLM judge for semantic validation\n */\n\nimport {\n LinearTaskManager,\n PebblesTask,\n TaskStatus,\n TaskPriority,\n} from '../../features/tasks/linear-task-manager.js';\nimport { logger } from '../../core/monitoring/logger.js';\nimport { FrameManager } from '../../core/context/index.js';\nimport { TaskError, ErrorCode } from '../../core/errors/index.js';\n\nexport interface AgentTaskSession {\n id: string;\n frameId: string;\n taskId: string;\n turnCount: number;\n maxTurns: number;\n status: 'active' | 'completed' | 'failed' | 'timeout';\n startedAt: Date;\n completedAt?: Date;\n verificationResults: VerificationResult[];\n contextWindow: string[];\n feedbackLoop: FeedbackEntry[];\n}\n\nexport interface VerificationResult {\n verifierId: string;\n passed: boolean;\n message: string;\n severity: 'error' | 'warning' | 'info';\n timestamp: Date;\n autoFix?: string;\n}\n\nexport interface FeedbackEntry {\n turn: number;\n action: string;\n result: string;\n verificationPassed: boolean;\n contextAdjustment?: string;\n}\n\nexport interface TaskBreakdown {\n parentTaskId: string;\n subtasks: SubtaskDefinition[];\n dependencies: Map<string, string[]>;\n estimatedTurns: number;\n}\n\nexport interface SubtaskDefinition {\n title: string;\n description: string;\n acceptanceCriteria: string[];\n estimatedTurns: number;\n verifiers: string[];\n}\n\n/**\n * Supported agent types\n */\nexport enum AgentType {\n FORMATTER = 'formatter',\n SECURITY = 'security',\n TESTING = 'testing',\n PERFORMANCE = 'performance',\n DOCUMENTATION = 'documentation',\n REFACTORING = 'refactoring'\n}\n\n/**\n * Spotify-inspired Agent Task Manager\n */\nexport class AgentTaskManager {\n private taskStore: LinearTaskManager;\n private frameManager: FrameManager;\n private activeSessions: Map<string, AgentTaskSession> = new Map();\n private sessionTimeouts: Map<string, NodeJS.Timeout> = new Map();\n private agentRegistry: Map<AgentType, any> = new Map();\n\n // Spotify strategy constants\n private readonly MAX_TURNS_PER_SESSION = 10;\n private readonly MAX_SESSION_RETRIES = 3;\n private readonly SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes\n private readonly CONTEXT_WINDOW_SIZE = 5; // Last 5 significant events\n\n constructor(taskStore: LinearTaskManager, frameManager: FrameManager) {\n this.taskStore = taskStore;\n this.frameManager = frameManager;\n }\n\n /**\n * Start a new agent task session with Spotify's 10-turn limit\n */\n async startTaskSession(\n taskId: string,\n frameId: string\n ): Promise<AgentTaskSession> {\n const task = this.taskStore.getTask(taskId);\n if (!task) {\n throw new TaskError(\n `Task ${taskId} not found`,\n ErrorCode.TASK_NOT_FOUND,\n { taskId }\n );\n }\n\n // Check if task needs breakdown (Spotify strategy)\n if (this.needsBreakdown(task)) {\n const breakdown = await this.breakdownTask(task);\n return this.startMultiTaskSession(breakdown, frameId);\n }\n\n const sessionId = this.generateSessionId(taskId);\n const session: AgentTaskSession = {\n id: sessionId,\n frameId,\n taskId,\n turnCount: 0,\n maxTurns: this.MAX_TURNS_PER_SESSION,\n status: 'active',\n startedAt: new Date(),\n verificationResults: [],\n contextWindow: [],\n feedbackLoop: [],\n };\n\n this.activeSessions.set(sessionId, session);\n this.startSessionTimeout(sessionId);\n\n // Update task status\n this.taskStore.updateTaskStatus(\n taskId,\n 'in_progress',\n 'Agent session started'\n );\n\n logger.info('Started agent task session', {\n sessionId,\n taskId,\n taskTitle: task.title,\n maxTurns: this.MAX_TURNS_PER_SESSION,\n });\n\n return session;\n }\n\n /**\n * Execute a turn in the session with verification\n */\n async executeTurn(\n sessionId: string,\n action: string,\n context: Record<string, any>\n ): Promise<{\n success: boolean;\n feedback: string;\n shouldContinue: boolean;\n verificationResults: VerificationResult[];\n }> {\n const session = this.activeSessions.get(sessionId);\n if (!session || session.status !== 'active') {\n throw new TaskError(\n 'Invalid or inactive session',\n ErrorCode.TASK_INVALID_STATE,\n { sessionId }\n );\n }\n\n session.turnCount++;\n\n // Check turn limit (Spotify strategy)\n if (session.turnCount >= session.maxTurns) {\n return this.handleTurnLimitReached(session);\n }\n\n // Execute action with verification loop\n const verificationResults = await this.runVerificationLoop(\n action,\n context,\n session\n );\n\n // Update context window (keep last N significant events)\n this.updateContextWindow(session, action, verificationResults);\n\n // Generate feedback based on verification\n const feedback = this.generateFeedback(verificationResults);\n const success = verificationResults.every(\n (r) => r.passed || r.severity !== 'error'\n );\n\n // Record in feedback loop\n session.feedbackLoop.push({\n turn: session.turnCount,\n action,\n result: feedback,\n verificationPassed: success,\n contextAdjustment:\n this.suggestContextAdjustment(verificationResults) || undefined,\n });\n\n // Determine if should continue\n const shouldContinue = success && session.turnCount < session.maxTurns;\n\n if (!shouldContinue && success) {\n await this.completeSession(session);\n }\n\n return {\n success,\n feedback,\n shouldContinue,\n verificationResults,\n };\n }\n\n /**\n * Run Spotify-style verification loop\n */\n private async runVerificationLoop(\n action: string,\n context: Record<string, any>,\n session: AgentTaskSession\n ): Promise<VerificationResult[]> {\n const results: VerificationResult[] = [];\n\n // Get applicable verifiers based on context\n const verifiers = this.getApplicableVerifiers(context);\n\n for (const verifier of verifiers) {\n const result = await this.runVerifier(verifier, action, context);\n results.push(result);\n session.verificationResults.push(result);\n\n // Stop on critical errors (Spotify strategy)\n if (!result.passed && result.severity === 'error') {\n logger.warn('Verification failed, stopping execution', {\n verifier: result.verifierId,\n message: result.message,\n });\n break;\n }\n }\n\n return results;\n }\n\n /**\n * Get verifiers applicable to current context\n */\n private getApplicableVerifiers(context: Record<string, any>): string[] {\n const verifiers: string[] = [];\n\n // Add verifiers based on context (Spotify's context-aware approach)\n if (context['codeChange']) {\n verifiers.push('formatter', 'linter', 'type-checker');\n }\n if (context['testsPresent']) {\n verifiers.push('test-runner');\n }\n if (context['hasDocumentation']) {\n verifiers.push('doc-validator');\n }\n if (context['performanceCritical']) {\n verifiers.push('performance-analyzer');\n }\n\n // Always include semantic validator (LLM judge from Spotify)\n verifiers.push('semantic-validator');\n\n return verifiers;\n }\n\n /**\n * Run a single verifier\n */\n private async runVerifier(\n verifierId: string,\n action: string,\n context: Record<string, any>\n ): Promise<VerificationResult> {\n // This would integrate with actual verifiers\n // For now, return mock result\n const mockResults: Record<string, () => VerificationResult> = {\n formatter: () => ({\n verifierId: 'formatter',\n passed: Math.random() > 0.1,\n message: 'Code formatting check',\n severity: 'warning',\n timestamp: new Date(),\n autoFix: 'prettier --write',\n }),\n linter: () => ({\n verifierId: 'linter',\n passed: Math.random() > 0.2,\n message: 'Linting check',\n severity: 'error',\n timestamp: new Date(),\n }),\n 'test-runner': () => ({\n verifierId: 'test-runner',\n passed: Math.random() > 0.3,\n message: 'Test execution',\n severity: 'error',\n timestamp: new Date(),\n }),\n 'semantic-validator': () => ({\n verifierId: 'semantic-validator',\n passed: Math.random() > 0.25, // ~75% pass rate like Spotify\n message: 'Semantic validation against original requirements',\n severity: 'error',\n timestamp: new Date(),\n }),\n };\n\n const verifierFn =\n mockResults[verifierId] ||\n (() => ({\n verifierId,\n passed: true,\n message: 'Unknown verifier',\n severity: 'info' as const,\n timestamp: new Date(),\n }));\n\n return verifierFn();\n }\n\n /**\n * Check if task needs breakdown (Spotify strategy for complex tasks)\n */\n private needsBreakdown(task: PebblesTask): boolean {\n // Heuristics for determining if task is too complex\n const indicators = {\n hasMultipleComponents:\n (task.description?.match(/\\band\\b/gi)?.length || 0) > 2,\n longDescription: (task.description?.length || 0) > 500,\n highComplexityTags: task.tags.some((tag) =>\n ['refactor', 'migration', 'architecture', 'redesign'].includes(\n tag.toLowerCase()\n )\n ),\n hasManydependencies: task.depends_on.length > 3,\n };\n\n const complexityScore = Object.values(indicators).filter(Boolean).length;\n return complexityScore >= 2;\n }\n\n /**\n * Break down complex task into subtasks\n */\n private async breakdownTask(task: PebblesTask): Promise<TaskBreakdown> {\n // This would use LLM to intelligently break down the task\n // For now, return a simple breakdown\n const subtasks: SubtaskDefinition[] = [\n {\n title: `Analyze requirements for ${task.title}`,\n description: 'Understand and document requirements',\n acceptanceCriteria: [\n 'Requirements documented',\n 'Constraints identified',\n ],\n estimatedTurns: 2,\n verifiers: ['semantic-validator'],\n },\n {\n title: `Implement core functionality for ${task.title}`,\n description: 'Build the main implementation',\n acceptanceCriteria: ['Core logic implemented', 'Tests passing'],\n estimatedTurns: 5,\n verifiers: ['linter', 'test-runner'],\n },\n {\n title: `Verify and refine ${task.title}`,\n description: 'Final verification and improvements',\n acceptanceCriteria: ['All tests passing', 'Documentation complete'],\n estimatedTurns: 3,\n verifiers: ['formatter', 'linter', 'test-runner', 'semantic-validator'],\n },\n ];\n\n return {\n parentTaskId: task.id,\n subtasks,\n dependencies: new Map([\n [subtasks[1]!.title, [subtasks[0]!.title]],\n [subtasks[2]!.title, [subtasks[1]!.title]],\n ]),\n estimatedTurns: subtasks.reduce((sum, st) => sum + st.estimatedTurns, 0),\n };\n }\n\n /**\n * Start multi-task session for complex tasks\n */\n private async startMultiTaskSession(\n breakdown: TaskBreakdown,\n frameId: string\n ): Promise<AgentTaskSession> {\n // Create subtasks in task store\n const subtaskIds: string[] = [];\n\n for (const subtask of breakdown.subtasks) {\n const subtaskId = this.taskStore.createTask({\n title: subtask.title,\n description: subtask.description,\n frameId,\n parentId: breakdown.parentTaskId,\n tags: ['agent-subtask', ...subtask.verifiers],\n estimatedEffort: subtask.estimatedTurns * 5, // Rough conversion to minutes\n });\n subtaskIds.push(subtaskId);\n }\n\n // Add dependencies\n const titleToId = new Map(\n breakdown.subtasks.map((st, i) => [st.title, subtaskIds[i]])\n );\n\n for (const [title, deps] of breakdown.dependencies) {\n const taskId = titleToId.get(title);\n if (taskId) {\n for (const dep of deps) {\n const depId = titleToId.get(dep);\n if (depId) {\n this.taskStore.addDependency(taskId, depId);\n }\n }\n }\n }\n\n // Start session for first subtask\n return this.startTaskSession(subtaskIds[0]!, frameId);\n }\n\n /**\n * Update context window with significant events\n */\n private updateContextWindow(\n session: AgentTaskSession,\n action: string,\n verificationResults: VerificationResult[]\n ): void {\n const significantEvent = {\n turn: session.turnCount,\n action: action.substring(0, 100),\n verificationSummary: verificationResults.map((r) => ({\n verifier: r.verifierId,\n passed: r.passed,\n })),\n timestamp: new Date().toISOString(),\n };\n\n session.contextWindow.push(JSON.stringify(significantEvent));\n\n // Keep only last N events (Spotify's context window optimization)\n if (session.contextWindow.length > this.CONTEXT_WINDOW_SIZE) {\n session.contextWindow = session.contextWindow.slice(\n -this.CONTEXT_WINDOW_SIZE\n );\n }\n }\n\n /**\n * Generate feedback from verification results\n */\n private generateFeedback(results: VerificationResult[]): string {\n const failed = results.filter((r) => !r.passed);\n const warnings = results.filter(\n (r) => !r.passed && r.severity === 'warning'\n );\n const errors = results.filter((r) => !r.passed && r.severity === 'error');\n\n if (errors.length > 0) {\n const errorMessages = errors.map((e) => `- ${e.message}`).join('\\n');\n return `Verification failed with ${errors.length} error(s):\\n${errorMessages}`;\n }\n\n if (warnings.length > 0) {\n const warningMessages = warnings.map((w) => `- ${w.message}`).join('\\n');\n return `Verification passed with ${warnings.length} warning(s):\\n${warningMessages}`;\n }\n\n return 'All verifications passed successfully';\n }\n\n /**\n * Suggest context adjustment based on verification results\n */\n private suggestContextAdjustment(\n results: VerificationResult[]\n ): string | undefined {\n const failed = results.filter((r) => !r.passed && r.severity === 'error');\n\n if (failed.length === 0) {\n return undefined;\n }\n\n // Generate suggestions based on failure patterns\n const suggestions: string[] = [];\n\n if (failed.some((r) => r.verifierId === 'test-runner')) {\n suggestions.push('Focus on fixing failing tests');\n }\n if (failed.some((r) => r.verifierId === 'linter')) {\n suggestions.push('Address linting errors before proceeding');\n }\n if (failed.some((r) => r.verifierId === 'semantic-validator')) {\n suggestions.push('Review original requirements and adjust approach');\n }\n\n return suggestions.length > 0 ? suggestions.join('; ') : undefined;\n }\n\n /**\n * Handle turn limit reached\n */\n private async handleTurnLimitReached(session: AgentTaskSession): Promise<{\n success: boolean;\n feedback: string;\n shouldContinue: boolean;\n verificationResults: VerificationResult[];\n }> {\n logger.warn('Session reached turn limit', {\n sessionId: session.id,\n taskId: session.taskId,\n turnCount: session.turnCount,\n });\n\n // Check if task can be considered complete\n const task = this.taskStore.getTask(session.taskId);\n const isComplete = this.assessTaskCompletion(session);\n\n if (isComplete) {\n await this.completeSession(session);\n return {\n success: true,\n feedback: 'Task completed successfully within turn limit',\n shouldContinue: false,\n verificationResults: [],\n };\n }\n\n // Mark session as timeout\n session.status = 'timeout';\n this.taskStore.updateTaskStatus(\n session.taskId,\n 'blocked',\n 'Session timeout - manual review needed'\n );\n\n return {\n success: false,\n feedback: `Session reached ${this.MAX_TURNS_PER_SESSION} turn limit. Task requires manual review or retry.`,\n shouldContinue: false,\n verificationResults: [],\n };\n }\n\n /**\n * Assess if task is complete enough\n */\n private assessTaskCompletion(session: AgentTaskSession): boolean {\n // Check if recent verifications are passing\n const recentResults = session.verificationResults.slice(-5);\n const recentPassRate =\n recentResults.filter((r) => r.passed).length / recentResults.length;\n\n // Check if semantic validator passed recently (Spotify's LLM judge)\n const semanticPassed = recentResults.some(\n (r) => r.verifierId === 'semantic-validator' && r.passed\n );\n\n return recentPassRate >= 0.8 && semanticPassed;\n }\n\n /**\n * Complete a session\n */\n private async completeSession(session: AgentTaskSession): Promise<void> {\n session.status = 'completed';\n session.completedAt = new Date();\n\n // Update task status\n this.taskStore.updateTaskStatus(\n session.taskId,\n 'completed',\n 'Agent session completed'\n );\n\n // Clear timeout\n const timeout = this.sessionTimeouts.get(session.id);\n if (timeout) {\n clearTimeout(timeout);\n this.sessionTimeouts.delete(session.id);\n }\n\n // Generate and save session summary to frame\n const summary = this.generateSessionSummary(session);\n this.frameManager.addEvent('observation', {\n type: 'session_summary',\n frameId: session.frameId,\n summary,\n });\n\n logger.info('Session completed', {\n sessionId: session.id,\n taskId: session.taskId,\n turnCount: session.turnCount,\n duration: session.completedAt.getTime() - session.startedAt.getTime(),\n });\n }\n\n /**\n * Generate session summary for frame digest\n */\n private generateSessionSummary(\n session: AgentTaskSession\n ): Record<string, any> {\n const verificationStats = {\n total: session.verificationResults.length,\n passed: session.verificationResults.filter((r) => r.passed).length,\n failed: session.verificationResults.filter((r) => !r.passed).length,\n };\n\n return {\n sessionId: session.id,\n taskId: session.taskId,\n status: session.status,\n turnCount: session.turnCount,\n duration: session.completedAt\n ? session.completedAt.getTime() - session.startedAt.getTime()\n : 0,\n verificationStats,\n feedbackLoop: session.feedbackLoop.slice(-3), // Last 3 feedback entries\n contextWindow: session.contextWindow.slice(-2), // Last 2 context entries\n };\n }\n\n /**\n * Start timeout for session\n */\n private startSessionTimeout(sessionId: string): void {\n const timeout = setTimeout(() => {\n const session = this.activeSessions.get(sessionId);\n if (session && session.status === 'active') {\n session.status = 'timeout';\n this.taskStore.updateTaskStatus(\n session.taskId,\n 'blocked',\n 'Session timeout - no activity'\n );\n logger.warn('Session timed out due to inactivity', { sessionId });\n }\n }, this.SESSION_TIMEOUT_MS);\n\n this.sessionTimeouts.set(sessionId, timeout);\n }\n\n /**\n * Generate unique session ID\n */\n private generateSessionId(taskId: string): string {\n return `session-${taskId}-${Date.now()}`;\n }\n\n /**\n * Get active sessions summary\n */\n getActiveSessions(): Array<{\n sessionId: string;\n taskId: string;\n turnCount: number;\n status: string;\n startedAt: Date;\n }> {\n return Array.from(this.activeSessions.values()).map((session) => ({\n sessionId: session.id,\n taskId: session.taskId,\n turnCount: session.turnCount,\n status: session.status,\n startedAt: session.startedAt,\n }));\n }\n\n /**\n * Retry a failed session (Spotify's 3-retry strategy)\n */\n async retrySession(sessionId: string): Promise<AgentTaskSession | null> {\n const session = this.activeSessions.get(sessionId);\n if (!session || session.status === 'active') {\n return null;\n }\n\n // Count previous retries\n const retryCount = Array.from(this.activeSessions.values()).filter(\n (s) => s.taskId === session.taskId && s.status === 'failed'\n ).length;\n\n if (retryCount >= this.MAX_SESSION_RETRIES) {\n logger.warn('Max retries reached for task', {\n taskId: session.taskId,\n retries: retryCount,\n });\n return null;\n }\n\n // Start new session with learned context\n const newSession = await this.startTaskSession(\n session.taskId,\n session.frameId\n );\n\n // Transfer learned context from previous session\n newSession.contextWindow = session.contextWindow.slice(-3);\n newSession.feedbackLoop = [\n {\n turn: 0,\n action: 'Session retry with learned context',\n result: `Retrying after ${retryCount} previous attempts`,\n verificationPassed: true,\n contextAdjustment: session.feedbackLoop\n .filter((f) => f.contextAdjustment)\n .map((f) => f.contextAdjustment)\n .join('; '),\n },\n ];\n\n return newSession;\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": ["AgentType"]
7
7
  }
@@ -17,7 +17,15 @@ import {
17
17
  generateSessionSummary,
18
18
  formatSummaryMessage
19
19
  } from "../hooks/session-summary.js";
20
- import { sendNotification } from "../hooks/sms-notify.js";
20
+ import { sendNotification, loadSMSConfig } from "../hooks/sms-notify.js";
21
+ import { enableAutoSync } from "../hooks/whatsapp-sync.js";
22
+ import { enableCommands } from "../hooks/whatsapp-commands.js";
23
+ import { startScheduler, listSchedules } from "../hooks/whatsapp-scheduler.js";
24
+ import {
25
+ getModelRouter,
26
+ loadModelRouterConfig
27
+ } from "../core/models/model-router.js";
28
+ import { FallbackMonitor } from "../core/models/fallback-monitor.js";
21
29
  const DEFAULT_SM_CONFIG = {
22
30
  defaultWorktree: false,
23
31
  defaultSandbox: false,
@@ -25,7 +33,8 @@ const DEFAULT_SM_CONFIG = {
25
33
  defaultTracing: true,
26
34
  defaultRemote: false,
27
35
  defaultNotifyOnDone: true,
28
- defaultWhatsApp: false
36
+ defaultWhatsApp: false,
37
+ defaultModelRouting: false
29
38
  };
30
39
  function getConfigPath() {
31
40
  return path.join(os.homedir(), ".stackmemory", "claude-sm.json");
@@ -68,7 +77,9 @@ class ClaudeSM {
68
77
  contextEnabled: true,
69
78
  tracingEnabled: this.smConfig.defaultTracing,
70
79
  verboseTracing: false,
71
- sessionStartTime: Date.now()
80
+ sessionStartTime: Date.now(),
81
+ useModelRouting: this.smConfig.defaultModelRouting,
82
+ useThinkingMode: false
72
83
  };
73
84
  this.stackmemoryPath = this.findStackMemory();
74
85
  this.worktreeScriptPath = path.join(
@@ -249,7 +260,41 @@ class ClaudeSM {
249
260
  }
250
261
  async startWhatsAppServices() {
251
262
  const WEBHOOK_PORT = 3456;
252
- console.log(chalk.cyan("Starting WhatsApp services..."));
263
+ console.log(chalk.cyan("\n\u{1F4F1} WhatsApp Mode - Full Integration"));
264
+ console.log(chalk.gray("\u2500".repeat(42)));
265
+ const smsConfig = loadSMSConfig();
266
+ if (!smsConfig.enabled) {
267
+ console.log(
268
+ chalk.yellow(" Notifications disabled. Run: stackmemory notify enable")
269
+ );
270
+ }
271
+ try {
272
+ enableAutoSync();
273
+ console.log(
274
+ chalk.green(" \u2713 Auto-sync enabled (digests on frame close)")
275
+ );
276
+ } catch {
277
+ console.log(chalk.gray(" Auto-sync: skipped"));
278
+ }
279
+ try {
280
+ enableCommands();
281
+ console.log(chalk.green(" \u2713 Command processing enabled"));
282
+ console.log(
283
+ chalk.gray(" Reply: status, tasks, context, help, build, test, lint")
284
+ );
285
+ } catch {
286
+ console.log(chalk.gray(" Command processing: skipped"));
287
+ }
288
+ try {
289
+ const schedules = listSchedules();
290
+ if (schedules.length > 0) {
291
+ startScheduler();
292
+ console.log(
293
+ chalk.green(` \u2713 Scheduler started (${schedules.length} schedules)`)
294
+ );
295
+ }
296
+ } catch {
297
+ }
253
298
  const webhookRunning = await fetch(
254
299
  `http://localhost:${WEBHOOK_PORT}/health`
255
300
  ).then((r) => r.ok).catch(() => false);
@@ -262,11 +307,11 @@ class ClaudeSM {
262
307
  });
263
308
  webhookProcess.unref();
264
309
  console.log(
265
- chalk.gray(` Webhook server starting on port ${WEBHOOK_PORT}`)
310
+ chalk.green(` \u2713 Webhook server started (port ${WEBHOOK_PORT})`)
266
311
  );
267
312
  } else {
268
313
  console.log(
269
- chalk.gray(` Webhook already running on port ${WEBHOOK_PORT}`)
314
+ chalk.green(` \u2713 Webhook already running (port ${WEBHOOK_PORT})`)
270
315
  );
271
316
  }
272
317
  const ngrokRunning = await fetch("http://localhost:4040/api/tunnels").then((r) => r.ok).catch(() => false);
@@ -276,7 +321,7 @@ class ClaudeSM {
276
321
  stdio: "ignore"
277
322
  });
278
323
  ngrokProcess.unref();
279
- console.log(chalk.gray(" ngrok tunnel starting..."));
324
+ console.log(chalk.gray(" Starting ngrok tunnel..."));
280
325
  await new Promise((resolve) => setTimeout(resolve, 3e3));
281
326
  }
282
327
  try {
@@ -291,15 +336,34 @@ class ClaudeSM {
291
336
  fs.mkdirSync(configDir, { recursive: true });
292
337
  }
293
338
  fs.writeFileSync(configPath, publicUrl);
294
- console.log(
295
- chalk.green(` WhatsApp webhook: ${publicUrl}/sms/incoming`)
296
- );
339
+ console.log(chalk.green(` \u2713 ngrok tunnel: ${publicUrl}/sms/incoming`));
297
340
  }
298
341
  } catch {
342
+ console.log(chalk.yellow(" ngrok: waiting for tunnel..."));
343
+ }
344
+ if (smsConfig.pendingPrompts.length > 0) {
345
+ console.log();
299
346
  console.log(
300
- chalk.yellow(" Waiting for ngrok... URL will be available shortly")
347
+ chalk.yellow(
348
+ ` \u23F3 ${smsConfig.pendingPrompts.length} pending prompt(s) awaiting response:`
349
+ )
301
350
  );
351
+ smsConfig.pendingPrompts.slice(0, 3).forEach((p, i) => {
352
+ const msg = p.message.length > 40 ? p.message.slice(0, 40) + "..." : p.message;
353
+ console.log(chalk.gray(` ${i + 1}. [${p.id}] ${msg}`));
354
+ });
355
+ if (smsConfig.pendingPrompts.length > 3) {
356
+ console.log(
357
+ chalk.gray(` ... and ${smsConfig.pendingPrompts.length - 3} more`)
358
+ );
359
+ }
302
360
  }
361
+ console.log();
362
+ console.log(chalk.gray(" Quick actions from WhatsApp:"));
363
+ console.log(chalk.gray(' "status" - session status'));
364
+ console.log(chalk.gray(' "context" - current frame digest'));
365
+ console.log(chalk.gray(' "1", "2" - respond to prompts'));
366
+ console.log(chalk.gray("\u2500".repeat(42)));
303
367
  }
304
368
  async sendDoneNotification(exitCode) {
305
369
  try {
@@ -430,6 +494,31 @@ class ClaudeSM {
430
494
  this.config.useWorktree = this.hasUncommittedChanges() || this.detectMultipleInstances();
431
495
  }
432
496
  break;
497
+ case "--think":
498
+ case "--think-hard":
499
+ case "--ultrathink":
500
+ this.config.useThinkingMode = true;
501
+ this.config.useModelRouting = true;
502
+ this.config.forceProvider = "qwen";
503
+ break;
504
+ case "--qwen":
505
+ this.config.useModelRouting = true;
506
+ this.config.forceProvider = "qwen";
507
+ break;
508
+ case "--openai":
509
+ this.config.useModelRouting = true;
510
+ this.config.forceProvider = "openai";
511
+ break;
512
+ case "--ollama":
513
+ this.config.useModelRouting = true;
514
+ this.config.forceProvider = "ollama";
515
+ break;
516
+ case "--model-routing":
517
+ this.config.useModelRouting = true;
518
+ break;
519
+ case "--no-model-routing":
520
+ this.config.useModelRouting = false;
521
+ break;
433
522
  default:
434
523
  claudeArgs.push(arg);
435
524
  }
@@ -517,6 +606,45 @@ class ClaudeSM {
517
606
  );
518
607
  }
519
608
  }
609
+ if (this.config.useModelRouting) {
610
+ const routerConfig = loadModelRouterConfig();
611
+ if (routerConfig.enabled || this.config.forceProvider) {
612
+ const router = getModelRouter();
613
+ let routeResult;
614
+ if (this.config.forceProvider) {
615
+ const env = router.switchTo(this.config.forceProvider);
616
+ Object.assign(process.env, env);
617
+ console.log(
618
+ chalk.magenta(`\u{1F500} Model: ${this.config.forceProvider} (forced)`)
619
+ );
620
+ if (this.config.forceProvider === "qwen" && this.config.useThinkingMode) {
621
+ const qwenConfig = routerConfig.providers.qwen;
622
+ if (qwenConfig?.params?.enable_thinking) {
623
+ console.log(
624
+ chalk.gray(
625
+ ` Thinking mode: budget ${qwenConfig.params.thinking_budget || 1e4} tokens`
626
+ )
627
+ );
628
+ }
629
+ }
630
+ } else {
631
+ const taskType = this.config.useThinkingMode ? "think" : "default";
632
+ routeResult = router.route(taskType, this.config.task);
633
+ Object.assign(process.env, routeResult.env);
634
+ if (routeResult.switched) {
635
+ console.log(
636
+ chalk.magenta(`\u{1F500} Model routed to: ${routeResult.provider}`)
637
+ );
638
+ }
639
+ }
640
+ } else {
641
+ console.log(
642
+ chalk.gray(
643
+ " Model routing: disabled (run: stackmemory model enable)"
644
+ )
645
+ );
646
+ }
647
+ }
520
648
  if (this.config.useWhatsApp) {
521
649
  console.log(
522
650
  chalk.cyan("\u{1F4F1} WhatsApp mode: notifications + webhook enabled")
@@ -537,10 +665,36 @@ class ClaudeSM {
537
665
  process.exit(1);
538
666
  return;
539
667
  }
540
- const claude = spawn(claudeBin, claudeArgs, {
541
- stdio: "inherit",
542
- env: process.env
668
+ const fallbackMonitor = new FallbackMonitor({
669
+ enabled: true,
670
+ maxRestarts: 2,
671
+ restartDelayMs: 1500,
672
+ onFallback: (provider, reason) => {
673
+ console.log(chalk.yellow(`
674
+ [auto-fallback] Switching to ${provider}`));
675
+ console.log(chalk.gray(` Reason: ${reason}`));
676
+ console.log(chalk.gray(` Session will continue on ${provider}...`));
677
+ if (this.config.notifyOnDone || this.config.useWhatsApp) {
678
+ sendNotification({
679
+ type: "custom",
680
+ title: "Model Fallback",
681
+ message: `Claude unavailable (${reason}). Switched to ${provider}.`
682
+ }).catch(() => {
683
+ });
684
+ }
685
+ }
543
686
  });
687
+ const fallbackAvailable = fallbackMonitor.isFallbackAvailable();
688
+ if (fallbackAvailable) {
689
+ console.log(
690
+ chalk.gray(` Auto-fallback: Qwen ready (on rate limit/error)`)
691
+ );
692
+ }
693
+ const wrapper = fallbackMonitor.wrapProcess(claudeBin, claudeArgs, {
694
+ env: process.env,
695
+ cwd: process.cwd()
696
+ });
697
+ const claude = wrapper.start();
544
698
  claude.on("error", (err) => {
545
699
  console.error(chalk.red("\u274C Failed to launch Claude CLI."));
546
700
  if (err.code === "ENOENT") {
@@ -559,6 +713,15 @@ class ClaudeSM {
559
713
  process.exit(1);
560
714
  });
561
715
  claude.on("exit", async (code) => {
716
+ const status = fallbackMonitor.getStatus();
717
+ if (status.inFallback) {
718
+ console.log(
719
+ chalk.yellow(
720
+ `
721
+ Session completed on fallback provider: ${status.currentProvider}`
722
+ )
723
+ );
724
+ }
562
725
  this.saveContext("Claude session ended", {
563
726
  action: "session_end",
564
727
  exitCode: code
@@ -624,6 +787,9 @@ configCmd.command("show").description("Show current default settings").action(()
624
787
  console.log(
625
788
  ` defaultWhatsApp: ${config.defaultWhatsApp ? chalk.green("true") : chalk.gray("false")}`
626
789
  );
790
+ console.log(
791
+ ` defaultModelRouting: ${config.defaultModelRouting ? chalk.green("true") : chalk.gray("false")}`
792
+ );
627
793
  console.log(chalk.gray(`
628
794
  Config: ${getConfigPath()}`));
629
795
  });
@@ -638,7 +804,9 @@ configCmd.command("set <key> <value>").description("Set a default (e.g., set wor
638
804
  remote: "defaultRemote",
639
805
  "notify-done": "defaultNotifyOnDone",
640
806
  notifyondone: "defaultNotifyOnDone",
641
- whatsapp: "defaultWhatsApp"
807
+ whatsapp: "defaultWhatsApp",
808
+ "model-routing": "defaultModelRouting",
809
+ modelrouting: "defaultModelRouting"
642
810
  };
643
811
  const configKey = keyMap[key];
644
812
  if (!configKey) {
@@ -704,10 +872,25 @@ configCmd.command("whatsapp-off").description("Disable WhatsApp mode by default"
704
872
  saveSMConfig(config);
705
873
  console.log(chalk.green("WhatsApp mode disabled by default"));
706
874
  });
875
+ configCmd.command("model-routing-on").description(
876
+ "Enable model routing by default (route tasks to Qwen/other models)"
877
+ ).action(() => {
878
+ const config = loadSMConfig();
879
+ config.defaultModelRouting = true;
880
+ saveSMConfig(config);
881
+ console.log(chalk.green("Model routing enabled by default"));
882
+ console.log(chalk.gray("Configure with: stackmemory model setup-qwen"));
883
+ });
884
+ configCmd.command("model-routing-off").description("Disable model routing by default (use Claude only)").action(() => {
885
+ const config = loadSMConfig();
886
+ config.defaultModelRouting = false;
887
+ saveSMConfig(config);
888
+ console.log(chalk.green("Model routing disabled by default"));
889
+ });
707
890
  program.option("-w, --worktree", "Create isolated worktree for this instance").option("-W, --no-worktree", "Disable worktree (override default)").option("-r, --remote", "Enable remote mode (WhatsApp for all questions)").option("--no-remote", "Disable remote mode (override default)").option("-n, --notify-done", "Send WhatsApp notification when session ends").option("--no-notify-done", "Disable notification when session ends").option(
708
891
  "--whatsapp",
709
892
  "Enable WhatsApp mode (auto-start webhook + ngrok + notifications)"
710
- ).option("--no-whatsapp", "Disable WhatsApp mode (override default)").option("-s, --sandbox", "Enable sandbox mode (file/network restrictions)").option("-c, --chrome", "Enable Chrome automation").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--claude-bin <path>", "Path to claude CLI (or use CLAUDE_BIN)").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
893
+ ).option("--no-whatsapp", "Disable WhatsApp mode (override default)").option("-s, --sandbox", "Enable sandbox mode (file/network restrictions)").option("-c, --chrome", "Enable Chrome automation").option("-a, --auto", "Automatically detect and apply best settings").option("-b, --branch <name>", "Specify branch name for worktree").option("-t, --task <desc>", "Task description for context").option("--claude-bin <path>", "Path to claude CLI (or use CLAUDE_BIN)").option("--no-context", "Disable StackMemory context integration").option("--no-trace", "Disable debug tracing (enabled by default)").option("--verbose-trace", "Enable verbose debug tracing with full details").option("--think", "Enable thinking mode with Qwen (deep reasoning)").option("--think-hard", "Alias for --think").option("--ultrathink", "Alias for --think").option("--qwen", "Force Qwen provider for this session").option("--openai", "Force OpenAI provider for this session").option("--ollama", "Force Ollama provider for this session").option("--model-routing", "Enable model routing").option("--no-model-routing", "Disable model routing").helpOption("-h, --help", "Display help").allowUnknownOption(true).action(async (_options) => {
711
894
  const claudeSM = new ClaudeSM();
712
895
  const args = process.argv.slice(2);
713
896
  await claudeSM.run(args);