kantban-cli 0.1.31 → 0.1.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Y3D6ATF2.js → chunk-7HJZFR7Y.js} +14 -1
- package/dist/chunk-7HJZFR7Y.js.map +1 -0
- package/dist/{cron-CHOJF3GJ.js → cron-RG4VCGME.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/{pipeline-YJ7B27QX.js → pipeline-7CMDTEI2.js} +19 -11
- package/dist/{pipeline-YJ7B27QX.js.map → pipeline-7CMDTEI2.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-Y3D6ATF2.js.map +0 -1
- /package/dist/{cron-CHOJF3GJ.js.map → cron-RG4VCGME.js.map} +0 -0
|
@@ -237,6 +237,19 @@ You MUST change approach. What you've been doing is not working. Consider:
|
|
|
237
237
|
- **${ticketContext.tool_prefix}append_run_memory** \u2014 Share discoveries with future agents.
|
|
238
238
|
Params: \`{ section: 'conventions'|'interfaces'|'failures'|'decisions', content: string }\`
|
|
239
239
|
|
|
240
|
+
## Additional MCP Tools
|
|
241
|
+
You also have access to these KantBan MCP tools for reading project data. USE THEM instead of writing scripts or creating files manually:
|
|
242
|
+
- **${ticketContext.tool_prefix}get_document** \u2014 Read a document's full content. Params: \`{ projectId, documentId }\`
|
|
243
|
+
- **${ticketContext.tool_prefix}get_ticket** \u2014 Get ticket details. Params: \`{ projectId, ticketId }\`
|
|
244
|
+
- **${ticketContext.tool_prefix}search_documents_chunked** \u2014 Search documents by query. Params: \`{ projectId, query }\`
|
|
245
|
+
- **${ticketContext.tool_prefix}list_signals** \u2014 List signals for context. Params: \`{ projectId }\`
|
|
246
|
+
- **${ticketContext.tool_prefix}get_field_values** \u2014 Get custom field values. Params: \`{ projectId, ticketId }\`
|
|
247
|
+
- **${ticketContext.tool_prefix}list_comments** \u2014 Read ticket comments. Params: \`{ projectId, ticketId }\`
|
|
248
|
+
- **${ticketContext.tool_prefix}search_tickets** \u2014 Search tickets by query. Params: \`{ projectId, query }\`
|
|
249
|
+
- **${ticketContext.tool_prefix}get_board_context** \u2014 Get full board state. Params: \`{ projectId, boardId }\`
|
|
250
|
+
|
|
251
|
+
**IMPORTANT:** Always prefer MCP tools over file operations for reading project data. Do NOT write helper scripts to fetch data \u2014 call the MCP tools directly.
|
|
252
|
+
|
|
240
253
|
## Knowledge Sharing
|
|
241
254
|
Before moving the ticket to the next column, you MUST:
|
|
242
255
|
1. **Write a signal** via \`${ticketContext.tool_prefix}create_signal\` summarizing the key outcome of your work:
|
|
@@ -1165,4 +1178,4 @@ export {
|
|
|
1165
1178
|
cleanupGateProxyConfigs,
|
|
1166
1179
|
ClaudeProvider
|
|
1167
1180
|
};
|
|
1168
|
-
//# sourceMappingURL=chunk-
|
|
1181
|
+
//# sourceMappingURL=chunk-7HJZFR7Y.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/stuck-detector.ts","../src/lib/parse-utils.ts","../src/lib/prompt-composer.ts","../src/lib/ralph-loop.ts","../src/lib/mcp-config.ts","../src/providers/claude-provider.ts","../src/providers/claude-stream-parser.ts"],"sourcesContent":["import { z } from 'zod';\nimport { parseJsonFromLlmOutput } from './parse-utils.js';\nimport type { GateSnapshot, GateDelta } from '@kantban/types';\n\nexport interface StuckPattern {\n status: 'progressing' | 'spinning' | 'regressing' | 'blocked';\n evidence: string;\n confidence: number;\n}\n\n// Legacy exports preserved for backward compatibility during transition\nexport interface StuckDetectionConfig {\n enabled: boolean;\n firstCheck?: number;\n interval?: number;\n}\n\nexport function shouldCheckStuckDetection(config: StuckDetectionConfig, iteration: number): boolean {\n if (!config.enabled) return false;\n const firstCheck = config.firstCheck ?? 3;\n const interval = config.interval ?? 2;\n if (interval <= 0) return iteration === firstCheck;\n if (iteration < firstCheck) return false;\n if (iteration === firstCheck) return true;\n return (iteration - firstCheck) % interval === 0;\n}\n\n// Legacy types — kept for ralph-loop.ts compatibility until Task 12 replaces them\nexport interface StuckDetectionInput {\n ticketNumber: number;\n ticketTitle: string;\n columnName: string;\n iteration: number;\n maxIterations: number;\n recentComments: Array<{ author: string; body: string }>;\n}\n\n// Legacy function — kept for pipeline.ts compatibility until orchestrator layer is updated\nexport function composeStuckDetectionPrompt(input: StuckDetectionInput): string {\n const commentLines =\n input.recentComments.length > 0\n ? input.recentComments.map((c) => ` [${c.author}] ${c.body.slice(0, 200)}`).join('\\n')\n : ' (no comments)';\n\n return `You are a pipeline trajectory classifier. Analyze recent agent activity and classify the trajectory.\n\nTicket: #${String(input.ticketNumber)} \"${input.ticketTitle}\"\nColumn: ${input.columnName}\nIteration: ${String(input.iteration)} of ${String(input.maxIterations)}\n\nRecent iteration comments:\n${commentLines}\n\nClassify as one of:\n- \"progressing\" — each iteration makes distinct forward progress (new code, new tests, new fields set)\n- \"spinning\" — agent is active but repeating similar actions without advancing (same error, same approach retried, output duplicated)\n- \"blocked\" — agent cannot make progress due to external dependency, missing information, or fundamental task issue\n\nRespond with ONLY a JSON object:\n{\"status\": \"progressing|spinning|blocked\", \"confidence\": 0.0-1.0, \"evidence\": \"one sentence\"}`;\n}\n\nconst StuckDetectionResponseSchema = z.object({\n status: z.enum(['progressing', 'spinning', 'blocked']),\n confidence: z.number().min(0).max(1),\n evidence: z.string(),\n});\n\nexport type StuckDetectionResult = z.infer<typeof StuckDetectionResponseSchema>;\n\n// Legacy function — kept for pipeline.ts compatibility until orchestrator layer is updated\nexport function parseStuckDetectionResponse(raw: string): StuckDetectionResult {\n let parsed: unknown;\n try {\n parsed = parseJsonFromLlmOutput(raw);\n } catch (err) {\n throw new Error(`StuckDetectionResponse: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n // Clamp confidence before validation\n if (parsed && typeof parsed === 'object' && 'confidence' in parsed) {\n const p = parsed as Record<string, unknown>;\n if (typeof p.confidence === 'number') {\n p.confidence = Math.min(1, Math.max(0, p.confidence));\n }\n }\n\n const result = StuckDetectionResponseSchema.safeParse(parsed);\n if (!result.success) {\n throw new Error(`StuckDetectionResponse: validation failed — ${result.error.message}`);\n }\n\n return result.data;\n}\n\n/**\n * Deterministic trajectory classification from gate snapshot history.\n * Replaces the Haiku-based comment classifier.\n */\nexport function classifyTrajectory(snapshots: GateSnapshot[]): StuckPattern {\n if (snapshots.length === 0) {\n return { status: 'progressing', evidence: 'no data', confidence: 0.5 };\n }\n\n const recent = snapshots.slice(-3);\n const deltas = recent.map((s) => s.delta_from_previous);\n\n // Filter out first_check — it's not a real signal\n const meaningful = deltas.filter((d): d is Exclude<GateDelta, 'first_check'> => d !== 'first_check');\n\n if (meaningful.length === 0) {\n return { status: 'progressing', evidence: 'only initial checks', confidence: 0.5 };\n }\n\n // Need at least 2 meaningful deltas to identify a trajectory pattern.\n // A single delta is not enough evidence — the agent may be mid-work\n // (e.g. running a long build/test command) without having produced\n // a visible state change yet.\n if (meaningful.length < 2) {\n return { status: 'progressing', evidence: 'insufficient data for trajectory', confidence: 0.3 };\n }\n\n // All same = spinning\n if (meaningful.every((d) => d === 'same')) {\n return { status: 'spinning', evidence: 'identical gate results', confidence: 1.0 };\n }\n\n // Monotonic regression\n if (meaningful.every((d) => d === 'regressed' || d === 'same')) {\n return { status: 'regressing', evidence: 'gate results degrading', confidence: 1.0 };\n }\n\n // Recent improvement takes precedence over oscillation\n if (meaningful.some((d) => d === 'improved')) {\n const lastMeaningful = meaningful[meaningful.length - 1];\n if (lastMeaningful === 'improved') {\n return { status: 'progressing', evidence: 'gate results improving', confidence: 1.0 };\n }\n }\n\n // Oscillation: improved AND regressed, but last delta is NOT improved\n if (meaningful.some((d) => d === 'improved') && meaningful.some((d) => d === 'regressed')) {\n return { status: 'spinning', evidence: 'oscillating gate results', confidence: 1.0 };\n }\n\n // Stale improvement — has improved sometime but no recent progress\n if (meaningful.some((d) => d === 'improved')) {\n return { status: 'spinning', evidence: 'stale improvement — no recent progress', confidence: 0.8 };\n }\n\n return { status: 'spinning', evidence: 'no improvement detected', confidence: 1.0 };\n}\n","/**\n * Strip markdown code fences from LLM output and parse as JSON.\n * Used by advisor, light-call, and stuck-detector response parsers.\n */\nexport function parseJsonFromLlmOutput(raw: string): unknown {\n // First: try parsing the whole string (handles pure JSON input from tests/structured responses)\n try {\n return JSON.parse(raw.trim());\n } catch {\n // Not a bare JSON string — fall through to fence and brace extraction\n }\n\n // Second: try code fence extraction\n const fenceMatch = raw.match(/```(?:json)?\\s*([\\s\\S]*?)```/i);\n if (fenceMatch?.[1]) {\n const fenced = fenceMatch[1].trim();\n try {\n return JSON.parse(fenced);\n } catch {\n // Fence content wasn't valid JSON — fall through to brace matching\n }\n }\n\n // Fallback: find valid JSON objects with string-context-aware brace matching\n for (let i = 0; i < raw.length; i++) {\n if (raw[i] !== '{') continue;\n let depth = 0;\n let inString = false;\n let escape = false;\n for (let j = i; j < raw.length; j++) {\n const ch = raw[j];\n if (escape) {\n escape = false;\n continue;\n }\n if (ch === '\\\\' && inString) {\n escape = true;\n continue;\n }\n if (ch === '\"') {\n inString = !inString;\n continue;\n }\n if (inString) continue;\n if (ch === '{') depth++;\n if (ch === '}') depth--;\n if (depth === 0) {\n const candidate = raw.slice(i, j + 1);\n try {\n return JSON.parse(candidate);\n } catch {\n break; // This opening brace didn't work, try next\n }\n }\n }\n }\n\n throw new Error(`Invalid JSON in LLM output: ${raw.slice(0, 120)}`);\n}\n","import type { AgentConfig, GateResult } from '@kantban/types';\nimport { VALID_BRANCH_RE } from './gate-config.js';\n\ninterface ColumnContext {\n scope: 'column';\n column: { id: string; name: string; goal: string | null };\n prompt_document: { id: string; title: string; content: string } | null;\n agent_config: AgentConfig | null;\n tickets: Array<{ id: string; ticket_number: number; title: string }>;\n transition_rules: string;\n signals: string[];\n field_definitions: Array<{ id: string; name: string; type: string }>;\n tool_prefix: string;\n}\n\ninterface TicketContext {\n scope: 'ticket';\n ticket: {\n id: string;\n ticket_number: number;\n title: string;\n description: string;\n backward_transitions: number;\n assignee: { id: string; name: string } | null;\n column: { id: string; name: string; type: string } | null;\n };\n field_values: Array<{ field_name: string; value: unknown }>;\n transitions: Array<{ from: string | null; to: string; handoff: unknown; timestamp: string }>;\n comments: Array<{ author: string; body: string; created_at: string; pinned?: boolean }>;\n ticket_links: Array<{\n direction: 'outward' | 'inward';\n link_type: 'blocks' | 'relates_to' | 'duplicates';\n ticket_id: string;\n ticket_number: number;\n title: string;\n column_name: string | null;\n resolved: boolean;\n }>;\n parent: { id: string; ticket_number: number; title: string } | null;\n children: Array<{ id: string; ticket_number: number; title: string; column_name: string | null }>;\n signals: string[];\n linked_documents: Array<{ id: string; title: string; content?: string; truncated?: boolean }>;\n transition_rules: string;\n dependency_requirements: string;\n tool_prefix: string;\n}\n\ninterface IterationMeta {\n iteration: number;\n maxIterations: number;\n projectId: string;\n gutterCount: number;\n gutterThreshold: number;\n lookaheadDocument?: { title: string; content: string } | undefined;\n runMemoryContent?: string | undefined;\n gateResults?: GateResult[] | undefined;\n rejectionFindings?: string | undefined;\n}\n\nexport type { ColumnContext, TicketContext };\n\n// --- Token budget system ---\n\nexport const PROMPT_BUDGETS = {\n system_preamble: 800,\n gate_results: 500,\n column_prompt: Infinity, // NEVER truncated\n lookahead: 1000,\n run_memory: 1000,\n rejection: 500,\n ticket_details: 1500,\n comments: 2000,\n transition_rules: 500,\n transition_history: 1000,\n dependency_requirements: 500,\n linked_documents: 2000,\n metadata: 200,\n} as const;\n\nexport function estimateTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n\nexport function truncateToTokens(text: string, maxTokens: number): string {\n if (maxTokens === Infinity) return text;\n const maxChars = maxTokens * 4;\n if (text.length <= maxChars) return text;\n return text.slice(0, maxChars) + '\\n[...truncated]';\n}\n\nexport function windowComments(\n comments: Array<{ author: string; body: string; created_at: string; pinned?: boolean }>,\n maxTokens: number,\n): string {\n const pinned = comments.filter((c) => c.pinned);\n const unpinned = comments.filter((c) => !c.pinned);\n const recentFull = unpinned.slice(-3);\n const older = unpinned.slice(0, -3);\n\n const parts: string[] = [];\n\n // Pinned always included in full\n for (const c of pinned) {\n parts.push(`[pinned] **${c.author}** (${c.created_at}):\\n${c.body}\\n`);\n }\n\n // Recent 3 in full\n for (const c of recentFull) {\n parts.push(`**${c.author}** (${c.created_at}):\\n${c.body}\\n`);\n }\n\n // Older compressed to one line\n for (const c of older) {\n const firstLine = c.body.split('\\n')[0]?.slice(0, 100) ?? '';\n parts.push(`- ${c.author}: ${firstLine}`);\n }\n\n const joined = parts.join('\\n');\n return truncateToTokens(joined, maxTokens);\n}\n\n// --- Prompt composer ---\n\nexport function composePrompt(\n columnContext: ColumnContext,\n ticketContext: TicketContext,\n meta: IterationMeta,\n): string {\n const parts: string[] = [];\n\n if (!columnContext.prompt_document?.content) {\n throw new Error(\n `Column \"${columnContext.column.name}\" has no prompt document. ` +\n `Configure a prompt document for this column before running the pipeline.`\n );\n }\n\n // --- 1. System preamble (identity, iteration N/M) ---\n const hasUnresolvedBlockers = ticketContext.ticket_links.some(\n (l) => l.direction === 'inward' && l.link_type === 'blocks' && !l.resolved,\n );\n\n parts.push(`# Pipeline Agent Instructions\n\nYou are a pipeline automation agent processing ticket #${String(ticketContext.ticket.ticket_number)}: \"${ticketContext.ticket.title}\".\n\n## Your Goal\nAdvance this ticket through the pipeline. Your success criteria:\n1. Complete the work described in the ticket and column prompt below\n2. Move the ticket to the next column using \\`${ticketContext.tool_prefix}move_ticket\\`\n3. Or mark it complete using \\`${ticketContext.tool_prefix}complete_task\\`\n\n## Iteration ${meta.iteration} of ${meta.maxIterations}\n${meta.iteration >= meta.maxIterations - 1 ? '**FINAL ITERATIONS** — prioritize moving the ticket NOW or it will be marked stalled.' : `You have ${meta.maxIterations - meta.iteration} iterations remaining. Make meaningful progress each iteration.`}\n${meta.gutterCount > 0 ? `\n## Progress Warning\nNo meaningful progress detected for ${meta.gutterCount} consecutive iteration(s).\n${meta.gutterThreshold - meta.gutterCount} iteration(s) remain before this loop is terminated as stalled.\n\nYou MUST change approach. What you've been doing is not working. Consider:\n- Breaking the problem into smaller steps\n- Setting a field value to record partial progress\n- Creating a comment explaining what's blocking you\n- Asking for help via a signal\n` : ''}\n## Available Tools (prefix: ${ticketContext.tool_prefix})\n- **${ticketContext.tool_prefix}check_transition** — ALWAYS call this before moving. Returns allowed/blocked with recovery steps.\n Params: \\`{ projectId, boardId, ticketId, targetColumnId }\\`\n- **${ticketContext.tool_prefix}move_ticket** — Move ticket to a new column. Include handoff data for the next agent.\n Params: \\`{ projectId, ticketId, column_id, handoff: { branch?, commit_sha?, build_status?, notes? } }\\`\n- **${ticketContext.tool_prefix}complete_task** — Move to a done column with handoff data.\n Params: \\`{ projectId, ticketId, handoff: { ... } }\\`\n- **${ticketContext.tool_prefix}set_field_value** — Set a required field before moving (check transition rules).\n Params: \\`{ projectId, ticketId, fieldId, value }\\`\n- **${ticketContext.tool_prefix}create_signal** — Leave knowledge for future agents at any scope.\n- **${ticketContext.tool_prefix}create_comment** — Add progress notes to the ticket.\n- **${ticketContext.tool_prefix}append_run_memory** — Share discoveries with future agents.\n Params: \\`{ section: 'conventions'|'interfaces'|'failures'|'decisions', content: string }\\`\n\n## Additional MCP Tools\nYou also have access to these KantBan MCP tools for reading project data. USE THEM instead of writing scripts or creating files manually:\n- **${ticketContext.tool_prefix}get_document** — Read a document's full content. Params: \\`{ projectId, documentId }\\`\n- **${ticketContext.tool_prefix}get_ticket** — Get ticket details. Params: \\`{ projectId, ticketId }\\`\n- **${ticketContext.tool_prefix}search_documents_chunked** — Search documents by query. Params: \\`{ projectId, query }\\`\n- **${ticketContext.tool_prefix}list_signals** — List signals for context. Params: \\`{ projectId }\\`\n- **${ticketContext.tool_prefix}get_field_values** — Get custom field values. Params: \\`{ projectId, ticketId }\\`\n- **${ticketContext.tool_prefix}list_comments** — Read ticket comments. Params: \\`{ projectId, ticketId }\\`\n- **${ticketContext.tool_prefix}search_tickets** — Search tickets by query. Params: \\`{ projectId, query }\\`\n- **${ticketContext.tool_prefix}get_board_context** — Get full board state. Params: \\`{ projectId, boardId }\\`\n\n**IMPORTANT:** Always prefer MCP tools over file operations for reading project data. Do NOT write helper scripts to fetch data — call the MCP tools directly.\n\n## Knowledge Sharing\nBefore moving the ticket to the next column, you MUST:\n1. **Write a signal** via \\`${ticketContext.tool_prefix}create_signal\\` summarizing the key outcome of your work:\n - What you discovered, built, validated, or decided\n - Scope it to the ticket (\\`ticketId\\`) so future agents on this ticket see it\n - One concise signal is better than none — don't skip this step\n2. **Contribute to run memory** via \\`${ticketContext.tool_prefix}append_run_memory\\` if you discovered:\n - Conventions or patterns others should follow (\\`section: 'conventions'\\`)\n - Interfaces or APIs consumed or created (\\`section: 'interfaces'\\`)\n - Failures or dead-ends to avoid (\\`section: 'failures'\\`)\n - Design decisions and their rationale (\\`section: 'decisions'\\`)\n\nSignals and run memory are how you communicate with future agents. Without them, the next agent starts blind.\n\n## Iteration Summary\nIf you made meaningful progress this iteration, create a comment using \\`${ticketContext.tool_prefix}create_comment\\` summarizing:\n- What you accomplished\n- What remains to be done\n- Any blockers or issues encountered\n\n## Critical Rules\n1. **Always call ${ticketContext.tool_prefix}check_transition before ${ticketContext.tool_prefix}move_ticket** — moves that violate workflow rules will fail.\n2. **If the ticket has UNRESOLVED blockers, do not attempt to move it.** Create a comment explaining you are waiting, then stop.\n3. **Include handoff data** when moving — the next agent needs context (branch name, build status, etc.).\n4. **If you cannot make progress**, create a comment explaining why and stop. Do not burn iterations.\n${hasUnresolvedBlockers ? '\\n**WARNING: This ticket has UNRESOLVED blockers. Do NOT attempt to move it. Document your status and stop.**\\n' : ''}\n---\n`);\n\n // Worktree context — operational instructions when worktree isolation is active\n const worktreeConfig = columnContext.agent_config?.worktree;\n if (worktreeConfig?.enabled) {\n const integrationBranch = worktreeConfig.integration_branch ?? 'main';\n\n if (!VALID_BRANCH_RE.test(integrationBranch)) {\n parts.push(`## Git Worktree\\n\\nYou are working in an isolated git worktree. Integration branch name is invalid — contact the pipeline operator.`);\n } else {\n parts.push(`## Git Worktree\n\nYou are working in an isolated git worktree. Before starting any code changes:\n1. Verify the origin remote exists: \\`git remote -v\\`. If origin is missing, add it: \\`git remote add origin $(git -C \"$(git worktree list | head -1 | awk '{print $1}')\" remote get-url origin)\\`\n2. Merge \\`${integrationBranch}\\` into your current branch: \\`git merge '${integrationBranch}'\\`\n3. Resolve any merge conflicts before proceeding with ticket work\n4. Never rebase — always merge (rebase destroys traceability across the pipeline)\n\nAfter completing your work, commit all changes to your worktree branch.\n`);\n }\n }\n\n // --- 2. Signals (guardrails — at the top) ---\n if (ticketContext.signals.length > 0) {\n parts.push(`\\n## Signals (Guardrails)\\n`);\n for (const s of ticketContext.signals) {\n parts.push(`- ${s}`);\n }\n }\n\n // --- 3. Previous gate results (what failed) ---\n if (meta.gateResults && meta.gateResults.length > 0) {\n parts.push(`\\n## Previous Gate Results\\n`);\n for (const r of meta.gateResults) {\n const status = r.passed ? 'PASS' : 'FAIL';\n const req = r.required ? '(required)' : '(advisory)';\n parts.push(`- **${r.name}** ${status} ${req} [${String(r.duration_ms)}ms]`);\n if (!r.passed && r.output) {\n parts.push(` \\`\\`\\`\\n ${truncateToTokens(r.output, 200)}\\n \\`\\`\\``);\n }\n }\n }\n\n // --- 4. Rejection elevation ---\n // Prefer meta.rejectionFindings, fall back to comment-based detection for backward compat\n if (meta.rejectionFindings) {\n parts.push(`\\n## Previous Rejection (fix these before resubmitting)\\n`);\n parts.push(truncateToTokens(meta.rejectionFindings, PROMPT_BUDGETS.rejection));\n } else {\n const rejectionComment = ticketContext.comments\n .slice()\n .reverse()\n .find((c) => c.body.startsWith('QA REJECTION:') || c.body.startsWith('REJECTION:'));\n if (rejectionComment) {\n parts.push(`\\n## Previous Rejection (fix these before resubmitting)\\n`);\n parts.push(truncateToTokens(rejectionComment.body, PROMPT_BUDGETS.rejection));\n }\n }\n\n // --- 5. Column prompt document (NEVER truncated) ---\n if (columnContext.prompt_document?.content) {\n parts.push(columnContext.prompt_document.content);\n }\n\n // --- 6. Lookahead — downstream column criteria ---\n if (meta.lookaheadDocument?.content) {\n parts.push(`\\n## Downstream Criteria (build to pass these)\\n`);\n parts.push(`*From: ${meta.lookaheadDocument.title}*\\n`);\n parts.push(truncateToTokens(meta.lookaheadDocument.content, PROMPT_BUDGETS.lookahead));\n }\n\n // --- 7. Run memory — cross-agent knowledge ---\n if (meta.runMemoryContent) {\n parts.push(`\\n## Run Memory\\n`);\n parts.push(truncateToTokens(meta.runMemoryContent, PROMPT_BUDGETS.run_memory));\n }\n\n // --- 8. Ticket details (budgeted as a whole section) ---\n const ticketParts: string[] = [];\n ticketParts.push(`## Current Ticket\\n`);\n ticketParts.push(`**Title:** ${ticketContext.ticket.title}`);\n ticketParts.push(`**Ticket ID:** ${ticketContext.ticket.id}`);\n ticketParts.push(`**Ticket Number:** ${String(ticketContext.ticket.ticket_number)}`);\n if (ticketContext.ticket.description) {\n ticketParts.push(`\\n${ticketContext.ticket.description}`);\n }\n\n if (ticketContext.ticket.assignee) {\n ticketParts.push(`**Assignee:** ${ticketContext.ticket.assignee.name}`);\n }\n if (ticketContext.ticket.column) {\n ticketParts.push(`**Current Column:** ${ticketContext.ticket.column.name} (${ticketContext.ticket.column.type})`);\n }\n if (ticketContext.ticket.backward_transitions > 0) {\n ticketParts.push(`**Backward Transitions:** ${String(ticketContext.ticket.backward_transitions)}`);\n }\n\n if (ticketContext.field_values.length > 0) {\n ticketParts.push(`\\n## Field Values\\n`);\n for (const fv of ticketContext.field_values) {\n ticketParts.push(`- **${fv.field_name}:** ${fv.value !== null ? JSON.stringify(fv.value) : '(not set)'}`);\n }\n }\n\n if (ticketContext.parent) {\n ticketParts.push(`\\n## Parent Ticket\\n`);\n ticketParts.push(`- #${String(ticketContext.parent.ticket_number)}: ${ticketContext.parent.title}`);\n }\n if (ticketContext.children.length > 0) {\n ticketParts.push(`\\n## Child Tickets\\n`);\n for (const child of ticketContext.children) {\n ticketParts.push(`- #${String(child.ticket_number)}: ${child.title}${child.column_name ? ` (${child.column_name})` : ''}`);\n }\n }\n\n if (ticketContext.transitions.length > 0) {\n ticketParts.push(`\\n## Transition History\\n`);\n let transitionTokens = 0;\n for (const t of ticketContext.transitions) {\n let line = `- ${t.from ?? 'Backlog'} → ${t.to}`;\n if (t.handoff) line += ` (handoff: ${JSON.stringify(t.handoff)})`;\n const lineTokens = estimateTokens(line);\n if (transitionTokens + lineTokens > PROMPT_BUDGETS.transition_history) {\n ticketParts.push(`- [...truncated — ${String(ticketContext.transitions.length)} total transitions]`);\n break;\n }\n ticketParts.push(line);\n transitionTokens += lineTokens;\n }\n }\n\n if (ticketContext.ticket_links.length > 0) {\n ticketParts.push(`\\n## Ticket Links\\n`);\n const blockers = ticketContext.ticket_links.filter(l => l.direction === 'inward' && l.link_type === 'blocks');\n const blocking = ticketContext.ticket_links.filter(l => l.direction === 'outward' && l.link_type === 'blocks');\n const related = ticketContext.ticket_links.filter(l => l.link_type === 'relates_to');\n if (blockers.length > 0) {\n ticketParts.push(`**Blocked by:**`);\n for (const l of blockers) {\n const status = l.resolved ? '(resolved)' : 'UNRESOLVED';\n ticketParts.push(`- #${String(l.ticket_number)}: ${l.title} [${l.column_name ?? 'backlog'}] ${status}`);\n }\n }\n if (blocking.length > 0) {\n ticketParts.push(`**Blocks:**`);\n for (const l of blocking) {\n ticketParts.push(`- #${String(l.ticket_number)}: ${l.title} [${l.column_name ?? 'backlog'}]`);\n }\n }\n if (related.length > 0) {\n ticketParts.push(`**Related:**`);\n for (const l of related) {\n ticketParts.push(`- #${String(l.ticket_number)}: ${l.title}`);\n }\n }\n }\n\n parts.push(truncateToTokens(ticketParts.join('\\n'), PROMPT_BUDGETS.ticket_details));\n\n // --- 9. Comments (windowed) ---\n if (ticketContext.comments.length > 0) {\n parts.push(`\\n## Comments\\n`);\n parts.push(windowComments(ticketContext.comments, PROMPT_BUDGETS.comments));\n }\n\n // --- 10. Transition rules ---\n if (ticketContext.transition_rules) {\n parts.push(`\\n## Transition Rules\\n`);\n parts.push(truncateToTokens(ticketContext.transition_rules, PROMPT_BUDGETS.transition_rules));\n }\n\n // Dependency requirements (budgeted)\n if (ticketContext.dependency_requirements && ticketContext.dependency_requirements !== 'No dependency or field requirements configured.') {\n parts.push(`\\n## Dependency & Field Requirements\\n`);\n parts.push(truncateToTokens(ticketContext.dependency_requirements, PROMPT_BUDGETS.dependency_requirements));\n }\n\n // --- 11. Linked documents (truncated to budget) ---\n if (ticketContext.linked_documents.length > 0) {\n parts.push(`\\n## Linked Documents\\n`);\n let remainingTokens: number = PROMPT_BUDGETS.linked_documents;\n for (const doc of ticketContext.linked_documents) {\n if (doc.content) {\n const docHeader = `### ${doc.title}\\n`;\n const headerTokens = estimateTokens(docHeader);\n const contentTokens = estimateTokens(doc.content);\n if (headerTokens + contentTokens <= remainingTokens) {\n parts.push(docHeader);\n parts.push(doc.content);\n remainingTokens -= headerTokens + contentTokens;\n } else if (remainingTokens > headerTokens + 50) {\n // Truncate the document content to fit remaining budget\n parts.push(docHeader);\n parts.push(truncateToTokens(doc.content, remainingTokens - headerTokens));\n remainingTokens = 0;\n } else {\n parts.push(`- ${doc.title} (truncated — token budget exceeded)`);\n remainingTokens = 0;\n }\n } else if (doc.truncated) {\n parts.push(`- ${doc.title} (document too large — read via kantban_get_document)`);\n }\n if (remainingTokens <= 0) break;\n }\n }\n\n // --- 12. Metadata ---\n parts.push(`\\n## Pipeline Metadata\\n`);\n parts.push(`- Iteration: ${meta.iteration} / ${meta.maxIterations}`);\n parts.push(`- Project ID: ${meta.projectId}`);\n parts.push(`- Tool Prefix: ${ticketContext.tool_prefix}`);\n parts.push(`- Column: ${columnContext.column.name}`);\n parts.push(`- Goal: ${columnContext.column.goal ?? 'No goal set'}`);\n\n return parts.join('\\n');\n}\n","import { composePrompt, type ColumnContext, type TicketContext } from './prompt-composer.js';\nimport type { TicketFingerprint, GateSnapshot } from '@kantban/types';\nimport type { LoopCheckpoint } from './checkpoint.js';\nimport { shouldCheckStuckDetection, classifyTrajectory, type StuckDetectionConfig, type StuckDetectionInput, type StuckDetectionResult } from './stuck-detector.js';\nimport type { AgentProvider, AgentRequest, NormalizedStreamEvent, McpConfig } from '../providers/types.js';\n\nexport interface LoopConfig {\n maxIterations: number;\n gutterThreshold: number;\n model?: string;\n maxBudgetUsd?: number | null;\n worktreeName?: string;\n postMoveRetryDelayMs?: number;\n lookaheadColumnId?: string;\n runId?: string;\n startIteration?: number;\n startGutterCount?: number;\n onCheckpoint?: (ticketId: string, checkpoint: LoopCheckpoint) => Promise<void>;\n startFingerprint?: TicketFingerprint;\n stuckDetection?: StuckDetectionConfig;\n invokeStuckDetection?: (input: StuckDetectionInput) => Promise<StuckDetectionResult>;\n /** Run gates after each iteration and record snapshot. Returns GateSnapshot. */\n onPostIterationGates?: (ticketId: string, iteration: number) => Promise<GateSnapshot>;\n /** Check if the pipeline-level budget is exhausted. Checked between iterations. */\n isBudgetExhausted?: () => boolean;\n /** Resolved tool restrictions from profile */\n toolRestrictions?: {\n tools?: string;\n allowedTools?: string[];\n disallowedTools?: string[];\n includeMcpConfig?: boolean;\n };\n}\n\nexport interface LoopResult {\n reason: 'moved' | 'max_iterations' | 'stalled' | 'stopped' | 'error' | 'deleted' | 'budget';\n iterations: number;\n gutterCount: number;\n lastError?: string;\n model?: string;\n /** Final gate snapshot (if gates enabled) */\n finalGateSnapshot?: GateSnapshot;\n /** Final output from the loop (used by evaluator columns for verdict parsing) */\n output?: string;\n /** Cumulative tokens consumed (input) across all iterations */\n tokensIn?: number;\n /** Cumulative tokens consumed (output) across all iterations */\n tokensOut?: number;\n /** Cumulative tool calls across all iterations */\n toolCallCount?: number;\n /** Total wall-clock duration in ms across all iterations */\n durationMs?: number;\n}\n\nexport interface RalphLoopDeps {\n fetchTicketContext: (ticketId: string) => Promise<TicketContext>;\n fetchColumnContext: (columnId: string) => Promise<ColumnContext>;\n fetchFingerprint: (ticketId: string) => Promise<TicketFingerprint>;\n provider: AgentProvider;\n mcpConfig?: McpConfig;\n projectId: string;\n log?: (message: string) => void;\n /** Fetch run memory content. Returns '' if not available. */\n fetchRunMemoryContent?: (() => Promise<string>) | undefined;\n /** Fetch the lookahead document for QA-forward prompting. */\n fetchLookaheadDocument?: (() => Promise<{ title: string; content: string } | undefined>) | undefined;\n /** Forward each normalized stream event (for live pipeline streaming to browser) */\n onStreamEvent?: ((event: NormalizedStreamEvent, context: { runId: string; ticketId: string; columnId: string }) => void) | undefined;\n /** Emitted before each provider invocation */\n onSessionStart?: ((meta: { runId: string; model: string; iteration: number }) => void) | undefined;\n /** Emitted after each provider invocation completes */\n onSessionEnd?: ((meta: { runId: string; exitCode: number; tokensIn: number; tokensOut: number; toolCallCount: number; durationMs: number }) => void) | undefined;\n}\n\nconst API_TIMEOUT_MS = 30_000; // 30s timeout for API calls (fingerprint, context)\n\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n let timer: ReturnType<typeof setTimeout>;\n return Promise.race([\n promise.finally(() => clearTimeout(timer)),\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms);\n }),\n ]);\n}\n\nexport class RalphLoop {\n private ticketId: string;\n private columnId: string;\n private config: LoopConfig;\n private deps: RalphLoopDeps;\n private stopped = false;\n private currentIteration = 0;\n\n constructor(ticketId: string, columnId: string, config: LoopConfig, deps: RalphLoopDeps) {\n this.ticketId = ticketId;\n this.columnId = columnId;\n this.config = config;\n this.deps = deps;\n }\n\n stop(): void {\n this.stopped = true;\n }\n\n get iteration(): number {\n return this.currentIteration;\n }\n\n private looksLike404(err: unknown): boolean {\n const statusCode = (err as { statusCode?: number })?.statusCode\n ?? (err as { status?: number })?.status;\n if (statusCode === 404) return true;\n const message = err instanceof Error ? err.message : String(err);\n return message.includes('API error 404');\n }\n\n async run(): Promise<LoopResult> {\n let gutterCount = this.config.startGutterCount ?? 0;\n let lastFingerprint: TicketFingerprint | null = this.config.startFingerprint ?? null;\n const log = this.deps.log ?? (() => {});\n const resolvedModel = this.config.model ?? 'default';\n const gateSnapshots: GateSnapshot[] = [];\n let cumulativeTokensIn = 0;\n let cumulativeTokensOut = 0;\n let cumulativeToolCalls = 0;\n let cumulativeDurationMs = 0;\n\n const withCosts = (r: LoopResult): LoopResult => ({\n ...r,\n tokensIn: cumulativeTokensIn,\n tokensOut: cumulativeTokensOut,\n toolCallCount: cumulativeToolCalls,\n durationMs: cumulativeDurationMs,\n });\n let lastOutput = '';\n\n const startIter = this.config.startIteration ?? 1;\n for (let i = startIter; i <= this.config.maxIterations; i++) {\n if (this.stopped) return withCosts({ reason: 'stopped', iterations: i - 1, gutterCount, model: resolvedModel });\n if (this.config.isBudgetExhausted?.()) {\n log(`Budget exhausted — stopping after ${i - 1} iterations`);\n return withCosts({ reason: 'budget', iterations: i - 1, gutterCount, model: resolvedModel });\n }\n this.currentIteration = i;\n log(`Iteration ${i}/${this.config.maxIterations} starting`);\n\n // Capture baseline fingerprint on first iteration\n if (!lastFingerprint) {\n try {\n lastFingerprint = await withTimeout(\n this.deps.fetchFingerprint(this.ticketId),\n API_TIMEOUT_MS, 'fetchFingerprint (baseline)',\n );\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log(`Baseline fingerprint fetch failed: ${message}`);\n if (this.looksLike404(err)) {\n return withCosts({ reason: 'deleted', iterations: i, gutterCount, model: resolvedModel });\n }\n return withCosts({ reason: 'error', iterations: i, gutterCount, lastError: `Baseline fingerprint failed: ${message}`, model: resolvedModel });\n }\n }\n\n // Fetch ticket and column context for this iteration\n let ticketCtx: TicketContext;\n let columnCtx: ColumnContext;\n try {\n [ticketCtx, columnCtx] = await Promise.all([\n withTimeout(this.deps.fetchTicketContext(this.ticketId), API_TIMEOUT_MS, 'fetchTicketContext'),\n withTimeout(this.deps.fetchColumnContext(this.columnId), API_TIMEOUT_MS, 'fetchColumnContext'),\n ]);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log(`Context fetch failed: ${message}`);\n if (this.looksLike404(err)) {\n return withCosts({ reason: 'deleted', iterations: i, gutterCount, model: resolvedModel });\n }\n return withCosts({ reason: 'error', iterations: i, gutterCount, lastError: `Context fetch failed: ${message}`, model: resolvedModel });\n }\n\n // Pre-flight column check — bail if ticket already moved\n if (ticketCtx.ticket.column && ticketCtx.ticket.column.id !== this.columnId) {\n log(`Ticket already in column ${ticketCtx.ticket.column.name}, not ${this.columnId} — exiting as moved`);\n return withCosts({ reason: 'moved', iterations: i - 1, gutterCount, model: resolvedModel });\n }\n\n // Fetch run memory and lookahead enrichments — non-blocking, failures are silenced\n const runMemoryContent = this.deps.fetchRunMemoryContent\n ? await this.deps.fetchRunMemoryContent().catch(() => '')\n : undefined;\n\n const lookaheadDocument = this.deps.fetchLookaheadDocument\n ? await this.deps.fetchLookaheadDocument().catch(() => undefined)\n : undefined;\n\n // Compose prompt from column context, ticket context, and enrichments\n let prompt: string;\n try {\n prompt = composePrompt(columnCtx, ticketCtx, {\n iteration: i,\n maxIterations: this.config.maxIterations,\n projectId: this.deps.projectId,\n gutterCount,\n gutterThreshold: this.config.gutterThreshold,\n runMemoryContent: runMemoryContent || undefined,\n lookaheadDocument,\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log(`Prompt composition failed: ${message}`);\n return withCosts({ reason: 'error', iterations: i, gutterCount, lastError: `Prompt composition failed: ${message}`, model: resolvedModel });\n }\n\n // Invoke the agent provider with the composed prompt\n if (this.stopped) return withCosts({ reason: 'stopped', iterations: i - 1, gutterCount, model: resolvedModel });\n log(`Invoking ${this.deps.provider.displayName} (model=${this.config.model ?? 'default'})`);\n\n const iterationRunId = `${this.config.runId ?? this.ticketId}-iter-${String(i)}`;\n\n this.deps.onSessionStart?.({\n runId: iterationRunId,\n model: this.config.model ?? 'default',\n iteration: i,\n });\n\n const streamContext = { runId: iterationRunId, ticketId: this.ticketId, columnId: this.columnId };\n const agentRequest: AgentRequest = {\n prompt,\n ...(this.config.model != null && { model: this.config.model }),\n ...(this.config.maxBudgetUsd != null && { maxTurns: Math.max(1, Math.ceil(this.config.maxBudgetUsd * 10)) }),\n ...(this.config.worktreeName != null && { workingDirectory: this.config.worktreeName }),\n ...(this.deps.mcpConfig != null && { mcpConfig: this.deps.mcpConfig }),\n ...(this.config.toolRestrictions != null && {\n toolRestrictions: { ...this.config.toolRestrictions, includeMcpConfig: this.config.toolRestrictions.includeMcpConfig ?? true },\n }),\n onStreamEvent: (event) => this.deps.onStreamEvent?.(event, streamContext),\n };\n\n const { exitCode, output, toolCallCount, usage, durationMs: iterationDurationMs, degradedCapabilities } = await this.deps.provider.invoke(agentRequest);\n const tokensIn = usage.inputTokens;\n const tokensOut = usage.outputTokens;\n\n // Log degraded capabilities on first iteration only\n if (i === startIter && degradedCapabilities?.length) {\n log(`Provider ${this.deps.provider.id} degraded: ${degradedCapabilities.join(', ')}`);\n }\n\n cumulativeTokensIn += tokensIn;\n cumulativeTokensOut += tokensOut;\n cumulativeToolCalls += toolCallCount;\n cumulativeDurationMs += iterationDurationMs;\n\n this.deps.onSessionEnd?.({\n runId: iterationRunId,\n exitCode,\n tokensIn,\n tokensOut,\n toolCallCount,\n durationMs: iterationDurationMs,\n });\n\n if (exitCode !== 0) {\n const snippet = output.slice(-200);\n log(`${this.deps.provider.displayName} exited with code ${exitCode}: ${snippet}`);\n return withCosts({ reason: 'error', iterations: i, gutterCount, lastError: `non-zero exit code: ${exitCode}. Last output: ${snippet}`, model: resolvedModel });\n }\n log(`${this.deps.provider.displayName} exited successfully`);\n lastOutput = output;\n\n // Post-iteration fingerprint check — detect if ticket moved out of column\n let afterFp: TicketFingerprint;\n try {\n const retryDelayMs = this.config.postMoveRetryDelayMs ?? 1500;\n afterFp = await withTimeout(\n this.deps.fetchFingerprint(this.ticketId),\n API_TIMEOUT_MS, 'fetchFingerprint (post-iteration)',\n );\n\n if (afterFp.column_id === this.columnId) {\n for (let retry = 0; retry < 2; retry++) {\n await new Promise((r) => setTimeout(r, retryDelayMs));\n if (this.stopped) break; // Honor stop during retry delay\n try {\n afterFp = await withTimeout(\n this.deps.fetchFingerprint(this.ticketId),\n API_TIMEOUT_MS, 'fetchFingerprint (retry)',\n );\n } catch (err) {\n log(`Fingerprint retry ${retry + 1} failed: ${err instanceof Error ? err.message : String(err)} — using last good value`);\n break; // retry fingerprint failed — use last good value\n }\n if (afterFp.column_id !== this.columnId) break;\n }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log(`Post-iteration fingerprint failed: ${message}`);\n if (this.looksLike404(err)) {\n return withCosts({ reason: 'deleted', iterations: i, gutterCount, model: resolvedModel });\n }\n return withCosts({ reason: 'error', iterations: i, gutterCount, lastError: `Post-iteration fingerprint failed: ${message}`, model: resolvedModel });\n }\n\n // Did ticket move?\n if (afterFp.column_id !== this.columnId) {\n log(`Ticket moved to column ${afterFp.column_id ?? 'null'}`);\n return withCosts({ reason: 'moved', iterations: i, gutterCount, model: resolvedModel, output: lastOutput });\n }\n\n // Save gutter count before gate delta adjustment so stuck detection\n // can replace (not compound on) the per-iteration gate delta signal.\n const gutterBeforeGates = gutterCount;\n\n // Gate-based gutter detection (preferred) or fingerprint fallback\n if (this.config.onPostIterationGates) {\n try {\n const snapshot = await this.config.onPostIterationGates(this.ticketId, i);\n gateSnapshots.push(snapshot);\n\n // Also check if any field values changed (complementary signal)\n const fieldDelta = lastFingerprint ? afterFp.field_value_count !== lastFingerprint.field_value_count : false;\n\n switch (snapshot.delta_from_previous) {\n case 'improved':\n if (gutterCount > 0) log(`Gate improvement detected — gutter counter reset`);\n gutterCount = 0;\n break;\n case 'same':\n if (!fieldDelta) {\n gutterCount++;\n log(`No gate progress (gutter ${gutterCount}/${this.config.gutterThreshold})`);\n } else {\n log(`Gates unchanged but fields changed — not incrementing gutter`);\n }\n break;\n case 'regressed':\n gutterCount += 2;\n log(`Gate regression detected (gutter ${gutterCount}/${this.config.gutterThreshold})`);\n break;\n case 'first_check':\n // First check — no delta info yet, don't adjust gutter\n break;\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log(`Post-iteration gate check failed (non-blocking): ${message}`);\n // Fall back to fingerprint gutter on gate failure\n if (lastFingerprint && fingerprintsMatch(lastFingerprint, afterFp)) {\n gutterCount++;\n log(`No progress detected via fingerprint fallback (gutter ${gutterCount}/${this.config.gutterThreshold})`);\n } else {\n if (gutterCount > 0) log(`Progress detected — gutter counter reset`);\n gutterCount = 0;\n }\n }\n } else {\n // Original fingerprint-based gutter detection\n if (lastFingerprint && fingerprintsMatch(lastFingerprint, afterFp)) {\n gutterCount++;\n log(`No progress detected (gutter ${gutterCount}/${this.config.gutterThreshold})`);\n } else {\n if (gutterCount > 0) log(`Progress detected — gutter counter reset`);\n gutterCount = 0;\n }\n }\n lastFingerprint = afterFp;\n\n // Pattern-based stuck detection — classifies trajectory as progressing/spinning/blocked\n if (this.config.stuckDetection && shouldCheckStuckDetection(this.config.stuckDetection, i)) {\n try {\n let sdStatus: 'progressing' | 'spinning' | 'blocked';\n let sdEvidence: string;\n let sdConfidence: number;\n\n if (gateSnapshots.length > 0) {\n // Deterministic classification from gate data\n const trajectory = classifyTrajectory(gateSnapshots);\n sdStatus = trajectory.status === 'regressing' ? 'spinning' : trajectory.status;\n sdEvidence = trajectory.evidence;\n sdConfidence = trajectory.confidence;\n log(`Gate-based stuck detection: ${sdStatus} (confidence=${String(sdConfidence)}) — ${sdEvidence}`);\n } else if (this.config.invokeStuckDetection) {\n // Legacy LLM-based classification\n const sdInput: StuckDetectionInput = {\n ticketNumber: ticketCtx.ticket.ticket_number,\n ticketTitle: ticketCtx.ticket.title,\n columnName: columnCtx.column.name,\n iteration: i,\n maxIterations: this.config.maxIterations,\n recentComments: ticketCtx.comments.slice(-3).map((c) => ({\n author: c.author,\n body: c.body,\n })),\n };\n const sdResult = await this.config.invokeStuckDetection(sdInput);\n sdStatus = sdResult.status;\n sdEvidence = sdResult.evidence;\n sdConfidence = sdResult.confidence;\n log(`Stuck detection: ${sdStatus} (confidence=${String(sdConfidence)}) — ${sdEvidence}`);\n } else {\n // No detection method available — skip\n sdStatus = 'progressing';\n sdEvidence = 'no detection method';\n sdConfidence = 0;\n }\n\n switch (sdStatus) {\n case 'progressing':\n if (gutterCount > 0) {\n log(`Stuck detection override: resetting gutter counter (was ${String(gutterCount)})`);\n gutterCount = 0;\n }\n break;\n case 'spinning':\n // Use pre-gate-delta base to avoid double-counting: the gate delta\n // already incremented gutter from the same underlying signal.\n gutterCount = gutterBeforeGates + 2;\n log(`Stuck detection: spinning — gutter set to ${String(gutterCount)}/${String(this.config.gutterThreshold)}`);\n break;\n case 'blocked':\n log(`Stuck detection: blocked — exiting immediately`);\n return withCosts({\n reason: 'stalled',\n iterations: i,\n gutterCount: this.config.gutterThreshold,\n model: resolvedModel,\n output: lastOutput,\n ...(gateSnapshots.length > 0 && { finalGateSnapshot: gateSnapshots[gateSnapshots.length - 1] }),\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n log(`Stuck detection failed (non-blocking): ${message}`);\n }\n }\n\n // Gutter threshold exit (after stuck detection may have adjusted gutterCount)\n if (gutterCount >= this.config.gutterThreshold) {\n return withCosts({\n reason: 'stalled',\n iterations: i,\n gutterCount,\n model: resolvedModel,\n output: lastOutput,\n ...(gateSnapshots.length > 0 && { finalGateSnapshot: gateSnapshots[gateSnapshots.length - 1] }),\n });\n }\n\n // Write checkpoint after iteration to allow resume on restart\n if (this.config.onCheckpoint) {\n try {\n await this.config.onCheckpoint(this.ticketId, {\n run_id: this.config.runId ?? '00000000-0000-0000-0000-000000000000',\n column_id: this.columnId,\n iteration: i,\n gutter_count: gutterCount,\n advisor_invocations: 0, // tracked by orchestrator, not loop\n model_tier: resolvedModel,\n last_fingerprint: afterFp,\n updated_at: new Date().toISOString(),\n worktree_name: this.config.worktreeName,\n });\n } catch (err) {\n // Checkpoint write must never block the loop\n log(`Checkpoint write failed (non-blocking): ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n\n return withCosts({\n reason: 'max_iterations',\n iterations: this.config.maxIterations,\n gutterCount,\n model: resolvedModel,\n output: lastOutput,\n ...(gateSnapshots.length > 0 && { finalGateSnapshot: gateSnapshots[gateSnapshots.length - 1] }),\n });\n }\n}\n\nfunction fingerprintsMatch(a: TicketFingerprint, b: TicketFingerprint): boolean {\n // column_id change is detected separately (exits as 'moved').\n // signal_count is excluded because dependency-blocked agents inflate it.\n // comment_count IS included because agents are instructed to create\n // iteration summary comments — genuine new comments indicate progress.\n return a.column_id === b.column_id\n && a.field_value_count === b.field_value_count\n && a.comment_count === b.comment_count;\n}\n","import { writeFileSync, unlinkSync, mkdirSync, existsSync, readdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { homedir } from 'node:os';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Generate (or overwrite) a stable MCP config file for the given board.\n * Uses a deterministic path (~/.kantban/pipelines/<boardId>/mcp-config.json)\n * so that child claude -p processes can always find it, even if the\n * orchestrator restarts between iterations.\n */\nexport function generateMcpConfig(apiUrl: string, apiToken: string, boardId: string): string {\n // Use local MCP server if running from the monorepo (dev mode),\n // otherwise fall back to the published npm package.\n // From src/lib/ the MCP package is at ../../../mcp/dist/index.js.\n// From dist/ (after tsup bundles) it's at ../../mcp/dist/index.js.\n// Try both to handle dev and built modes.\nconst localMcpPath = existsSync(join(__dirname, '..', '..', 'mcp', 'dist', 'index.js'))\n ? join(__dirname, '..', '..', 'mcp', 'dist', 'index.js')\n : join(__dirname, '..', '..', '..', 'mcp', 'dist', 'index.js');\n const useLocal = existsSync(localMcpPath);\n\n const kantbanServer = useLocal\n ? {\n command: 'node',\n args: [localMcpPath],\n env: {\n KANTBAN_API_TOKEN: apiToken,\n KANTBAN_API_URL: apiUrl,\n },\n }\n : {\n command: 'npx',\n args: ['-y', 'kantban-mcp@latest'],\n env: {\n KANTBAN_API_TOKEN: apiToken,\n KANTBAN_API_URL: apiUrl,\n },\n };\n\n const config = {\n mcpServers: {\n kantban: kantbanServer,\n },\n };\n\n const dir = join(homedir(), '.kantban', 'pipelines', boardId);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const filePath = join(dir, 'mcp-config.json');\n writeFileSync(filePath, JSON.stringify(config, null, 2), { mode: 0o600 });\n return filePath;\n}\n\nexport function cleanupMcpConfig(filePath: string): void {\n try {\n if (existsSync(filePath)) {\n unlinkSync(filePath);\n }\n } catch {\n // Non-critical — file may already be gone\n }\n}\n\n/**\n * Generate an MCP config file that includes BOTH the kantban server AND the\n * gate proxy server for a specific column. The gate proxy runs as a local\n * Node process bundled with the CLI and enforces column-level gates during\n * pipeline execution.\n *\n * The file is written to a deterministic, column-scoped path:\n * ~/.kantban/pipelines/<boardId>/mcp-config-<columnId>.json\n */\nexport function generateGateProxyMcpConfig(\n apiUrl: string,\n apiToken: string,\n boardId: string,\n gateConfigPath: string,\n columnId: string,\n columnName: string,\n projectId: string,\n gateCwd?: string,\n ticketId?: string,\n): string {\n // From src/lib/ the MCP package is at ../../../mcp/dist/index.js.\n// From dist/ (after tsup bundles) it's at ../../mcp/dist/index.js.\n// Try both to handle dev and built modes.\nconst localMcpPath = existsSync(join(__dirname, '..', '..', 'mcp', 'dist', 'index.js'))\n ? join(__dirname, '..', '..', 'mcp', 'dist', 'index.js')\n : join(__dirname, '..', '..', '..', 'mcp', 'dist', 'index.js');\n const useLocal = existsSync(localMcpPath);\n\n // Hide move_ticket/complete_task from the kantban server so agents use the\n // gate proxy versions instead. Without this, agents call the kantban server's\n // move_ticket directly, bypassing gate enforcement.\n const kantbanServer = useLocal\n ? { command: 'node', args: [localMcpPath], env: { KANTBAN_API_TOKEN: apiToken, KANTBAN_API_URL: apiUrl, KANTBAN_HIDDEN_TOOLS: 'kantban_move_ticket,kantban_move_tickets,kantban_complete_task,kantban_move_to_board' } }\n : { command: 'npx', args: ['-y', 'kantban-mcp@latest'], env: { KANTBAN_API_TOKEN: apiToken, KANTBAN_API_URL: apiUrl, KANTBAN_HIDDEN_TOOLS: 'kantban_move_ticket,kantban_move_tickets,kantban_complete_task,kantban_move_to_board' } };\n\n // Gate proxy runs as a local Node script bundled with the CLI.\n // From dist/ the file is at lib/gate-proxy-server.js; from src/lib/ it's sibling.\n const gateProxyPath = existsSync(join(__dirname, 'lib', 'gate-proxy-server.js'))\n ? join(__dirname, 'lib', 'gate-proxy-server.js')\n : join(__dirname, 'gate-proxy-server.js');\n const gateProxyEnv: Record<string, string> = {\n GATE_CONFIG_PATH: gateConfigPath,\n COLUMN_ID: columnId,\n COLUMN_NAME: columnName,\n PROJECT_ID: projectId,\n KANTBAN_API_TOKEN: apiToken,\n KANTBAN_API_URL: apiUrl,\n // Ensure gate commands can find npm/node even if the agent CLI spawns\n // MCP servers with a replacement env instead of merging with process.env.\n // Without this, gate-runner's `sh -c \"npm run ...\"` fails with ENOENT.\n PATH: process.env.PATH ?? '/usr/local/bin:/usr/bin:/bin',\n };\n if (gateCwd) gateProxyEnv['GATE_CWD'] = gateCwd;\n\n const gateProxyServer = {\n command: 'node',\n args: [gateProxyPath],\n env: gateProxyEnv,\n };\n\n const config = {\n mcpServers: {\n kantban: kantbanServer,\n 'kantban-gates': gateProxyServer,\n },\n };\n\n const dir = join(homedir(), '.kantban', 'pipelines', boardId);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n // Per-ticket config avoids race conditions when multiple tickets in the same\n // column are processed concurrently (each has a different worktree path).\n const suffix = ticketId ? `${columnId}-${ticketId}` : columnId;\n const filePath = join(dir, `mcp-config-${suffix}.json`);\n writeFileSync(filePath, JSON.stringify(config, null, 2), { mode: 0o600 });\n return filePath;\n}\n\n/**\n * Remove all gate-proxy MCP config files for a given pipeline directory.\n * Called on shutdown to ensure credentials don't persist on disk.\n */\nexport function cleanupGateProxyConfigs(pipelineDir: string): void {\n try {\n const files = readdirSync(pipelineDir);\n for (const f of files) {\n if (f.startsWith('mcp-config-') && f.endsWith('.json')) {\n try { unlinkSync(join(pipelineDir, f)); } catch { /* ignore */ }\n }\n }\n } catch { /* directory may not exist */ }\n}\n","import { spawn, type ChildProcess } from 'node:child_process';\nimport { writeFileSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type {\n AgentProvider, AgentRequest, AgentResult, PreflightResult, ProviderCapabilities,\n} from './types.js';\nimport { ClaudeStreamParser } from './claude-stream-parser.js';\n\nconst CLAUDE_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour\n\nexport class ClaudeProvider implements AgentProvider {\n readonly id = 'claude';\n readonly displayName = 'Claude Code';\n\n capabilities(): ProviderCapabilities {\n return {\n supportsToolAllowlist: true,\n supportsToolDenylist: true,\n supportsBuiltinToolStripping: true,\n supportsMaxTurns: true,\n supportsMcpConfigInjection: true,\n supportsMcpConfigOverride: false,\n supportsWorktreeFlag: true,\n supportsSandboxModes: false,\n supportedModels: [\n { id: 'claude-haiku-4-5-20251001', displayName: 'Haiku 4.5', tier: 'fast' },\n { id: 'claude-sonnet-4-6', displayName: 'Sonnet 4.6', tier: 'default' },\n { id: 'claude-opus-4-6', displayName: 'Opus 4.6', tier: 'thorough' },\n ],\n streamFormat: 'stream-json',\n };\n }\n\n async invoke(request: AgentRequest): Promise<AgentResult> {\n const args = this.buildArgs(request);\n const startTime = Date.now();\n\n return new Promise((resolve) => {\n const child: ChildProcess = spawn('claude', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n // Do NOT set cwd to the worktree name — the worktree doesn't exist yet.\n // Claude Code's --worktree flag creates it internally.\n });\n\n const parser = new ClaudeStreamParser();\n parser.onEvent = (event) => request.onStreamEvent?.(event);\n parser.onError = (err) => process.stderr.write(`[claude-stream] ${err.message}\\n`);\n\n let stderr = '';\n\n child.stdout?.on('data', (chunk: Buffer) => parser.feed(chunk.toString()));\n child.stderr?.on('data', (chunk: Buffer) => { stderr += chunk.toString(); });\n child.stdin?.end();\n\n // Abort signal support\n if (request.abortSignal) {\n request.abortSignal.addEventListener('abort', () => {\n try { child.kill('SIGTERM'); } catch { /* already dead */ }\n }, { once: true });\n }\n\n // Timeout\n let killTimer: ReturnType<typeof setTimeout> | undefined;\n const timeoutHandle = setTimeout(() => {\n try { child.kill('SIGTERM'); } catch { /* already dead */ }\n killTimer = setTimeout(() => {\n try { child.kill('SIGKILL'); } catch { /* already dead */ }\n }, 5000);\n }, CLAUDE_TIMEOUT_MS);\n\n let resolved = false;\n const finish = (code: number | null, errorMsg?: string) => {\n if (resolved) return;\n resolved = true;\n clearTimeout(timeoutHandle);\n if (killTimer) clearTimeout(killTimer);\n parser.flush();\n\n const usage = parser.getUsage();\n resolve({\n exitCode: code ?? 1,\n output: errorMsg ?? (parser.getLastOutput() || stderr),\n toolCallCount: parser.getToolCallCount(),\n usage,\n durationMs: Date.now() - startTime,\n });\n };\n\n child.on('close', (code) => finish(code));\n child.on('error', (err) => finish(1, err.message));\n });\n }\n\n async preflight(): Promise<PreflightResult> {\n try {\n // Dynamic import for CJS interop — which@2 uses module.exports = fn\n const whichModule = await import('which');\n const syncFn = whichModule.default?.sync ?? whichModule.sync;\n syncFn('claude');\n return { available: true, authenticated: true };\n } catch {\n return { available: false, authenticated: false, error: 'claude binary not found on PATH' };\n }\n }\n\n private buildArgs(request: AgentRequest): string[] {\n const args: string[] = [\n '-p', request.prompt,\n '--dangerously-skip-permissions',\n '--output-format', 'stream-json',\n '--verbose',\n ];\n\n // MCP config — write JSON file from McpConfig if provided\n if (request.mcpConfig && request.toolRestrictions?.includeMcpConfig !== false) {\n const configPath = this.writeMcpConfigJson(request.mcpConfig);\n args.push('--mcp-config', configPath);\n }\n\n if (request.model) args.push('--model', request.model);\n\n if (request.maxTurns) {\n args.push('--max-turns', String(request.maxTurns));\n }\n\n if (request.workingDirectory) {\n args.push('--worktree', request.workingDirectory);\n }\n\n // Tool scoping\n if (request.toolRestrictions) {\n const tr = request.toolRestrictions;\n if (tr.tools !== undefined) args.push('--tools', tr.tools);\n if (tr.allowedTools?.length) args.push('--allowedTools', ...tr.allowedTools);\n if (tr.disallowedTools?.length) args.push('--disallowedTools', ...tr.disallowedTools);\n }\n\n return args;\n }\n\n private writeMcpConfigJson(mcpConfig: AgentRequest['mcpConfig']): string {\n if (!mcpConfig) return '';\n const config = { mcpServers: mcpConfig.servers };\n const dir = join(homedir(), '.kantban', 'tmp');\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const filePath = join(dir, `mcp-config-${Date.now()}.json`);\n writeFileSync(filePath, JSON.stringify(config, null, 2), { mode: 0o600 });\n return filePath;\n }\n}\n","import type { NormalizedStreamEvent } from './types.js';\n\n/**\n * Parses Claude Code's --output-format stream-json (newline-delimited JSON)\n * and emits NormalizedStreamEvents.\n */\nexport class ClaudeStreamParser {\n private buffer = '';\n private toolCallCount = 0;\n private inputTokens = 0;\n private outputTokens = 0;\n private lastOutput = '';\n\n onEvent: (event: NormalizedStreamEvent) => void = () => {};\n onError: (error: Error) => void = () => {};\n\n feed(chunk: string): void {\n this.buffer += chunk;\n const lines = this.buffer.split('\\n');\n this.buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n this.parseLine(line.trim());\n }\n }\n\n flush(): void {\n const trimmed = this.buffer.trim();\n this.buffer = '';\n if (trimmed) this.parseLine(trimmed);\n }\n\n getToolCallCount(): number {\n return this.toolCallCount;\n }\n\n getUsage(): { inputTokens: number; outputTokens: number } {\n return { inputTokens: this.inputTokens, outputTokens: this.outputTokens };\n }\n\n getLastOutput(): string {\n return this.lastOutput;\n }\n\n reset(): void {\n this.buffer = '';\n this.toolCallCount = 0;\n this.inputTokens = 0;\n this.outputTokens = 0;\n this.lastOutput = '';\n }\n\n private parseLine(line: string): void {\n if (!line) return;\n try {\n const raw = JSON.parse(line) as Record<string, unknown>;\n this.translateEvent(raw);\n } catch {\n this.onError(new Error(`Failed to parse stream-json line: ${line.slice(0, 100)}`));\n }\n }\n\n private translateEvent(raw: Record<string, unknown>): void {\n // Content lives at raw.message.content (stream-json format) or raw.content (legacy)\n const message = raw.message as Record<string, unknown> | undefined;\n const content = (message?.content ?? raw.content) as Array<Record<string, unknown>> | undefined;\n if (raw.type === 'assistant' && Array.isArray(content)) {\n for (const block of content) {\n if (block.type === 'text' && typeof block.text === 'string') {\n this.onEvent({ type: 'text', text: block.text });\n } else if (block.type === 'tool_use') {\n this.toolCallCount++;\n this.onEvent({\n type: 'tool_call',\n tool: (block.name as string) ?? 'unknown',\n input: block.input,\n });\n } else if (block.type === 'tool_result') {\n this.onEvent({\n type: 'tool_result',\n tool: (block.name as string) ?? 'unknown',\n output: block.content ?? block.output,\n });\n }\n }\n } else if (raw.type === 'result') {\n const usage = raw.usage as { input_tokens?: number; output_tokens?: number } | undefined;\n const inTok = usage?.input_tokens ?? 0;\n const outTok = usage?.output_tokens ?? 0;\n this.inputTokens += inTok;\n this.outputTokens += outTok;\n if (inTok || outTok) {\n this.onEvent({ type: 'usage', inputTokens: inTok, outputTokens: outTok });\n }\n if (typeof raw.result === 'string') {\n this.lastOutput = raw.result;\n this.onEvent({ type: 'done', result: raw.result });\n }\n }\n }\n}\n"],"mappings":"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","names":["writeFileSync","mkdirSync","join","homedir","join","homedir","mkdirSync","writeFileSync"]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
RalphLoop,
|
|
4
4
|
cleanupMcpConfig,
|
|
5
5
|
generateMcpConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7HJZFR7Y.js";
|
|
7
7
|
import "./chunk-ZTQJMXJM.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/cron.ts
|
|
@@ -101,4 +101,4 @@ async function runCron(client, args) {
|
|
|
101
101
|
export {
|
|
102
102
|
runCron
|
|
103
103
|
};
|
|
104
|
-
//# sourceMappingURL=cron-
|
|
104
|
+
//# sourceMappingURL=cron-RG4VCGME.js.map
|
package/dist/index.js
CHANGED
|
@@ -31,16 +31,16 @@ async function main() {
|
|
|
31
31
|
}
|
|
32
32
|
case "pipeline": {
|
|
33
33
|
if (args[0] === "stop") {
|
|
34
|
-
const { stopPipeline } = await import("./pipeline-
|
|
34
|
+
const { stopPipeline } = await import("./pipeline-7CMDTEI2.js");
|
|
35
35
|
await stopPipeline(args.slice(1));
|
|
36
36
|
} else {
|
|
37
|
-
const { runPipeline } = await import("./pipeline-
|
|
37
|
+
const { runPipeline } = await import("./pipeline-7CMDTEI2.js");
|
|
38
38
|
await runPipeline(client, args);
|
|
39
39
|
}
|
|
40
40
|
break;
|
|
41
41
|
}
|
|
42
42
|
case "cron": {
|
|
43
|
-
const { runCron } = await import("./cron-
|
|
43
|
+
const { runCron } = await import("./cron-RG4VCGME.js");
|
|
44
44
|
await runCron(client, args);
|
|
45
45
|
break;
|
|
46
46
|
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
generateMcpConfig,
|
|
13
13
|
parseJsonFromLlmOutput,
|
|
14
14
|
parseStuckDetectionResponse
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-7HJZFR7Y.js";
|
|
16
16
|
import {
|
|
17
17
|
LoopCheckpointSchema,
|
|
18
18
|
VerdictSchema,
|
|
@@ -3532,19 +3532,22 @@ var GeminiJsonlParser = class {
|
|
|
3532
3532
|
const eventType = raw.type;
|
|
3533
3533
|
if (eventType === "message") {
|
|
3534
3534
|
if (raw.role === "assistant" && typeof raw.content === "string") {
|
|
3535
|
+
if (raw.delta) this.lastOutput += raw.content;
|
|
3536
|
+
else this.lastOutput = raw.content;
|
|
3535
3537
|
this.onEvent({ type: "text", text: raw.content });
|
|
3536
3538
|
}
|
|
3537
3539
|
} else if (eventType === "tool_use") {
|
|
3538
3540
|
this.toolCallCount++;
|
|
3539
3541
|
this.onEvent({
|
|
3540
3542
|
type: "tool_call",
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
+
// stream-json uses tool_name; json format uses name
|
|
3544
|
+
tool: raw.tool_name ?? raw.name ?? "unknown",
|
|
3545
|
+
input: raw.parameters ?? raw.args ?? raw.input
|
|
3543
3546
|
});
|
|
3544
3547
|
} else if (eventType === "tool_result") {
|
|
3545
3548
|
this.onEvent({
|
|
3546
3549
|
type: "tool_result",
|
|
3547
|
-
tool: raw.name ?? "unknown",
|
|
3550
|
+
tool: raw.tool_name ?? raw.name ?? "unknown",
|
|
3548
3551
|
output: raw.output ?? raw.result
|
|
3549
3552
|
});
|
|
3550
3553
|
} else if (eventType === "result" || raw.session_id && raw.stats) {
|
|
@@ -3554,12 +3557,12 @@ var GeminiJsonlParser = class {
|
|
|
3554
3557
|
if (inTok || outTok) {
|
|
3555
3558
|
this.onEvent({ type: "usage", inputTokens: inTok, outputTokens: outTok });
|
|
3556
3559
|
}
|
|
3557
|
-
const
|
|
3560
|
+
const statsObj = raw.stats;
|
|
3561
|
+
if (statsObj?.tool_calls) this.toolCallCount = Math.max(this.toolCallCount, statsObj.tool_calls);
|
|
3562
|
+
const tools = statsObj?.tools;
|
|
3558
3563
|
if (tools?.totalCalls) this.toolCallCount = Math.max(this.toolCallCount, tools.totalCalls);
|
|
3559
|
-
if (typeof raw.response === "string")
|
|
3560
|
-
|
|
3561
|
-
this.onEvent({ type: "done", result: raw.response });
|
|
3562
|
-
}
|
|
3564
|
+
if (typeof raw.response === "string") this.lastOutput = raw.response;
|
|
3565
|
+
this.onEvent({ type: "done", result: this.lastOutput });
|
|
3563
3566
|
} else if (eventType === "error") {
|
|
3564
3567
|
const msg = raw.message ?? "unknown error";
|
|
3565
3568
|
this.lastOutput = msg;
|
|
@@ -3776,13 +3779,18 @@ var GeminiProvider = class _GeminiProvider {
|
|
|
3776
3779
|
request.prompt,
|
|
3777
3780
|
"--yolo",
|
|
3778
3781
|
"--output-format",
|
|
3779
|
-
"json"
|
|
3782
|
+
"stream-json"
|
|
3780
3783
|
];
|
|
3781
3784
|
if (request.model) args.push("--model", request.model);
|
|
3782
3785
|
if (!request.workingDirectory) {
|
|
3783
3786
|
const projectRoot = process.cwd();
|
|
3784
3787
|
args.push("--include-directories", projectRoot);
|
|
3785
3788
|
}
|
|
3789
|
+
if (request.mcpConfig && Object.keys(request.mcpConfig.servers).length > 0) {
|
|
3790
|
+
for (const name of Object.keys(request.mcpConfig.servers)) {
|
|
3791
|
+
args.push("--allowed-mcp-server-names", name);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3786
3794
|
return args;
|
|
3787
3795
|
}
|
|
3788
3796
|
writeGeminiSettings(request, degraded) {
|
|
@@ -4845,4 +4853,4 @@ export {
|
|
|
4845
4853
|
runPipeline,
|
|
4846
4854
|
stopPipeline
|
|
4847
4855
|
};
|
|
4848
|
-
//# sourceMappingURL=pipeline-
|
|
4856
|
+
//# sourceMappingURL=pipeline-7CMDTEI2.js.map
|