@mastra/code-sdk 1.0.3-alpha.1 → 1.1.0-alpha.2

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 (258) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/acp/agent.js +155 -167
  3. package/dist/acp/agent.js.map +1 -1
  4. package/dist/acp/event-mapper.js +193 -168
  5. package/dist/acp/event-mapper.js.map +1 -1
  6. package/dist/acp/index.js +60 -49
  7. package/dist/acp/index.js.map +1 -1
  8. package/dist/acp/server.js +31 -30
  9. package/dist/acp/server.js.map +1 -1
  10. package/dist/acp.js +1 -4
  11. package/dist/agents/credential-resolver.js +40 -29
  12. package/dist/agents/credential-resolver.js.map +1 -1
  13. package/dist/agents/custom-provider-source.js +16 -9
  14. package/dist/agents/custom-provider-source.js.map +1 -1
  15. package/dist/agents/instructions.js +27 -36
  16. package/dist/agents/instructions.js.map +1 -1
  17. package/dist/agents/mastracode-gateway.js +355 -428
  18. package/dist/agents/mastracode-gateway.js.map +1 -1
  19. package/dist/agents/memory.js +87 -78
  20. package/dist/agents/memory.js.map +1 -1
  21. package/dist/agents/model.js +114 -119
  22. package/dist/agents/model.js.map +1 -1
  23. package/dist/agents/modes/build.js +12 -13
  24. package/dist/agents/modes/build.js.map +1 -1
  25. package/dist/agents/modes/explore.js +14 -13
  26. package/dist/agents/modes/explore.js.map +1 -1
  27. package/dist/agents/modes/plan.js +14 -15
  28. package/dist/agents/modes/plan.js.map +1 -1
  29. package/dist/agents/prompts/agent-instructions.js +78 -69
  30. package/dist/agents/prompts/agent-instructions.js.map +1 -1
  31. package/dist/agents/prompts/build.js +19 -16
  32. package/dist/agents/prompts/build.js.map +1 -1
  33. package/dist/agents/prompts/fast.js +8 -4
  34. package/dist/agents/prompts/fast.js.map +1 -1
  35. package/dist/agents/prompts/index.js +48 -47
  36. package/dist/agents/prompts/index.js.map +1 -1
  37. package/dist/agents/prompts/model.js +6 -5
  38. package/dist/agents/prompts/model.js.map +1 -1
  39. package/dist/agents/prompts/plan.js +20 -16
  40. package/dist/agents/prompts/plan.js.map +1 -1
  41. package/dist/agents/prompts/tool-guidance.js +77 -109
  42. package/dist/agents/prompts/tool-guidance.js.map +1 -1
  43. package/dist/agents/sandbox-filesystem.js +245 -259
  44. package/dist/agents/sandbox-filesystem.js.map +1 -1
  45. package/dist/agents/sandbox-reattach.js +9 -11
  46. package/dist/agents/sandbox-reattach.js.map +1 -1
  47. package/dist/agents/thread-caveman-state.js +92 -81
  48. package/dist/agents/thread-caveman-state.js.map +1 -1
  49. package/dist/agents/tool-availability.js +62 -80
  50. package/dist/agents/tool-availability.js.map +1 -1
  51. package/dist/agents/tools.js +99 -139
  52. package/dist/agents/tools.js.map +1 -1
  53. package/dist/agents/workspace.js +232 -225
  54. package/dist/agents/workspace.js.map +1 -1
  55. package/dist/analytics.js +139 -128
  56. package/dist/analytics.js.map +1 -1
  57. package/dist/auth/authorization-input.js +39 -25
  58. package/dist/auth/authorization-input.js.map +1 -1
  59. package/dist/auth/device-code.js +112 -80
  60. package/dist/auth/device-code.js.map +1 -1
  61. package/dist/auth/index.js +2 -9
  62. package/dist/auth/pkce.js +27 -16
  63. package/dist/auth/pkce.js.map +1 -1
  64. package/dist/auth/providers/anthropic.js +107 -93
  65. package/dist/auth/providers/anthropic.js.map +1 -1
  66. package/dist/auth/providers/github-copilot.js +304 -293
  67. package/dist/auth/providers/github-copilot.js.map +1 -1
  68. package/dist/auth/providers/openai-codex.js +442 -460
  69. package/dist/auth/providers/openai-codex.js.map +1 -1
  70. package/dist/auth/providers/xai.js +179 -145
  71. package/dist/auth/providers/xai.js.map +1 -1
  72. package/dist/auth/storage.js +182 -175
  73. package/dist/auth/storage.js.map +1 -1
  74. package/dist/auth/types.js +1 -1
  75. package/dist/clipboard/index.js +206 -172
  76. package/dist/clipboard/index.js.map +1 -1
  77. package/dist/constants.js +11 -16
  78. package/dist/constants.js.map +1 -1
  79. package/dist/error-classification.js +19 -11
  80. package/dist/error-classification.js.map +1 -1
  81. package/dist/evals/context-builder.js +145 -104
  82. package/dist/evals/context-builder.js.map +1 -1
  83. package/dist/evals/index.js +4 -7
  84. package/dist/evals/scorers/classify-command.js +62 -41
  85. package/dist/evals/scorers/classify-command.js.map +1 -1
  86. package/dist/evals/scorers/efficiency.js +239 -181
  87. package/dist/evals/scorers/efficiency.js.map +1 -1
  88. package/dist/evals/scorers/extract-tools.js +51 -47
  89. package/dist/evals/scorers/extract-tools.js.map +1 -1
  90. package/dist/evals/scorers/index.js +1 -5
  91. package/dist/evals/scorers/outcome.js +321 -212
  92. package/dist/evals/scorers/outcome.js.map +1 -1
  93. package/dist/goal-manager.js +266 -261
  94. package/dist/goal-manager.js.map +1 -1
  95. package/dist/goal-signal.js +14 -13
  96. package/dist/goal-signal.js.map +1 -1
  97. package/dist/headless/cli.js +153 -154
  98. package/dist/headless/cli.js.map +1 -1
  99. package/dist/headless/flags.js +180 -178
  100. package/dist/headless/flags.js.map +1 -1
  101. package/dist/headless/format.js +72 -71
  102. package/dist/headless/format.js.map +1 -1
  103. package/dist/headless/index.js +5 -31
  104. package/dist/headless/policy.js +35 -31
  105. package/dist/headless/policy.js.map +1 -1
  106. package/dist/headless/run-mc.js +332 -334
  107. package/dist/headless/run-mc.js.map +1 -1
  108. package/dist/headless/types.js +16 -7
  109. package/dist/headless/types.js.map +1 -1
  110. package/dist/hooks/config.js +56 -61
  111. package/dist/hooks/config.js.map +1 -1
  112. package/dist/hooks/executor.js +118 -116
  113. package/dist/hooks/executor.js.map +1 -1
  114. package/dist/hooks/index.js +4 -15
  115. package/dist/hooks/manager.js +221 -280
  116. package/dist/hooks/manager.js.map +1 -1
  117. package/dist/hooks/types.js +5 -4
  118. package/dist/hooks/types.js.map +1 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +632 -712
  121. package/dist/index.js.map +1 -1
  122. package/dist/ipc/ipc-reporter.js +19 -22
  123. package/dist/ipc/ipc-reporter.js.map +1 -1
  124. package/dist/lsp/client.js +248 -306
  125. package/dist/lsp/client.js.map +1 -1
  126. package/dist/lsp/index.js +1 -6
  127. package/dist/lsp/language.js +39 -43
  128. package/dist/lsp/language.js.map +1 -1
  129. package/dist/lsp/manager.js +78 -93
  130. package/dist/lsp/manager.js.map +1 -1
  131. package/dist/lsp/server.js +151 -120
  132. package/dist/lsp/server.js.map +1 -1
  133. package/dist/lsp/workspace.js +50 -47
  134. package/dist/lsp/workspace.js.map +1 -1
  135. package/dist/mcp/config.js +210 -199
  136. package/dist/mcp/config.js.map +1 -1
  137. package/dist/mcp/index.js +2 -9
  138. package/dist/mcp/manager.js +482 -475
  139. package/dist/mcp/manager.js.map +1 -1
  140. package/dist/mcp/types.js +1 -1
  141. package/dist/onboarding/custom-packs.js +14 -17
  142. package/dist/onboarding/custom-packs.js.map +1 -1
  143. package/dist/onboarding/custom-providers.js +22 -20
  144. package/dist/onboarding/custom-providers.js.map +1 -1
  145. package/dist/onboarding/index.d.ts +1 -1
  146. package/dist/onboarding/index.d.ts.map +1 -1
  147. package/dist/onboarding/index.js +3 -21
  148. package/dist/onboarding/om-settings.d.ts +5 -0
  149. package/dist/onboarding/om-settings.d.ts.map +1 -1
  150. package/dist/onboarding/om-settings.js +41 -20
  151. package/dist/onboarding/om-settings.js.map +1 -1
  152. package/dist/onboarding/packs.d.ts +4 -6
  153. package/dist/onboarding/packs.d.ts.map +1 -1
  154. package/dist/onboarding/packs.js +154 -105
  155. package/dist/onboarding/packs.js.map +1 -1
  156. package/dist/onboarding/settings.js +575 -517
  157. package/dist/onboarding/settings.js.map +1 -1
  158. package/dist/permissions.js +112 -99
  159. package/dist/permissions.js.map +1 -1
  160. package/dist/plugin.js +21 -23
  161. package/dist/plugin.js.map +1 -1
  162. package/dist/plugins/dependencies.js +64 -87
  163. package/dist/plugins/dependencies.js.map +1 -1
  164. package/dist/plugins/install.js +159 -169
  165. package/dist/plugins/install.js.map +1 -1
  166. package/dist/plugins/loader.js +170 -185
  167. package/dist/plugins/loader.js.map +1 -1
  168. package/dist/plugins/manager.js +374 -380
  169. package/dist/plugins/manager.js.map +1 -1
  170. package/dist/plugins/manifest.js +38 -63
  171. package/dist/plugins/manifest.js.map +1 -1
  172. package/dist/plugins/package-link.js +48 -64
  173. package/dist/plugins/package-link.js.map +1 -1
  174. package/dist/plugins/paths.js +18 -19
  175. package/dist/plugins/paths.js.map +1 -1
  176. package/dist/plugins/registry.js +97 -81
  177. package/dist/plugins/registry.js.map +1 -1
  178. package/dist/plugins/scaffold.js +62 -98
  179. package/dist/plugins/scaffold.js.map +1 -1
  180. package/dist/plugins/types.js +1 -1
  181. package/dist/processors/plan-rejection-abort.js +45 -44
  182. package/dist/processors/plan-rejection-abort.js.map +1 -1
  183. package/dist/providers/amazon-bedrock-gateway.js +134 -125
  184. package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
  185. package/dist/providers/amazon-bedrock.js +68 -47
  186. package/dist/providers/amazon-bedrock.js.map +1 -1
  187. package/dist/providers/claude-max.js +153 -133
  188. package/dist/providers/claude-max.js.map +1 -1
  189. package/dist/providers/github-copilot.js +232 -230
  190. package/dist/providers/github-copilot.js.map +1 -1
  191. package/dist/providers/openai-codex.js +271 -260
  192. package/dist/providers/openai-codex.js.map +1 -1
  193. package/dist/providers/xai.js +57 -50
  194. package/dist/providers/xai.js.map +1 -1
  195. package/dist/schema.js +91 -89
  196. package/dist/schema.js.map +1 -1
  197. package/dist/theme-palette.js +18 -14
  198. package/dist/theme-palette.js.map +1 -1
  199. package/dist/tool-names.js +46 -37
  200. package/dist/tool-names.js.map +1 -1
  201. package/dist/tools/index.js +2 -8
  202. package/dist/tools/request-sandbox-access.js +71 -80
  203. package/dist/tools/request-sandbox-access.js.map +1 -1
  204. package/dist/tools/utils.js +26 -16
  205. package/dist/tools/utils.js.map +1 -1
  206. package/dist/tools/web-search.js +51 -55
  207. package/dist/tools/web-search.js.map +1 -1
  208. package/dist/utils/binaries.js +65 -61
  209. package/dist/utils/binaries.js.map +1 -1
  210. package/dist/utils/debug-log.js +51 -47
  211. package/dist/utils/debug-log.js.map +1 -1
  212. package/dist/utils/errors.js +157 -192
  213. package/dist/utils/errors.js.map +1 -1
  214. package/dist/utils/gateway-sync.js +49 -37
  215. package/dist/utils/gateway-sync.js.map +1 -1
  216. package/dist/utils/path-security.js +7 -6
  217. package/dist/utils/path-security.js.map +1 -1
  218. package/dist/utils/plan-diff.js +58 -39
  219. package/dist/utils/plan-diff.js.map +1 -1
  220. package/dist/utils/plans.js +92 -74
  221. package/dist/utils/plans.js.map +1 -1
  222. package/dist/utils/project.js +318 -256
  223. package/dist/utils/project.js.map +1 -1
  224. package/dist/utils/signals-pubsub.js +117 -99
  225. package/dist/utils/signals-pubsub.js.map +1 -1
  226. package/dist/utils/slash-command-loader.js +118 -154
  227. package/dist/utils/slash-command-loader.js.map +1 -1
  228. package/dist/utils/slash-command-processor.js +92 -83
  229. package/dist/utils/slash-command-processor.js.map +1 -1
  230. package/dist/utils/stdin-pipe.js +87 -63
  231. package/dist/utils/stdin-pipe.js.map +1 -1
  232. package/dist/utils/storage-factory.js +104 -84
  233. package/dist/utils/storage-factory.js.map +1 -1
  234. package/dist/utils/storage-maintenance.js +249 -211
  235. package/dist/utils/storage-maintenance.js.map +1 -1
  236. package/dist/utils/thread-lock.js +96 -92
  237. package/dist/utils/thread-lock.js.map +1 -1
  238. package/dist/utils/token-estimator.js +12 -12
  239. package/dist/utils/token-estimator.js.map +1 -1
  240. package/dist/utils/update-check.d.ts +1 -1
  241. package/dist/utils/update-check.js +341 -259
  242. package/dist/utils/update-check.js.map +1 -1
  243. package/dist/voice/stt-registry.js +131 -113
  244. package/dist/voice/stt-registry.js.map +1 -1
  245. package/package.json +12 -12
  246. package/dist/acp.js.map +0 -1
  247. package/dist/auth/index.js.map +0 -1
  248. package/dist/auth/types.js.map +0 -1
  249. package/dist/evals/index.js.map +0 -1
  250. package/dist/evals/scorers/index.js.map +0 -1
  251. package/dist/headless/index.js.map +0 -1
  252. package/dist/hooks/index.js.map +0 -1
  253. package/dist/lsp/index.js.map +0 -1
  254. package/dist/mcp/index.js.map +0 -1
  255. package/dist/mcp/types.js.map +0 -1
  256. package/dist/onboarding/index.js.map +0 -1
  257. package/dist/plugins/types.js.map +0 -1
  258. package/dist/tools/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/goal-manager.ts"],"sourcesContent":["/**\n * GoalManager — persistent cross-turn goals, backed by the Agent's native goal\n * mechanism.\n *\n * The objective lives in the durable `threadState` `'goal'` slot (via\n * `agent.setObjective`/`getObjective`/`clearObjective`/`updateObjectiveOptions`)\n * and is judged in-loop by the core goal step. This manager is a thin adapter:\n * it keeps a synchronous in-memory view of the current objective for the TUI\n * (status line, modal, keyboard shortcuts) and delegates persistence to the\n * agent. There is no standalone judge agent and no between-turn re-invocation —\n * the core goal step drives continuation and surfaces progress via `goal` stream\n * chunks.\n */\nimport { randomUUID } from 'node:crypto';\nimport { getGoalActivityDurationMs } from '@mastra/core/agent';\nimport type { Agent } from '@mastra/core/agent';\nimport type { AgentController, Session } from '@mastra/core/agent-controller';\nimport type { GoalObjectiveRecord } from '@mastra/core/storage';\nimport { loadSettings } from './onboarding/settings.js';\n\nexport interface GoalManagerState<TState extends Record<string, unknown> = Record<string, unknown>> {\n controller: AgentController<TState>;\n session: Session<TState>;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type GoalStatus = 'active' | 'paused' | 'done';\n\n/**\n * TUI-facing view of a goal. Derived from the durable {@link GoalObjectiveRecord}\n * plus the effective judge/max-runs settings, with display-only timer fields and\n * a stable `id` used to match plan-started goals.\n */\nexport interface GoalState {\n id: string;\n objective: string;\n status: GoalStatus;\n turnsUsed: number;\n maxTurns: number;\n judgeModelId: string;\n startedAt: string;\n activeStartedAt?: string;\n activeDurationMs?: number;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const DEFAULT_MAX_TURNS = 50;\nconst THREAD_GOAL_KEY = 'goal';\n\nfunction normalizeActiveDurationMs(value: number | undefined): number {\n return value !== undefined && Number.isFinite(value) && value >= 0 ? value : 0;\n}\n\n// =============================================================================\n// GoalManager\n// =============================================================================\n\nexport class GoalManager {\n /** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */\n private record: (GoalObjectiveRecord & { id: string }) | null = null;\n private threadId: string | undefined;\n private agentId: string | undefined;\n private persistGoalOnNextThreadCreate = false;\n\n // ---------------------------------------------------------------------------\n // Synchronous TUI surface\n // ---------------------------------------------------------------------------\n\n getGoal(): GoalState | null {\n if (!this.record) return null;\n const { judgeModelId, maxTurns } = this.effectiveSettings(this.record);\n return {\n id: this.record.id,\n objective: this.record.objective,\n status: this.record.status,\n turnsUsed: this.record.runsUsed,\n maxTurns,\n judgeModelId,\n startedAt: new Date(this.record.startedAt).toISOString(),\n activeDurationMs:\n this.agentId && this.threadId\n ? getGoalActivityDurationMs({\n agentId: this.agentId,\n threadId: this.threadId,\n objectiveId: this.record.id,\n activeDurationMs: this.record.activeDurationMs,\n })\n : normalizeActiveDurationMs(this.record.activeDurationMs),\n };\n }\n\n isActive(): boolean {\n return this.record?.status === 'active';\n }\n\n persistOnNextThreadCreate(): void {\n this.persistGoalOnNextThreadCreate = true;\n }\n\n consumePersistOnNextThreadCreate(): boolean {\n if (!this.persistGoalOnNextThreadCreate) return false;\n this.persistGoalOnNextThreadCreate = false;\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Objective lifecycle (ThreadState-backed via the agent)\n // ---------------------------------------------------------------------------\n\n /**\n * Set a new objective. Persists to ThreadState via `agent.setObjective` and\n * updates the in-memory view. Only the provided settings are persisted into\n * the record; unset ones fall back to the agent's `goal` config at read time.\n */\n async setGoal(\n state: GoalManagerState,\n objective: string,\n judgeModelId: string,\n maxTurns: number = DEFAULT_MAX_TURNS,\n ): Promise<GoalState | null> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n const now = Date.now();\n const id = randomUUID();\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n\n if (agent && threadId) {\n const persisted = await agent.setObjective(objective, {\n id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n this.record = persisted\n ? { ...persisted, id: persisted.id ?? id }\n : this.localRecord(objective, judgeModelId, maxTurns, now, id);\n } else {\n this.record = this.localRecord(objective, judgeModelId, maxTurns, now, id);\n }\n\n return this.getGoal();\n }\n\n /**\n * Update the judge model / max-runs defaults. Persists into the active record\n * (so the override is remembered in thread state) when a goal is set.\n */\n async updateJudgeDefaults(\n state: GoalManagerState,\n judgeModelId: string,\n maxTurns: number,\n ): Promise<GoalState | null> {\n if (!this.record) return null;\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n if (agent && threadId) {\n const updated = await agent.updateObjectiveOptions({\n threadId,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n if (updated) this.record = { ...updated, id: this.record.id };\n } else {\n this.record = {\n ...this.record,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n updatedAt: Date.now(),\n };\n }\n return this.getGoal();\n }\n\n pause(reason?: string): GoalState | null {\n if (this.record && this.record.status === 'active') {\n this.record = { ...this.record, status: 'paused', pausedReason: reason, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n resume(): GoalState | null {\n if (this.record && this.record.status === 'paused') {\n this.record = { ...this.record, status: 'active', pausedReason: undefined, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n markDone(): void {\n if (this.record) {\n this.record = { ...this.record, status: 'done', updatedAt: Date.now() };\n }\n }\n\n clear(): void {\n this.record = null;\n this.threadId = undefined;\n this.agentId = undefined;\n this.persistGoalOnNextThreadCreate = false;\n }\n\n /**\n * Sync the latest objective record from ThreadState into the in-memory view.\n * Called from the `goal` stream-chunk handler after each evaluation.\n */\n applyEvaluation(update: { runsUsed: number; status: GoalStatus }): GoalState | null {\n if (!this.record) return null;\n this.record = { ...this.record, runsUsed: update.runsUsed, status: update.status, updatedAt: Date.now() };\n return this.getGoal();\n }\n\n // ---------------------------------------------------------------------------\n // Persistence\n // ---------------------------------------------------------------------------\n\n /**\n * Persist the active objective to ThreadState via the agent. The objective\n * record is the source of truth; the legacy thread-metadata key is cleared so\n * stale state from older sessions does not resurface.\n */\n async saveToThread(state: GoalManagerState): Promise<void> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n try {\n if (agent && threadId) {\n if (this.record) {\n // Push the current status/options into the existing record. If no\n // record is persisted yet (e.g. first save), create one.\n const updated = await agent.updateObjectiveOptions({\n threadId,\n status: this.record.status,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (!updated) {\n // No persisted record yet: create one. `setObjective` always writes\n // `status: 'active'`, so re-apply the in-memory status afterwards if\n // the local goal was already paused/done — otherwise the resumed\n // thread state would no longer match the in-memory state.\n const desiredStatus = this.record.status;\n await agent.setObjective(this.record.objective, {\n id: this.record.id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n activeDurationMs: normalizeActiveDurationMs(this.record.activeDurationMs),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (desiredStatus !== 'active') {\n await agent.updateObjectiveOptions({\n threadId,\n status: desiredStatus,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n });\n }\n }\n } else {\n await agent.clearObjective({ threadId });\n }\n }\n // Clear any legacy thread-metadata goal so it can't shadow the record.\n await state.session.thread.setSetting({ key: THREAD_GOAL_KEY, value: undefined });\n } catch {\n // Persistence is not critical.\n }\n }\n\n /**\n * Load the objective from ThreadState (called on thread switch). Falls back to\n * the legacy thread-metadata goal for threads created before this migration.\n */\n async loadFromThread(state: GoalManagerState): Promise<void> {\n this.persistGoalOnNextThreadCreate = false;\n\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n if (agent && threadId) {\n try {\n const record = await agent.getObjective({ threadId });\n if (record) {\n this.record = {\n ...record,\n id: record.id ?? randomUUID(),\n activeDurationMs: normalizeActiveDurationMs(record.activeDurationMs),\n };\n return;\n }\n } catch {\n // fall through to legacy metadata\n }\n }\n this.record = null;\n }\n\n /**\n * Legacy entry point retained for thread-switch call sites that only have the\n * thread metadata available. Hydrates from a previously-persisted GoalState.\n */\n loadFromThreadMetadata(metadata: Record<string, unknown> | undefined): void {\n const saved = metadata?.[THREAD_GOAL_KEY] as Partial<GoalState> | undefined;\n this.persistGoalOnNextThreadCreate = false;\n this.threadId = undefined;\n this.agentId = undefined;\n if (saved && saved.objective && saved.status) {\n this.record = {\n objective: saved.objective,\n status: saved.status,\n runsUsed: saved.turnsUsed ?? 0,\n activeDurationMs: normalizeActiveDurationMs(saved.activeDurationMs),\n maxRuns: saved.maxTurns ?? DEFAULT_MAX_TURNS,\n judgeModelId: saved.judgeModelId ?? '',\n startedAt: saved.startedAt ? Date.parse(saved.startedAt) || Date.now() : Date.now(),\n updatedAt: Date.now(),\n id: saved.id ?? randomUUID(),\n };\n } else {\n this.record = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private getAgent(state: GoalManagerState): Agent | undefined {\n try {\n return state.controller.getCurrentAgent(state.session);\n } catch {\n return undefined;\n }\n }\n\n /** Resolve effective judge model + max runs (record value → settings default). */\n private effectiveSettings(record: GoalObjectiveRecord): { judgeModelId: string; maxTurns: number } {\n const settings = loadSettings();\n return {\n judgeModelId: record.judgeModelId ?? settings.models.goalJudgeModel ?? '',\n maxTurns: record.maxRuns ?? settings.models.goalMaxTurns ?? DEFAULT_MAX_TURNS,\n };\n }\n\n private localRecord(\n objective: string,\n judgeModelId: string,\n maxTurns: number,\n now: number,\n id: string,\n ): GoalObjectiveRecord & { id: string } {\n return {\n objective,\n status: 'active',\n runsUsed: 0,\n activeDurationMs: 0,\n maxRuns: maxTurns,\n ...(judgeModelId ? { judgeModelId } : {}),\n startedAt: now,\n updatedAt: now,\n id,\n };\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"goal-manager.js","names":[],"sources":["../src/goal-manager.ts"],"sourcesContent":["/**\n * GoalManager — persistent cross-turn goals, backed by the Agent's native goal\n * mechanism.\n *\n * The objective lives in the durable `threadState` `'goal'` slot (via\n * `agent.setObjective`/`getObjective`/`clearObjective`/`updateObjectiveOptions`)\n * and is judged in-loop by the core goal step. This manager is a thin adapter:\n * it keeps a synchronous in-memory view of the current objective for the TUI\n * (status line, modal, keyboard shortcuts) and delegates persistence to the\n * agent. There is no standalone judge agent and no between-turn re-invocation —\n * the core goal step drives continuation and surfaces progress via `goal` stream\n * chunks.\n */\nimport { randomUUID } from 'node:crypto';\nimport { getGoalActivityDurationMs } from '@mastra/core/agent';\nimport type { Agent } from '@mastra/core/agent';\nimport type { AgentController, Session } from '@mastra/core/agent-controller';\nimport type { GoalObjectiveRecord } from '@mastra/core/storage';\nimport { loadSettings } from './onboarding/settings.js';\n\nexport interface GoalManagerState<TState extends Record<string, unknown> = Record<string, unknown>> {\n controller: AgentController<TState>;\n session: Session<TState>;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type GoalStatus = 'active' | 'paused' | 'done';\n\n/**\n * TUI-facing view of a goal. Derived from the durable {@link GoalObjectiveRecord}\n * plus the effective judge/max-runs settings, with display-only timer fields and\n * a stable `id` used to match plan-started goals.\n */\nexport interface GoalState {\n id: string;\n objective: string;\n status: GoalStatus;\n turnsUsed: number;\n maxTurns: number;\n judgeModelId: string;\n startedAt: string;\n activeStartedAt?: string;\n activeDurationMs?: number;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const DEFAULT_MAX_TURNS = 50;\nconst THREAD_GOAL_KEY = 'goal';\n\nfunction normalizeActiveDurationMs(value: number | undefined): number {\n return value !== undefined && Number.isFinite(value) && value >= 0 ? value : 0;\n}\n\n// =============================================================================\n// GoalManager\n// =============================================================================\n\nexport class GoalManager {\n /** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */\n private record: (GoalObjectiveRecord & { id: string }) | null = null;\n private threadId: string | undefined;\n private agentId: string | undefined;\n private persistGoalOnNextThreadCreate = false;\n\n // ---------------------------------------------------------------------------\n // Synchronous TUI surface\n // ---------------------------------------------------------------------------\n\n getGoal(): GoalState | null {\n if (!this.record) return null;\n const { judgeModelId, maxTurns } = this.effectiveSettings(this.record);\n return {\n id: this.record.id,\n objective: this.record.objective,\n status: this.record.status,\n turnsUsed: this.record.runsUsed,\n maxTurns,\n judgeModelId,\n startedAt: new Date(this.record.startedAt).toISOString(),\n activeDurationMs:\n this.agentId && this.threadId\n ? getGoalActivityDurationMs({\n agentId: this.agentId,\n threadId: this.threadId,\n objectiveId: this.record.id,\n activeDurationMs: this.record.activeDurationMs,\n })\n : normalizeActiveDurationMs(this.record.activeDurationMs),\n };\n }\n\n isActive(): boolean {\n return this.record?.status === 'active';\n }\n\n persistOnNextThreadCreate(): void {\n this.persistGoalOnNextThreadCreate = true;\n }\n\n consumePersistOnNextThreadCreate(): boolean {\n if (!this.persistGoalOnNextThreadCreate) return false;\n this.persistGoalOnNextThreadCreate = false;\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Objective lifecycle (ThreadState-backed via the agent)\n // ---------------------------------------------------------------------------\n\n /**\n * Set a new objective. Persists to ThreadState via `agent.setObjective` and\n * updates the in-memory view. Only the provided settings are persisted into\n * the record; unset ones fall back to the agent's `goal` config at read time.\n */\n async setGoal(\n state: GoalManagerState,\n objective: string,\n judgeModelId: string,\n maxTurns: number = DEFAULT_MAX_TURNS,\n ): Promise<GoalState | null> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n const now = Date.now();\n const id = randomUUID();\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n\n if (agent && threadId) {\n const persisted = await agent.setObjective(objective, {\n id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n this.record = persisted\n ? { ...persisted, id: persisted.id ?? id }\n : this.localRecord(objective, judgeModelId, maxTurns, now, id);\n } else {\n this.record = this.localRecord(objective, judgeModelId, maxTurns, now, id);\n }\n\n return this.getGoal();\n }\n\n /**\n * Update the judge model / max-runs defaults. Persists into the active record\n * (so the override is remembered in thread state) when a goal is set.\n */\n async updateJudgeDefaults(\n state: GoalManagerState,\n judgeModelId: string,\n maxTurns: number,\n ): Promise<GoalState | null> {\n if (!this.record) return null;\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n if (agent && threadId) {\n const updated = await agent.updateObjectiveOptions({\n threadId,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n if (updated) this.record = { ...updated, id: this.record.id };\n } else {\n this.record = {\n ...this.record,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n updatedAt: Date.now(),\n };\n }\n return this.getGoal();\n }\n\n pause(reason?: string): GoalState | null {\n if (this.record && this.record.status === 'active') {\n this.record = { ...this.record, status: 'paused', pausedReason: reason, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n resume(): GoalState | null {\n if (this.record && this.record.status === 'paused') {\n this.record = { ...this.record, status: 'active', pausedReason: undefined, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n markDone(): void {\n if (this.record) {\n this.record = { ...this.record, status: 'done', updatedAt: Date.now() };\n }\n }\n\n clear(): void {\n this.record = null;\n this.threadId = undefined;\n this.agentId = undefined;\n this.persistGoalOnNextThreadCreate = false;\n }\n\n /**\n * Sync the latest objective record from ThreadState into the in-memory view.\n * Called from the `goal` stream-chunk handler after each evaluation.\n */\n applyEvaluation(update: { runsUsed: number; status: GoalStatus }): GoalState | null {\n if (!this.record) return null;\n this.record = { ...this.record, runsUsed: update.runsUsed, status: update.status, updatedAt: Date.now() };\n return this.getGoal();\n }\n\n // ---------------------------------------------------------------------------\n // Persistence\n // ---------------------------------------------------------------------------\n\n /**\n * Persist the active objective to ThreadState via the agent. The objective\n * record is the source of truth; the legacy thread-metadata key is cleared so\n * stale state from older sessions does not resurface.\n */\n async saveToThread(state: GoalManagerState): Promise<void> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n try {\n if (agent && threadId) {\n if (this.record) {\n // Push the current status/options into the existing record. If no\n // record is persisted yet (e.g. first save), create one.\n const updated = await agent.updateObjectiveOptions({\n threadId,\n status: this.record.status,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (!updated) {\n // No persisted record yet: create one. `setObjective` always writes\n // `status: 'active'`, so re-apply the in-memory status afterwards if\n // the local goal was already paused/done — otherwise the resumed\n // thread state would no longer match the in-memory state.\n const desiredStatus = this.record.status;\n await agent.setObjective(this.record.objective, {\n id: this.record.id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n activeDurationMs: normalizeActiveDurationMs(this.record.activeDurationMs),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (desiredStatus !== 'active') {\n await agent.updateObjectiveOptions({\n threadId,\n status: desiredStatus,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n });\n }\n }\n } else {\n await agent.clearObjective({ threadId });\n }\n }\n // Clear any legacy thread-metadata goal so it can't shadow the record.\n await state.session.thread.setSetting({ key: THREAD_GOAL_KEY, value: undefined });\n } catch {\n // Persistence is not critical.\n }\n }\n\n /**\n * Load the objective from ThreadState (called on thread switch). Falls back to\n * the legacy thread-metadata goal for threads created before this migration.\n */\n async loadFromThread(state: GoalManagerState): Promise<void> {\n this.persistGoalOnNextThreadCreate = false;\n\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n if (agent && threadId) {\n try {\n const record = await agent.getObjective({ threadId });\n if (record) {\n this.record = {\n ...record,\n id: record.id ?? randomUUID(),\n activeDurationMs: normalizeActiveDurationMs(record.activeDurationMs),\n };\n return;\n }\n } catch {\n // fall through to legacy metadata\n }\n }\n this.record = null;\n }\n\n /**\n * Legacy entry point retained for thread-switch call sites that only have the\n * thread metadata available. Hydrates from a previously-persisted GoalState.\n */\n loadFromThreadMetadata(metadata: Record<string, unknown> | undefined): void {\n const saved = metadata?.[THREAD_GOAL_KEY] as Partial<GoalState> | undefined;\n this.persistGoalOnNextThreadCreate = false;\n this.threadId = undefined;\n this.agentId = undefined;\n if (saved && saved.objective && saved.status) {\n this.record = {\n objective: saved.objective,\n status: saved.status,\n runsUsed: saved.turnsUsed ?? 0,\n activeDurationMs: normalizeActiveDurationMs(saved.activeDurationMs),\n maxRuns: saved.maxTurns ?? DEFAULT_MAX_TURNS,\n judgeModelId: saved.judgeModelId ?? '',\n startedAt: saved.startedAt ? Date.parse(saved.startedAt) || Date.now() : Date.now(),\n updatedAt: Date.now(),\n id: saved.id ?? randomUUID(),\n };\n } else {\n this.record = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private getAgent(state: GoalManagerState): Agent | undefined {\n try {\n return state.controller.getCurrentAgent(state.session);\n } catch {\n return undefined;\n }\n }\n\n /** Resolve effective judge model + max runs (record value → settings default). */\n private effectiveSettings(record: GoalObjectiveRecord): { judgeModelId: string; maxTurns: number } {\n const settings = loadSettings();\n return {\n judgeModelId: record.judgeModelId ?? settings.models.goalJudgeModel ?? '',\n maxTurns: record.maxRuns ?? settings.models.goalMaxTurns ?? DEFAULT_MAX_TURNS,\n };\n }\n\n private localRecord(\n objective: string,\n judgeModelId: string,\n maxTurns: number,\n now: number,\n id: string,\n ): GoalObjectiveRecord & { id: string } {\n return {\n objective,\n status: 'active',\n runsUsed: 0,\n activeDurationMs: 0,\n maxRuns: maxTurns,\n ...(judgeModelId ? { judgeModelId } : {}),\n startedAt: now,\n updatedAt: now,\n id,\n };\n }\n}\n"],"mappings":"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"}
@@ -1,16 +1,17 @@
1
+ //#region src/goal-signal.ts
1
2
  function createGoalReminderSignal(goal) {
2
- return {
3
- type: "system-reminder",
4
- contents: goal.objective,
5
- attributes: { type: "goal" },
6
- metadata: {
7
- goalId: goal.id,
8
- maxTurns: goal.maxTurns,
9
- judgeModelId: goal.judgeModelId
10
- }
11
- };
3
+ return {
4
+ type: "system-reminder",
5
+ contents: goal.objective,
6
+ attributes: { type: "goal" },
7
+ metadata: {
8
+ goalId: goal.id,
9
+ maxTurns: goal.maxTurns,
10
+ judgeModelId: goal.judgeModelId
11
+ }
12
+ };
12
13
  }
13
- export {
14
- createGoalReminderSignal
15
- };
14
+ //#endregion
15
+ export { createGoalReminderSignal };
16
+
16
17
  //# sourceMappingURL=goal-signal.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/goal-signal.ts"],"sourcesContent":["import type { GoalState } from './goal-manager.js';\n\nexport function createGoalReminderSignal(goal: GoalState) {\n return {\n type: 'system-reminder' as const,\n contents: goal.objective,\n attributes: { type: 'goal' },\n metadata: {\n goalId: goal.id,\n maxTurns: goal.maxTurns,\n judgeModelId: goal.judgeModelId,\n },\n };\n}\n"],"mappings":"AAEO,SAAS,yBAAyB,MAAiB;AACxD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,KAAK;AAAA,IACf,YAAY,EAAE,MAAM,OAAO;AAAA,IAC3B,UAAU;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf,cAAc,KAAK;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"file":"goal-signal.js","names":[],"sources":["../src/goal-signal.ts"],"sourcesContent":["import type { GoalState } from './goal-manager.js';\n\nexport function createGoalReminderSignal(goal: GoalState) {\n return {\n type: 'system-reminder' as const,\n contents: goal.objective,\n attributes: { type: 'goal' },\n metadata: {\n goalId: goal.id,\n maxTurns: goal.maxTurns,\n judgeModelId: goal.judgeModelId,\n },\n };\n}\n"],"mappings":";AAEA,SAAgB,yBAAyB,MAAiB;CACxD,OAAO;EACL,MAAM;EACN,UAAU,KAAK;EACf,YAAY,EAAE,MAAM,OAAO;EAC3B,UAAU;GACR,QAAQ,KAAK;GACb,UAAU,KAAK;GACf,cAAc,KAAK;EACrB;CACF;AACF"}
@@ -1,60 +1,73 @@
1
- import { existsSync } from "node:fs";
2
- import { parseArgs } from "node:util";
3
- import { createMastraCode } from "../index.js";
4
- import { setupDebugLogging } from "../utils/debug-log.js";
5
1
  import { releaseAllThreadLocks } from "../utils/thread-lock.js";
6
- import { buildParseArgsOptions, FLAGS, renderFlagUsage } from "./flags.js";
7
- import { createHumanFormatState, formatHuman, formatJsonl, renderJsonResult } from "./format.js";
8
2
  import { permissionModeToPolicy } from "./policy.js";
9
3
  import { runMC } from "./run-mc.js";
4
+ import { createHumanFormatState, formatHuman, formatJsonl, renderJsonResult } from "./format.js";
5
+ import { setupDebugLogging } from "../utils/debug-log.js";
6
+ import { FLAGS, buildParseArgsOptions, renderFlagUsage } from "./flags.js";
7
+ import { createMastraCode } from "../index.js";
8
+ import { existsSync } from "fs";
9
+ import { parseArgs } from "util";
10
+ //#region src/headless/cli.ts
11
+ /**
12
+ * CLI adapter for headless MastraCode runs.
13
+ *
14
+ * This is the only headless layer that touches the process: it parses argv,
15
+ * reads stdin, bootstraps MastraCode via `createMastraCode`, drives `runMC`,
16
+ * renders events/results to stdout/stderr through the pure formatters, maps the
17
+ * result to an exit code, and owns teardown + `process.exit`.
18
+ */
10
19
  const parseArgsOptions = buildParseArgsOptions();
20
+ /**
21
+ * Returns true if `argv` selects headless mode. This must agree with what
22
+ * {@link parseHeadlessArgs} (and `runMCCli`) accept as a prompt: `--prompt`/`-p`
23
+ * or a bare positional prompt (e.g. `mastracode "Fix the bug"`). Note that a
24
+ * prompt piped via stdin without a flag is handled separately by the caller.
25
+ */
11
26
  function hasHeadlessFlag(argv) {
12
- if (argv.some((a) => a === "--prompt" || a === "-p")) return true;
13
- try {
14
- const { values, positionals } = parseArgs({
15
- args: argv.slice(2),
16
- options: parseArgsOptions,
17
- strict: false,
18
- allowPositionals: true
19
- });
20
- return positionals.length > 0 && !values.help;
21
- } catch {
22
- return false;
23
- }
27
+ if (argv.some((a) => a === "--prompt" || a === "-p")) return true;
28
+ try {
29
+ const { values, positionals } = parseArgs({
30
+ args: argv.slice(2),
31
+ options: parseArgsOptions,
32
+ strict: false,
33
+ allowPositionals: true
34
+ });
35
+ return positionals.length > 0 && !values.help;
36
+ } catch {
37
+ return false;
38
+ }
24
39
  }
40
+ /**
41
+ * Parse CLI arguments for headless mode. The flag table in `flags.ts` is the
42
+ * single source of truth: each flag carries its own coercion/validation, so this
43
+ * function just walks {@link FLAGS} and assembles the typed {@link HeadlessArgs}.
44
+ */
25
45
  function parseHeadlessArgs(argv) {
26
- const { values, positionals } = parseArgs({
27
- args: argv.slice(2),
28
- options: parseArgsOptions,
29
- strict: false,
30
- allowPositionals: true
31
- });
32
- const args = {
33
- output: "human",
34
- continue_: false,
35
- cloneThread: false
36
- };
37
- const sink = args;
38
- for (const flag of FLAGS) {
39
- if (!flag.field) continue;
40
- const raw = values[flag.key];
41
- if (raw === void 0) continue;
42
- if (flag.type === "boolean") {
43
- sink[flag.field] = Boolean(raw);
44
- } else if (typeof raw === "string") {
45
- sink[flag.field] = flag.coerce ? flag.coerce(raw) : raw;
46
- }
47
- }
48
- if (args.prompt === void 0 && positionals[0] !== void 0) {
49
- args.prompt = positionals[0];
50
- }
51
- if (args.continue_ && args.thread) {
52
- throw new Error("--continue and --thread cannot be used together");
53
- }
54
- return args;
46
+ const { values, positionals } = parseArgs({
47
+ args: argv.slice(2),
48
+ options: parseArgsOptions,
49
+ strict: false,
50
+ allowPositionals: true
51
+ });
52
+ const args = {
53
+ output: "human",
54
+ continue_: false,
55
+ cloneThread: false
56
+ };
57
+ const sink = args;
58
+ for (const flag of FLAGS) {
59
+ if (!flag.field) continue;
60
+ const raw = values[flag.key];
61
+ if (raw === void 0) continue;
62
+ if (flag.type === "boolean") sink[flag.field] = Boolean(raw);
63
+ else if (typeof raw === "string") sink[flag.field] = flag.coerce ? flag.coerce(raw) : raw;
64
+ }
65
+ if (args.prompt === void 0 && positionals[0] !== void 0) args.prompt = positionals[0];
66
+ if (args.continue_ && args.thread) throw new Error("--continue and --thread cannot be used together");
67
+ return args;
55
68
  }
56
69
  function printHeadlessUsage() {
57
- process.stdout.write(`
70
+ process.stdout.write(`
58
71
  Usage: mastracode --prompt <text> [options]
59
72
 
60
73
  Headless (non-interactive) mode options:
@@ -85,112 +98,98 @@ Examples:
85
98
  echo "Summarize the repo" | mastracode --prompt -
86
99
  `);
87
100
  }
101
+ /**
102
+ * Headless CLI entry point: parse arguments, read stdin, initialize MastraCode,
103
+ * run via `runMC`, render output, and exit with the mapped code.
104
+ */
88
105
  async function runMCCli(predrainedInput) {
89
- if (process.argv.includes("--help") || process.argv.includes("-h")) {
90
- printHeadlessUsage();
91
- process.exit(0);
92
- }
93
- let args;
94
- try {
95
- args = parseHeadlessArgs(process.argv);
96
- } catch (e) {
97
- process.stderr.write(`Error: ${e.message}
98
- `);
99
- process.exit(1);
100
- }
101
- let prompt = args.prompt;
102
- if (predrainedInput !== void 0) {
103
- prompt = predrainedInput ?? "";
104
- } else if (prompt === "-" || !prompt && !process.stdin.isTTY) {
105
- const chunks = [];
106
- for await (const chunk of process.stdin) {
107
- chunks.push(chunk);
108
- }
109
- prompt = Buffer.concat(chunks).toString("utf-8").trim();
110
- }
111
- if (!prompt) {
112
- printHeadlessUsage();
113
- process.stderr.write("Error: --prompt is required (or pipe via stdin)\n");
114
- process.exit(1);
115
- }
116
- if (args.settings && !existsSync(args.settings)) {
117
- process.stderr.write(`Error: Settings file not found: ${args.settings}
118
- `);
119
- process.exit(1);
120
- }
121
- const boot = await createMastraCode({ settingsPath: args.settings });
122
- const { controller, session, mcpManager, effectiveDefaults } = boot;
123
- if (mcpManager?.hasServers()) {
124
- try {
125
- await mcpManager.initInBackground();
126
- } catch (err) {
127
- process.stderr.write(`Warning: MCP server initialization failed: ${err.message ?? err}
128
- `);
129
- }
130
- }
131
- setupDebugLogging();
132
- let exitCode = 1;
133
- try {
134
- const humanState = createHumanFormatState();
135
- const run = runMC({
136
- controller,
137
- session,
138
- prompt,
139
- model: args.model,
140
- mode: args.mode,
141
- modeDefaults: effectiveDefaults,
142
- thinkingLevel: args.thinkingLevel,
143
- thread: { id: args.thread, continueLatest: args.continue_, clone: args.cloneThread },
144
- resourceId: args.resourceId,
145
- title: args.title,
146
- timeoutMs: args.timeout ? args.timeout * 1e3 : void 0,
147
- maxTurns: args.maxTurns,
148
- policy: args.permissionMode ? permissionModeToPolicy(args.permissionMode) : void 0
149
- });
150
- for await (const event of run) {
151
- if (args.output === "human") {
152
- const out = formatHuman(event, humanState);
153
- if (out.stdout) process.stdout.write(out.stdout);
154
- if (out.stderr) process.stderr.write(out.stderr);
155
- } else if (args.output === "jsonl") {
156
- process.stdout.write(JSON.stringify(formatJsonl(event)) + "\n");
157
- }
158
- }
159
- const result = await run.result;
160
- exitCode = result.exitCode;
161
- if (args.output === "json") {
162
- process.stdout.write(renderJsonResult(result));
163
- } else if (args.output === "jsonl") {
164
- process.stdout.write(JSON.stringify({ type: "result", ...result }) + "\n");
165
- }
166
- if (result.status === "timeout") {
167
- process.stderr.write(`
168
- Timeout elapsed. Aborted.
169
- `);
170
- } else if (result.error && args.output === "human") {
171
- process.stderr.write(`Error: ${result.error.message}
172
- `);
173
- }
174
- } catch (err) {
175
- process.stderr.write(`Error: ${err.message ?? err}
176
- `);
177
- exitCode = 1;
178
- } finally {
179
- releaseAllThreadLocks();
180
- const closeSignalsPubSub = boot.signalsPubSub?.close;
181
- await Promise.allSettled([
182
- mcpManager?.disconnect(),
183
- controller.getMastra()?.stopWorkers(),
184
- controller?.stopIntervals(),
185
- closeSignalsPubSub?.()
186
- ]);
187
- }
188
- process.exit(exitCode);
106
+ if (process.argv.includes("--help") || process.argv.includes("-h")) {
107
+ printHeadlessUsage();
108
+ process.exit(0);
109
+ }
110
+ let args;
111
+ try {
112
+ args = parseHeadlessArgs(process.argv);
113
+ } catch (e) {
114
+ process.stderr.write(`Error: ${e.message}\n`);
115
+ process.exit(1);
116
+ }
117
+ let prompt = args.prompt;
118
+ if (predrainedInput !== void 0) prompt = predrainedInput ?? "";
119
+ else if (prompt === "-" || !prompt && !process.stdin.isTTY) {
120
+ const chunks = [];
121
+ for await (const chunk of process.stdin) chunks.push(chunk);
122
+ prompt = Buffer.concat(chunks).toString("utf-8").trim();
123
+ }
124
+ if (!prompt) {
125
+ printHeadlessUsage();
126
+ process.stderr.write("Error: --prompt is required (or pipe via stdin)\n");
127
+ process.exit(1);
128
+ }
129
+ if (args.settings && !existsSync(args.settings)) {
130
+ process.stderr.write(`Error: Settings file not found: ${args.settings}\n`);
131
+ process.exit(1);
132
+ }
133
+ const boot = await createMastraCode({ settingsPath: args.settings });
134
+ const { controller, session, mcpManager, effectiveDefaults } = boot;
135
+ if (mcpManager?.hasServers()) try {
136
+ await mcpManager.initInBackground();
137
+ } catch (err) {
138
+ process.stderr.write(`Warning: MCP server initialization failed: ${err.message ?? err}\n`);
139
+ }
140
+ setupDebugLogging();
141
+ let exitCode = 1;
142
+ try {
143
+ const humanState = createHumanFormatState();
144
+ const run = runMC({
145
+ controller,
146
+ session,
147
+ prompt,
148
+ model: args.model,
149
+ mode: args.mode,
150
+ modeDefaults: effectiveDefaults,
151
+ thinkingLevel: args.thinkingLevel,
152
+ thread: {
153
+ id: args.thread,
154
+ continueLatest: args.continue_,
155
+ clone: args.cloneThread
156
+ },
157
+ resourceId: args.resourceId,
158
+ title: args.title,
159
+ timeoutMs: args.timeout ? args.timeout * 1e3 : void 0,
160
+ maxTurns: args.maxTurns,
161
+ policy: args.permissionMode ? permissionModeToPolicy(args.permissionMode) : void 0
162
+ });
163
+ for await (const event of run) if (args.output === "human") {
164
+ const out = formatHuman(event, humanState);
165
+ if (out.stdout) process.stdout.write(out.stdout);
166
+ if (out.stderr) process.stderr.write(out.stderr);
167
+ } else if (args.output === "jsonl") process.stdout.write(JSON.stringify(formatJsonl(event)) + "\n");
168
+ const result = await run.result;
169
+ exitCode = result.exitCode;
170
+ if (args.output === "json") process.stdout.write(renderJsonResult(result));
171
+ else if (args.output === "jsonl") process.stdout.write(JSON.stringify({
172
+ type: "result",
173
+ ...result
174
+ }) + "\n");
175
+ if (result.status === "timeout") process.stderr.write(`\nTimeout elapsed. Aborted.\n`);
176
+ else if (result.error && args.output === "human") process.stderr.write(`Error: ${result.error.message}\n`);
177
+ } catch (err) {
178
+ process.stderr.write(`Error: ${err.message ?? err}\n`);
179
+ exitCode = 1;
180
+ } finally {
181
+ releaseAllThreadLocks();
182
+ const closeSignalsPubSub = boot.signalsPubSub?.close;
183
+ await Promise.allSettled([
184
+ mcpManager?.disconnect(),
185
+ controller.getMastra()?.stopWorkers(),
186
+ controller?.stopIntervals(),
187
+ closeSignalsPubSub?.()
188
+ ]);
189
+ }
190
+ process.exit(exitCode);
189
191
  }
190
- export {
191
- hasHeadlessFlag,
192
- parseHeadlessArgs,
193
- printHeadlessUsage,
194
- runMCCli
195
- };
192
+ //#endregion
193
+ export { hasHeadlessFlag, parseHeadlessArgs, printHeadlessUsage, runMCCli };
194
+
196
195
  //# sourceMappingURL=cli.js.map
@@ -1 +1 @@
1
- {"version":3,"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 { parseArgs } from 'node:util';\n\nimport { createMastraCode } from '../index.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, 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 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();\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 * 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(predrainedInput?: string | null): 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 const boot = await createMastraCode({ settingsPath: args.settings });\n const { controller, session, mcpManager, effectiveDefaults } = boot;\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 // 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 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 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') {\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 const closeSignalsPubSub = (boot.signalsPubSub as { close?: () => Promise<void> | void } | undefined)?.close;\n await Promise.allSettled([\n mcpManager?.disconnect(),\n controller.getMastra()?.stopWorkers(),\n controller?.stopIntervals(),\n closeSignalsPubSub?.(),\n ]);\n }\n\n process.exit(exitCode);\n}\n"],"mappings":"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","names":[]}
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 { parseArgs } from 'node:util';\n\nimport { createMastraCode } from '../index.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, 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 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();\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 * 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(predrainedInput?: string | null): 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 const boot = await createMastraCode({ settingsPath: args.settings });\n const { controller, session, mcpManager, effectiveDefaults } = boot;\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 // 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 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 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') {\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 const closeSignalsPubSub = (boot.signalsPubSub as { close?: () => Promise<void> | void } | undefined)?.close;\n await Promise.allSettled([\n mcpManager?.disconnect(),\n controller.getMastra()?.stopWorkers(),\n controller?.stopIntervals(),\n closeSignalsPubSub?.(),\n ]);\n }\n\n process.exit(exitCode);\n}\n"],"mappings":"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"}