@mastra/code-sdk 1.15.0-alpha.10 → 1.15.0-alpha.12

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 (131) hide show
  1. package/dist/acp/agent.js +2 -1
  2. package/dist/acp/agent.js.map +1 -1
  3. package/dist/acp/event-mapper.js +0 -2
  4. package/dist/acp/event-mapper.js.map +1 -1
  5. package/dist/acp.js +1 -2
  6. package/dist/agent-connections/registry.js.map +1 -1
  7. package/dist/agent-connections/signal-provider.js.map +1 -1
  8. package/dist/agent-connections/state-processor.js.map +1 -1
  9. package/dist/agent-connections/tools.js +26 -24
  10. package/dist/agent-connections/tools.js.map +1 -1
  11. package/dist/agents/instructions.js +3 -2
  12. package/dist/agents/instructions.js.map +1 -1
  13. package/dist/agents/mastracode-gateway.js +35 -27
  14. package/dist/agents/mastracode-gateway.js.map +1 -1
  15. package/dist/agents/memory.d.ts.map +1 -1
  16. package/dist/agents/memory.js +16 -2
  17. package/dist/agents/memory.js.map +1 -1
  18. package/dist/agents/prompts/agent-instructions.js +1 -1
  19. package/dist/agents/prompts/agent-instructions.js.map +1 -1
  20. package/dist/agents/prompts/index.js +3 -2
  21. package/dist/agents/prompts/index.js.map +1 -1
  22. package/dist/agents/prompts/plan.js +2 -1
  23. package/dist/agents/prompts/plan.js.map +1 -1
  24. package/dist/agents/sandbox-filesystem.d.ts +28 -0
  25. package/dist/agents/sandbox-filesystem.d.ts.map +1 -1
  26. package/dist/agents/sandbox-filesystem.js +81 -2
  27. package/dist/agents/sandbox-filesystem.js.map +1 -1
  28. package/dist/agents/tools.d.ts.map +1 -1
  29. package/dist/agents/tools.js +18 -12
  30. package/dist/agents/tools.js.map +1 -1
  31. package/dist/agents/workspace.js +2 -1
  32. package/dist/agents/workspace.js.map +1 -1
  33. package/dist/auth/account-rotation-processor.js +2 -1
  34. package/dist/auth/account-rotation-processor.js.map +1 -1
  35. package/dist/auth/providers/anthropic.js +2 -2
  36. package/dist/auth/providers/anthropic.js.map +1 -1
  37. package/dist/auth/providers/github-copilot.js +1 -1
  38. package/dist/auth/providers/github-copilot.js.map +1 -1
  39. package/dist/auth/providers/openai-codex.js +3 -2
  40. package/dist/auth/providers/openai-codex.js.map +1 -1
  41. package/dist/auth/providers/xai.js +1 -1
  42. package/dist/auth/providers/xai.js.map +1 -1
  43. package/dist/clipboard/index.js +5 -4
  44. package/dist/clipboard/index.js.map +1 -1
  45. package/dist/evals/context-builder.js +1 -3
  46. package/dist/evals/context-builder.js.map +1 -1
  47. package/dist/evals/scorers/outcome.js +2 -1
  48. package/dist/evals/scorers/outcome.js.map +1 -1
  49. package/dist/factory/remote-controller.js.map +1 -1
  50. package/dist/factory/remote-session.js +1 -3
  51. package/dist/factory/remote-session.js.map +1 -1
  52. package/dist/headless/cli.js +2 -1
  53. package/dist/headless/cli.js.map +1 -1
  54. package/dist/headless/run-mc.js +9 -9
  55. package/dist/headless/run-mc.js.map +1 -1
  56. package/dist/hooks/executor.js +3 -1
  57. package/dist/hooks/executor.js.map +1 -1
  58. package/dist/hooks/manager.js +40 -26
  59. package/dist/hooks/manager.js.map +1 -1
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +30 -18
  62. package/dist/index.js.map +1 -1
  63. package/dist/knowledge-inspector.js +2 -2
  64. package/dist/knowledge-inspector.js.map +1 -1
  65. package/dist/lsp/server.js +4 -2
  66. package/dist/lsp/server.js.map +1 -1
  67. package/dist/mcp/manager.d.ts.map +1 -1
  68. package/dist/mcp/manager.js +14 -2
  69. package/dist/mcp/manager.js.map +1 -1
  70. package/dist/onboarding/settings.js +5 -2
  71. package/dist/onboarding/settings.js.map +1 -1
  72. package/dist/platform/index.js +5 -3
  73. package/dist/platform/index.js.map +1 -1
  74. package/dist/plugins/loader.js +3 -1
  75. package/dist/plugins/loader.js.map +1 -1
  76. package/dist/plugins/manager.js +2 -1
  77. package/dist/plugins/manager.js.map +1 -1
  78. package/dist/plugins/package-link.js.map +1 -1
  79. package/dist/plugins/scaffold.js +4 -2
  80. package/dist/plugins/scaffold.js.map +1 -1
  81. package/dist/plugins/signal-lane.js.map +1 -1
  82. package/dist/process-memory-diagnostics.js +2 -1
  83. package/dist/process-memory-diagnostics.js.map +1 -1
  84. package/dist/providers/amazon-bedrock-gateway.js +2 -1
  85. package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
  86. package/dist/providers/amazon-bedrock.js +2 -2
  87. package/dist/providers/amazon-bedrock.js.map +1 -1
  88. package/dist/providers/claude-max.js +13 -9
  89. package/dist/providers/claude-max.js.map +1 -1
  90. package/dist/providers/github-copilot-gateway.js.map +1 -1
  91. package/dist/providers/github-copilot.js +13 -12
  92. package/dist/providers/github-copilot.js.map +1 -1
  93. package/dist/providers/openai-codex.js +14 -11
  94. package/dist/providers/openai-codex.js.map +1 -1
  95. package/dist/providers/provider-thinking.js.map +1 -1
  96. package/dist/schedules/run-script.js +1 -1
  97. package/dist/schedules/run-script.js.map +1 -1
  98. package/dist/schedules/scheduler.js.map +1 -1
  99. package/dist/tools/request-sandbox-access.js +7 -5
  100. package/dist/tools/request-sandbox-access.js.map +1 -1
  101. package/dist/tools/utils.js +3 -1
  102. package/dist/tools/utils.js.map +1 -1
  103. package/dist/tools/web-search.d.ts +13 -11
  104. package/dist/tools/web-search.d.ts.map +1 -1
  105. package/dist/tools/web-search.js +21 -15
  106. package/dist/tools/web-search.js.map +1 -1
  107. package/dist/utils/debug-log.js +2 -2
  108. package/dist/utils/debug-log.js.map +1 -1
  109. package/dist/utils/gateway-sync.js +1 -1
  110. package/dist/utils/gateway-sync.js.map +1 -1
  111. package/dist/utils/local-observability.d.ts +1 -1
  112. package/dist/utils/local-observability.d.ts.map +1 -1
  113. package/dist/utils/local-observability.js +1 -1
  114. package/dist/utils/local-observability.js.map +1 -1
  115. package/dist/utils/prune-cli.js +10 -8
  116. package/dist/utils/prune-cli.js.map +1 -1
  117. package/dist/utils/signals-pubsub.js.map +1 -1
  118. package/dist/utils/slash-command-processor.js +1 -1
  119. package/dist/utils/slash-command-processor.js.map +1 -1
  120. package/dist/utils/stdin-pipe.js +1 -1
  121. package/dist/utils/stdin-pipe.js.map +1 -1
  122. package/dist/utils/storage-factory.d.ts.map +1 -1
  123. package/dist/utils/storage-factory.js +1 -1
  124. package/dist/utils/storage-factory.js.map +1 -1
  125. package/dist/utils/storage-maintenance.js +1 -1
  126. package/dist/utils/storage-maintenance.js.map +1 -1
  127. package/dist/utils/update-check.js +2 -1
  128. package/dist/utils/update-check.js.map +1 -1
  129. package/dist/workflows/register-primitives.js +1 -1
  130. package/dist/workflows/register-primitives.js.map +1 -1
  131. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"outcome.js","names":[],"sources":["../../../src/evals/scorers/outcome.ts"],"sourcesContent":["/**\n * MastraCode Outcome Scorer (Code-based, Agent type, always-on)\n *\n * Grades the objective outcome of a coding session by checking hard facts:\n * - Did the build/typecheck pass?\n * - Did tests pass?\n * - What was the tool error rate?\n * - Did the agent get stuck in a loop?\n * - Was there a regression (build passed earlier, failed later)?\n * - Did the agent use ask_user unnecessarily? (autonomy)\n *\n * This scorer is always-on (no sampling) because it's cheap — it only\n * reads exit codes and error states from tool results already in the messages.\n *\n * Scoring philosophy (from Anthropic's eval guidance):\n * \"Grade outcomes, not paths\" — we check what happened, not how.\n * Partial credit: a session that passes build but fails tests scores\n * higher than one that fails build entirely.\n *\n * Dimensions that don't apply to a session (e.g. build for a docs-only task)\n * are excluded from the weighted average, so sessions aren't penalized for\n * irrelevant criteria.\n */\n\nimport type { MastraDBMessage } from '@mastra/core/agent';\nimport { createScorer, filterRun } from '@mastra/core/evals';\n\nimport { isBuildCommand, isTestCommand, getExitCode } from './classify-command.js';\nimport type { ExtractedToolCall } from './extract-tools.js';\nimport { extractToolCalls } from './extract-tools.js';\n\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n// CONFIGURATION — Adjust weights and thresholds here\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n/**\n * Dimension weights. Used to compute the weighted average of applicable dimensions.\n * Dimensions that don't apply (e.g. no build ran) are excluded from the average.\n */\nconst WEIGHTS = {\n build: 0.3,\n tests: 0.25,\n toolErrors: 0.2,\n loops: 0.1,\n regression: 0.1,\n autonomy: 0.05,\n} as const;\n\n/**\n * Scoring thresholds and multipliers.\n */\nconst THRESHOLDS = {\n /** Multiplier for tool error rate penalty. Score = max(0, 1 - rate × this). */\n toolErrorPenaltyMultiplier: 1.0,\n\n /** How many consecutive identical calls (or same-error repeats) before it's a \"loop\". */\n loopMinRepetitions: 3,\n\n /** Score penalty per detected loop. */\n loopPenaltyPerOccurrence: 0.3,\n\n /** Score deducted per ask_user call. */\n autonomyPenaltyPerAsk: 0.25,\n\n /** Score when a command ran but exit code is ambiguous. */\n ambiguousExitScore: 0.75,\n\n /** Minimum tool calls required to produce a meaningful score. */\n minToolCalls: 1,\n\n /** Tool names whose errors are expected/benign (not penalized). */\n benignErrorTools: ['search_content', 'find_files', 'lsp_inspect'] as string[],\n} as const;\n\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\ntype DimensionResult = { score: number; detail: string; applicable: boolean };\n\n/**\n * Check if the primary verb of an execute_command is a build/typecheck command.\n * Splits compound commands (&&, ;) and checks the first segment.\n */\n/** Extract command string from tool call args, then delegate to shared classifier. */\nfunction isBuildArgs(args: Record<string, unknown>): boolean {\n return isBuildCommand(String(args.command ?? ''));\n}\n\nfunction isTestArgs(args: Record<string, unknown>): boolean {\n return isTestCommand(String(args.command ?? ''));\n}\n\n/**\n * Score build/typecheck outcome (final state).\n */\nfunction scoreBuild(execResults: ExtractedToolCall[]): DimensionResult {\n const builds = execResults.filter(r => r.toolName === 'execute_command' && isBuildArgs(r.args));\n if (builds.length === 0) return { score: 0, detail: 'No build/typecheck ran', applicable: false };\n\n const last = builds[builds.length - 1]!;\n if (last.isError) return { score: 0, detail: 'Build/typecheck tool errored', applicable: true };\n\n const exitCode = getExitCode(last.result);\n if (exitCode === 0) return { score: 1, detail: 'Build/typecheck passed', applicable: true };\n if (exitCode !== null && exitCode !== 0)\n return { score: 0, detail: `Build failed (exit ${exitCode})`, applicable: true };\n\n // Fallback: check result text for TS errors\n const text = typeof last.result === 'string' ? last.result : JSON.stringify(last.result ?? '');\n if (/error TS\\d+|Cannot find module|is not assignable/i.test(text)) {\n return { score: 0, detail: 'Build failed (TypeScript errors in output)', applicable: true };\n }\n\n return { score: THRESHOLDS.ambiguousExitScore, detail: 'Build ran, outcome unclear', applicable: true };\n}\n\n/**\n * Score test outcome (final state).\n */\nfunction scoreTests(execResults: ExtractedToolCall[]): DimensionResult {\n const tests = execResults.filter(r => r.toolName === 'execute_command' && isTestArgs(r.args));\n if (tests.length === 0) return { score: 0, detail: 'No tests ran', applicable: false };\n\n const last = tests[tests.length - 1]!;\n if (last.isError) return { score: 0, detail: 'Test command errored', applicable: true };\n\n const exitCode = getExitCode(last.result);\n if (exitCode === 0) return { score: 1, detail: 'Tests passed', applicable: true };\n if (exitCode !== null && exitCode !== 0)\n return { score: 0, detail: `Tests failed (exit ${exitCode})`, applicable: true };\n\n const text = typeof last.result === 'string' ? last.result : JSON.stringify(last.result ?? '');\n if (/\\d+ (?:tests? )?passed|✓|PASS/i.test(text) && !/\\bfail(?:ed|ure)?\\b|\\berror\\b/i.test(text)) {\n return { score: 1, detail: 'Tests passed (inferred)', applicable: true };\n }\n if (/\\bFAIL\\b|failed|✗|✘/i.test(text)) {\n return { score: 0, detail: 'Tests failed (inferred)', applicable: true };\n }\n\n return { score: THRESHOLDS.ambiguousExitScore, detail: 'Tests ran, outcome unclear', applicable: true };\n}\n\n/**\n * Score tool error rate, excluding benign errors from expected-to-fail tools.\n */\nfunction scoreToolErrors(results: ExtractedToolCall[]): DimensionResult {\n if (results.length === 0) return { score: 1, detail: 'No tool calls', applicable: false };\n\n // Only count errors from non-benign tools\n const nonBenign = results.filter(r => !THRESHOLDS.benignErrorTools.includes(r.toolName));\n if (nonBenign.length === 0) return { score: 1, detail: 'All tool calls are benign-error tools', applicable: true };\n\n const errors = nonBenign.filter(r => r.isError);\n const rate = errors.length / nonBenign.length;\n\n if (rate === 0) return { score: 1, detail: 'No tool errors', applicable: true };\n\n const score = Math.max(0, 1 - rate * THRESHOLDS.toolErrorPenaltyMultiplier);\n return {\n score,\n detail: `${errors.length}/${nonBenign.length} tools errored (${(rate * 100).toFixed(0)}%)`,\n applicable: true,\n };\n}\n\n/**\n * Detect stuck loops: same tool + same args appearing 3+ times consecutively,\n * or same tool + same args erroring 3+ times total.\n */\nfunction scoreStuckLoops(results: ExtractedToolCall[]): DimensionResult {\n if (results.length < 3) return { score: 1, detail: 'Too few calls for loop detection', applicable: true };\n\n const fingerprint = (r: ExtractedToolCall) => `${r.toolName}:${JSON.stringify(r.args)}`;\n\n // Check consecutive identical calls\n let maxConsecutive = 1;\n let currentRun = 1;\n for (let i = 1; i < results.length; i++) {\n if (fingerprint(results[i]!) === fingerprint(results[i - 1]!)) {\n currentRun++;\n maxConsecutive = Math.max(maxConsecutive, currentRun);\n } else {\n currentRun = 1;\n }\n }\n\n // Check repeated error fingerprints\n const errorCounts = new Map<string, number>();\n for (const r of results) {\n if (!r.isError) continue;\n const fp = fingerprint(r);\n errorCounts.set(fp, (errorCounts.get(fp) ?? 0) + 1);\n }\n const maxErrorRepeat = Math.max(0, ...[...errorCounts.values()]);\n\n const minReps = THRESHOLDS.loopMinRepetitions;\n const loopSeverity = Math.max(\n maxConsecutive >= minReps ? maxConsecutive - (minReps - 1) : 0,\n maxErrorRepeat >= minReps ? maxErrorRepeat - (minReps - 1) : 0,\n );\n\n if (loopSeverity === 0) return { score: 1, detail: 'No stuck loops', applicable: true };\n\n const score = Math.max(0, 1 - loopSeverity * THRESHOLDS.loopPenaltyPerOccurrence);\n const details: string[] = [];\n if (maxConsecutive >= minReps) details.push(`${maxConsecutive} consecutive identical calls`);\n if (maxErrorRepeat >= minReps) details.push(`same error repeated ${maxErrorRepeat}x`);\n return { score, detail: details.join('; '), applicable: true };\n}\n\n/**\n * Detect regressions: build/test passed at some point, then failed later.\n * Gives partial credit if the agent recovered (final state passes).\n */\nfunction scoreRegression(execResults: ExtractedToolCall[]): DimensionResult {\n const regressions: { label: string; recovered: boolean }[] = [];\n\n for (const [label, filter] of [\n ['Build', isBuildArgs],\n ['Tests', isTestArgs],\n ] as const) {\n const cmds = execResults.filter(r => r.toolName === 'execute_command' && filter(r.args));\n if (cmds.length < 2) continue;\n\n let sawPass = false;\n let sawRegression = false;\n for (const cmd of cmds) {\n const exit = getExitCode(cmd.result);\n if (exit === 0) {\n if (sawRegression) {\n // Recovered from regression\n regressions.push({ label, recovered: true });\n sawRegression = false;\n }\n sawPass = true;\n }\n if (sawPass && exit !== null && exit !== 0) {\n sawRegression = true;\n }\n }\n\n if (sawRegression) {\n // Regression persisted at end of session\n regressions.push({ label, recovered: false });\n }\n }\n\n if (regressions.length === 0) return { score: 1, detail: 'No regressions', applicable: true };\n\n // Persistent regressions are severe (0), recovered regressions get partial credit (0.5)\n const persistentCount = regressions.filter(r => !r.recovered).length;\n const recoveredCount = regressions.filter(r => r.recovered).length;\n const score = Math.max(0, 1 - persistentCount * 0.5 - recoveredCount * 0.1);\n const details = regressions.map(\n r => `${r.label} ${r.recovered ? 'regressed then recovered' : 'regressed (persisted)'}`,\n );\n return { score, detail: details.join('; '), applicable: true };\n}\n\n/**\n * Score autonomy: penalize ask_user calls.\n * 0 calls = 1.0, each call reduces by 0.25 (up to a floor of 0).\n */\nfunction scoreAutonomy(results: ExtractedToolCall[]): DimensionResult {\n const askUserCalls = results.filter(r => r.toolName === 'ask_user');\n const count = askUserCalls.length;\n\n if (count === 0) return { score: 1, detail: 'No ask_user calls (fully autonomous)', applicable: true };\n\n const score = Math.max(0, 1 - count * THRESHOLDS.autonomyPenaltyPerAsk);\n return { score, detail: `${count} ask_user call${count > 1 ? 's' : ''}`, applicable: true };\n}\n\n/**\n * Compute weighted average of only applicable dimensions.\n * Returns null if no dimensions are applicable.\n */\nfunction weightedAverage(dimensions: Record<string, DimensionResult>, weights: Record<string, number>): number | null {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const [key, result] of Object.entries(dimensions)) {\n if (!result.applicable) continue;\n const weight = weights[key] ?? 0;\n totalWeight += weight;\n weightedSum += result.score * weight;\n }\n\n if (totalWeight === 0) return null;\n return weightedSum / totalWeight;\n}\n\nexport function createOutcomeScorer() {\n return createScorer({\n id: 'mastracode-outcome',\n name: 'MastraCode Outcome',\n description:\n 'Grades coding session outcomes: build/test pass, tool errors, stuck loops, regressions, autonomy. Always-on.',\n type: 'agent',\n prepareRun: filterRun({ partTypes: ['tool-invocation', 'text'] }),\n })\n .preprocess(async ({ run }) => {\n const messages = (run.output ?? []) as MastraDBMessage[];\n const allResults = extractToolCalls(messages);\n const totalCalls = allResults.length;\n\n // H1: Empty sessions or sessions with no tool calls → score 0\n if (totalCalls < THRESHOLDS.minToolCalls) {\n const empty: DimensionResult = { score: 0, detail: 'No tool calls', applicable: false };\n return {\n empty: true as const,\n build: empty,\n tests: empty,\n toolErrors: empty,\n loops: empty,\n regression: empty,\n autonomy: empty,\n totalCalls,\n };\n }\n\n const execResults = allResults.filter(r => r.toolName === 'execute_command');\n\n return {\n empty: false as const,\n build: scoreBuild(execResults),\n tests: scoreTests(execResults),\n toolErrors: scoreToolErrors(allResults),\n loops: scoreStuckLoops(allResults),\n regression: scoreRegression(execResults),\n autonomy: scoreAutonomy(allResults),\n totalCalls,\n };\n })\n .generateScore(({ results }) => {\n const p = results.preprocessStepResult;\n if (p.empty) return 0;\n\n const score = weightedAverage(\n {\n build: p.build,\n tests: p.tests,\n toolErrors: p.toolErrors,\n loops: p.loops,\n regression: p.regression,\n autonomy: p.autonomy,\n },\n WEIGHTS,\n );\n\n return score === null ? 0 : Math.round(score * 100) / 100;\n })\n .generateReason(({ results, score }) => {\n const p = results.preprocessStepResult;\n if (p.empty) {\n return `Score: 0 (${p.totalCalls} tool calls — below minimum threshold for scoring)`;\n }\n\n const pct = (w: number) => `${(w * 100).toFixed(0)}%`;\n const dimLine = (name: string, weight: number, r: DimensionResult) =>\n r.applicable\n ? `${name} (${pct(weight)}): ${r.detail} [${r.score}]`\n : `${name}: ${r.detail} [N/A — excluded from average]`;\n\n const parts: string[] = [`Score: ${score} (${p.totalCalls} tool calls total)`];\n parts.push(dimLine('Build', WEIGHTS.build, p.build));\n parts.push(dimLine('Tests', WEIGHTS.tests, p.tests));\n parts.push(dimLine('Tool errors', WEIGHTS.toolErrors, p.toolErrors));\n parts.push(dimLine('Loops', WEIGHTS.loops, p.loops));\n parts.push(dimLine('Regression', WEIGHTS.regression, p.regression));\n parts.push(dimLine('Autonomy', WEIGHTS.autonomy, p.autonomy));\n\n return parts.join('\\n');\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"outcome.js","names":[],"sources":["../../../src/evals/scorers/outcome.ts"],"sourcesContent":["/**\n * MastraCode Outcome Scorer (Code-based, Agent type, always-on)\n *\n * Grades the objective outcome of a coding session by checking hard facts:\n * - Did the build/typecheck pass?\n * - Did tests pass?\n * - What was the tool error rate?\n * - Did the agent get stuck in a loop?\n * - Was there a regression (build passed earlier, failed later)?\n * - Did the agent use ask_user unnecessarily? (autonomy)\n *\n * This scorer is always-on (no sampling) because it's cheap — it only\n * reads exit codes and error states from tool results already in the messages.\n *\n * Scoring philosophy (from Anthropic's eval guidance):\n * \"Grade outcomes, not paths\" — we check what happened, not how.\n * Partial credit: a session that passes build but fails tests scores\n * higher than one that fails build entirely.\n *\n * Dimensions that don't apply to a session (e.g. build for a docs-only task)\n * are excluded from the weighted average, so sessions aren't penalized for\n * irrelevant criteria.\n */\n\nimport type { MastraDBMessage } from '@mastra/core/agent';\nimport { createScorer, filterRun } from '@mastra/core/evals';\n\nimport { isBuildCommand, isTestCommand, getExitCode } from './classify-command.js';\nimport type { ExtractedToolCall } from './extract-tools.js';\nimport { extractToolCalls } from './extract-tools.js';\n\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n// CONFIGURATION — Adjust weights and thresholds here\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n/**\n * Dimension weights. Used to compute the weighted average of applicable dimensions.\n * Dimensions that don't apply (e.g. no build ran) are excluded from the average.\n */\nconst WEIGHTS = {\n build: 0.3,\n tests: 0.25,\n toolErrors: 0.2,\n loops: 0.1,\n regression: 0.1,\n autonomy: 0.05,\n} as const;\n\n/**\n * Scoring thresholds and multipliers.\n */\nconst THRESHOLDS = {\n /** Multiplier for tool error rate penalty. Score = max(0, 1 - rate × this). */\n toolErrorPenaltyMultiplier: 1.0,\n\n /** How many consecutive identical calls (or same-error repeats) before it's a \"loop\". */\n loopMinRepetitions: 3,\n\n /** Score penalty per detected loop. */\n loopPenaltyPerOccurrence: 0.3,\n\n /** Score deducted per ask_user call. */\n autonomyPenaltyPerAsk: 0.25,\n\n /** Score when a command ran but exit code is ambiguous. */\n ambiguousExitScore: 0.75,\n\n /** Minimum tool calls required to produce a meaningful score. */\n minToolCalls: 1,\n\n /** Tool names whose errors are expected/benign (not penalized). */\n benignErrorTools: ['search_content', 'find_files', 'lsp_inspect'] as string[],\n} as const;\n\n// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\ntype DimensionResult = { score: number; detail: string; applicable: boolean };\n\n/**\n * Check if the primary verb of an execute_command is a build/typecheck command.\n * Splits compound commands (&&, ;) and checks the first segment.\n */\n/** Extract command string from tool call args, then delegate to shared classifier. */\nfunction isBuildArgs(args: Record<string, unknown>): boolean {\n return isBuildCommand(String(args.command ?? ''));\n}\n\nfunction isTestArgs(args: Record<string, unknown>): boolean {\n return isTestCommand(String(args.command ?? ''));\n}\n\n/**\n * Score build/typecheck outcome (final state).\n */\nfunction scoreBuild(execResults: ExtractedToolCall[]): DimensionResult {\n const builds = execResults.filter(r => r.toolName === 'execute_command' && isBuildArgs(r.args));\n if (builds.length === 0) return { score: 0, detail: 'No build/typecheck ran', applicable: false };\n\n const last = builds[builds.length - 1]!;\n if (last.isError) return { score: 0, detail: 'Build/typecheck tool errored', applicable: true };\n\n const exitCode = getExitCode(last.result);\n if (exitCode === 0) return { score: 1, detail: 'Build/typecheck passed', applicable: true };\n if (exitCode !== null && exitCode !== 0)\n return { score: 0, detail: `Build failed (exit ${exitCode})`, applicable: true };\n\n // Fallback: check result text for TS errors\n const text = typeof last.result === 'string' ? last.result : JSON.stringify(last.result ?? '');\n if (/error TS\\d+|Cannot find module|is not assignable/i.test(text)) {\n return { score: 0, detail: 'Build failed (TypeScript errors in output)', applicable: true };\n }\n\n return { score: THRESHOLDS.ambiguousExitScore, detail: 'Build ran, outcome unclear', applicable: true };\n}\n\n/**\n * Score test outcome (final state).\n */\nfunction scoreTests(execResults: ExtractedToolCall[]): DimensionResult {\n const tests = execResults.filter(r => r.toolName === 'execute_command' && isTestArgs(r.args));\n if (tests.length === 0) return { score: 0, detail: 'No tests ran', applicable: false };\n\n const last = tests[tests.length - 1]!;\n if (last.isError) return { score: 0, detail: 'Test command errored', applicable: true };\n\n const exitCode = getExitCode(last.result);\n if (exitCode === 0) return { score: 1, detail: 'Tests passed', applicable: true };\n if (exitCode !== null && exitCode !== 0)\n return { score: 0, detail: `Tests failed (exit ${exitCode})`, applicable: true };\n\n const text = typeof last.result === 'string' ? last.result : JSON.stringify(last.result ?? '');\n if (/\\d+ (?:tests? )?passed|✓|PASS/i.test(text) && !/\\bfail(?:ed|ure)?\\b|\\berror\\b/i.test(text)) {\n return { score: 1, detail: 'Tests passed (inferred)', applicable: true };\n }\n if (/\\bFAIL\\b|failed|✗|✘/i.test(text)) {\n return { score: 0, detail: 'Tests failed (inferred)', applicable: true };\n }\n\n return { score: THRESHOLDS.ambiguousExitScore, detail: 'Tests ran, outcome unclear', applicable: true };\n}\n\n/**\n * Score tool error rate, excluding benign errors from expected-to-fail tools.\n */\nfunction scoreToolErrors(results: ExtractedToolCall[]): DimensionResult {\n if (results.length === 0) return { score: 1, detail: 'No tool calls', applicable: false };\n\n // Only count errors from non-benign tools\n const nonBenign = results.filter(r => !THRESHOLDS.benignErrorTools.includes(r.toolName));\n if (nonBenign.length === 0) return { score: 1, detail: 'All tool calls are benign-error tools', applicable: true };\n\n const errors = nonBenign.filter(r => r.isError);\n const rate = errors.length / nonBenign.length;\n\n if (rate === 0) return { score: 1, detail: 'No tool errors', applicable: true };\n\n const score = Math.max(0, 1 - rate * THRESHOLDS.toolErrorPenaltyMultiplier);\n return {\n score,\n detail: `${errors.length}/${nonBenign.length} tools errored (${(rate * 100).toFixed(0)}%)`,\n applicable: true,\n };\n}\n\n/**\n * Detect stuck loops: same tool + same args appearing 3+ times consecutively,\n * or same tool + same args erroring 3+ times total.\n */\nfunction scoreStuckLoops(results: ExtractedToolCall[]): DimensionResult {\n if (results.length < 3) return { score: 1, detail: 'Too few calls for loop detection', applicable: true };\n\n const fingerprint = (r: ExtractedToolCall) => `${r.toolName}:${JSON.stringify(r.args)}`;\n\n // Check consecutive identical calls\n let maxConsecutive = 1;\n let currentRun = 1;\n for (let i = 1; i < results.length; i++) {\n if (fingerprint(results[i]!) === fingerprint(results[i - 1]!)) {\n currentRun++;\n maxConsecutive = Math.max(maxConsecutive, currentRun);\n } else {\n currentRun = 1;\n }\n }\n\n // Check repeated error fingerprints\n const errorCounts = new Map<string, number>();\n for (const r of results) {\n if (!r.isError) continue;\n const fp = fingerprint(r);\n errorCounts.set(fp, (errorCounts.get(fp) ?? 0) + 1);\n }\n const maxErrorRepeat = Math.max(0, ...[...errorCounts.values()]);\n\n const minReps = THRESHOLDS.loopMinRepetitions;\n const loopSeverity = Math.max(\n maxConsecutive >= minReps ? maxConsecutive - (minReps - 1) : 0,\n maxErrorRepeat >= minReps ? maxErrorRepeat - (minReps - 1) : 0,\n );\n\n if (loopSeverity === 0) return { score: 1, detail: 'No stuck loops', applicable: true };\n\n const score = Math.max(0, 1 - loopSeverity * THRESHOLDS.loopPenaltyPerOccurrence);\n const details: string[] = [];\n if (maxConsecutive >= minReps) details.push(`${maxConsecutive} consecutive identical calls`);\n if (maxErrorRepeat >= minReps) details.push(`same error repeated ${maxErrorRepeat}x`);\n return { score, detail: details.join('; '), applicable: true };\n}\n\n/**\n * Detect regressions: build/test passed at some point, then failed later.\n * Gives partial credit if the agent recovered (final state passes).\n */\nfunction scoreRegression(execResults: ExtractedToolCall[]): DimensionResult {\n const regressions: { label: string; recovered: boolean }[] = [];\n\n for (const [label, filter] of [\n ['Build', isBuildArgs],\n ['Tests', isTestArgs],\n ] as const) {\n const cmds = execResults.filter(r => r.toolName === 'execute_command' && filter(r.args));\n if (cmds.length < 2) continue;\n\n let sawPass = false;\n let sawRegression = false;\n for (const cmd of cmds) {\n const exit = getExitCode(cmd.result);\n if (exit === 0) {\n if (sawRegression) {\n // Recovered from regression\n regressions.push({ label, recovered: true });\n sawRegression = false;\n }\n sawPass = true;\n }\n if (sawPass && exit !== null && exit !== 0) {\n sawRegression = true;\n }\n }\n\n if (sawRegression) {\n // Regression persisted at end of session\n regressions.push({ label, recovered: false });\n }\n }\n\n if (regressions.length === 0) return { score: 1, detail: 'No regressions', applicable: true };\n\n // Persistent regressions are severe (0), recovered regressions get partial credit (0.5)\n const persistentCount = regressions.filter(r => !r.recovered).length;\n const recoveredCount = regressions.filter(r => r.recovered).length;\n const score = Math.max(0, 1 - persistentCount * 0.5 - recoveredCount * 0.1);\n const details = regressions.map(\n r => `${r.label} ${r.recovered ? 'regressed then recovered' : 'regressed (persisted)'}`,\n );\n return { score, detail: details.join('; '), applicable: true };\n}\n\n/**\n * Score autonomy: penalize ask_user calls.\n * 0 calls = 1.0, each call reduces by 0.25 (up to a floor of 0).\n */\nfunction scoreAutonomy(results: ExtractedToolCall[]): DimensionResult {\n const askUserCalls = results.filter(r => r.toolName === 'ask_user');\n const count = askUserCalls.length;\n\n if (count === 0) return { score: 1, detail: 'No ask_user calls (fully autonomous)', applicable: true };\n\n const score = Math.max(0, 1 - count * THRESHOLDS.autonomyPenaltyPerAsk);\n return { score, detail: `${count} ask_user call${count > 1 ? 's' : ''}`, applicable: true };\n}\n\n/**\n * Compute weighted average of only applicable dimensions.\n * Returns null if no dimensions are applicable.\n */\nfunction weightedAverage(dimensions: Record<string, DimensionResult>, weights: Record<string, number>): number | null {\n let totalWeight = 0;\n let weightedSum = 0;\n\n for (const [key, result] of Object.entries(dimensions)) {\n if (!result.applicable) continue;\n const weight = weights[key] ?? 0;\n totalWeight += weight;\n weightedSum += result.score * weight;\n }\n\n if (totalWeight === 0) return null;\n return weightedSum / totalWeight;\n}\n\nexport function createOutcomeScorer() {\n return createScorer({\n id: 'mastracode-outcome',\n name: 'MastraCode Outcome',\n description:\n 'Grades coding session outcomes: build/test pass, tool errors, stuck loops, regressions, autonomy. Always-on.',\n type: 'agent',\n prepareRun: filterRun({ partTypes: ['tool-invocation', 'text'] }),\n })\n .preprocess(async ({ run }) => {\n const messages = (run.output ?? []) as MastraDBMessage[];\n const allResults = extractToolCalls(messages);\n const totalCalls = allResults.length;\n\n // H1: Empty sessions or sessions with no tool calls → score 0\n if (totalCalls < THRESHOLDS.minToolCalls) {\n const empty: DimensionResult = { score: 0, detail: 'No tool calls', applicable: false };\n return {\n empty: true as const,\n build: empty,\n tests: empty,\n toolErrors: empty,\n loops: empty,\n regression: empty,\n autonomy: empty,\n totalCalls,\n };\n }\n\n const execResults = allResults.filter(r => r.toolName === 'execute_command');\n\n return {\n empty: false as const,\n build: scoreBuild(execResults),\n tests: scoreTests(execResults),\n toolErrors: scoreToolErrors(allResults),\n loops: scoreStuckLoops(allResults),\n regression: scoreRegression(execResults),\n autonomy: scoreAutonomy(allResults),\n totalCalls,\n };\n })\n .generateScore(({ results }) => {\n const p = results.preprocessStepResult;\n if (p.empty) return 0;\n\n const score = weightedAverage(\n {\n build: p.build,\n tests: p.tests,\n toolErrors: p.toolErrors,\n loops: p.loops,\n regression: p.regression,\n autonomy: p.autonomy,\n },\n WEIGHTS,\n );\n\n return score === null ? 0 : Math.round(score * 100) / 100;\n })\n .generateReason(({ results, score }) => {\n const p = results.preprocessStepResult;\n if (p.empty) {\n return `Score: 0 (${p.totalCalls} tool calls — below minimum threshold for scoring)`;\n }\n\n const pct = (w: number) => `${(w * 100).toFixed(0)}%`;\n const dimLine = (name: string, weight: number, r: DimensionResult) =>\n r.applicable\n ? `${name} (${pct(weight)}): ${r.detail} [${r.score}]`\n : `${name}: ${r.detail} [N/A — excluded from average]`;\n\n const parts: string[] = [`Score: ${score} (${p.totalCalls} tool calls total)`];\n parts.push(dimLine('Build', WEIGHTS.build, p.build));\n parts.push(dimLine('Tests', WEIGHTS.tests, p.tests));\n parts.push(dimLine('Tool errors', WEIGHTS.toolErrors, p.toolErrors));\n parts.push(dimLine('Loops', WEIGHTS.loops, p.loops));\n parts.push(dimLine('Regression', WEIGHTS.regression, p.regression));\n parts.push(dimLine('Autonomy', WEIGHTS.autonomy, p.autonomy));\n\n return parts.join('\\n');\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"remote-controller.js","names":["#remote","#modes","#models"],"sources":["../../src/factory/remote-controller.ts"],"sourcesContent":["import type { AvailableModel, ModelAuthStatus } from '@mastra/core/agent-controller';\n\nimport type { FactoryAddress } from './client.js';\nimport { createFactoryClient, FACTORY_CONTROLLER_ID } from './client.js';\n\nexport interface RemoteMode {\n id: string;\n name?: string;\n description?: string;\n default?: boolean;\n defaultModelId?: string;\n metadata?: Record<string, unknown>;\n}\n\ntype ControllerHandle = ReturnType<ReturnType<typeof createFactoryClient>['getAgentController']>;\n\n/**\n * The Factory-side counterpart to the local agent controller. Modes, models, and\n * provider auth come from the Factory, never from local settings or auth storage.\n */\nexport class RemoteController {\n readonly id = FACTORY_CONTROLLER_ID;\n readonly #remote: ControllerHandle;\n #modes: RemoteMode[] = [];\n #models: Promise<AvailableModel[]> | undefined;\n\n constructor(address: FactoryAddress, remote?: ControllerHandle) {\n this.#remote = remote ?? createFactoryClient(address).getAgentController(FACTORY_CONTROLLER_ID);\n }\n\n /** Load the mode list; `listModes()` is synchronous so it must be primed. */\n async connect(): Promise<void> {\n this.#modes = (await this.#remote.listModes()) as RemoteMode[];\n }\n\n listModes(): RemoteMode[] {\n return this.#modes;\n }\n\n listAvailableModels(): Promise<AvailableModel[]> {\n this.#models ??= this.#remote.listModels().then(models => models as AvailableModel[]);\n this.#models.catch(() => (this.#models = undefined));\n return this.#models;\n }\n\n invalidateAvailableModelsCache(): void {\n this.#models = undefined;\n }\n\n /** Auth is the Factory's: report what it reports, or assume ok and let the run surface errors. */\n async getCurrentModelAuthStatus(session: { model: { get(): string } }): Promise<ModelAuthStatus> {\n const modelId = session.model.get();\n const models = await this.listAvailableModels().catch(() => []);\n const model = models.find(m => m.id === modelId);\n return model ? { hasAuth: model.hasApiKey, apiKeyEnvVar: model.apiKeyEnvVar } : { hasAuth: true };\n }\n}\n"],"mappings":";;;;;;AAoBA,IAAa,mBAAb,MAA8B;CAC5B,KAAc;CACd;CACA,SAAuB,CAAC;CACxB;CAEA,YAAY,SAAyB,QAA2B;EAC9D,KAAKA,UAAU,UAAU,oBAAoB,OAAO,CAAC,CAAC,mBAAA,MAAwC;CAChG;;CAGA,MAAM,UAAyB;EAC7B,KAAKC,SAAU,MAAM,KAAKD,QAAQ,UAAU;CAC9C;CAEA,YAA0B;EACxB,OAAO,KAAKC;CACd;CAEA,sBAAiD;EAC/C,KAAKC,YAAY,KAAKF,QAAQ,WAAW,CAAC,CAAC,MAAK,WAAU,MAA0B;EACpF,KAAKE,QAAQ,YAAa,KAAKA,UAAU,KAAA,CAAU;EACnD,OAAO,KAAKA;CACd;CAEA,iCAAuC;EACrC,KAAKA,UAAU,KAAA;CACjB;;CAGA,MAAM,0BAA0B,SAAiE;EAC/F,MAAM,UAAU,QAAQ,MAAM,IAAI;EAElC,MAAM,SAAQ,MADO,KAAK,oBAAoB,CAAC,CAAC,YAAY,CAAC,CAAC,EAAA,CACzC,MAAK,MAAK,EAAE,OAAO,OAAO;EAC/C,OAAO,QAAQ;GAAE,SAAS,MAAM;GAAW,cAAc,MAAM;EAAa,IAAI,EAAE,SAAS,KAAK;CAClG;AACF"}
1
+ {"version":3,"file":"remote-controller.js","names":[],"sources":["../../src/factory/remote-controller.ts"],"sourcesContent":["import type { AvailableModel, ModelAuthStatus } from '@mastra/core/agent-controller';\n\nimport type { FactoryAddress } from './client.js';\nimport { createFactoryClient, FACTORY_CONTROLLER_ID } from './client.js';\n\nexport interface RemoteMode {\n id: string;\n name?: string;\n description?: string;\n default?: boolean;\n defaultModelId?: string;\n metadata?: Record<string, unknown>;\n}\n\ntype ControllerHandle = ReturnType<ReturnType<typeof createFactoryClient>['getAgentController']>;\n\n/**\n * The Factory-side counterpart to the local agent controller. Modes, models, and\n * provider auth come from the Factory, never from local settings or auth storage.\n */\nexport class RemoteController {\n readonly id = FACTORY_CONTROLLER_ID;\n readonly #remote: ControllerHandle;\n #modes: RemoteMode[] = [];\n #models: Promise<AvailableModel[]> | undefined;\n\n constructor(address: FactoryAddress, remote?: ControllerHandle) {\n this.#remote = remote ?? createFactoryClient(address).getAgentController(FACTORY_CONTROLLER_ID);\n }\n\n /** Load the mode list; `listModes()` is synchronous so it must be primed. */\n async connect(): Promise<void> {\n this.#modes = (await this.#remote.listModes()) as RemoteMode[];\n }\n\n listModes(): RemoteMode[] {\n return this.#modes;\n }\n\n listAvailableModels(): Promise<AvailableModel[]> {\n this.#models ??= this.#remote.listModels().then(models => models as AvailableModel[]);\n this.#models.catch(() => (this.#models = undefined));\n return this.#models;\n }\n\n invalidateAvailableModelsCache(): void {\n this.#models = undefined;\n }\n\n /** Auth is the Factory's: report what it reports, or assume ok and let the run surface errors. */\n async getCurrentModelAuthStatus(session: { model: { get(): string } }): Promise<ModelAuthStatus> {\n const modelId = session.model.get();\n const models = await this.listAvailableModels().catch(() => []);\n const model = models.find(m => m.id === modelId);\n return model ? { hasAuth: model.hasApiKey, apiKeyEnvVar: model.apiKeyEnvVar } : { hasAuth: true };\n }\n}\n"],"mappings":";;;;;;AAoBA,IAAa,mBAAb,MAA8B;CAC5B,KAAc;CACd;CACA,SAAuB,CAAC;CACxB;CAEA,YAAY,SAAyB,QAA2B;EAC9D,KAAK,UAAU,UAAU,oBAAoB,OAAO,CAAC,CAAC,mBAAA,MAAwC;CAChG;;CAGA,MAAM,UAAyB;EAC7B,KAAK,SAAU,MAAM,KAAK,QAAQ,UAAU;CAC9C;CAEA,YAA0B;EACxB,OAAO,KAAK;CACd;CAEA,sBAAiD;EAC/C,KAAK,YAAY,KAAK,QAAQ,WAAW,CAAC,CAAC,MAAK,WAAU,MAA0B;EACpF,KAAK,QAAQ,YAAa,KAAK,UAAU,KAAA,CAAU;EACnD,OAAO,KAAK;CACd;CAEA,iCAAuC;EACrC,KAAK,UAAU,KAAA;CACjB;;CAGA,MAAM,0BAA0B,SAAiE;EAC/F,MAAM,UAAU,QAAQ,MAAM,IAAI;EAElC,MAAM,SAAQ,MADO,KAAK,oBAAoB,CAAC,CAAC,YAAY,CAAC,CAAC,EAAA,CACzC,MAAK,MAAK,EAAE,OAAO,OAAO;EAC/C,OAAO,QAAQ;GAAE,SAAS,MAAM;GAAW,cAAc,MAAM;EAAa,IAAI,EAAE,SAAS,KAAK;CAClG;AACF"}
@@ -170,9 +170,7 @@ var RemoteSession = class {
170
170
  case "agent_start":
171
171
  this.#running = true;
172
172
  break;
173
- case "agent_end":
174
- this.#running = false;
175
- break;
173
+ case "agent_end": this.#running = false;
176
174
  }
177
175
  for (const listener of this.#listeners) Promise.resolve(listener(event)).catch(() => {});
178
176
  }
@@ -1 +1 @@
1
- {"version":3,"file":"remote-session.js","names":["#remote","#sessionId","#listeners","#userIdMap","#resourceId","#hydrate","#refreshPermissions","#receive","#unsubscribe","#modes","#threadId","#modeId","#modelId","#running","#state","#displayState","#permissionRules","#pendingUserIds","#adoptLocalUserId","#emitError"],"sources":["../../src/factory/remote-session.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { AgentControllerEvent as ClientEvent, AgentControllerModeInfo } from '@mastra/client-js';\nimport type {\n AgentControllerDisplayState,\n AgentControllerEvent,\n AgentControllerEventListener,\n AgentControllerRequestStateUpdater,\n AgentControllerThread,\n PermissionRules,\n Session,\n} from '@mastra/core/agent-controller';\nimport { defaultDisplayState } from '@mastra/core/agent-controller';\n\nimport type { FactoryAddress } from './client.js';\nimport { createFactoryClient, FACTORY_CONTROLLER_ID } from './client.js';\n\ntype RemoteHandle = ReturnType<ReturnType<ReturnType<typeof createFactoryClient>['getAgentController']>['session']>;\ntype RemoteMode = AgentControllerModeInfo;\ntype Files = Array<{ data: string; mediaType: string; filename?: string }>;\n\nconst MAP_FIELDS = [\n 'activeTools',\n 'toolInputBuffers',\n 'pendingSuspensions',\n 'pendingApprovals',\n 'activeSubagents',\n 'modifiedFiles',\n] as const;\n\n/** The wire flattens the display state's Maps to objects; restore them. */\nfunction hydrateDisplayState(wire: Record<string, unknown>): AgentControllerDisplayState {\n const state: Record<string, unknown> = { ...defaultDisplayState(), ...wire };\n for (const field of MAP_FIELDS) {\n const value = wire[field];\n state[field] = value instanceof Map ? value : new Map(Object.entries((value as object | undefined) ?? {}));\n }\n return state as unknown as AgentControllerDisplayState;\n}\n\n/** The wire flattens errors to `{ name, message }`; restore them. */\nfunction hydrateEvent(event: ClientEvent): AgentControllerEvent {\n const out: Record<string, unknown> = { ...event };\n if (out.displayState && typeof out.displayState === 'object') {\n out.displayState = hydrateDisplayState(out.displayState as Record<string, unknown>);\n }\n const error = out.error as { name?: string; message?: string } | undefined;\n if (error && typeof error === 'object' && !(error instanceof Error)) {\n out.error = Object.assign(new Error(error.message ?? 'Remote error'), { name: error.name ?? 'Error' });\n }\n return out as unknown as AgentControllerEvent;\n}\n\nfunction toThread(\n thread: { id: string; title?: string; resourceId?: string; createdAt?: unknown; updatedAt?: unknown },\n resourceId: string,\n): AgentControllerThread {\n const date = (value: unknown) => (value ? new Date(value as string) : new Date());\n return {\n id: thread.id,\n resourceId: thread.resourceId ?? resourceId,\n title: thread.title ?? '',\n createdAt: date(thread.createdAt),\n updatedAt: date(thread.updatedAt),\n } as AgentControllerThread;\n}\n\n/**\n * Stands in for an in-process `Session` while the agent, tools, and storage\n * run inside a Factory. Reads are served from state mirrored off the session's\n * event stream; every action is forwarded to the Factory over HTTP.\n *\n * Only the surface the TUI drives is implemented. Anything else resolves to a\n * no-op so local-only features (thread locks, grants) degrade instead of crash.\n */\nexport class RemoteSession {\n readonly #remote: RemoteHandle;\n readonly #sessionId: string;\n /** Whose threads and memory the session works on; the Factory reports it when the session is created. */\n #resourceId: string;\n readonly #listeners = new Set<AgentControllerEventListener>();\n #unsubscribe: (() => void) | undefined;\n #threadId: string | null = null;\n #modeId = '';\n #modelId = '';\n #running = false;\n #state: Record<string, unknown> = {};\n #displayState: AgentControllerDisplayState = defaultDisplayState();\n #modes: RemoteMode[] = [];\n #permissionRules: PermissionRules = { categories: {}, tools: {} };\n #pendingUserIds: string[] = [];\n readonly #userIdMap = new Map<string, string>();\n\n constructor(address: FactoryAddress, sessionId: string, remote?: RemoteHandle) {\n this.#sessionId = sessionId;\n this.#resourceId = sessionId;\n this.#remote = remote ?? createFactoryClient(address).getAgentController(FACTORY_CONTROLLER_ID).session(sessionId);\n }\n\n /** Create/resume the remote session, hydrate the mirror, and open the event stream. */\n async connect(): Promise<void> {\n const created = await this.#remote.create();\n if (created?.resourceId) this.#resourceId = created.resourceId;\n await Promise.all([this.#hydrate(), this.#refreshPermissions()]);\n const subscription = await this.#remote.subscribe({\n reconnect: true,\n onEvent: event => this.#receive(hydrateEvent(event)),\n // The server does not replay missed events; resync after a drop.\n onReconnect: () => void this.#hydrate().catch(() => {}),\n });\n this.#unsubscribe = subscription.unsubscribe;\n }\n\n /** Modes come from the controller; used to resolve the current mode's name and color. */\n setModes(modes: RemoteMode[]): void {\n this.#modes = modes;\n }\n\n disconnect(): void {\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n }\n\n async #hydrate(): Promise<void> {\n const snapshot = await this.#remote.state();\n this.#threadId = snapshot.threadId ?? null;\n this.#modeId = snapshot.modeId;\n this.#modelId = snapshot.modelId;\n this.#running = Boolean(snapshot.running);\n // Settings (yolo, thinkingLevel, notifications, smartEditing) are session\n // state; the TUI reads them through state.get().\n if (snapshot.settings) this.#state = { ...this.#state, ...snapshot.settings };\n this.#displayState = {\n ...this.#displayState,\n ...(snapshot.tasks ? { tasks: snapshot.tasks } : {}),\n ...(snapshot.tokenUsage ? { tokenUsage: snapshot.tokenUsage } : {}),\n } as AgentControllerDisplayState;\n }\n\n async #refreshPermissions(): Promise<void> {\n const rules = await this.#remote.getPermissions();\n this.#permissionRules = {\n categories: (rules.categories ?? {}) as PermissionRules['categories'],\n tools: rules.tools ?? {},\n };\n }\n\n /** Surface a failed remote command in the transcript instead of dropping it. */\n #emitError(message: string): void {\n this.#receive({ type: 'error', error: new Error(message) } as AgentControllerEvent);\n }\n\n /**\n * The TUI renders a user message as soon as it is sent, keyed by the id\n * `sendSignal` returned. The Factory echoes it back under its own id, so map\n * that echo onto the local id to keep it from rendering twice.\n */\n #adoptLocalUserId(e: Record<string, any>): void {\n const message = e.message as\n | { id?: string; role?: string; content?: { metadata?: { signal?: { type?: string } } } }\n | undefined;\n const isUser =\n message?.role === 'user' || (message?.role === 'signal' && message.content?.metadata?.signal?.type === 'user');\n if (!message?.id || !isUser) return;\n let localId = this.#userIdMap.get(message.id);\n if (!localId && e.type === 'message_start') {\n localId = this.#pendingUserIds.shift();\n if (localId) this.#userIdMap.set(message.id, localId);\n }\n if (localId) e.message = { ...message, id: localId };\n }\n\n #receive(event: AgentControllerEvent): void {\n const e = event as AgentControllerEvent & Record<string, any>;\n if (e.type === 'message_start' || e.type === 'message_update' || e.type === 'message_end') {\n this.#adoptLocalUserId(e);\n }\n switch (e.type) {\n case 'mode_changed':\n this.#modeId = e.modeId;\n break;\n case 'model_changed':\n this.#modelId = e.modelId;\n break;\n case 'thread_changed':\n this.#threadId = e.threadId;\n break;\n case 'thread_created':\n this.#threadId = e.thread.id;\n break;\n case 'state_changed':\n this.#state = { ...this.#state, ...e.state };\n break;\n case 'display_state_changed':\n this.#displayState = e.displayState;\n break;\n case 'agent_start':\n this.#running = true;\n break;\n case 'agent_end':\n this.#running = false;\n break;\n }\n for (const listener of this.#listeners) {\n void Promise.resolve(listener(event)).catch(() => {});\n }\n }\n\n subscribe(listener: AgentControllerEventListener): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n onBeforeAgentEnd(): () => void {\n return () => {};\n }\n\n readonly identity = {\n getResourceId: () => this.#resourceId,\n getDefaultResourceId: () => this.#resourceId,\n getId: () => this.#sessionId,\n getOwnerId: () => this.#sessionId,\n setResourceId: () => {},\n };\n\n readonly thread = (() => {\n const remote = () => this.#remote;\n const resourceId = () => this.#resourceId;\n return {\n getId: () => this.#threadId,\n isSet: () => this.#threadId !== null,\n requireId: () => {\n if (!this.#threadId) throw new Error('No active thread');\n return this.#threadId;\n },\n ensureId: async () => {\n if (!this.#threadId) await this.thread.create();\n return this.#threadId!;\n },\n list: async () => (await remote().listThreads()).map(t => toThread(t, resourceId())),\n getById: async ({ threadId }: { threadId: string }) =>\n (await this.thread.list()).find(t => t.id === threadId) ?? null,\n create: async ({ title }: { title?: string } = {}) => {\n const created = await remote().createThread(title);\n const thread = toThread(created as any, resourceId());\n this.#threadId = thread.id;\n return thread;\n },\n switch: async ({ threadId }: { threadId: string }) => {\n await remote().switchThread(threadId);\n this.#threadId = threadId;\n },\n clone: async ({ sourceThreadId, title }: { sourceThreadId?: string; title?: string } = {}) => {\n const created = await remote().cloneThread({ sourceThreadId, title });\n const thread = toThread(created as any, resourceId());\n this.#threadId = thread.id;\n return thread;\n },\n rename: async ({ title }: { title: string }) => {\n if (this.#threadId) await remote().renameThread(this.#threadId, title);\n },\n delete: async ({ threadId }: { threadId: string }) => {\n await remote().deleteThread(threadId);\n if (this.#threadId === threadId) this.#threadId = null;\n },\n listMessages: ({ threadId, limit }: { threadId: string; limit?: number }) =>\n remote().listMessages(threadId, limit),\n listActiveMessages: async ({ limit }: { limit?: number } = {}) =>\n this.#threadId ? remote().listMessages(this.#threadId, limit) : [],\n firstUserMessage: async () => null,\n firstUserMessages: async () => new Map(),\n // Thread settings live in the Factory; local overrides are not persisted.\n getSetting: async () => undefined,\n getSettingOn: async () => undefined,\n setSetting: async () => {},\n setSettingOn: async () => {},\n deleteSetting: async () => {},\n cleanupSubscription: () => {},\n detachFromCurrent: () => {},\n clear: () => {\n this.#threadId = null;\n },\n clearAndReleaseLock: async () => {},\n cloneToCurrentResource: async ({ sourceThreadId }: { sourceThreadId: string }) =>\n this.thread.clone({ sourceThreadId }),\n loadMetadata: async () => {},\n };\n })();\n\n readonly mode = {\n get: () => this.#modeId,\n hasSelection: () => Boolean(this.#modeId),\n resolve: () => this.#modes.find(mode => mode.id === this.#modeId) ?? { id: this.#modeId, name: this.#modeId },\n set: ({ modeId }: { modeId: string }) => {\n this.#modeId = modeId;\n },\n switch: async ({ modeId }: { modeId: string }) => {\n await this.#remote.switchMode(modeId);\n this.#modeId = modeId;\n },\n };\n\n readonly model = {\n get: () => this.#modelId,\n hasSelection: () => Boolean(this.#modelId),\n displayName: () => this.#modelId,\n set: ({ modelId }: { modelId: string }) => {\n this.#modelId = modelId;\n },\n switch: async (modelId: string, options: { thinkingLevel?: any } = {}) => {\n await this.#remote.switchModel(modelId, options);\n this.#modelId = modelId;\n },\n syncFromPersisted: async () => {},\n };\n\n readonly state = {\n get: () => this.#state,\n set: async (updates: Record<string, unknown>) => {\n this.#state = { ...this.#state, ...updates };\n await this.#remote.setState(updates);\n },\n setIf: async (updates: Record<string, unknown>, shouldApply: () => boolean) => {\n if (!shouldApply()) return false;\n await this.state.set(updates);\n return true;\n },\n update: async <TResult>(updater: AgentControllerRequestStateUpdater<any, TResult>): Promise<TResult> => {\n const { updates, result } = await updater(this.#state);\n if (updates) await this.state.set(updates);\n return result;\n },\n };\n\n readonly displayState = {\n get: () => this.#displayState,\n clearModifiedFiles: () => {\n this.#displayState = { ...this.#displayState, modifiedFiles: new Map() };\n },\n restoreTasks: () => {},\n clearPendingSuspensions: () => {},\n deletePendingSuspension: () => {},\n clearPendingApprovals: () => {},\n resetThread: () => {},\n apply: () => {},\n };\n\n readonly run = {\n isRunning: () => this.#running,\n getRunId: () => null,\n getTraceId: () => null,\n };\n\n readonly stream = { isActive: () => this.#running, isOpen: () => this.#running };\n\n readonly suspensions = {\n hasPending: () => this.#displayState.pendingSuspensions.size > 0,\n get: ({ toolCallId }: { toolCallId: string }) => this.#displayState.pendingSuspensions.get(toolCallId),\n has: ({ toolCallId }: { toolCallId: string }) => this.#displayState.pendingSuspensions.has(toolCallId),\n delete: () => {},\n };\n\n readonly permissions = {\n getRules: () => this.#permissionRules,\n setForCategory: async ({ category, policy }: { category: any; policy: any }) => {\n await this.#remote.setPermissionForCategory(category, policy);\n await this.#refreshPermissions();\n },\n setForTool: async ({ toolName, policy }: { toolName: string; policy: any }) => {\n await this.#remote.setPermissionForTool(toolName, policy);\n await this.#refreshPermissions();\n },\n };\n\n getCurrentRunId(): string | null {\n return null;\n }\n\n getTags(): Record<string, string> {\n return {};\n }\n\n getTokenUsage() {\n return this.#displayState.tokenUsage;\n }\n\n getGrants() {\n return { categories: [], tools: [] };\n }\n\n async sendMessage({ content, files }: { content: string; files?: Files }) {\n await this.#remote.sendMessage({ content, files });\n }\n\n async queueMessage({ content }: { content: string }) {\n await this.#remote.followUp(content);\n }\n\n sendSignal(input: Parameters<Session['sendSignal']>[0]) {\n const content: unknown = 'content' in input ? input.content : undefined;\n const text =\n typeof content === 'string'\n ? content\n : Array.isArray(content)\n ? content.map(part => (typeof part === 'string' ? part : ((part as { text?: string })?.text ?? ''))).join('')\n : JSON.stringify(content);\n const files = 'files' in input ? (input.files as Files | undefined) : undefined;\n const message = files?.length ? { content: text, files } : text;\n const id = randomUUID();\n this.#pendingUserIds.push(id);\n const sent = this.#running ? this.#remote.followUp(message) : this.#remote.sendMessage(message);\n sent.catch(() => {\n this.#pendingUserIds = this.#pendingUserIds.filter(pending => pending !== id);\n });\n const accepted = sent.then(() => ({ accepted: true as const }));\n accepted.catch(() => {});\n return { id, type: 'user' as const, accepted, persisted: sent };\n }\n\n async steer({ content }: { content: string }) {\n await this.#remote.steer(content);\n }\n\n async followUp({ content }: { content: string }) {\n await this.#remote.followUp(content);\n }\n\n respondToToolApproval({\n decision,\n toolCallId,\n }: {\n decision: 'approve' | 'decline' | 'always_allow_category';\n toolCallId: string;\n }) {\n void (async () => {\n const ack = await this.#remote.approveTool(toolCallId, decision !== 'decline', {\n alwaysAllowCategory: decision === 'always_allow_category',\n });\n if (ack && 'accepted' in ack && ack.accepted === false) {\n this.#emitError(`Tool approval was not applied: ${(ack as { reason?: string }).reason ?? 'rejected'}`);\n }\n })().catch(error =>\n this.#emitError(`Tool approval failed: ${error instanceof Error ? error.message : String(error)}`),\n );\n return { accepted: true as const };\n }\n\n async respondToToolSuspension({ resumeData, toolCallId }: { resumeData: unknown; toolCallId?: string }) {\n const id = toolCallId ?? this.#displayState.pendingSuspensions.keys().next().value;\n if (!id) throw new Error('No pending tool suspension to respond to');\n await this.#remote.respondToToolSuspension(\n id,\n resumeData as Parameters<RemoteHandle['respondToToolSuspension']>[1],\n );\n }\n\n claimToolResponse(): boolean {\n return true;\n }\n\n releaseToolResponse(): void {}\n\n abort(): void {\n void this.#remote\n .abort()\n .catch(error => this.#emitError(`Abort failed: ${error instanceof Error ? error.message : String(error)}`));\n }\n\n abortRun(): void {\n this.abort();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"remote-session.js","names":[],"sources":["../../src/factory/remote-session.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { AgentControllerEvent as ClientEvent, AgentControllerModeInfo } from '@mastra/client-js';\nimport type {\n AgentControllerDisplayState,\n AgentControllerEvent,\n AgentControllerEventListener,\n AgentControllerRequestStateUpdater,\n AgentControllerThread,\n PermissionRules,\n Session,\n} from '@mastra/core/agent-controller';\nimport { defaultDisplayState } from '@mastra/core/agent-controller';\n\nimport type { FactoryAddress } from './client.js';\nimport { createFactoryClient, FACTORY_CONTROLLER_ID } from './client.js';\n\ntype RemoteHandle = ReturnType<ReturnType<ReturnType<typeof createFactoryClient>['getAgentController']>['session']>;\ntype RemoteMode = AgentControllerModeInfo;\ntype Files = Array<{ data: string; mediaType: string; filename?: string }>;\n\nconst MAP_FIELDS = [\n 'activeTools',\n 'toolInputBuffers',\n 'pendingSuspensions',\n 'pendingApprovals',\n 'activeSubagents',\n 'modifiedFiles',\n] as const;\n\n/** The wire flattens the display state's Maps to objects; restore them. */\nfunction hydrateDisplayState(wire: Record<string, unknown>): AgentControllerDisplayState {\n const state: Record<string, unknown> = { ...defaultDisplayState(), ...wire };\n for (const field of MAP_FIELDS) {\n const value = wire[field];\n state[field] = value instanceof Map ? value : new Map(Object.entries((value as object | undefined) ?? {}));\n }\n return state as unknown as AgentControllerDisplayState;\n}\n\n/** The wire flattens errors to `{ name, message }`; restore them. */\nfunction hydrateEvent(event: ClientEvent): AgentControllerEvent {\n const out: Record<string, unknown> = { ...event };\n if (out.displayState && typeof out.displayState === 'object') {\n out.displayState = hydrateDisplayState(out.displayState as Record<string, unknown>);\n }\n const error = out.error as { name?: string; message?: string } | undefined;\n if (error && typeof error === 'object' && !(error instanceof Error)) {\n out.error = Object.assign(new Error(error.message ?? 'Remote error'), { name: error.name ?? 'Error' });\n }\n return out as unknown as AgentControllerEvent;\n}\n\nfunction toThread(\n thread: { id: string; title?: string; resourceId?: string; createdAt?: unknown; updatedAt?: unknown },\n resourceId: string,\n): AgentControllerThread {\n const date = (value: unknown) => (value ? new Date(value as string) : new Date());\n return {\n id: thread.id,\n resourceId: thread.resourceId ?? resourceId,\n title: thread.title ?? '',\n createdAt: date(thread.createdAt),\n updatedAt: date(thread.updatedAt),\n } as AgentControllerThread;\n}\n\n/**\n * Stands in for an in-process `Session` while the agent, tools, and storage\n * run inside a Factory. Reads are served from state mirrored off the session's\n * event stream; every action is forwarded to the Factory over HTTP.\n *\n * Only the surface the TUI drives is implemented. Anything else resolves to a\n * no-op so local-only features (thread locks, grants) degrade instead of crash.\n */\nexport class RemoteSession {\n readonly #remote: RemoteHandle;\n readonly #sessionId: string;\n /** Whose threads and memory the session works on; the Factory reports it when the session is created. */\n #resourceId: string;\n readonly #listeners = new Set<AgentControllerEventListener>();\n #unsubscribe: (() => void) | undefined;\n #threadId: string | null = null;\n #modeId = '';\n #modelId = '';\n #running = false;\n #state: Record<string, unknown> = {};\n #displayState: AgentControllerDisplayState = defaultDisplayState();\n #modes: RemoteMode[] = [];\n #permissionRules: PermissionRules = { categories: {}, tools: {} };\n #pendingUserIds: string[] = [];\n readonly #userIdMap = new Map<string, string>();\n\n constructor(address: FactoryAddress, sessionId: string, remote?: RemoteHandle) {\n this.#sessionId = sessionId;\n this.#resourceId = sessionId;\n this.#remote = remote ?? createFactoryClient(address).getAgentController(FACTORY_CONTROLLER_ID).session(sessionId);\n }\n\n /** Create/resume the remote session, hydrate the mirror, and open the event stream. */\n async connect(): Promise<void> {\n const created = await this.#remote.create();\n if (created?.resourceId) this.#resourceId = created.resourceId;\n await Promise.all([this.#hydrate(), this.#refreshPermissions()]);\n const subscription = await this.#remote.subscribe({\n reconnect: true,\n onEvent: event => this.#receive(hydrateEvent(event)),\n // The server does not replay missed events; resync after a drop.\n onReconnect: () => void this.#hydrate().catch(() => {}),\n });\n this.#unsubscribe = subscription.unsubscribe;\n }\n\n /** Modes come from the controller; used to resolve the current mode's name and color. */\n setModes(modes: RemoteMode[]): void {\n this.#modes = modes;\n }\n\n disconnect(): void {\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n }\n\n async #hydrate(): Promise<void> {\n const snapshot = await this.#remote.state();\n this.#threadId = snapshot.threadId ?? null;\n this.#modeId = snapshot.modeId;\n this.#modelId = snapshot.modelId;\n this.#running = Boolean(snapshot.running);\n // Settings (yolo, thinkingLevel, notifications, smartEditing) are session\n // state; the TUI reads them through state.get().\n if (snapshot.settings) this.#state = { ...this.#state, ...snapshot.settings };\n this.#displayState = {\n ...this.#displayState,\n ...(snapshot.tasks ? { tasks: snapshot.tasks } : {}),\n ...(snapshot.tokenUsage ? { tokenUsage: snapshot.tokenUsage } : {}),\n } as AgentControllerDisplayState;\n }\n\n async #refreshPermissions(): Promise<void> {\n const rules = await this.#remote.getPermissions();\n this.#permissionRules = {\n categories: (rules.categories ?? {}) as PermissionRules['categories'],\n tools: rules.tools ?? {},\n };\n }\n\n /** Surface a failed remote command in the transcript instead of dropping it. */\n #emitError(message: string): void {\n this.#receive({ type: 'error', error: new Error(message) } as AgentControllerEvent);\n }\n\n /**\n * The TUI renders a user message as soon as it is sent, keyed by the id\n * `sendSignal` returned. The Factory echoes it back under its own id, so map\n * that echo onto the local id to keep it from rendering twice.\n */\n #adoptLocalUserId(e: Record<string, any>): void {\n const message = e.message as\n | { id?: string; role?: string; content?: { metadata?: { signal?: { type?: string } } } }\n | undefined;\n const isUser =\n message?.role === 'user' || (message?.role === 'signal' && message.content?.metadata?.signal?.type === 'user');\n if (!message?.id || !isUser) return;\n let localId = this.#userIdMap.get(message.id);\n if (!localId && e.type === 'message_start') {\n localId = this.#pendingUserIds.shift();\n if (localId) this.#userIdMap.set(message.id, localId);\n }\n if (localId) e.message = { ...message, id: localId };\n }\n\n #receive(event: AgentControllerEvent): void {\n const e = event as AgentControllerEvent & Record<string, any>;\n if (e.type === 'message_start' || e.type === 'message_update' || e.type === 'message_end') {\n this.#adoptLocalUserId(e);\n }\n switch (e.type) {\n case 'mode_changed':\n this.#modeId = e.modeId;\n break;\n case 'model_changed':\n this.#modelId = e.modelId;\n break;\n case 'thread_changed':\n this.#threadId = e.threadId;\n break;\n case 'thread_created':\n this.#threadId = e.thread.id;\n break;\n case 'state_changed':\n this.#state = { ...this.#state, ...e.state };\n break;\n case 'display_state_changed':\n this.#displayState = e.displayState;\n break;\n case 'agent_start':\n this.#running = true;\n break;\n case 'agent_end':\n this.#running = false;\n break;\n }\n for (const listener of this.#listeners) {\n void Promise.resolve(listener(event)).catch(() => {});\n }\n }\n\n subscribe(listener: AgentControllerEventListener): () => void {\n this.#listeners.add(listener);\n return () => this.#listeners.delete(listener);\n }\n\n onBeforeAgentEnd(): () => void {\n return () => {};\n }\n\n readonly identity = {\n getResourceId: () => this.#resourceId,\n getDefaultResourceId: () => this.#resourceId,\n getId: () => this.#sessionId,\n getOwnerId: () => this.#sessionId,\n setResourceId: () => {},\n };\n\n readonly thread = (() => {\n const remote = () => this.#remote;\n const resourceId = () => this.#resourceId;\n return {\n getId: () => this.#threadId,\n isSet: () => this.#threadId !== null,\n requireId: () => {\n if (!this.#threadId) throw new Error('No active thread');\n return this.#threadId;\n },\n ensureId: async () => {\n if (!this.#threadId) await this.thread.create();\n return this.#threadId!;\n },\n list: async () => (await remote().listThreads()).map(t => toThread(t, resourceId())),\n getById: async ({ threadId }: { threadId: string }) =>\n (await this.thread.list()).find(t => t.id === threadId) ?? null,\n create: async ({ title }: { title?: string } = {}) => {\n const created = await remote().createThread(title);\n const thread = toThread(created as any, resourceId());\n this.#threadId = thread.id;\n return thread;\n },\n switch: async ({ threadId }: { threadId: string }) => {\n await remote().switchThread(threadId);\n this.#threadId = threadId;\n },\n clone: async ({ sourceThreadId, title }: { sourceThreadId?: string; title?: string } = {}) => {\n const created = await remote().cloneThread({ sourceThreadId, title });\n const thread = toThread(created as any, resourceId());\n this.#threadId = thread.id;\n return thread;\n },\n rename: async ({ title }: { title: string }) => {\n if (this.#threadId) await remote().renameThread(this.#threadId, title);\n },\n delete: async ({ threadId }: { threadId: string }) => {\n await remote().deleteThread(threadId);\n if (this.#threadId === threadId) this.#threadId = null;\n },\n listMessages: ({ threadId, limit }: { threadId: string; limit?: number }) =>\n remote().listMessages(threadId, limit),\n listActiveMessages: async ({ limit }: { limit?: number } = {}) =>\n this.#threadId ? remote().listMessages(this.#threadId, limit) : [],\n firstUserMessage: async () => null,\n firstUserMessages: async () => new Map(),\n // Thread settings live in the Factory; local overrides are not persisted.\n getSetting: async () => undefined,\n getSettingOn: async () => undefined,\n setSetting: async () => {},\n setSettingOn: async () => {},\n deleteSetting: async () => {},\n cleanupSubscription: () => {},\n detachFromCurrent: () => {},\n clear: () => {\n this.#threadId = null;\n },\n clearAndReleaseLock: async () => {},\n cloneToCurrentResource: async ({ sourceThreadId }: { sourceThreadId: string }) =>\n this.thread.clone({ sourceThreadId }),\n loadMetadata: async () => {},\n };\n })();\n\n readonly mode = {\n get: () => this.#modeId,\n hasSelection: () => Boolean(this.#modeId),\n resolve: () => this.#modes.find(mode => mode.id === this.#modeId) ?? { id: this.#modeId, name: this.#modeId },\n set: ({ modeId }: { modeId: string }) => {\n this.#modeId = modeId;\n },\n switch: async ({ modeId }: { modeId: string }) => {\n await this.#remote.switchMode(modeId);\n this.#modeId = modeId;\n },\n };\n\n readonly model = {\n get: () => this.#modelId,\n hasSelection: () => Boolean(this.#modelId),\n displayName: () => this.#modelId,\n set: ({ modelId }: { modelId: string }) => {\n this.#modelId = modelId;\n },\n switch: async (modelId: string, options: { thinkingLevel?: any } = {}) => {\n await this.#remote.switchModel(modelId, options);\n this.#modelId = modelId;\n },\n syncFromPersisted: async () => {},\n };\n\n readonly state = {\n get: () => this.#state,\n set: async (updates: Record<string, unknown>) => {\n this.#state = { ...this.#state, ...updates };\n await this.#remote.setState(updates);\n },\n setIf: async (updates: Record<string, unknown>, shouldApply: () => boolean) => {\n if (!shouldApply()) return false;\n await this.state.set(updates);\n return true;\n },\n update: async <TResult>(updater: AgentControllerRequestStateUpdater<any, TResult>): Promise<TResult> => {\n const { updates, result } = await updater(this.#state);\n if (updates) await this.state.set(updates);\n return result;\n },\n };\n\n readonly displayState = {\n get: () => this.#displayState,\n clearModifiedFiles: () => {\n this.#displayState = { ...this.#displayState, modifiedFiles: new Map() };\n },\n restoreTasks: () => {},\n clearPendingSuspensions: () => {},\n deletePendingSuspension: () => {},\n clearPendingApprovals: () => {},\n resetThread: () => {},\n apply: () => {},\n };\n\n readonly run = {\n isRunning: () => this.#running,\n getRunId: () => null,\n getTraceId: () => null,\n };\n\n readonly stream = { isActive: () => this.#running, isOpen: () => this.#running };\n\n readonly suspensions = {\n hasPending: () => this.#displayState.pendingSuspensions.size > 0,\n get: ({ toolCallId }: { toolCallId: string }) => this.#displayState.pendingSuspensions.get(toolCallId),\n has: ({ toolCallId }: { toolCallId: string }) => this.#displayState.pendingSuspensions.has(toolCallId),\n delete: () => {},\n };\n\n readonly permissions = {\n getRules: () => this.#permissionRules,\n setForCategory: async ({ category, policy }: { category: any; policy: any }) => {\n await this.#remote.setPermissionForCategory(category, policy);\n await this.#refreshPermissions();\n },\n setForTool: async ({ toolName, policy }: { toolName: string; policy: any }) => {\n await this.#remote.setPermissionForTool(toolName, policy);\n await this.#refreshPermissions();\n },\n };\n\n getCurrentRunId(): string | null {\n return null;\n }\n\n getTags(): Record<string, string> {\n return {};\n }\n\n getTokenUsage() {\n return this.#displayState.tokenUsage;\n }\n\n getGrants() {\n return { categories: [], tools: [] };\n }\n\n async sendMessage({ content, files }: { content: string; files?: Files }) {\n await this.#remote.sendMessage({ content, files });\n }\n\n async queueMessage({ content }: { content: string }) {\n await this.#remote.followUp(content);\n }\n\n sendSignal(input: Parameters<Session['sendSignal']>[0]) {\n const content: unknown = 'content' in input ? input.content : undefined;\n const text =\n typeof content === 'string'\n ? content\n : Array.isArray(content)\n ? content.map(part => (typeof part === 'string' ? part : ((part as { text?: string })?.text ?? ''))).join('')\n : JSON.stringify(content);\n const files = 'files' in input ? (input.files as Files | undefined) : undefined;\n const message = files?.length ? { content: text, files } : text;\n const id = randomUUID();\n this.#pendingUserIds.push(id);\n const sent = this.#running ? this.#remote.followUp(message) : this.#remote.sendMessage(message);\n sent.catch(() => {\n this.#pendingUserIds = this.#pendingUserIds.filter(pending => pending !== id);\n });\n const accepted = sent.then(() => ({ accepted: true as const }));\n accepted.catch(() => {});\n return { id, type: 'user' as const, accepted, persisted: sent };\n }\n\n async steer({ content }: { content: string }) {\n await this.#remote.steer(content);\n }\n\n async followUp({ content }: { content: string }) {\n await this.#remote.followUp(content);\n }\n\n respondToToolApproval({\n decision,\n toolCallId,\n }: {\n decision: 'approve' | 'decline' | 'always_allow_category';\n toolCallId: string;\n }) {\n void (async () => {\n const ack = await this.#remote.approveTool(toolCallId, decision !== 'decline', {\n alwaysAllowCategory: decision === 'always_allow_category',\n });\n if (ack && 'accepted' in ack && ack.accepted === false) {\n this.#emitError(`Tool approval was not applied: ${(ack as { reason?: string }).reason ?? 'rejected'}`);\n }\n })().catch(error =>\n this.#emitError(`Tool approval failed: ${error instanceof Error ? error.message : String(error)}`),\n );\n return { accepted: true as const };\n }\n\n async respondToToolSuspension({ resumeData, toolCallId }: { resumeData: unknown; toolCallId?: string }) {\n const id = toolCallId ?? this.#displayState.pendingSuspensions.keys().next().value;\n if (!id) throw new Error('No pending tool suspension to respond to');\n await this.#remote.respondToToolSuspension(\n id,\n resumeData as Parameters<RemoteHandle['respondToToolSuspension']>[1],\n );\n }\n\n claimToolResponse(): boolean {\n return true;\n }\n\n releaseToolResponse(): void {}\n\n abort(): void {\n void this.#remote\n .abort()\n .catch(error => this.#emitError(`Abort failed: ${error instanceof Error ? error.message : String(error)}`));\n }\n\n abortRun(): void {\n this.abort();\n }\n}\n"],"mappings":"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"}
@@ -159,7 +159,8 @@ async function runMCCli(predrainedInput, options) {
159
159
  }
160
160
  packId = resolved.packId;
161
161
  }
162
- const processMemoryDiagnostics = await startConfiguredProcessMemoryDiagnostics(createProcessMemoryDiagnosticsFromEnvironment(process.env), (warning) => {
162
+ const diagnosticsSetup = createProcessMemoryDiagnosticsFromEnvironment(process.env);
163
+ const processMemoryDiagnostics = await startConfiguredProcessMemoryDiagnostics(diagnosticsSetup, (warning) => {
163
164
  process.stderr.write(`Warning: ${warning}\n`);
164
165
  });
165
166
  let boot;
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../../src/headless/cli.ts"],"sourcesContent":["/**\n * CLI adapter for headless MastraCode runs.\n *\n * This is the only headless layer that touches the process: it parses argv,\n * reads stdin, bootstraps MastraCode via `createMastraCode`, drives `runMC`,\n * renders events/results to stdout/stderr through the pure formatters, maps the\n * result to an exit code, and owns teardown + `process.exit`.\n */\nimport { existsSync } from 'node:fs';\nimport type { Writable } from 'node:stream';\nimport { parseArgs } from 'node:util';\n\nimport { createMastraCode } from '../index.js';\nimport { activateModePackInSettings, resolveModePackArg, resolvePackSelection } from '../onboarding/pack-activation.js';\nimport { listBuiltinModePacks } from '../onboarding/packs.js';\nimport { loadSettings, resolveModelDefaults, saveSettings } from '../onboarding/settings.js';\nimport {\n createProcessMemoryDiagnosticsFromEnvironment,\n startConfiguredProcessMemoryDiagnostics,\n stopProcessMemoryDiagnosticsWithTimeout,\n} from '../process-memory-diagnostics.js';\nimport { setupDebugLogging } from '../utils/debug-log.js';\nimport { releaseAllThreadLocks } from '../utils/thread-lock.js';\n\nimport { buildParseArgsOptions, FLAGS, renderFlagUsage } from './flags.js';\nimport { createHumanFormatState, formatHuman, formatJsonl, isJsonlEvent, renderJsonResult } from './format.js';\nimport { permissionModeToPolicy } from './policy.js';\nimport { runMC } from './run-mc.js';\nimport type { PermissionMode, RunMode, ThinkingLevel } from './types.js';\n\n/** Consolidated output mode (replaces the old `--format` + `--output-format`). */\nexport type OutputMode = 'human' | 'json' | 'jsonl';\n\nexport interface HeadlessArgs {\n prompt?: string;\n /** Timeout in seconds (CLI surface); converted to ms before `runMC`. */\n timeout?: number;\n output: OutputMode;\n continue_: boolean;\n model?: string;\n pack?: string;\n mode?: RunMode;\n thinkingLevel?: ThinkingLevel;\n settings?: string;\n thread?: string;\n title?: string;\n cloneThread: boolean;\n resourceId?: string;\n /** Max agentic turns before the run aborts with exit code 1. */\n maxTurns?: number;\n /** Named permission mode resolving to a built-in policy. Defaults to `auto`. */\n permissionMode?: PermissionMode;\n}\n\nconst parseArgsOptions = buildParseArgsOptions();\nconst STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS = 2_000;\n\n/**\n * Returns true if `argv` selects headless mode. This must agree with what\n * {@link parseHeadlessArgs} (and `runMCCli`) accept as a prompt: `--prompt`/`-p`\n * or a bare positional prompt (e.g. `mastracode \"Fix the bug\"`). Note that a\n * prompt piped via stdin without a flag is handled separately by the caller.\n */\nexport function hasHeadlessFlag(argv: string[]): boolean {\n if (argv.some(a => a === '--prompt' || a === '-p')) return true;\n try {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n // A positional prompt only counts when not asking for help.\n return positionals.length > 0 && !values.help;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse CLI arguments for headless mode. The flag table in `flags.ts` is the\n * single source of truth: each flag carries its own coercion/validation, so this\n * function just walks {@link FLAGS} and assembles the typed {@link HeadlessArgs}.\n */\nexport function parseHeadlessArgs(argv: string[]): HeadlessArgs {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n\n // Seed defaults; per-flag values below override these.\n const args: HeadlessArgs = {\n output: 'human',\n continue_: false,\n cloneThread: false,\n };\n const sink = args as unknown as Record<string, unknown>;\n\n for (const flag of FLAGS) {\n if (!flag.field) continue; // e.g. --help, handled by the caller\n const raw = values[flag.key];\n if (raw === undefined) continue;\n\n if (flag.type === 'boolean') {\n sink[flag.field] = Boolean(raw);\n } else if (typeof raw === 'string') {\n sink[flag.field] = flag.coerce ? flag.coerce(raw) : raw;\n }\n }\n\n // A bare positional acts as the prompt when --prompt/-p is absent.\n if (args.prompt === undefined && positionals[0] !== undefined) {\n args.prompt = positionals[0];\n }\n\n if (args.continue_ && args.thread) {\n throw new Error('--continue and --thread cannot be used together');\n }\n\n return args;\n}\n\nexport function printHeadlessUsage(): void {\n process.stdout.write(`\nUsage: mastracode --prompt <text> [options]\n\nHeadless (non-interactive) mode options:\n${renderFlagUsage()}\n\nThread behavior:\n By default, a new thread is created for each run.\n Use --continue to resume the most recent thread, or --thread to target a specific one.\n Use --clone-thread to branch off a copy before running.\n\nSettings file:\n Uses the same settings.json as the interactive TUI. Pass --settings to use\n a custom settings file (e.g., settings-ci.json for CI). All model, pack,\n subagent, and OM configuration is resolved from settings at startup.\n\nExit codes:\n 0 Agent completed successfully\n 1 Error, aborted, or max turns reached\n 2 Timeout\n\nExamples:\n mastracode --prompt \"Fix the bug in auth.ts\"\n mastracode --prompt \"Add tests\" --timeout 300 --output json\n mastracode --prompt \"Refactor\" --output jsonl\n mastracode --prompt \"Review this PR\" --permission-mode deny --max-turns 10\n mastracode --settings ./settings-ci.json --prompt \"Run tests\"\n mastracode -c --prompt \"Continue where you left off\"\n echo \"Summarize the repo\" | mastracode --prompt -\n`);\n}\n\n/**\n * Wait until everything written to `stream` has been handed to the OS, since\n * `process.exit()` drops writes a pipe has not accepted yet. Resolves `false`\n * if the stream fails or closes first; never rejects or hangs on a gone reader.\n */\nexport function flushWritable(stream: Writable): Promise<boolean> {\n if (stream.destroyed || stream.errored) return Promise.resolve(false);\n if (stream.writableLength === 0) return Promise.resolve(true);\n return new Promise(resolve => {\n // Left attached: a stream can emit `error` after failing pending callbacks.\n stream.once('error', () => resolve(false));\n stream.once('close', () => resolve(false));\n stream.write('', error => resolve(!error));\n });\n}\n\n/**\n * Headless CLI entry point: parse arguments, read stdin, initialize MastraCode,\n * run via `runMC`, render output, and exit with the mapped code.\n */\nexport async function runMCCli(\n predrainedInput?: string | null,\n options?: { coAuthor?: { name?: string; email?: string } },\n): Promise<never> {\n if (process.argv.includes('--help') || process.argv.includes('-h')) {\n printHeadlessUsage();\n process.exit(0);\n }\n\n let args: HeadlessArgs;\n try {\n args = parseHeadlessArgs(process.argv);\n } catch (e) {\n process.stderr.write(`Error: ${(e as Error).message}\\n`);\n process.exit(1);\n }\n\n let prompt = args.prompt;\n if (predrainedInput !== undefined) {\n prompt = predrainedInput ?? '';\n } else if (prompt === '-' || (!prompt && !process.stdin.isTTY)) {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n prompt = Buffer.concat(chunks).toString('utf-8').trim();\n }\n\n if (!prompt) {\n printHeadlessUsage();\n process.stderr.write('Error: --prompt is required (or pipe via stdin)\\n');\n process.exit(1);\n }\n\n if (args.settings && !existsSync(args.settings)) {\n process.stderr.write(`Error: Settings file not found: ${args.settings}\\n`);\n process.exit(1);\n }\n\n // Resolve --pack up front: an unknown pack fails fast with the valid list,\n // before MastraCode boots. The selection itself is resolved inside runMC\n // after thread selection, because a resumed thread restores its persisted\n // mode — the mode that decides whether the pack has a model for this run.\n // Activation (which mirrors /models → Activate and becomes the global\n // default pack in settings) is saved via onApplied only once the pack has\n // actually been applied to the session — a rejected pack must not change\n // the persisted default on its way out.\n const settings = loadSettings(args.settings);\n let packId: string | undefined;\n if (args.pack !== undefined) {\n const resolved = resolveModePackArg(settings, args.pack);\n if ('error' in resolved) {\n process.stderr.write(`Error: ${resolved.error}\\n`);\n process.exit(1);\n }\n packId = resolved.packId;\n }\n\n const diagnosticsSetup = createProcessMemoryDiagnosticsFromEnvironment(process.env);\n const processMemoryDiagnostics = await startConfiguredProcessMemoryDiagnostics(diagnosticsSetup, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n\n let boot: Awaited<ReturnType<typeof createMastraCode>> | undefined;\n // Default to a non-zero exit so an unexpected throw before the run resolves\n // still surfaces as a failure to the caller / CI.\n let exitCode = 1;\n try {\n boot = await createMastraCode({ settingsPath: args.settings, coAuthor: options?.coAuthor });\n const { controller, session, mcpManager } = boot;\n\n const effectiveDefaults = resolveModelDefaults(settings, listBuiltinModePacks());\n\n if (mcpManager?.hasServers()) {\n try {\n await mcpManager.initInBackground();\n } catch (err) {\n process.stderr.write(`Warning: MCP server initialization failed: ${(err as Error).message ?? err}\\n`);\n }\n }\n\n setupDebugLogging();\n\n const humanState = createHumanFormatState();\n const run = runMC({\n controller,\n session,\n prompt,\n model: args.model,\n mode: args.mode,\n modeDefaults: effectiveDefaults,\n ...(packId\n ? {\n pack: {\n id: packId,\n resolve: (modeId: string) => resolvePackSelection(settings, packId, modeId),\n onApplied: () => {\n activateModePackInSettings(settings, packId);\n saveSettings(settings, args.settings);\n },\n },\n }\n : {}),\n thinkingLevel: args.thinkingLevel,\n thread: { id: args.thread, continueLatest: args.continue_, clone: args.cloneThread },\n resourceId: args.resourceId,\n title: args.title,\n timeoutMs: args.timeout ? args.timeout * 1000 : undefined,\n maxTurns: args.maxTurns,\n policy: args.permissionMode ? permissionModeToPolicy(args.permissionMode) : undefined,\n });\n\n // Stream live events for human + jsonl modes. (json mode prints only the final object.)\n for await (const event of run) {\n if (args.output === 'human') {\n const out = formatHuman(event, humanState);\n if (out.stdout) process.stdout.write(out.stdout);\n if (out.stderr) process.stderr.write(out.stderr);\n } else if (args.output === 'jsonl' && isJsonlEvent(event)) {\n process.stdout.write(JSON.stringify(formatJsonl(event)) + '\\n');\n }\n }\n\n const result = await run.result;\n exitCode = result.exitCode;\n\n if (args.output === 'json') {\n process.stdout.write(renderJsonResult(result));\n } else if (args.output === 'jsonl') {\n process.stdout.write(JSON.stringify({ type: 'result', ...result }) + '\\n');\n }\n\n if (result.status === 'timeout') {\n process.stderr.write(`\\nTimeout elapsed. Aborted.\\n`);\n } else if (result.error && args.output === 'human') {\n process.stderr.write(`Error: ${result.error.message}\\n`);\n }\n } catch (err) {\n process.stderr.write(`Error: ${(err as Error).message ?? err}\\n`);\n exitCode = 1;\n } finally {\n // --- Teardown (always runs, even on a thrown error) ---\n releaseAllThreadLocks();\n if (boot) {\n // Stop plugin-contributed signal providers (and the plugin reload listener)\n // before quiescing workers: a provider that keeps polling past this point\n // could dispatch into a controller that is shutting down. Release thread\n // claims first so a restarted process need not wait out this teardown.\n try {\n boot.releaseThreadClaims();\n } catch {\n // Best-effort — the process is exiting.\n }\n try {\n boot.stopPluginSignalProviders();\n } catch {\n // Best-effort — the process is exiting.\n }\n const { controller, mcpManager } = boot;\n // Release notification dispatch leases before the pubsub that holds them\n // closes. Headless has no shutdown deadline, so don't let a stalled\n // in-flight dispatch hold up exit.\n await Promise.race([\n boot.stopNotificationDispatch?.().catch(() => {}),\n new Promise<void>(resolve => setTimeout(resolve, STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS).unref()),\n ]);\n await Promise.allSettled([\n mcpManager?.disconnect(),\n controller.getMastra()?.stopWorkers(),\n controller.stopIntervals(),\n ]);\n // The signals pubsub is Mastra's event bus, so close it after the workers\n // stop. Call close() on the object; a detached method loses `this` and\n // rejects silently, leaving socket files behind.\n const signalsPubSub = boot.signalsPubSub as { close?: () => Promise<void> | void } | undefined;\n await Promise.allSettled([signalsPubSub?.close?.()]);\n }\n await stopProcessMemoryDiagnosticsWithTimeout(processMemoryDiagnostics, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n }\n\n // Let piped output (the final result line in particular) reach the reader\n // before exiting. Output that never arrived must not count as success.\n const [stdoutFlushed] = await Promise.all([flushWritable(process.stdout), flushWritable(process.stderr)]);\n if (!stdoutFlushed && exitCode === 0) exitCode = 1;\n process.exit(exitCode);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../../src/headless/cli.ts"],"sourcesContent":["/**\n * CLI adapter for headless MastraCode runs.\n *\n * This is the only headless layer that touches the process: it parses argv,\n * reads stdin, bootstraps MastraCode via `createMastraCode`, drives `runMC`,\n * renders events/results to stdout/stderr through the pure formatters, maps the\n * result to an exit code, and owns teardown + `process.exit`.\n */\nimport { existsSync } from 'node:fs';\nimport type { Writable } from 'node:stream';\nimport { parseArgs } from 'node:util';\n\nimport { createMastraCode } from '../index.js';\nimport { activateModePackInSettings, resolveModePackArg, resolvePackSelection } from '../onboarding/pack-activation.js';\nimport { listBuiltinModePacks } from '../onboarding/packs.js';\nimport { loadSettings, resolveModelDefaults, saveSettings } from '../onboarding/settings.js';\nimport {\n createProcessMemoryDiagnosticsFromEnvironment,\n startConfiguredProcessMemoryDiagnostics,\n stopProcessMemoryDiagnosticsWithTimeout,\n} from '../process-memory-diagnostics.js';\nimport { setupDebugLogging } from '../utils/debug-log.js';\nimport { releaseAllThreadLocks } from '../utils/thread-lock.js';\n\nimport { buildParseArgsOptions, FLAGS, renderFlagUsage } from './flags.js';\nimport { createHumanFormatState, formatHuman, formatJsonl, isJsonlEvent, renderJsonResult } from './format.js';\nimport { permissionModeToPolicy } from './policy.js';\nimport { runMC } from './run-mc.js';\nimport type { PermissionMode, RunMode, ThinkingLevel } from './types.js';\n\n/** Consolidated output mode (replaces the old `--format` + `--output-format`). */\nexport type OutputMode = 'human' | 'json' | 'jsonl';\n\nexport interface HeadlessArgs {\n prompt?: string;\n /** Timeout in seconds (CLI surface); converted to ms before `runMC`. */\n timeout?: number;\n output: OutputMode;\n continue_: boolean;\n model?: string;\n pack?: string;\n mode?: RunMode;\n thinkingLevel?: ThinkingLevel;\n settings?: string;\n thread?: string;\n title?: string;\n cloneThread: boolean;\n resourceId?: string;\n /** Max agentic turns before the run aborts with exit code 1. */\n maxTurns?: number;\n /** Named permission mode resolving to a built-in policy. Defaults to `auto`. */\n permissionMode?: PermissionMode;\n}\n\nconst parseArgsOptions = buildParseArgsOptions();\nconst STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS = 2_000;\n\n/**\n * Returns true if `argv` selects headless mode. This must agree with what\n * {@link parseHeadlessArgs} (and `runMCCli`) accept as a prompt: `--prompt`/`-p`\n * or a bare positional prompt (e.g. `mastracode \"Fix the bug\"`). Note that a\n * prompt piped via stdin without a flag is handled separately by the caller.\n */\nexport function hasHeadlessFlag(argv: string[]): boolean {\n if (argv.some(a => a === '--prompt' || a === '-p')) return true;\n try {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n // A positional prompt only counts when not asking for help.\n return positionals.length > 0 && !values.help;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse CLI arguments for headless mode. The flag table in `flags.ts` is the\n * single source of truth: each flag carries its own coercion/validation, so this\n * function just walks {@link FLAGS} and assembles the typed {@link HeadlessArgs}.\n */\nexport function parseHeadlessArgs(argv: string[]): HeadlessArgs {\n const { values, positionals } = parseArgs({\n args: argv.slice(2),\n options: parseArgsOptions,\n strict: false,\n allowPositionals: true,\n });\n\n // Seed defaults; per-flag values below override these.\n const args: HeadlessArgs = {\n output: 'human',\n continue_: false,\n cloneThread: false,\n };\n const sink = args as unknown as Record<string, unknown>;\n\n for (const flag of FLAGS) {\n if (!flag.field) continue; // e.g. --help, handled by the caller\n const raw = values[flag.key];\n if (raw === undefined) continue;\n\n if (flag.type === 'boolean') {\n sink[flag.field] = Boolean(raw);\n } else if (typeof raw === 'string') {\n sink[flag.field] = flag.coerce ? flag.coerce(raw) : raw;\n }\n }\n\n // A bare positional acts as the prompt when --prompt/-p is absent.\n if (args.prompt === undefined && positionals[0] !== undefined) {\n args.prompt = positionals[0];\n }\n\n if (args.continue_ && args.thread) {\n throw new Error('--continue and --thread cannot be used together');\n }\n\n return args;\n}\n\nexport function printHeadlessUsage(): void {\n process.stdout.write(`\nUsage: mastracode --prompt <text> [options]\n\nHeadless (non-interactive) mode options:\n${renderFlagUsage()}\n\nThread behavior:\n By default, a new thread is created for each run.\n Use --continue to resume the most recent thread, or --thread to target a specific one.\n Use --clone-thread to branch off a copy before running.\n\nSettings file:\n Uses the same settings.json as the interactive TUI. Pass --settings to use\n a custom settings file (e.g., settings-ci.json for CI). All model, pack,\n subagent, and OM configuration is resolved from settings at startup.\n\nExit codes:\n 0 Agent completed successfully\n 1 Error, aborted, or max turns reached\n 2 Timeout\n\nExamples:\n mastracode --prompt \"Fix the bug in auth.ts\"\n mastracode --prompt \"Add tests\" --timeout 300 --output json\n mastracode --prompt \"Refactor\" --output jsonl\n mastracode --prompt \"Review this PR\" --permission-mode deny --max-turns 10\n mastracode --settings ./settings-ci.json --prompt \"Run tests\"\n mastracode -c --prompt \"Continue where you left off\"\n echo \"Summarize the repo\" | mastracode --prompt -\n`);\n}\n\n/**\n * Wait until everything written to `stream` has been handed to the OS, since\n * `process.exit()` drops writes a pipe has not accepted yet. Resolves `false`\n * if the stream fails or closes first; never rejects or hangs on a gone reader.\n */\nexport function flushWritable(stream: Writable): Promise<boolean> {\n if (stream.destroyed || stream.errored) return Promise.resolve(false);\n if (stream.writableLength === 0) return Promise.resolve(true);\n return new Promise(resolve => {\n // Left attached: a stream can emit `error` after failing pending callbacks.\n stream.once('error', () => resolve(false));\n stream.once('close', () => resolve(false));\n stream.write('', error => resolve(!error));\n });\n}\n\n/**\n * Headless CLI entry point: parse arguments, read stdin, initialize MastraCode,\n * run via `runMC`, render output, and exit with the mapped code.\n */\nexport async function runMCCli(\n predrainedInput?: string | null,\n options?: { coAuthor?: { name?: string; email?: string } },\n): Promise<never> {\n if (process.argv.includes('--help') || process.argv.includes('-h')) {\n printHeadlessUsage();\n process.exit(0);\n }\n\n let args: HeadlessArgs;\n try {\n args = parseHeadlessArgs(process.argv);\n } catch (e) {\n process.stderr.write(`Error: ${(e as Error).message}\\n`);\n process.exit(1);\n }\n\n let prompt = args.prompt;\n if (predrainedInput !== undefined) {\n prompt = predrainedInput ?? '';\n } else if (prompt === '-' || (!prompt && !process.stdin.isTTY)) {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(chunk as Buffer);\n }\n prompt = Buffer.concat(chunks).toString('utf-8').trim();\n }\n\n if (!prompt) {\n printHeadlessUsage();\n process.stderr.write('Error: --prompt is required (or pipe via stdin)\\n');\n process.exit(1);\n }\n\n if (args.settings && !existsSync(args.settings)) {\n process.stderr.write(`Error: Settings file not found: ${args.settings}\\n`);\n process.exit(1);\n }\n\n // Resolve --pack up front: an unknown pack fails fast with the valid list,\n // before MastraCode boots. The selection itself is resolved inside runMC\n // after thread selection, because a resumed thread restores its persisted\n // mode — the mode that decides whether the pack has a model for this run.\n // Activation (which mirrors /models → Activate and becomes the global\n // default pack in settings) is saved via onApplied only once the pack has\n // actually been applied to the session — a rejected pack must not change\n // the persisted default on its way out.\n const settings = loadSettings(args.settings);\n let packId: string | undefined;\n if (args.pack !== undefined) {\n const resolved = resolveModePackArg(settings, args.pack);\n if ('error' in resolved) {\n process.stderr.write(`Error: ${resolved.error}\\n`);\n process.exit(1);\n }\n packId = resolved.packId;\n }\n\n const diagnosticsSetup = createProcessMemoryDiagnosticsFromEnvironment(process.env);\n const processMemoryDiagnostics = await startConfiguredProcessMemoryDiagnostics(diagnosticsSetup, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n\n let boot: Awaited<ReturnType<typeof createMastraCode>> | undefined;\n // Default to a non-zero exit so an unexpected throw before the run resolves\n // still surfaces as a failure to the caller / CI.\n let exitCode = 1;\n try {\n boot = await createMastraCode({ settingsPath: args.settings, coAuthor: options?.coAuthor });\n const { controller, session, mcpManager } = boot;\n\n const effectiveDefaults = resolveModelDefaults(settings, listBuiltinModePacks());\n\n if (mcpManager?.hasServers()) {\n try {\n await mcpManager.initInBackground();\n } catch (err) {\n process.stderr.write(`Warning: MCP server initialization failed: ${(err as Error).message ?? err}\\n`);\n }\n }\n\n setupDebugLogging();\n\n const humanState = createHumanFormatState();\n const run = runMC({\n controller,\n session,\n prompt,\n model: args.model,\n mode: args.mode,\n modeDefaults: effectiveDefaults,\n ...(packId\n ? {\n pack: {\n id: packId,\n resolve: (modeId: string) => resolvePackSelection(settings, packId, modeId),\n onApplied: () => {\n activateModePackInSettings(settings, packId);\n saveSettings(settings, args.settings);\n },\n },\n }\n : {}),\n thinkingLevel: args.thinkingLevel,\n thread: { id: args.thread, continueLatest: args.continue_, clone: args.cloneThread },\n resourceId: args.resourceId,\n title: args.title,\n timeoutMs: args.timeout ? args.timeout * 1000 : undefined,\n maxTurns: args.maxTurns,\n policy: args.permissionMode ? permissionModeToPolicy(args.permissionMode) : undefined,\n });\n\n // Stream live events for human + jsonl modes. (json mode prints only the final object.)\n for await (const event of run) {\n if (args.output === 'human') {\n const out = formatHuman(event, humanState);\n if (out.stdout) process.stdout.write(out.stdout);\n if (out.stderr) process.stderr.write(out.stderr);\n } else if (args.output === 'jsonl' && isJsonlEvent(event)) {\n process.stdout.write(JSON.stringify(formatJsonl(event)) + '\\n');\n }\n }\n\n const result = await run.result;\n exitCode = result.exitCode;\n\n if (args.output === 'json') {\n process.stdout.write(renderJsonResult(result));\n } else if (args.output === 'jsonl') {\n process.stdout.write(JSON.stringify({ type: 'result', ...result }) + '\\n');\n }\n\n if (result.status === 'timeout') {\n process.stderr.write(`\\nTimeout elapsed. Aborted.\\n`);\n } else if (result.error && args.output === 'human') {\n process.stderr.write(`Error: ${result.error.message}\\n`);\n }\n } catch (err) {\n process.stderr.write(`Error: ${(err as Error).message ?? err}\\n`);\n exitCode = 1;\n } finally {\n // --- Teardown (always runs, even on a thrown error) ---\n releaseAllThreadLocks();\n if (boot) {\n // Stop plugin-contributed signal providers (and the plugin reload listener)\n // before quiescing workers: a provider that keeps polling past this point\n // could dispatch into a controller that is shutting down. Release thread\n // claims first so a restarted process need not wait out this teardown.\n try {\n boot.releaseThreadClaims();\n } catch {\n // Best-effort — the process is exiting.\n }\n try {\n boot.stopPluginSignalProviders();\n } catch {\n // Best-effort — the process is exiting.\n }\n const { controller, mcpManager } = boot;\n // Release notification dispatch leases before the pubsub that holds them\n // closes. Headless has no shutdown deadline, so don't let a stalled\n // in-flight dispatch hold up exit.\n await Promise.race([\n boot.stopNotificationDispatch?.().catch(() => {}),\n new Promise<void>(resolve => setTimeout(resolve, STOP_NOTIFICATION_DISPATCH_TIMEOUT_MS).unref()),\n ]);\n await Promise.allSettled([\n mcpManager?.disconnect(),\n controller.getMastra()?.stopWorkers(),\n controller.stopIntervals(),\n ]);\n // The signals pubsub is Mastra's event bus, so close it after the workers\n // stop. Call close() on the object; a detached method loses `this` and\n // rejects silently, leaving socket files behind.\n const signalsPubSub = boot.signalsPubSub as { close?: () => Promise<void> | void } | undefined;\n await Promise.allSettled([signalsPubSub?.close?.()]);\n }\n await stopProcessMemoryDiagnosticsWithTimeout(processMemoryDiagnostics, warning => {\n process.stderr.write(`Warning: ${warning}\\n`);\n });\n }\n\n // Let piped output (the final result line in particular) reach the reader\n // before exiting. Output that never arrived must not count as success.\n const [stdoutFlushed] = await Promise.all([flushWritable(process.stdout), flushWritable(process.stderr)]);\n if (!stdoutFlushed && exitCode === 0) exitCode = 1;\n process.exit(exitCode);\n}\n"],"mappings":"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"}
@@ -60,13 +60,11 @@ function aggregate(event, acc) {
60
60
  totalTokens: event.usage.totalTokens
61
61
  };
62
62
  break;
63
- case "error":
64
- acc.error = {
65
- name: event.error.name,
66
- message: event.error.message,
67
- stack: event.error.stack
68
- };
69
- break;
63
+ case "error": acc.error = {
64
+ name: event.error.name,
65
+ message: event.error.message,
66
+ stack: event.error.stack
67
+ };
70
68
  }
71
69
  }
72
70
  /** Resolve a thread by its exact ID. Titles are not unique, so we don't match on them. */
@@ -228,8 +226,10 @@ function runMC(options) {
228
226
  aborted = true;
229
227
  session.abort();
230
228
  }
231
- if (options.signal) if (options.signal.aborted) queueMicrotask(() => abort());
232
- else options.signal.addEventListener("abort", () => abort(), { once: true });
229
+ if (options.signal) {
230
+ if (options.signal.aborted) queueMicrotask(() => abort());
231
+ else options.signal.addEventListener("abort", () => abort(), { once: true });
232
+ }
233
233
  function armTimeout() {
234
234
  if (!options.timeoutMs || settled) return;
235
235
  if (timeoutId) clearTimeout(timeoutId);
@@ -1 +1 @@
1
- {"version":3,"file":"run-mc.js","names":["#maxBuffer","#closed","#resolvers","#buffer"],"sources":["../../src/headless/run-mc.ts"],"sourcesContent":["/**\n * Core programmatic runner for MastraCode headless runs.\n *\n * `runMC` is a pure async runner: it takes an already-built `controller` +\n * `session` (from `createMastraCode(...)`), resolves run config, subscribes to\n * the session, applies a {@link ResolutionPolicy} for approvals/suspensions,\n * sends the prompt, and aggregates events into a {@link RunMCResult}.\n *\n * It never touches `process.*` and never calls `process.exit`. The returned\n * {@link MCRun} is async-iterable over controller events and also resolves to a\n * final result via `result`.\n */\nimport { isSessionStartupCancelledError } from '@mastra/core/agent-controller';\nimport type { AgentControllerEvent, Session } from '@mastra/core/agent-controller';\n\nimport { MODEL_FALLBACK_STATE_KEY } from '../auth/account-rotation-processor.js';\nimport { GoalManager } from '../goal-manager.js';\nimport { createGoalReminderSignal } from '../goal-signal.js';\nimport { THREAD_ACTIVE_MODEL_PACK_ID_KEY } from '../onboarding/settings.js';\n\nimport { autoApprovePolicy } from './policy.js';\nimport type { MCRun, ResolutionPolicy, RunMCOptions, RunMCResult, RunMCStatus } from './types.js';\n\nfunction exitCodeForStatus(status: RunMCStatus): number {\n switch (status) {\n case 'completed':\n case 'done':\n return 0;\n case 'timeout':\n return 2;\n default:\n return 1;\n }\n}\n\ninterface MutableResult {\n text: string;\n finishReason?: string;\n usage?: RunMCResult['usage'];\n toolCalls: RunMCResult['toolCalls'];\n toolResults: RunMCResult['toolResults'];\n error?: RunMCResult['error'];\n threadId?: string;\n activeAssistantMessageId?: string;\n}\n\nfunction aggregate(event: AgentControllerEvent, acc: MutableResult): void {\n switch (event.type) {\n case 'message_start':\n if (event.message.role === 'assistant') acc.activeAssistantMessageId = event.message.id;\n break;\n case 'message_update':\n if (event.event.type === 'text-delta' && event.id === acc.activeAssistantMessageId) {\n acc.text += event.event.delta;\n }\n break;\n case 'message_end':\n if (event.id === acc.activeAssistantMessageId) acc.activeAssistantMessageId = undefined;\n break;\n case 'tool_start':\n acc.toolCalls.push({ id: event.toolCallId, name: event.toolName, args: event.args });\n break;\n case 'tool_end': {\n const matching = acc.toolCalls.find(c => c.id === event.toolCallId);\n acc.toolResults.push({\n id: event.toolCallId,\n name: matching?.name ?? '',\n result: event.result,\n isError: event.isError,\n });\n break;\n }\n case 'usage_update':\n acc.usage = {\n inputTokens: event.usage.promptTokens,\n outputTokens: event.usage.completionTokens,\n totalTokens: event.usage.totalTokens,\n };\n break;\n case 'error':\n acc.error = {\n name: event.error.name,\n message: event.error.message,\n stack: event.error.stack,\n };\n break;\n }\n}\n\n/** Resolve a thread by its exact ID. Titles are not unique, so we don't match on them. */\nasync function resolveThread<TState extends Record<string, unknown>>(\n session: Session<TState>,\n threadId: string,\n resourceId?: string,\n): Promise<{ threadId: string } | { error: string }> {\n const threads = await session.thread.list(resourceId ? { allResources: true } : undefined);\n\n const byId = threads.find(thread => thread.id === threadId && (!resourceId || thread.resourceId === resourceId));\n if (byId) return { threadId: byId.id };\n\n return { error: `No thread found with ID \"${threadId}\"` };\n}\n\n/**\n * A simple back-pressured async queue. Events are pushed in; consumers pull via\n * the async iterator. `close()` ends iteration after draining buffered events.\n *\n * Buffered events are bounded by `maxBuffer`: if a consumer never iterates (the\n * result-only path, `await run.result` without `for await (...)`) — or simply\n * falls far behind — the oldest buffered events are dropped instead of growing\n * without limit. The aggregated {@link RunMCResult} is built independently of\n * this queue, so dropping buffered events never affects the final result.\n */\nclass EventQueue<T> {\n #buffer: T[] = [];\n #resolvers: Array<(r: IteratorResult<T>) => void> = [];\n #closed = false;\n readonly #maxBuffer: number;\n\n constructor(maxBuffer = 10_000) {\n this.#maxBuffer = maxBuffer;\n }\n\n push(value: T): void {\n if (this.#closed) return;\n const resolve = this.#resolvers.shift();\n if (resolve) {\n resolve({ value, done: false });\n return;\n }\n this.#buffer.push(value);\n if (this.#buffer.length > this.#maxBuffer) {\n // Bound memory: drop the oldest event once over the cap.\n this.#buffer.shift();\n }\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n for (const resolve of this.#resolvers.splice(0)) {\n resolve({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.#buffer.length > 0) {\n return Promise.resolve({ value: this.#buffer.shift()!, done: false });\n }\n if (this.#closed) {\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n }\n return new Promise<IteratorResult<T>>(resolve => this.#resolvers.push(resolve));\n },\n };\n }\n}\n\n/**\n * Run a headless MastraCode turn. Returns an {@link MCRun} handle that is\n * async-iterable over controller events and resolves to a {@link RunMCResult}.\n */\nexport function runMC<TState extends Record<string, unknown>>(options: RunMCOptions<TState>): MCRun {\n const { controller, session } = options;\n const goal = options.goal;\n const policy: ResolutionPolicy = options.policy ?? autoApprovePolicy;\n\n const queue = new EventQueue<AgentControllerEvent>();\n const acc: MutableResult = { text: '', toolCalls: [], toolResults: [] };\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let timedOut = false;\n let aborted = false;\n let maxTurnsExceeded = false;\n let assistantTurns = 0;\n let activeAssistantMessageId: string | undefined;\n let settled = false;\n let agentStarts = 0;\n let unsubscribe: (() => void) | undefined;\n let resolveResult!: (r: RunMCResult) => void;\n\n const result = new Promise<RunMCResult>(resolve => {\n resolveResult = resolve;\n });\n\n function finish(status: RunMCStatus, data: Partial<RunMCResult> = {}): void {\n if (settled) return;\n settled = true;\n if (timeoutId) clearTimeout(timeoutId);\n unsubscribe?.();\n queue.close();\n resolveResult({\n status,\n text: acc.text,\n finishReason: acc.finishReason,\n usage: acc.usage,\n toolCalls: acc.toolCalls,\n toolResults: acc.toolResults,\n threadId: acc.threadId ?? session.thread.getId() ?? undefined,\n error: acc.error,\n exitCode: exitCodeForStatus(status),\n ...data,\n });\n }\n\n function fail(message: string, name = 'Error'): void {\n if (!acc.error) acc.error = { name, message };\n finish('error');\n }\n\n function failUnresolvedSuspension(toolName?: string): void {\n fail(`Run ended with an unresolved ${toolName ? `${toolName} suspension` : 'tool suspension'}.`);\n }\n\n /**\n * Ask the policy for the next parked tool. The session starts a fresh\n * run for it, so this run stays open until that run ends. Returns false when\n * nothing is left to resume.\n */\n function resumeNextSuspension(): boolean {\n const next = session.displayState.get().pendingSuspensions.values().next();\n if (next.done) return false;\n const { toolCallId, toolName } = next.value;\n let outcome: ReturnType<ResolutionPolicy['onSuspension']>;\n try {\n outcome = policy.onSuspension({ type: 'tool_suspended', ...next.value });\n } catch (err) {\n fail(`Resolution policy failed: ${(err as Error).message}`);\n return true;\n }\n if ('abort' in outcome) {\n abort();\n // This run has already ended suspended, so no further agent_end is due.\n finish('aborted');\n return true;\n }\n const { resumeData } = outcome;\n // The session clears the ending run's state right after emitting\n // agent_end, so resume on a later task, as an interactive client would.\n setTimeout(() => {\n if (settled) return;\n if (timedOut) return finish('timeout');\n if (aborted) return finish('aborted');\n const startsBeforeResume = agentStarts;\n session.respondToToolSuspension({ toolCallId, resumeData }).then(\n () => {\n // The session returns without starting a run when the suspension is\n // no longer parked; nothing else would end this run then.\n if (!settled && agentStarts === startsBeforeResume) failUnresolvedSuspension(toolName);\n },\n (err: unknown) => fail(`Failed to resume ${toolName}: ${(err as Error).message}`),\n );\n }, 0);\n return true;\n }\n\n function abort(): void {\n if (settled) return;\n aborted = true;\n session.abort();\n }\n\n if (options.signal) {\n if (options.signal.aborted) {\n // Defer so the caller can attach iteration / result handlers first.\n queueMicrotask(() => abort());\n } else {\n options.signal.addEventListener('abort', () => abort(), { once: true });\n }\n }\n\n function armTimeout(): void {\n if (!options.timeoutMs || settled) return;\n if (timeoutId) clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n timedOut = true;\n session.abort();\n if (goal) finish('timeout');\n }, options.timeoutMs);\n }\n\n // Kick off the run asynchronously so the MCRun handle is returned synchronously.\n void (async () => {\n armTimeout();\n\n // --- Subscribe ---\n unsubscribe = session.subscribe(event => {\n if (settled) return;\n if (event.type === 'agent_start') agentStarts += 1;\n if (goal) armTimeout();\n\n if (event.type === 'tool_approval_required') {\n let decision: 'approve' | 'deny';\n try {\n decision = policy.onToolApproval(event);\n } catch (err) {\n fail(`Resolution policy failed: ${(err as Error).message}`);\n return;\n }\n session.respondToToolApproval({\n decision: decision === 'approve' ? 'approve' : 'decline',\n toolCallId: event.toolCallId,\n });\n queue.push(event);\n return;\n }\n\n if (event.type === 'message_start' && event.message.role === 'assistant') {\n activeAssistantMessageId = event.message.id;\n }\n\n aggregate(event, acc);\n queue.push(event);\n\n if (event.type === 'error' && goal) {\n finish('error');\n return;\n }\n\n // Count agentic turns (one assistant response = one turn). When the cap is\n // reached, abort the run; agent_end then resolves as 'max_turns'.\n if (event.type === 'message_end' && event.id === activeAssistantMessageId && options.maxTurns !== undefined) {\n activeAssistantMessageId = undefined;\n assistantTurns += 1;\n if (assistantTurns >= options.maxTurns && !maxTurnsExceeded) {\n maxTurnsExceeded = true;\n abort();\n }\n }\n\n if (event.type === 'goal_evaluation' && goal && !event.payload.pending) {\n if (event.payload.status === 'done' || event.payload.status === 'paused') {\n finish(event.payload.status, {\n objective: goal.objective,\n goalEvent: event,\n reason: event.payload.reason ?? event.payload.results?.[0]?.reason,\n iterations: event.payload.iteration,\n maxRuns: event.payload.maxRuns,\n });\n }\n return;\n }\n\n if (event.type === 'agent_end') {\n acc.finishReason = event.reason;\n // A suspended run is parked, not finished: nothing more arrives until a\n // parked tool is answered, including goal evaluations.\n if (event.reason === 'suspended' && !timedOut && !aborted) {\n if (!resumeNextSuspension()) failUnresolvedSuspension();\n return;\n }\n // Goal evaluations can arrive after the agent stream ends, so goal runs\n // remain active until a terminal goal_evaluation is emitted.\n if (goal) return;\n if (timedOut) {\n finish('timeout');\n } else if (event.reason === 'error') {\n finish('error');\n } else if (maxTurnsExceeded && event.reason !== 'complete') {\n // The cap forced an abort while the agent still had work to do.\n finish('max_turns');\n } else if ((event.reason === 'aborted' || aborted) && !maxTurnsExceeded) {\n finish('aborted');\n } else {\n finish('completed');\n }\n }\n });\n\n // --- Thread resolution (read-only) ---\n const thread = options.thread;\n const targetResourceId = options.resourceId ?? session.identity.getResourceId();\n let restoredThreadId: string | undefined;\n try {\n if (thread?.id) {\n const resolved = await resolveThread(session, thread.id, targetResourceId);\n if ('error' in resolved) return fail(resolved.error);\n restoredThreadId = resolved.threadId;\n } else if (thread?.continueLatest) {\n const threads = await session.thread.list(targetResourceId ? { allResources: true } : undefined);\n const resourceThreads = targetResourceId\n ? threads.filter(candidate => candidate.resourceId === targetResourceId)\n : threads;\n if (resourceThreads.length > 0) {\n restoredThreadId = [...resourceThreads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())[0]!.id;\n }\n }\n } catch (err) {\n return fail(`Failed to resolve thread: ${(err as Error).message}`);\n }\n\n // --- Config validation (read-only) ---\n const restoredThreadSelection = Boolean(restoredThreadId || thread?.clone);\n const modeIdForModel = options.mode ?? (options.modeDefaults ? session.mode.get() : undefined);\n const configuredDefaultModelId = modeIdForModel ? options.modeDefaults?.[modeIdForModel] : undefined;\n // A pack's model is not resolved here: a resumed thread restores its\n // persisted mode during thread selection, and the pack selection depends\n // on that mode. It is resolved and validated during config application.\n const modelIdToApply = options.pack\n ? options.model\n : (options.model ?? (options.mode || !restoredThreadSelection ? configuredDefaultModelId : undefined));\n // Where modelIdToApply came from, so validation errors name the right source.\n const modelSource = options.model ? 'flag' : 'mode';\n try {\n if (options.mode && !controller.listModes().some(mode => mode.id === options.mode)) {\n return fail(`Unknown mode: \"${options.mode}\"`);\n }\n\n if (modelIdToApply) {\n const available = await controller.listAvailableModels();\n const match = available.find(model => model.id === modelIdToApply);\n if (!match) {\n if (modelSource === 'flag') return fail(`Unknown model: \"${modelIdToApply}\"`);\n return fail(`Unknown model \"${modelIdToApply}\" configured for mode \"${modeIdForModel}\"`);\n }\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n if (modelSource === 'flag') return fail(`Model \"${modelIdToApply}\" has no API key configured.${keyHint}`);\n return fail(`Model \"${modelIdToApply}\" (mode: ${modeIdForModel}) has no API key configured.${keyHint}`);\n }\n }\n } catch (err) {\n return fail(`Failed to resolve run config: ${(err as Error).message}`);\n }\n\n // --- Resource id ---\n try {\n if (options.resourceId) {\n await controller.setResourceId(session, { resourceId: options.resourceId });\n }\n } catch (err) {\n return fail(`Failed to set resource id: ${(err as Error).message}`);\n }\n\n // --- Thread selection ---\n try {\n if (restoredThreadId) {\n await session.thread.switch({ threadId: restoredThreadId });\n } else if (!thread?.clone) {\n await session.thread.create();\n }\n } catch (err) {\n return fail(`Failed to select thread: ${(err as Error).message}`);\n }\n\n // --- Clone ---\n if (thread?.clone) {\n try {\n await session.thread.clone();\n } catch (err) {\n return fail(`Failed to clone thread: ${(err as Error).message}`);\n }\n }\n\n // --- Config application ---\n try {\n if (options.mode) {\n await session.mode.switch({ modeId: options.mode });\n }\n let packModelId: string | undefined;\n if (options.pack) {\n // Resolve the selection against the mode in effect now — thread\n // selection above may have restored a resumed thread's persisted mode.\n const packModeId = options.mode ?? session.mode.get();\n const packSelection = options.pack.resolve(packModeId);\n if (!packSelection) {\n return fail(`Model pack \"${options.pack.id}\" has no model for ${packModeId} mode`);\n }\n const available = await controller.listAvailableModels();\n const match = available.find(model => model.id === packSelection.modelId);\n if (!match) {\n return fail(`Unknown model \"${packSelection.modelId}\" from pack \"${options.pack.id}\"`);\n }\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n return fail(\n `Model \"${packSelection.modelId}\" (pack: ${options.pack.id}) has no API key configured.${keyHint}`,\n );\n }\n packModelId = packSelection.modelId;\n // Mirror /models → Activate on the session: sticky thread pack id,\n // subagent models, and the fallback route. Clears any pending fallback\n // from a continued thread so the pack's own route starts clean.\n await session.thread.setSetting({ key: THREAD_ACTIVE_MODEL_PACK_ID_KEY, value: options.pack.id });\n await session.thread.setSetting({ key: MODEL_FALLBACK_STATE_KEY, value: undefined });\n for (const [agentType, modelId] of Object.entries(packSelection.subagentModels ?? {})) {\n await session.subagents.model.set({ modelId, agentType });\n }\n await session.state.set({\n modelRoute: packSelection.modelRoute,\n [MODEL_FALLBACK_STATE_KEY]: null,\n } as unknown as Partial<TState>);\n }\n const sessionModelId = modelIdToApply ?? packModelId;\n if (sessionModelId) {\n await session.model.switch(sessionModelId);\n }\n // Persist the pack activation (global default) only once the pack has\n // fully taken effect on the session — a failed model switch above must\n // not leave the global default changed.\n options.pack?.onApplied?.();\n if (options.thinkingLevel) {\n await session.state.set({ thinkingLevel: options.thinkingLevel } as unknown as Partial<TState>);\n }\n } catch (err) {\n return fail(`Failed to apply run config: ${(err as Error).message}`);\n }\n\n // --- Title ---\n if (options.title) {\n try {\n await session.thread.rename({ title: options.title });\n } catch (err) {\n return fail(`Failed to set thread title: ${(err as Error).message}`);\n }\n }\n\n // --- Start ---\n try {\n if (goal) {\n const threadId = session.thread.getId();\n if (!threadId || !(await session.thread.getById({ threadId }))) {\n await session.thread.create();\n }\n\n const goalManager = goal.goalManager ?? new GoalManager();\n const state = {\n controller,\n session,\n goalManager,\n pendingNewThread: false,\n } as any;\n const objective = await goalManager.setGoal(state, goal.objective, goal.judgeModelId, goal.maxRuns);\n if (!objective) return fail('Failed to set goal.');\n\n const agent = controller.getCurrentAgent(session);\n const persisted = await agent.getObjective({ threadId: session.thread.getId()! });\n if (!persisted || persisted.id !== objective.id) {\n return fail('Failed to persist goal objective before sending goal signal.');\n }\n\n await goalManager.saveToThread(state);\n if (!settled) await session.sendSignal(createGoalReminderSignal(objective)).accepted;\n } else {\n if (!options.prompt) return fail('A prompt or goal is required.');\n await session.sendMessage({ content: options.prompt });\n }\n } catch (err) {\n if (\n isSessionStartupCancelledError(err) ||\n (err instanceof Error && err.name === 'AbortError' && (aborted || timedOut || options.signal?.aborted))\n ) {\n return finish(timedOut ? 'timeout' : maxTurnsExceeded ? 'max_turns' : 'aborted');\n }\n return fail(`Failed to start run: ${(err as Error).message}`);\n }\n })();\n\n return {\n result,\n abort,\n [Symbol.asyncIterator](): AsyncIterator<AgentControllerEvent> {\n return queue[Symbol.asyncIterator]();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"run-mc.js","names":[],"sources":["../../src/headless/run-mc.ts"],"sourcesContent":["/**\n * Core programmatic runner for MastraCode headless runs.\n *\n * `runMC` is a pure async runner: it takes an already-built `controller` +\n * `session` (from `createMastraCode(...)`), resolves run config, subscribes to\n * the session, applies a {@link ResolutionPolicy} for approvals/suspensions,\n * sends the prompt, and aggregates events into a {@link RunMCResult}.\n *\n * It never touches `process.*` and never calls `process.exit`. The returned\n * {@link MCRun} is async-iterable over controller events and also resolves to a\n * final result via `result`.\n */\nimport { isSessionStartupCancelledError } from '@mastra/core/agent-controller';\nimport type { AgentControllerEvent, Session } from '@mastra/core/agent-controller';\n\nimport { MODEL_FALLBACK_STATE_KEY } from '../auth/account-rotation-processor.js';\nimport { GoalManager } from '../goal-manager.js';\nimport { createGoalReminderSignal } from '../goal-signal.js';\nimport { THREAD_ACTIVE_MODEL_PACK_ID_KEY } from '../onboarding/settings.js';\n\nimport { autoApprovePolicy } from './policy.js';\nimport type { MCRun, ResolutionPolicy, RunMCOptions, RunMCResult, RunMCStatus } from './types.js';\n\nfunction exitCodeForStatus(status: RunMCStatus): number {\n switch (status) {\n case 'completed':\n case 'done':\n return 0;\n case 'timeout':\n return 2;\n default:\n return 1;\n }\n}\n\ninterface MutableResult {\n text: string;\n finishReason?: string;\n usage?: RunMCResult['usage'];\n toolCalls: RunMCResult['toolCalls'];\n toolResults: RunMCResult['toolResults'];\n error?: RunMCResult['error'];\n threadId?: string;\n activeAssistantMessageId?: string;\n}\n\nfunction aggregate(event: AgentControllerEvent, acc: MutableResult): void {\n switch (event.type) {\n case 'message_start':\n if (event.message.role === 'assistant') acc.activeAssistantMessageId = event.message.id;\n break;\n case 'message_update':\n if (event.event.type === 'text-delta' && event.id === acc.activeAssistantMessageId) {\n acc.text += event.event.delta;\n }\n break;\n case 'message_end':\n if (event.id === acc.activeAssistantMessageId) acc.activeAssistantMessageId = undefined;\n break;\n case 'tool_start':\n acc.toolCalls.push({ id: event.toolCallId, name: event.toolName, args: event.args });\n break;\n case 'tool_end': {\n const matching = acc.toolCalls.find(c => c.id === event.toolCallId);\n acc.toolResults.push({\n id: event.toolCallId,\n name: matching?.name ?? '',\n result: event.result,\n isError: event.isError,\n });\n break;\n }\n case 'usage_update':\n acc.usage = {\n inputTokens: event.usage.promptTokens,\n outputTokens: event.usage.completionTokens,\n totalTokens: event.usage.totalTokens,\n };\n break;\n case 'error':\n acc.error = {\n name: event.error.name,\n message: event.error.message,\n stack: event.error.stack,\n };\n break;\n }\n}\n\n/** Resolve a thread by its exact ID. Titles are not unique, so we don't match on them. */\nasync function resolveThread<TState extends Record<string, unknown>>(\n session: Session<TState>,\n threadId: string,\n resourceId?: string,\n): Promise<{ threadId: string } | { error: string }> {\n const threads = await session.thread.list(resourceId ? { allResources: true } : undefined);\n\n const byId = threads.find(thread => thread.id === threadId && (!resourceId || thread.resourceId === resourceId));\n if (byId) return { threadId: byId.id };\n\n return { error: `No thread found with ID \"${threadId}\"` };\n}\n\n/**\n * A simple back-pressured async queue. Events are pushed in; consumers pull via\n * the async iterator. `close()` ends iteration after draining buffered events.\n *\n * Buffered events are bounded by `maxBuffer`: if a consumer never iterates (the\n * result-only path, `await run.result` without `for await (...)`) — or simply\n * falls far behind — the oldest buffered events are dropped instead of growing\n * without limit. The aggregated {@link RunMCResult} is built independently of\n * this queue, so dropping buffered events never affects the final result.\n */\nclass EventQueue<T> {\n #buffer: T[] = [];\n #resolvers: Array<(r: IteratorResult<T>) => void> = [];\n #closed = false;\n readonly #maxBuffer: number;\n\n constructor(maxBuffer = 10_000) {\n this.#maxBuffer = maxBuffer;\n }\n\n push(value: T): void {\n if (this.#closed) return;\n const resolve = this.#resolvers.shift();\n if (resolve) {\n resolve({ value, done: false });\n return;\n }\n this.#buffer.push(value);\n if (this.#buffer.length > this.#maxBuffer) {\n // Bound memory: drop the oldest event once over the cap.\n this.#buffer.shift();\n }\n }\n\n close(): void {\n if (this.#closed) return;\n this.#closed = true;\n for (const resolve of this.#resolvers.splice(0)) {\n resolve({ value: undefined as unknown as T, done: true });\n }\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return {\n next: (): Promise<IteratorResult<T>> => {\n if (this.#buffer.length > 0) {\n return Promise.resolve({ value: this.#buffer.shift()!, done: false });\n }\n if (this.#closed) {\n return Promise.resolve({ value: undefined as unknown as T, done: true });\n }\n return new Promise<IteratorResult<T>>(resolve => this.#resolvers.push(resolve));\n },\n };\n }\n}\n\n/**\n * Run a headless MastraCode turn. Returns an {@link MCRun} handle that is\n * async-iterable over controller events and resolves to a {@link RunMCResult}.\n */\nexport function runMC<TState extends Record<string, unknown>>(options: RunMCOptions<TState>): MCRun {\n const { controller, session } = options;\n const goal = options.goal;\n const policy: ResolutionPolicy = options.policy ?? autoApprovePolicy;\n\n const queue = new EventQueue<AgentControllerEvent>();\n const acc: MutableResult = { text: '', toolCalls: [], toolResults: [] };\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n let timedOut = false;\n let aborted = false;\n let maxTurnsExceeded = false;\n let assistantTurns = 0;\n let activeAssistantMessageId: string | undefined;\n let settled = false;\n let agentStarts = 0;\n let unsubscribe: (() => void) | undefined;\n let resolveResult!: (r: RunMCResult) => void;\n\n const result = new Promise<RunMCResult>(resolve => {\n resolveResult = resolve;\n });\n\n function finish(status: RunMCStatus, data: Partial<RunMCResult> = {}): void {\n if (settled) return;\n settled = true;\n if (timeoutId) clearTimeout(timeoutId);\n unsubscribe?.();\n queue.close();\n resolveResult({\n status,\n text: acc.text,\n finishReason: acc.finishReason,\n usage: acc.usage,\n toolCalls: acc.toolCalls,\n toolResults: acc.toolResults,\n threadId: acc.threadId ?? session.thread.getId() ?? undefined,\n error: acc.error,\n exitCode: exitCodeForStatus(status),\n ...data,\n });\n }\n\n function fail(message: string, name = 'Error'): void {\n if (!acc.error) acc.error = { name, message };\n finish('error');\n }\n\n function failUnresolvedSuspension(toolName?: string): void {\n fail(`Run ended with an unresolved ${toolName ? `${toolName} suspension` : 'tool suspension'}.`);\n }\n\n /**\n * Ask the policy for the next parked tool. The session starts a fresh\n * run for it, so this run stays open until that run ends. Returns false when\n * nothing is left to resume.\n */\n function resumeNextSuspension(): boolean {\n const next = session.displayState.get().pendingSuspensions.values().next();\n if (next.done) return false;\n const { toolCallId, toolName } = next.value;\n let outcome: ReturnType<ResolutionPolicy['onSuspension']>;\n try {\n outcome = policy.onSuspension({ type: 'tool_suspended', ...next.value });\n } catch (err) {\n fail(`Resolution policy failed: ${(err as Error).message}`);\n return true;\n }\n if ('abort' in outcome) {\n abort();\n // This run has already ended suspended, so no further agent_end is due.\n finish('aborted');\n return true;\n }\n const { resumeData } = outcome;\n // The session clears the ending run's state right after emitting\n // agent_end, so resume on a later task, as an interactive client would.\n setTimeout(() => {\n if (settled) return;\n if (timedOut) return finish('timeout');\n if (aborted) return finish('aborted');\n const startsBeforeResume = agentStarts;\n session.respondToToolSuspension({ toolCallId, resumeData }).then(\n () => {\n // The session returns without starting a run when the suspension is\n // no longer parked; nothing else would end this run then.\n if (!settled && agentStarts === startsBeforeResume) failUnresolvedSuspension(toolName);\n },\n (err: unknown) => fail(`Failed to resume ${toolName}: ${(err as Error).message}`),\n );\n }, 0);\n return true;\n }\n\n function abort(): void {\n if (settled) return;\n aborted = true;\n session.abort();\n }\n\n if (options.signal) {\n if (options.signal.aborted) {\n // Defer so the caller can attach iteration / result handlers first.\n queueMicrotask(() => abort());\n } else {\n options.signal.addEventListener('abort', () => abort(), { once: true });\n }\n }\n\n function armTimeout(): void {\n if (!options.timeoutMs || settled) return;\n if (timeoutId) clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n timedOut = true;\n session.abort();\n if (goal) finish('timeout');\n }, options.timeoutMs);\n }\n\n // Kick off the run asynchronously so the MCRun handle is returned synchronously.\n void (async () => {\n armTimeout();\n\n // --- Subscribe ---\n unsubscribe = session.subscribe(event => {\n if (settled) return;\n if (event.type === 'agent_start') agentStarts += 1;\n if (goal) armTimeout();\n\n if (event.type === 'tool_approval_required') {\n let decision: 'approve' | 'deny';\n try {\n decision = policy.onToolApproval(event);\n } catch (err) {\n fail(`Resolution policy failed: ${(err as Error).message}`);\n return;\n }\n session.respondToToolApproval({\n decision: decision === 'approve' ? 'approve' : 'decline',\n toolCallId: event.toolCallId,\n });\n queue.push(event);\n return;\n }\n\n if (event.type === 'message_start' && event.message.role === 'assistant') {\n activeAssistantMessageId = event.message.id;\n }\n\n aggregate(event, acc);\n queue.push(event);\n\n if (event.type === 'error' && goal) {\n finish('error');\n return;\n }\n\n // Count agentic turns (one assistant response = one turn). When the cap is\n // reached, abort the run; agent_end then resolves as 'max_turns'.\n if (event.type === 'message_end' && event.id === activeAssistantMessageId && options.maxTurns !== undefined) {\n activeAssistantMessageId = undefined;\n assistantTurns += 1;\n if (assistantTurns >= options.maxTurns && !maxTurnsExceeded) {\n maxTurnsExceeded = true;\n abort();\n }\n }\n\n if (event.type === 'goal_evaluation' && goal && !event.payload.pending) {\n if (event.payload.status === 'done' || event.payload.status === 'paused') {\n finish(event.payload.status, {\n objective: goal.objective,\n goalEvent: event,\n reason: event.payload.reason ?? event.payload.results?.[0]?.reason,\n iterations: event.payload.iteration,\n maxRuns: event.payload.maxRuns,\n });\n }\n return;\n }\n\n if (event.type === 'agent_end') {\n acc.finishReason = event.reason;\n // A suspended run is parked, not finished: nothing more arrives until a\n // parked tool is answered, including goal evaluations.\n if (event.reason === 'suspended' && !timedOut && !aborted) {\n if (!resumeNextSuspension()) failUnresolvedSuspension();\n return;\n }\n // Goal evaluations can arrive after the agent stream ends, so goal runs\n // remain active until a terminal goal_evaluation is emitted.\n if (goal) return;\n if (timedOut) {\n finish('timeout');\n } else if (event.reason === 'error') {\n finish('error');\n } else if (maxTurnsExceeded && event.reason !== 'complete') {\n // The cap forced an abort while the agent still had work to do.\n finish('max_turns');\n } else if ((event.reason === 'aborted' || aborted) && !maxTurnsExceeded) {\n finish('aborted');\n } else {\n finish('completed');\n }\n }\n });\n\n // --- Thread resolution (read-only) ---\n const thread = options.thread;\n const targetResourceId = options.resourceId ?? session.identity.getResourceId();\n let restoredThreadId: string | undefined;\n try {\n if (thread?.id) {\n const resolved = await resolveThread(session, thread.id, targetResourceId);\n if ('error' in resolved) return fail(resolved.error);\n restoredThreadId = resolved.threadId;\n } else if (thread?.continueLatest) {\n const threads = await session.thread.list(targetResourceId ? { allResources: true } : undefined);\n const resourceThreads = targetResourceId\n ? threads.filter(candidate => candidate.resourceId === targetResourceId)\n : threads;\n if (resourceThreads.length > 0) {\n restoredThreadId = [...resourceThreads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())[0]!.id;\n }\n }\n } catch (err) {\n return fail(`Failed to resolve thread: ${(err as Error).message}`);\n }\n\n // --- Config validation (read-only) ---\n const restoredThreadSelection = Boolean(restoredThreadId || thread?.clone);\n const modeIdForModel = options.mode ?? (options.modeDefaults ? session.mode.get() : undefined);\n const configuredDefaultModelId = modeIdForModel ? options.modeDefaults?.[modeIdForModel] : undefined;\n // A pack's model is not resolved here: a resumed thread restores its\n // persisted mode during thread selection, and the pack selection depends\n // on that mode. It is resolved and validated during config application.\n const modelIdToApply = options.pack\n ? options.model\n : (options.model ?? (options.mode || !restoredThreadSelection ? configuredDefaultModelId : undefined));\n // Where modelIdToApply came from, so validation errors name the right source.\n const modelSource = options.model ? 'flag' : 'mode';\n try {\n if (options.mode && !controller.listModes().some(mode => mode.id === options.mode)) {\n return fail(`Unknown mode: \"${options.mode}\"`);\n }\n\n if (modelIdToApply) {\n const available = await controller.listAvailableModels();\n const match = available.find(model => model.id === modelIdToApply);\n if (!match) {\n if (modelSource === 'flag') return fail(`Unknown model: \"${modelIdToApply}\"`);\n return fail(`Unknown model \"${modelIdToApply}\" configured for mode \"${modeIdForModel}\"`);\n }\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n if (modelSource === 'flag') return fail(`Model \"${modelIdToApply}\" has no API key configured.${keyHint}`);\n return fail(`Model \"${modelIdToApply}\" (mode: ${modeIdForModel}) has no API key configured.${keyHint}`);\n }\n }\n } catch (err) {\n return fail(`Failed to resolve run config: ${(err as Error).message}`);\n }\n\n // --- Resource id ---\n try {\n if (options.resourceId) {\n await controller.setResourceId(session, { resourceId: options.resourceId });\n }\n } catch (err) {\n return fail(`Failed to set resource id: ${(err as Error).message}`);\n }\n\n // --- Thread selection ---\n try {\n if (restoredThreadId) {\n await session.thread.switch({ threadId: restoredThreadId });\n } else if (!thread?.clone) {\n await session.thread.create();\n }\n } catch (err) {\n return fail(`Failed to select thread: ${(err as Error).message}`);\n }\n\n // --- Clone ---\n if (thread?.clone) {\n try {\n await session.thread.clone();\n } catch (err) {\n return fail(`Failed to clone thread: ${(err as Error).message}`);\n }\n }\n\n // --- Config application ---\n try {\n if (options.mode) {\n await session.mode.switch({ modeId: options.mode });\n }\n let packModelId: string | undefined;\n if (options.pack) {\n // Resolve the selection against the mode in effect now — thread\n // selection above may have restored a resumed thread's persisted mode.\n const packModeId = options.mode ?? session.mode.get();\n const packSelection = options.pack.resolve(packModeId);\n if (!packSelection) {\n return fail(`Model pack \"${options.pack.id}\" has no model for ${packModeId} mode`);\n }\n const available = await controller.listAvailableModels();\n const match = available.find(model => model.id === packSelection.modelId);\n if (!match) {\n return fail(`Unknown model \"${packSelection.modelId}\" from pack \"${options.pack.id}\"`);\n }\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n return fail(\n `Model \"${packSelection.modelId}\" (pack: ${options.pack.id}) has no API key configured.${keyHint}`,\n );\n }\n packModelId = packSelection.modelId;\n // Mirror /models → Activate on the session: sticky thread pack id,\n // subagent models, and the fallback route. Clears any pending fallback\n // from a continued thread so the pack's own route starts clean.\n await session.thread.setSetting({ key: THREAD_ACTIVE_MODEL_PACK_ID_KEY, value: options.pack.id });\n await session.thread.setSetting({ key: MODEL_FALLBACK_STATE_KEY, value: undefined });\n for (const [agentType, modelId] of Object.entries(packSelection.subagentModels ?? {})) {\n await session.subagents.model.set({ modelId, agentType });\n }\n await session.state.set({\n modelRoute: packSelection.modelRoute,\n [MODEL_FALLBACK_STATE_KEY]: null,\n } as unknown as Partial<TState>);\n }\n const sessionModelId = modelIdToApply ?? packModelId;\n if (sessionModelId) {\n await session.model.switch(sessionModelId);\n }\n // Persist the pack activation (global default) only once the pack has\n // fully taken effect on the session — a failed model switch above must\n // not leave the global default changed.\n options.pack?.onApplied?.();\n if (options.thinkingLevel) {\n await session.state.set({ thinkingLevel: options.thinkingLevel } as unknown as Partial<TState>);\n }\n } catch (err) {\n return fail(`Failed to apply run config: ${(err as Error).message}`);\n }\n\n // --- Title ---\n if (options.title) {\n try {\n await session.thread.rename({ title: options.title });\n } catch (err) {\n return fail(`Failed to set thread title: ${(err as Error).message}`);\n }\n }\n\n // --- Start ---\n try {\n if (goal) {\n const threadId = session.thread.getId();\n if (!threadId || !(await session.thread.getById({ threadId }))) {\n await session.thread.create();\n }\n\n const goalManager = goal.goalManager ?? new GoalManager();\n const state = {\n controller,\n session,\n goalManager,\n pendingNewThread: false,\n } as any;\n const objective = await goalManager.setGoal(state, goal.objective, goal.judgeModelId, goal.maxRuns);\n if (!objective) return fail('Failed to set goal.');\n\n const agent = controller.getCurrentAgent(session);\n const persisted = await agent.getObjective({ threadId: session.thread.getId()! });\n if (!persisted || persisted.id !== objective.id) {\n return fail('Failed to persist goal objective before sending goal signal.');\n }\n\n await goalManager.saveToThread(state);\n if (!settled) await session.sendSignal(createGoalReminderSignal(objective)).accepted;\n } else {\n if (!options.prompt) return fail('A prompt or goal is required.');\n await session.sendMessage({ content: options.prompt });\n }\n } catch (err) {\n if (\n isSessionStartupCancelledError(err) ||\n (err instanceof Error && err.name === 'AbortError' && (aborted || timedOut || options.signal?.aborted))\n ) {\n return finish(timedOut ? 'timeout' : maxTurnsExceeded ? 'max_turns' : 'aborted');\n }\n return fail(`Failed to start run: ${(err as Error).message}`);\n }\n })();\n\n return {\n result,\n abort,\n [Symbol.asyncIterator](): AsyncIterator<AgentControllerEvent> {\n return queue[Symbol.asyncIterator]();\n },\n };\n}\n"],"mappings":"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"}
@@ -11,7 +11,9 @@ async function executeHook(hook, stdinPayload) {
11
11
  const startTime = Date.now();
12
12
  return new Promise((resolve) => {
13
13
  const isWindows = process.platform === "win32";
14
- const child = spawn(isWindows ? "cmd" : "/bin/sh", isWindows ? ["/c", hook.command] : ["-c", hook.command], {
14
+ const shell = isWindows ? "cmd" : "/bin/sh";
15
+ const shellArgs = isWindows ? ["/c", hook.command] : ["-c", hook.command];
16
+ const child = spawn(shell, shellArgs, {
15
17
  stdio: [
16
18
  "pipe",
17
19
  "pipe",