@mastra/code-sdk 1.7.2 → 1.8.0-alpha.5

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 (53) hide show
  1. package/dist/acp/event-mapper.d.ts +1 -0
  2. package/dist/acp/event-mapper.d.ts.map +1 -1
  3. package/dist/acp/event-mapper.js +13 -15
  4. package/dist/acp/event-mapper.js.map +1 -1
  5. package/dist/agent-connections/tools.d.ts.map +1 -1
  6. package/dist/agent-connections/tools.js +26 -8
  7. package/dist/agent-connections/tools.js.map +1 -1
  8. package/dist/agents/background-completion-events.d.ts +16 -0
  9. package/dist/agents/background-completion-events.d.ts.map +1 -0
  10. package/dist/agents/background-completion-events.js +19 -0
  11. package/dist/agents/background-completion-events.js.map +1 -0
  12. package/dist/agents/background-completion.d.ts +9 -0
  13. package/dist/agents/background-completion.d.ts.map +1 -0
  14. package/dist/agents/background-completion.js +59 -0
  15. package/dist/agents/background-completion.js.map +1 -0
  16. package/dist/agents/tool-availability.d.ts +2 -0
  17. package/dist/agents/tool-availability.d.ts.map +1 -1
  18. package/dist/agents/tool-availability.js +33 -4
  19. package/dist/agents/tool-availability.js.map +1 -1
  20. package/dist/agents/tools.d.ts +1 -1
  21. package/dist/agents/tools.d.ts.map +1 -1
  22. package/dist/agents/tools.js +30 -2
  23. package/dist/agents/tools.js.map +1 -1
  24. package/dist/agents/workspace.d.ts +2 -1
  25. package/dist/agents/workspace.d.ts.map +1 -1
  26. package/dist/agents/workspace.js +3 -3
  27. package/dist/agents/workspace.js.map +1 -1
  28. package/dist/headless/format.d.ts +1 -0
  29. package/dist/headless/format.d.ts.map +1 -1
  30. package/dist/headless/format.js +13 -17
  31. package/dist/headless/format.js.map +1 -1
  32. package/dist/headless/run-mc.d.ts.map +1 -1
  33. package/dist/headless/run-mc.js +11 -5
  34. package/dist/headless/run-mc.js.map +1 -1
  35. package/dist/index.d.ts +4 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +67 -15
  38. package/dist/index.js.map +1 -1
  39. package/dist/onboarding/settings.d.ts +5 -0
  40. package/dist/onboarding/settings.d.ts.map +1 -1
  41. package/dist/onboarding/settings.js +7 -0
  42. package/dist/onboarding/settings.js.map +1 -1
  43. package/dist/plugin.d.ts +12 -0
  44. package/dist/plugin.d.ts.map +1 -1
  45. package/dist/plugin.js.map +1 -1
  46. package/dist/plugins/loader.d.ts.map +1 -1
  47. package/dist/plugins/loader.js +2 -1
  48. package/dist/plugins/loader.js.map +1 -1
  49. package/dist/tools/workflows/save-workflow.d.ts +2 -1009
  50. package/dist/tools/workflows/save-workflow.d.ts.map +1 -1
  51. package/dist/workflows/register-primitives.js +2 -2
  52. package/dist/workflows/register-primitives.js.map +1 -1
  53. package/package.json +14 -14
@@ -1 +1 @@
1
- {"version":3,"file":"tool-availability.js","names":[],"sources":["../../src/agents/tool-availability.ts"],"sourcesContent":["import { WORKSPACE_TOOLS } from '@mastra/core/workspace';\nimport type { WorkspaceToolHookContext, WorkspaceToolsConfig } from '@mastra/core/workspace';\nimport { MC_TOOLS, TOOL_NAME_OVERRIDES } from '../tool-names.js';\nimport { getLocalPlansRelativeDir, isPlanFilePath } from '../utils/plans.js';\n\nconst PLAN_MODE_WRITE_TOOL_NAMES = new Set<string>([\n WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE,\n WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE,\n]);\n\nfunction getRequestContextValue(requestContext: unknown, key: string): unknown {\n if (!requestContext) return undefined;\n if (typeof (requestContext as { get?: unknown }).get === 'function') {\n return (requestContext as { get: (key: string) => unknown }).get(key);\n }\n return (requestContext as Record<string, unknown>)[key];\n}\n\n/**\n * Subset of the real `HarnessRequestContext` shape (see\n * packages/core/src/harness/types.ts). The mode is a string property\n * (`session.modeId`) and live state is read via `session.state.get()`.\n */\ninterface PlanModeGuardHarness {\n session?: {\n modeId?: unknown;\n state?: { get?: () => unknown };\n };\n}\n\nfunction getHarnessContext(context: unknown): PlanModeGuardHarness | undefined {\n const requestContext = (context as { requestContext?: unknown } | undefined)?.requestContext;\n const harness = getRequestContextValue(requestContext, 'harness');\n return harness && typeof harness === 'object' ? (harness as PlanModeGuardHarness) : undefined;\n}\n\nfunction getHarnessState(harness: PlanModeGuardHarness | undefined): Record<string, unknown> | undefined {\n const state = harness?.session?.state?.get?.();\n return state && typeof state === 'object' ? (state as Record<string, unknown>) : undefined;\n}\n\nfunction getHarnessModeId(harness: PlanModeGuardHarness | undefined): string | undefined {\n const modeId = harness?.session?.modeId ?? getHarnessState(harness)?.modeId;\n return typeof modeId === 'string' ? modeId : undefined;\n}\n\nfunction getHarnessProjectPath(harness: PlanModeGuardHarness | undefined): string | undefined {\n const projectPath = getHarnessState(harness)?.projectPath;\n return typeof projectPath === 'string' ? projectPath : undefined;\n}\n\nfunction getHarnessFactoryProjectId(harness: PlanModeGuardHarness | undefined): string | undefined {\n const factoryProjectId = getHarnessState(harness)?.factoryProjectId;\n return typeof factoryProjectId === 'string' ? factoryProjectId : undefined;\n}\n\nfunction getToolInputPath(input: unknown): string | undefined {\n const toolPath = (input as { path?: unknown } | undefined)?.path;\n return typeof toolPath === 'string' ? toolPath : undefined;\n}\n\nexport function guardPlanModePlanFileWrites({ workspaceToolName, input, context }: WorkspaceToolHookContext) {\n if (!PLAN_MODE_WRITE_TOOL_NAMES.has(workspaceToolName)) return;\n\n const harness = getHarnessContext(context);\n if (getHarnessModeId(harness) !== 'plan') return;\n\n const factoryProjectId = getHarnessFactoryProjectId(harness);\n const planOptions = { factoryProjectId };\n const planDirHint = `${getLocalPlansRelativeDir(planOptions)}/`;\n const projectPath = getHarnessProjectPath(harness);\n const inputPath = getToolInputPath(input);\n if (!projectPath || !inputPath) {\n return {\n proceed: false as const,\n output: `Plan mode can only write plan files inside ${planDirHint}.`,\n };\n }\n\n // Plan mode may write any `.md` file directly inside the configured plan directory,\n // but nothing else in the project.\n if (isPlanFilePath(projectPath, inputPath, planOptions)) return;\n\n return {\n proceed: false as const,\n output: `Plan mode can only write plan files inside ${planDirHint}. Refusing to edit ${inputPath}.`,\n };\n}\n\nexport const MASTRACODE_WORKSPACE_TOOLS: WorkspaceToolsConfig = {\n ...TOOL_NAME_OVERRIDES,\n hooks: {\n beforeToolCall: guardPlanModePlanFileWrites,\n },\n};\n\nexport const PLAN_MODE_AVAILABLE_TOOLS: readonly string[] = [\n // Read-only exploration tools\n MC_TOOLS.VIEW,\n MC_TOOLS.FIND_FILES,\n MC_TOOLS.SEARCH_CONTENT,\n MC_TOOLS.FILE_STAT,\n MC_TOOLS.LSP_INSPECT,\n // Plan file writing. Tool hooks enforce that these can only write `.md`\n // files inside the session's configured plan directory while in plan mode.\n MC_TOOLS.WRITE_FILE,\n MC_TOOLS.STRING_REPLACE_LSP,\n // Plan delivery tools\n 'ask_user',\n 'submit_plan',\n // Task tools for plan-stage tracking\n 'task_write',\n 'task_update',\n 'task_complete',\n 'task_check',\n // Notification access\n MC_TOOLS.NOTIFICATION_INBOX,\n // Read-only workflow inspection (no create/run/delete in plan mode).\n 'list-workflows',\n 'get-workflow',\n];\n\nexport const EXPLORE_MODE_AVAILABLE_TOOLS: readonly string[] = [\n MC_TOOLS.VIEW,\n MC_TOOLS.FIND_FILES,\n MC_TOOLS.SEARCH_CONTENT,\n MC_TOOLS.FILE_STAT,\n MC_TOOLS.LSP_INSPECT,\n 'ask_user',\n // Read-only workflow inspection (no create/run/delete in fast mode either).\n 'list-workflows',\n 'get-workflow',\n];\n\nexport const GOAL_JUDGE_READONLY_TOOLS: readonly string[] = [\n MC_TOOLS.VIEW,\n MC_TOOLS.SEARCH_CONTENT,\n MC_TOOLS.FIND_FILES,\n MC_TOOLS.FILE_STAT,\n MC_TOOLS.LSP_INSPECT,\n];\n"],"mappings":";;;;AAKA,MAAM,6CAA6B,IAAI,IAAY,CACjD,gBAAgB,WAAW,YAC3B,gBAAgB,WAAW,SAC7B,CAAC;AAED,SAAS,uBAAuB,gBAAyB,KAAsB;CAC7E,IAAI,CAAC,gBAAgB,OAAO,KAAA;CAC5B,IAAI,OAAQ,eAAqC,QAAQ,YACvD,OAAQ,eAAqD,IAAI,GAAG;CAEtE,OAAQ,eAA2C;AACrD;AAcA,SAAS,kBAAkB,SAAoD;CAC7E,MAAM,iBAAkB,SAAsD;CAC9E,MAAM,UAAU,uBAAuB,gBAAgB,SAAS;CAChE,OAAO,WAAW,OAAO,YAAY,WAAY,UAAmC,KAAA;AACtF;AAEA,SAAS,gBAAgB,SAAgF;CACvG,MAAM,QAAQ,SAAS,SAAS,OAAO,MAAM;CAC7C,OAAO,SAAS,OAAO,UAAU,WAAY,QAAoC,KAAA;AACnF;AAEA,SAAS,iBAAiB,SAA+D;CACvF,MAAM,SAAS,SAAS,SAAS,UAAU,gBAAgB,OAAO,CAAC,EAAE;CACrE,OAAO,OAAO,WAAW,WAAW,SAAS,KAAA;AAC/C;AAEA,SAAS,sBAAsB,SAA+D;CAC5F,MAAM,cAAc,gBAAgB,OAAO,CAAC,EAAE;CAC9C,OAAO,OAAO,gBAAgB,WAAW,cAAc,KAAA;AACzD;AAEA,SAAS,2BAA2B,SAA+D;CACjG,MAAM,mBAAmB,gBAAgB,OAAO,CAAC,EAAE;CACnD,OAAO,OAAO,qBAAqB,WAAW,mBAAmB,KAAA;AACnE;AAEA,SAAS,iBAAiB,OAAoC;CAC5D,MAAM,WAAY,OAA0C;CAC5D,OAAO,OAAO,aAAa,WAAW,WAAW,KAAA;AACnD;AAEA,SAAgB,4BAA4B,EAAE,mBAAmB,OAAO,WAAqC;CAC3G,IAAI,CAAC,2BAA2B,IAAI,iBAAiB,GAAG;CAExD,MAAM,UAAU,kBAAkB,OAAO;CACzC,IAAI,iBAAiB,OAAO,MAAM,QAAQ;CAG1C,MAAM,cAAc,EAAE,kBADG,2BAA2B,OACf,EAAE;CACvC,MAAM,cAAc,GAAG,yBAAyB,WAAW,EAAE;CAC7D,MAAM,cAAc,sBAAsB,OAAO;CACjD,MAAM,YAAY,iBAAiB,KAAK;CACxC,IAAI,CAAC,eAAe,CAAC,WACnB,OAAO;EACL,SAAS;EACT,QAAQ,8CAA8C,YAAY;CACpE;CAKF,IAAI,eAAe,aAAa,WAAW,WAAW,GAAG;CAEzD,OAAO;EACL,SAAS;EACT,QAAQ,8CAA8C,YAAY,qBAAqB,UAAU;CACnG;AACF;AAEA,MAAa,6BAAmD;CAC9D,GAAG;CACH,OAAO,EACL,gBAAgB,4BAClB;AACF;AAEA,MAAa,4BAA+C;CAE1D,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CAGT,SAAS;CACT,SAAS;CAET;CACA;CAEA;CACA;CACA;CACA;CAEA,SAAS;CAET;CACA;AACF;AAEA,MAAa,+BAAkD;CAC7D,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACT;CAEA;CACA;AACF;AAEA,MAAa,4BAA+C;CAC1D,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;AACX"}
1
+ {"version":3,"file":"tool-availability.js","names":[],"sources":["../../src/agents/tool-availability.ts"],"sourcesContent":["import { WORKSPACE_TOOLS } from '@mastra/core/workspace';\nimport type { WorkspaceToolHookContext, WorkspaceToolsConfig } from '@mastra/core/workspace';\nimport { MC_TOOLS, TOOL_NAME_OVERRIDES } from '../tool-names.js';\nimport { getLocalPlansRelativeDir, isPlanFilePath } from '../utils/plans.js';\n\nconst PLAN_MODE_WRITE_TOOL_NAMES = new Set<string>([\n WORKSPACE_TOOLS.FILESYSTEM.WRITE_FILE,\n WORKSPACE_TOOLS.FILESYSTEM.EDIT_FILE,\n]);\n\nfunction getRequestContextValue(requestContext: unknown, key: string): unknown {\n if (!requestContext) return undefined;\n if (typeof (requestContext as { get?: unknown }).get === 'function') {\n return (requestContext as { get: (key: string) => unknown }).get(key);\n }\n return (requestContext as Record<string, unknown>)[key];\n}\n\n/**\n * Subset of the real `HarnessRequestContext` shape (see\n * packages/core/src/harness/types.ts). The mode is a string property\n * (`session.modeId`) and live state is read via `session.state.get()`.\n */\ninterface PlanModeGuardHarness {\n session?: {\n modeId?: unknown;\n state?: { get?: () => unknown };\n };\n}\n\nfunction getHarnessContext(context: unknown): PlanModeGuardHarness | undefined {\n const requestContext = (context as { requestContext?: unknown } | undefined)?.requestContext;\n const harness = getRequestContextValue(requestContext, 'harness');\n return harness && typeof harness === 'object' ? (harness as PlanModeGuardHarness) : undefined;\n}\n\nfunction getHarnessState(harness: PlanModeGuardHarness | undefined): Record<string, unknown> | undefined {\n const state = harness?.session?.state?.get?.();\n return state && typeof state === 'object' ? (state as Record<string, unknown>) : undefined;\n}\n\nfunction getHarnessModeId(harness: PlanModeGuardHarness | undefined): string | undefined {\n const modeId = harness?.session?.modeId ?? getHarnessState(harness)?.modeId;\n return typeof modeId === 'string' ? modeId : undefined;\n}\n\nfunction getHarnessProjectPath(harness: PlanModeGuardHarness | undefined): string | undefined {\n const projectPath = getHarnessState(harness)?.projectPath;\n return typeof projectPath === 'string' ? projectPath : undefined;\n}\n\nfunction getHarnessFactoryProjectId(harness: PlanModeGuardHarness | undefined): string | undefined {\n const factoryProjectId = getHarnessState(harness)?.factoryProjectId;\n return typeof factoryProjectId === 'string' ? factoryProjectId : undefined;\n}\n\nfunction getToolInputPath(input: unknown): string | undefined {\n const toolPath = (input as { path?: unknown } | undefined)?.path;\n return typeof toolPath === 'string' ? toolPath : undefined;\n}\n\nexport function guardPlanModePlanFileWrites({ workspaceToolName, input, context }: WorkspaceToolHookContext) {\n if (!PLAN_MODE_WRITE_TOOL_NAMES.has(workspaceToolName)) return;\n\n const harness = getHarnessContext(context);\n if (getHarnessModeId(harness) !== 'plan') return;\n\n const factoryProjectId = getHarnessFactoryProjectId(harness);\n const planOptions = { factoryProjectId };\n const planDirHint = `${getLocalPlansRelativeDir(planOptions)}/`;\n const projectPath = getHarnessProjectPath(harness);\n const inputPath = getToolInputPath(input);\n if (!projectPath || !inputPath) {\n return {\n proceed: false as const,\n output: `Plan mode can only write plan files inside ${planDirHint}.`,\n };\n }\n\n // Plan mode may write any `.md` file directly inside the configured plan directory,\n // but nothing else in the project.\n if (isPlanFilePath(projectPath, inputPath, planOptions)) return;\n\n return {\n proceed: false as const,\n output: `Plan mode can only write plan files inside ${planDirHint}. Refusing to edit ${inputPath}.`,\n };\n}\n\nconst BACKGROUND_TOOL_CONFIG = { enabled: true, defaultDisposition: 'foreground' } as const;\n\nexport function createMastraCodeWorkspaceTools(backgroundToolsEnabled = false): WorkspaceToolsConfig {\n const background = backgroundToolsEnabled ? BACKGROUND_TOOL_CONFIG : undefined;\n return {\n ...TOOL_NAME_OVERRIDES,\n [WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: {\n ...TOOL_NAME_OVERRIDES[WORKSPACE_TOOLS.FILESYSTEM.READ_FILE],\n ...(background ? { background } : {}),\n },\n [WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES]: {\n ...TOOL_NAME_OVERRIDES[WORKSPACE_TOOLS.FILESYSTEM.LIST_FILES],\n ...(background ? { background } : {}),\n },\n [WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT]: {\n ...TOOL_NAME_OVERRIDES[WORKSPACE_TOOLS.FILESYSTEM.FILE_STAT],\n ...(background ? { background } : {}),\n },\n [WORKSPACE_TOOLS.FILESYSTEM.GREP]: {\n ...TOOL_NAME_OVERRIDES[WORKSPACE_TOOLS.FILESYSTEM.GREP],\n ...(background ? { background } : {}),\n },\n [WORKSPACE_TOOLS.LSP.LSP_INSPECT]: {\n ...TOOL_NAME_OVERRIDES[WORKSPACE_TOOLS.LSP.LSP_INSPECT],\n ...(background ? { background } : {}),\n },\n hooks: {\n beforeToolCall: guardPlanModePlanFileWrites,\n },\n };\n}\n\n/** Default foreground-only configuration for consumers without Mastra Code settings. */\nexport const MASTRACODE_WORKSPACE_TOOLS = createMastraCodeWorkspaceTools();\n\nexport const PLAN_MODE_AVAILABLE_TOOLS: readonly string[] = [\n // Read-only exploration tools\n MC_TOOLS.VIEW,\n MC_TOOLS.FIND_FILES,\n MC_TOOLS.SEARCH_CONTENT,\n MC_TOOLS.FILE_STAT,\n MC_TOOLS.LSP_INSPECT,\n // Plan file writing. Tool hooks enforce that these can only write `.md`\n // files inside the session's configured plan directory while in plan mode.\n MC_TOOLS.WRITE_FILE,\n MC_TOOLS.STRING_REPLACE_LSP,\n // Plan delivery tools\n 'ask_user',\n 'submit_plan',\n // Task tools for plan-stage tracking\n 'task_write',\n 'task_update',\n 'task_complete',\n 'task_check',\n // Notification access\n MC_TOOLS.NOTIFICATION_INBOX,\n // Read-only workflow inspection (no create/run/delete in plan mode).\n 'list-workflows',\n 'get-workflow',\n];\n\nexport const EXPLORE_MODE_AVAILABLE_TOOLS: readonly string[] = [\n MC_TOOLS.VIEW,\n MC_TOOLS.FIND_FILES,\n MC_TOOLS.SEARCH_CONTENT,\n MC_TOOLS.FILE_STAT,\n MC_TOOLS.LSP_INSPECT,\n 'ask_user',\n // Read-only workflow inspection (no create/run/delete in fast mode either).\n 'list-workflows',\n 'get-workflow',\n];\n\nexport const GOAL_JUDGE_READONLY_TOOLS: readonly string[] = [\n MC_TOOLS.VIEW,\n MC_TOOLS.SEARCH_CONTENT,\n MC_TOOLS.FIND_FILES,\n MC_TOOLS.FILE_STAT,\n MC_TOOLS.LSP_INSPECT,\n];\n"],"mappings":"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"}
@@ -30,7 +30,7 @@ export declare function createToolHooks(hookManager?: HookManager, postToolObser
30
30
  type DynamicToolProvider = Record<string, ToolLike | undefined> | ((ctx: {
31
31
  requestContext: RequestContext;
32
32
  }) => Record<string, ToolLike | undefined> | Promise<Record<string, ToolLike | undefined>>);
33
- export declare function createDynamicTools(mcpManager?: McpManager, extraTools?: DynamicToolProvider, disabledTools?: string[], storage?: MastraCompositeStore, pluginTools?: Record<string, ToolLike>): ({ requestContext, }: {
33
+ export declare function createDynamicTools(mcpManager?: McpManager, extraTools?: DynamicToolProvider, disabledTools?: string[], storage?: MastraCompositeStore, pluginTools?: Record<string, ToolLike>, backgroundToolsEnabled?: boolean): ({ requestContext, }: {
34
34
  requestContext: RequestContext;
35
35
  }) => ToolsInput | Promise<ToolsInput>;
36
36
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/agents/tools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAA+B,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC/F,OAAO,KAAK,EACV,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,EACvB,8BAA8B,EAC/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAWlD,4DAA4D;AAC5D,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC1D,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAExB,qBAAa,wBAAyB,SAAQ,oBAAoB;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,oBAAoB;YAI5C,uBAAuB;IAQ/B,kBAAkB,CAAC,KAAK,EAAE,uBAAuB;IAIjD,iBAAiB,CAAC,KAAK,EAAE,sBAAsB;IAI/C,oBAAoB,CAAC,KAAK,EAAE,yBAAyB;IAIrD,eAAe,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE;IAIvD,kBAAkB,CAAC,KAAK,EAAE,uBAAuB;IAIxC,yBAAyB,CAAC,KAAK,EAAE,8BAA8B;IAIxE,mBAAmB;CAG1B;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvF,wBAAgB,eAAe,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,SAAS,GAAG,SAAS,CAoCrH;AAED,KAAK,mBAAmB,GACpB,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC,GACpC,CAAC,CAAC,GAAG,EAAE;IACL,cAAc,EAAE,cAAc,CAAC;CAChC,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAEhG,wBAAgB,kBAAkB,CAChC,UAAU,CAAC,EAAE,UAAU,EACvB,UAAU,CAAC,EAAE,mBAAmB,EAChC,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE,oBAAoB,EAC9B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAEN,qBAE7B;IACD,cAAc,EAAE,cAAc,CAAC;CAChC,KAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CA6FrC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/agents/tools.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAA+B,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC/F,OAAO,KAAK,EACV,uBAAuB,EACvB,yBAAyB,EACzB,sBAAsB,EACtB,uBAAuB,EACvB,8BAA8B,EAC/B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAWlD,4DAA4D;AAC5D,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAC1D,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAuCxB,qBAAa,wBAAyB,SAAQ,oBAAoB;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,oBAAoB;YAI5C,uBAAuB;IAQ/B,kBAAkB,CAAC,KAAK,EAAE,uBAAuB;IAIjD,iBAAiB,CAAC,KAAK,EAAE,sBAAsB;IAI/C,oBAAoB,CAAC,KAAK,EAAE,yBAAyB;IAIrD,eAAe,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE;IAIvD,kBAAkB,CAAC,KAAK,EAAE,uBAAuB;IAIxC,yBAAyB,CAAC,KAAK,EAAE,8BAA8B;IAIxE,mBAAmB;CAG1B;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvF,wBAAgB,eAAe,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,gBAAgB,CAAC,EAAE,gBAAgB,GAAG,SAAS,GAAG,SAAS,CAoCrH;AAED,KAAK,mBAAmB,GACpB,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC,GACpC,CAAC,CAAC,GAAG,EAAE;IACL,cAAc,EAAE,cAAc,CAAC;CAChC,KAAK,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAEhG,wBAAgB,kBAAkB,CAChC,UAAU,CAAC,EAAE,UAAU,EACvB,UAAU,CAAC,EAAE,mBAAmB,EAChC,aAAa,CAAC,EAAE,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE,oBAAoB,EAC9B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EACtC,sBAAsB,UAAQ,IAEE,qBAE7B;IACD,cAAc,EAAE,cAAc,CAAC;CAChC,KAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CA6FrC"}
@@ -12,6 +12,34 @@ import { createOpenAI } from "@ai-sdk/openai";
12
12
  import { NotificationsStorage, createNotificationInboxTool } from "@mastra/core/notifications";
13
13
  import { createAnthropic } from "@ai-sdk/anthropic";
14
14
  //#region src/agents/tools.ts
15
+ const BACKGROUND_ELIGIBLE_PLUGIN_TOOLS = /* @__PURE__ */ new Set(["mastra_expert"]);
16
+ let alexandriaExecutionTail = Promise.resolve();
17
+ async function executeAlexandriaSerially(tool, args) {
18
+ const previous = alexandriaExecutionTail;
19
+ let release;
20
+ const current = new Promise((resolve) => {
21
+ release = resolve;
22
+ });
23
+ alexandriaExecutionTail = previous.then(() => current, () => current);
24
+ await previous.catch(() => void 0);
25
+ try {
26
+ (args[1]?.abortSignal)?.throwIfAborted();
27
+ return await tool.execute?.apply(tool, args);
28
+ } finally {
29
+ release();
30
+ }
31
+ }
32
+ function configurePluginTool(name, tool, backgroundToolsEnabled) {
33
+ if (!backgroundToolsEnabled || !BACKGROUND_ELIGIBLE_PLUGIN_TOOLS.has(name)) return tool;
34
+ return {
35
+ ...tool,
36
+ background: {
37
+ enabled: true,
38
+ defaultDisposition: "foreground"
39
+ },
40
+ execute: (...args) => executeAlexandriaSerially(tool, args)
41
+ };
42
+ }
15
43
  var LazyNotificationsStorage = class extends NotificationsStorage {
16
44
  storage;
17
45
  constructor(storage) {
@@ -68,7 +96,7 @@ function createToolHooks(hookManager, postToolObserver) {
68
96
  }
69
97
  };
70
98
  }
71
- function createDynamicTools(mcpManager, extraTools, disabledTools, storage, pluginTools) {
99
+ function createDynamicTools(mcpManager, extraTools, disabledTools, storage, pluginTools, backgroundToolsEnabled = false) {
72
100
  return function getDynamicTools({ requestContext }) {
73
101
  const ctx = requestContext.get("controller");
74
102
  const state = ctx?.getState();
@@ -97,7 +125,7 @@ function createDynamicTools(mcpManager, extraTools, disabledTools, storage, plug
97
125
  for (const [name, tool] of Object.entries(resolvedExtra)) if (tool && !(name in tools)) tools[name] = tool;
98
126
  }
99
127
  if (pluginTools) {
100
- for (const [name, tool] of Object.entries(pluginTools)) if (!(name in tools)) tools[name] = tool;
128
+ for (const [name, tool] of Object.entries(pluginTools)) if (!(name in tools)) tools[name] = configurePluginTool(name, tool, backgroundToolsEnabled);
101
129
  }
102
130
  if (disabledTools?.length) for (const toolName of disabledTools) delete tools[toolName];
103
131
  const permissionRules = state?.permissionRules;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","names":[],"sources":["../../src/agents/tools.ts"],"sourcesContent":["import { createAnthropic } from '@ai-sdk/anthropic';\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { ToolsInput } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { createNotificationInboxTool, NotificationsStorage } from '@mastra/core/notifications';\nimport type {\n CreateNotificationInput,\n ListDueNotificationsInput,\n ListNotificationsInput,\n UpdateNotificationInput,\n UpdateNotificationsStatusInput,\n} from '@mastra/core/notifications';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { MastraCompositeStore } from '@mastra/core/storage';\nimport type { ToolAfterHookContext, ToolHooks } from '@mastra/core/tools';\nimport type { HookManager } from '../hooks/index.js';\nimport type { McpManager } from '../mcp/index.js';\nimport type { MastraCodeComposedState } from '../schema.js';\nimport { MC_TOOLS } from '../tool-names.js';\nimport { createConfiguredWebTools, requestSandboxAccessTool } from '../tools/index.js';\nimport { createWorkflowTool } from '../tools/workflows/create-workflow.js';\nimport { deleteWorkflowTool } from '../tools/workflows/delete-workflow.js';\nimport { getWorkflowTool } from '../tools/workflows/get-workflow.js';\nimport { listWorkflowsTool } from '../tools/workflows/list-workflows.js';\nimport { runWorkflowTool } from '../tools/workflows/run-workflow.js';\nimport { WORKFLOW_MANAGEMENT_TOOL_IDS } from '../tools/workflows/tool-ids.js';\n\n/** Minimal shape for tools passed to createDynamicTools. */\nexport type ToolLike = {\n execute?: (...args: any[]) => Promise<unknown> | unknown;\n} & Record<string, any>;\n\nexport class LazyNotificationsStorage extends NotificationsStorage {\n constructor(private readonly storage: MastraCompositeStore) {\n super();\n }\n\n private async getNotificationsStorage(): Promise<NotificationsStorage> {\n const notifications = await this.storage.getStore('notifications');\n if (!notifications) {\n throw new Error('notification_inbox requires a notifications storage domain');\n }\n return notifications;\n }\n\n async createNotification(input: CreateNotificationInput) {\n return (await this.getNotificationsStorage()).createNotification(input);\n }\n\n async listNotifications(input: ListNotificationsInput) {\n return (await this.getNotificationsStorage()).listNotifications(input);\n }\n\n async listDueNotifications(input: ListDueNotificationsInput) {\n return (await this.getNotificationsStorage()).listDueNotifications(input);\n }\n\n async getNotification(input: { threadId: string; id: string }) {\n return (await this.getNotificationsStorage()).getNotification(input);\n }\n\n async updateNotification(input: UpdateNotificationInput) {\n return (await this.getNotificationsStorage()).updateNotification(input);\n }\n\n override async updateNotificationsStatus(input: UpdateNotificationsStatusInput) {\n return (await this.getNotificationsStorage()).updateNotificationsStatus(input);\n }\n\n async dangerouslyClearAll() {\n return (await this.getNotificationsStorage()).dangerouslyClearAll();\n }\n}\n\nexport type PostToolObserver = (context: ToolAfterHookContext) => void | Promise<void>;\n\nexport function createToolHooks(hookManager?: HookManager, postToolObserver?: PostToolObserver): ToolHooks | undefined {\n if (!hookManager && !postToolObserver) return undefined;\n\n return {\n beforeToolCall: async ({ toolName, input }) => {\n if (!hookManager) return;\n const preResult = await hookManager.runPreToolUse(toolName, input);\n if (!preResult.allowed) {\n return {\n proceed: false as const,\n output: {\n error: preResult.blockReason ?? `Blocked by PreToolUse hook for tool \"${toolName}\"`,\n },\n };\n }\n },\n afterToolCall: async context => {\n const { toolName, input, output, error } = context;\n if (hookManager) {\n const failed = error !== undefined;\n await hookManager\n .runPostToolUse(\n toolName,\n input,\n failed ? { error: error instanceof Error ? error.message : String(error) } : output,\n failed,\n )\n .catch(() => undefined);\n }\n await Promise.resolve()\n .then(() => postToolObserver?.(context))\n .catch(error => {\n console.warn(`[MastraCode] Post-tool observer failed for ${toolName}.`, error);\n });\n },\n };\n}\n\ntype DynamicToolProvider =\n | Record<string, ToolLike | undefined>\n | ((ctx: {\n requestContext: RequestContext;\n }) => Record<string, ToolLike | undefined> | Promise<Record<string, ToolLike | undefined>>);\n\nexport function createDynamicTools(\n mcpManager?: McpManager,\n extraTools?: DynamicToolProvider,\n disabledTools?: string[],\n storage?: MastraCompositeStore,\n pluginTools?: Record<string, ToolLike>,\n) {\n return function getDynamicTools({\n requestContext,\n }: {\n requestContext: RequestContext;\n }): ToolsInput | Promise<ToolsInput> {\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeComposedState> | undefined;\n const state = ctx?.getState();\n\n const modelId = ctx?.session?.modelId;\n const isAnthropicModel = modelId?.startsWith('anthropic/');\n const isOpenAIModel = modelId?.startsWith('openai/');\n\n // Filesystem, grep, glob, edit, write, execute_command, and process\n // management tools are now provided by the workspace (see workspace.ts).\n // Only tools without a workspace equivalent remain here.\n const tools: Record<string, ToolLike> = {\n request_access: requestSandboxAccessTool,\n // Workflow surface. `create-workflow` delegates to the workflow-builder\n // sub-agent; the other four are Dynamic Workflow management operations.\n // Permission categories live in permissions.ts (TOOL_CATEGORY_MAP).\n [WORKFLOW_MANAGEMENT_TOOL_IDS.createWorkflow]: createWorkflowTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.listWorkflows]: listWorkflowsTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.getWorkflow]: getWorkflowTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.runWorkflow]: runWorkflowTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.deleteWorkflow]: deleteWorkflowTool,\n };\n\n if (storage) {\n tools[MC_TOOLS.NOTIFICATION_INBOX] = createNotificationInboxTool({\n storage: new LazyNotificationsStorage(storage),\n });\n }\n\n const configuredWebTools = createConfiguredWebTools();\n if (configuredWebTools) {\n Object.assign(tools, configuredWebTools);\n } else if (isAnthropicModel) {\n const anthropic = createAnthropic({});\n tools.web_search = anthropic.tools.webSearch_20250305();\n } else if (isOpenAIModel) {\n const openai = createOpenAI({});\n tools.web_search = openai.tools.webSearch();\n }\n\n if (mcpManager) {\n const mcpTools = mcpManager.getTools();\n Object.assign(tools, mcpTools);\n }\n\n const finish = (resolvedExtra: Record<string, ToolLike | undefined> | undefined) => {\n if (resolvedExtra) {\n for (const [name, tool] of Object.entries(resolvedExtra)) {\n if (tool && !(name in tools)) {\n tools[name] = tool;\n }\n }\n }\n\n if (pluginTools) {\n for (const [name, tool] of Object.entries(pluginTools)) {\n if (!(name in tools)) {\n tools[name] = tool;\n }\n }\n }\n\n // Remove tools explicitly disabled via config so the model never sees them.\n if (disabledTools?.length) {\n for (const toolName of disabledTools) {\n delete tools[toolName];\n }\n }\n\n // Remove tools that have a per-tool 'deny' policy so the model never sees them.\n const permissionRules = state?.permissionRules;\n if (permissionRules?.tools) {\n for (const [name, policy] of Object.entries(permissionRules.tools)) {\n if (policy === 'deny') {\n delete tools[name];\n }\n }\n }\n\n return tools as ToolsInput;\n };\n\n if (typeof extraTools === 'function') {\n const resolved = extraTools({ requestContext });\n // Stay synchronous for sync providers; only go async when the provider does.\n if (resolved && typeof (resolved as PromiseLike<unknown>).then === 'function') {\n return Promise.resolve(resolved).then(finish);\n }\n return finish(resolved as Record<string, ToolLike | undefined>);\n }\n\n return finish(extraTools);\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../../src/agents/tools.ts"],"sourcesContent":["import { createAnthropic } from '@ai-sdk/anthropic';\nimport { createOpenAI } from '@ai-sdk/openai';\nimport type { ToolsInput } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport { createNotificationInboxTool, NotificationsStorage } from '@mastra/core/notifications';\nimport type {\n CreateNotificationInput,\n ListDueNotificationsInput,\n ListNotificationsInput,\n UpdateNotificationInput,\n UpdateNotificationsStatusInput,\n} from '@mastra/core/notifications';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { MastraCompositeStore } from '@mastra/core/storage';\nimport type { ToolAfterHookContext, ToolHooks } from '@mastra/core/tools';\nimport type { HookManager } from '../hooks/index.js';\nimport type { McpManager } from '../mcp/index.js';\nimport type { MastraCodeComposedState } from '../schema.js';\nimport { MC_TOOLS } from '../tool-names.js';\nimport { createConfiguredWebTools, requestSandboxAccessTool } from '../tools/index.js';\nimport { createWorkflowTool } from '../tools/workflows/create-workflow.js';\nimport { deleteWorkflowTool } from '../tools/workflows/delete-workflow.js';\nimport { getWorkflowTool } from '../tools/workflows/get-workflow.js';\nimport { listWorkflowsTool } from '../tools/workflows/list-workflows.js';\nimport { runWorkflowTool } from '../tools/workflows/run-workflow.js';\nimport { WORKFLOW_MANAGEMENT_TOOL_IDS } from '../tools/workflows/tool-ids.js';\n\n/** Minimal shape for tools passed to createDynamicTools. */\nexport type ToolLike = {\n execute?: (...args: any[]) => Promise<unknown> | unknown;\n} & Record<string, any>;\n\nconst BACKGROUND_ELIGIBLE_PLUGIN_TOOLS = new Set(['mastra_expert']);\nlet alexandriaExecutionTail = Promise.resolve();\n\nasync function executeAlexandriaSerially(tool: ToolLike, args: any[]): Promise<unknown> {\n const previous = alexandriaExecutionTail;\n let release!: () => void;\n const current = new Promise<void>(resolve => {\n release = resolve;\n });\n alexandriaExecutionTail = previous.then(\n () => current,\n () => current,\n );\n\n await previous.catch(() => undefined);\n try {\n const abortSignal = args[1]?.abortSignal as AbortSignal | undefined;\n abortSignal?.throwIfAborted();\n return await tool.execute?.apply(tool, args);\n } finally {\n // The active tool receives the same abort signal and owns terminating its\n // nested execution. Never release this slot until that execution settles.\n release();\n }\n}\n\nfunction configurePluginTool(name: string, tool: ToolLike, backgroundToolsEnabled: boolean): ToolLike {\n if (!backgroundToolsEnabled || !BACKGROUND_ELIGIBLE_PLUGIN_TOOLS.has(name)) return tool;\n return {\n ...tool,\n // Eligible for backgrounding, but the agent must opt in per call — a plain\n // expert question should stay a normal awaited foreground call.\n background: { enabled: true, defaultDisposition: 'foreground' as const },\n execute: (...args: any[]) => executeAlexandriaSerially(tool, args),\n };\n}\n\nexport class LazyNotificationsStorage extends NotificationsStorage {\n constructor(private readonly storage: MastraCompositeStore) {\n super();\n }\n\n private async getNotificationsStorage(): Promise<NotificationsStorage> {\n const notifications = await this.storage.getStore('notifications');\n if (!notifications) {\n throw new Error('notification_inbox requires a notifications storage domain');\n }\n return notifications;\n }\n\n async createNotification(input: CreateNotificationInput) {\n return (await this.getNotificationsStorage()).createNotification(input);\n }\n\n async listNotifications(input: ListNotificationsInput) {\n return (await this.getNotificationsStorage()).listNotifications(input);\n }\n\n async listDueNotifications(input: ListDueNotificationsInput) {\n return (await this.getNotificationsStorage()).listDueNotifications(input);\n }\n\n async getNotification(input: { threadId: string; id: string }) {\n return (await this.getNotificationsStorage()).getNotification(input);\n }\n\n async updateNotification(input: UpdateNotificationInput) {\n return (await this.getNotificationsStorage()).updateNotification(input);\n }\n\n override async updateNotificationsStatus(input: UpdateNotificationsStatusInput) {\n return (await this.getNotificationsStorage()).updateNotificationsStatus(input);\n }\n\n async dangerouslyClearAll() {\n return (await this.getNotificationsStorage()).dangerouslyClearAll();\n }\n}\n\nexport type PostToolObserver = (context: ToolAfterHookContext) => void | Promise<void>;\n\nexport function createToolHooks(hookManager?: HookManager, postToolObserver?: PostToolObserver): ToolHooks | undefined {\n if (!hookManager && !postToolObserver) return undefined;\n\n return {\n beforeToolCall: async ({ toolName, input }) => {\n if (!hookManager) return;\n const preResult = await hookManager.runPreToolUse(toolName, input);\n if (!preResult.allowed) {\n return {\n proceed: false as const,\n output: {\n error: preResult.blockReason ?? `Blocked by PreToolUse hook for tool \"${toolName}\"`,\n },\n };\n }\n },\n afterToolCall: async context => {\n const { toolName, input, output, error } = context;\n if (hookManager) {\n const failed = error !== undefined;\n await hookManager\n .runPostToolUse(\n toolName,\n input,\n failed ? { error: error instanceof Error ? error.message : String(error) } : output,\n failed,\n )\n .catch(() => undefined);\n }\n await Promise.resolve()\n .then(() => postToolObserver?.(context))\n .catch(error => {\n console.warn(`[MastraCode] Post-tool observer failed for ${toolName}.`, error);\n });\n },\n };\n}\n\ntype DynamicToolProvider =\n | Record<string, ToolLike | undefined>\n | ((ctx: {\n requestContext: RequestContext;\n }) => Record<string, ToolLike | undefined> | Promise<Record<string, ToolLike | undefined>>);\n\nexport function createDynamicTools(\n mcpManager?: McpManager,\n extraTools?: DynamicToolProvider,\n disabledTools?: string[],\n storage?: MastraCompositeStore,\n pluginTools?: Record<string, ToolLike>,\n backgroundToolsEnabled = false,\n) {\n return function getDynamicTools({\n requestContext,\n }: {\n requestContext: RequestContext;\n }): ToolsInput | Promise<ToolsInput> {\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeComposedState> | undefined;\n const state = ctx?.getState();\n\n const modelId = ctx?.session?.modelId;\n const isAnthropicModel = modelId?.startsWith('anthropic/');\n const isOpenAIModel = modelId?.startsWith('openai/');\n\n // Filesystem, grep, glob, edit, write, execute_command, and process\n // management tools are now provided by the workspace (see workspace.ts).\n // Only tools without a workspace equivalent remain here.\n const tools: Record<string, ToolLike> = {\n request_access: requestSandboxAccessTool,\n // Workflow surface. `create-workflow` delegates to the workflow-builder\n // sub-agent; the other four are Dynamic Workflow management operations.\n // Permission categories live in permissions.ts (TOOL_CATEGORY_MAP).\n [WORKFLOW_MANAGEMENT_TOOL_IDS.createWorkflow]: createWorkflowTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.listWorkflows]: listWorkflowsTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.getWorkflow]: getWorkflowTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.runWorkflow]: runWorkflowTool,\n [WORKFLOW_MANAGEMENT_TOOL_IDS.deleteWorkflow]: deleteWorkflowTool,\n };\n\n if (storage) {\n tools[MC_TOOLS.NOTIFICATION_INBOX] = createNotificationInboxTool({\n storage: new LazyNotificationsStorage(storage),\n });\n }\n\n const configuredWebTools = createConfiguredWebTools();\n if (configuredWebTools) {\n Object.assign(tools, configuredWebTools);\n } else if (isAnthropicModel) {\n const anthropic = createAnthropic({});\n tools.web_search = anthropic.tools.webSearch_20250305();\n } else if (isOpenAIModel) {\n const openai = createOpenAI({});\n tools.web_search = openai.tools.webSearch();\n }\n\n if (mcpManager) {\n const mcpTools = mcpManager.getTools();\n Object.assign(tools, mcpTools);\n }\n\n const finish = (resolvedExtra: Record<string, ToolLike | undefined> | undefined) => {\n if (resolvedExtra) {\n for (const [name, tool] of Object.entries(resolvedExtra)) {\n if (tool && !(name in tools)) {\n tools[name] = tool;\n }\n }\n }\n\n if (pluginTools) {\n for (const [name, tool] of Object.entries(pluginTools)) {\n if (!(name in tools)) {\n tools[name] = configurePluginTool(name, tool, backgroundToolsEnabled);\n }\n }\n }\n\n // Remove tools explicitly disabled via config so the model never sees them.\n if (disabledTools?.length) {\n for (const toolName of disabledTools) {\n delete tools[toolName];\n }\n }\n\n // Remove tools that have a per-tool 'deny' policy so the model never sees them.\n const permissionRules = state?.permissionRules;\n if (permissionRules?.tools) {\n for (const [name, policy] of Object.entries(permissionRules.tools)) {\n if (policy === 'deny') {\n delete tools[name];\n }\n }\n }\n\n return tools as ToolsInput;\n };\n\n if (typeof extraTools === 'function') {\n const resolved = extraTools({ requestContext });\n // Stay synchronous for sync providers; only go async when the provider does.\n if (resolved && typeof (resolved as PromiseLike<unknown>).then === 'function') {\n return Promise.resolve(resolved).then(finish);\n }\n return finish(resolved as Record<string, ToolLike | undefined>);\n }\n\n return finish(extraTools);\n };\n}\n"],"mappings":"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"}
@@ -12,10 +12,11 @@ export interface WorkspaceSkillExtension {
12
12
  /** Compose the additional roots with the workspace's normal skill source. */
13
13
  createSource: (fallback: SkillSource, fallbackSkillRoots: string[]) => SkillSource;
14
14
  }
15
- export declare function getDynamicWorkspace({ requestContext, mastra, skillExtension, }: {
15
+ export declare function getDynamicWorkspace({ requestContext, mastra, skillExtension, backgroundToolsEnabled, }: {
16
16
  requestContext: RequestContext;
17
17
  mastra?: Mastra;
18
18
  skillExtension?: WorkspaceSkillExtension;
19
+ backgroundToolsEnabled?: boolean;
19
20
  }): Promise<Workspace<LocalFilesystem, LocalSandbox, undefined>>;
20
21
  /**
21
22
  * Resolver for the agent's `goal.tools` config. Builds the request's workspace
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/agents/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AACxG,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAyGrE,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,SAAe,EACtB,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,EAAE,CA0BV;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,uFAAuF;IACvF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,6EAA6E;IAC7E,YAAY,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,WAAW,CAAC;CACpF;AA2BD,wBAAsB,mBAAmB,CAAC,EACxC,cAAc,EACd,MAAM,EACN,cAAc,GACf,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,uBAAuB,CAAC;CAC1C,gEA0EA;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,cAAc,EACd,MAAM,GACP,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAclC"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/agents/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AACxG,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAyGrE,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,SAAe,EACtB,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,EAAE,CA0BV;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,uFAAuF;IACvF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,6EAA6E;IAC7E,YAAY,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,WAAW,CAAC;CACpF;AA2BD,wBAAsB,mBAAmB,CAAC,EACxC,cAAc,EACd,MAAM,EACN,cAAc,EACd,sBAA8B,GAC/B,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,uBAAuB,CAAC;IACzC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,gEA0EA;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,cAAc,EACd,MAAM,GACP,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAclC"}
@@ -2,7 +2,7 @@ import "../constants.js";
2
2
  import { loadSettings, resolveLspSetting } from "../onboarding/settings.js";
3
3
  import { getPlansDir } from "../utils/plans.js";
4
4
  import { isPathWithinRoot } from "../utils/path-security.js";
5
- import { GOAL_JUDGE_READONLY_TOOLS, MASTRACODE_WORKSPACE_TOOLS } from "./tool-availability.js";
5
+ import { GOAL_JUDGE_READONLY_TOOLS, createMastraCodeWorkspaceTools } from "./tool-availability.js";
6
6
  import fs, { existsSync } from "fs";
7
7
  import os from "os";
8
8
  import path, { dirname, join } from "path";
@@ -122,7 +122,7 @@ function detectPackageRunner(projectPath) {
122
122
  if (existsSync(join(projectPath, "package-lock.json"))) return "npx --yes";
123
123
  return "npx --yes";
124
124
  }
125
- async function getDynamicWorkspace({ requestContext, mastra, skillExtension }) {
125
+ async function getDynamicWorkspace({ requestContext, mastra, skillExtension, backgroundToolsEnabled = false }) {
126
126
  const state = requestContext.get("controller")?.getState();
127
127
  const rawProjectPath = state?.projectPath;
128
128
  if (!rawProjectPath) throw new Error("Project path is required");
@@ -137,7 +137,7 @@ async function getDynamicWorkspace({ requestContext, mastra, skillExtension }) {
137
137
  ...DEFAULT_ALLOWED_PATHS,
138
138
  ...sandboxPaths.map((p) => path.resolve(p))
139
139
  ];
140
- const workspaceTools = MASTRACODE_WORKSPACE_TOOLS;
140
+ const workspaceTools = createMastraCodeWorkspaceTools(backgroundToolsEnabled);
141
141
  let existing;
142
142
  try {
143
143
  existing = mastra?.getWorkspaceById(workspaceId);
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","names":[],"sources":["../../src/agents/workspace.ts"],"sourcesContent":["import fs, { existsSync } from 'node:fs';\nimport os from 'node:os';\nimport path, { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ToolsInput } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { Workspace, LocalFilesystem, LocalSandbox, createWorkspaceTools } from '@mastra/core/workspace';\nimport type { LSPConfig, SkillSource } from '@mastra/core/workspace';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport { loadSettings, resolveLspSetting } from '../onboarding/settings.js';\nimport type { MastraCodeState } from '../schema.js';\nimport { isPathWithinRoot } from '../utils/path-security.js';\nimport { getPlansDir } from '../utils/plans.js';\n\nimport { GOAL_JUDGE_READONLY_TOOLS, MASTRACODE_WORKSPACE_TOOLS } from './tool-availability.js';\n\n// =============================================================================\n// Sandbox Environment\n// =============================================================================\n\nfunction buildSandboxEnv(): NodeJS.ProcessEnv {\n return {\n ...process.env,\n // Explicit overrides for non-interactive subprocess execution\n FORCE_COLOR: '1',\n CLICOLOR_FORCE: '1',\n TERM: process.env.TERM || 'xterm-256color',\n CI: 'true',\n NONINTERACTIVE: '1',\n DEBIAN_FRONTEND: 'noninteractive',\n };\n}\n\n// =============================================================================\n// Create Workspace with Skills\n// =============================================================================\n\n// We support multiple skill locations for compatibility:\n// 1. Project-local: <configDir>/skills (project-specific mastracode skills)\n// 2. Project-local: .claude/skills (Claude Code compatible skills)\n// 3. Project-local: .agents/skills (Agent Skills spec compatible)\n// 4. Global: ~/<configDir>/skills (user-wide mastracode skills)\n// 5. Global: ~/.claude/skills (user-wide Claude Code skills)\n// 6. Global: ~/.agents/skills (user-wide Agent Skills spec compatible)\n\n// Mastra's LocalSkillSource.readdir uses Node's Dirent.isDirectory() which\n// returns false for symlinks. Tools like `npx skills add` install skills as\n// symlinks, so we need to resolve them. For each symlinked skill directory,\n// we add the real (resolved) parent path as an additional skill scan path.\nfunction collectSkillPaths(skillsDirs: string[], allowedRoot?: string): string[] {\n const paths: string[] = [];\n const seen = new Set<string>();\n let realAllowedRoot: string | undefined;\n\n if (allowedRoot) {\n try {\n realAllowedRoot = fs.realpathSync(allowedRoot);\n } catch {\n return [];\n }\n }\n\n for (const skillsDir of skillsDirs) {\n const skillsDirExists = fs.existsSync(skillsDir);\n if (skillsDirExists && realAllowedRoot) {\n try {\n const realSkillsDir = fs.realpathSync(skillsDir);\n if (!isPathWithinRoot(realSkillsDir, realAllowedRoot)) continue;\n } catch {\n continue;\n }\n }\n\n const resolved = path.resolve(skillsDir);\n if (!seen.has(resolved)) {\n seen.add(resolved);\n paths.push(skillsDir);\n }\n\n if (!skillsDirExists) continue;\n\n try {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isSymbolicLink()) {\n try {\n const linkPath = path.join(skillsDir, entry.name);\n const realPath = fs.realpathSync(linkPath);\n if (realAllowedRoot && !isPathWithinRoot(realPath, realAllowedRoot)) continue;\n const stat = fs.statSync(realPath);\n if (stat.isDirectory()) {\n const realParent = path.dirname(realPath);\n if (realAllowedRoot && !isPathWithinRoot(realParent, realAllowedRoot)) continue;\n if (!seen.has(realParent)) {\n seen.add(realParent);\n paths.push(realParent);\n }\n }\n } catch {\n continue;\n }\n }\n }\n } catch {\n // Ignore errors during symlink resolution\n }\n }\n\n return paths;\n}\n\n// Build skill paths dynamically based on configDir and projectPath\nexport function buildSkillPaths(\n projectPath: string,\n configDir: string,\n homeDir = os.homedir(),\n pluginSkillPaths: string[] = [],\n): string[] {\n const mastraCodeLocalSkillsPath = path.join(projectPath, configDir, 'skills');\n const claudeLocalSkillsPath = path.join(projectPath, '.claude', 'skills');\n const agentSkillsLocalPath = path.join(projectPath, '.agents', 'skills');\n const mastraCodeGlobalSkillsPath = path.join(homeDir, configDir, 'skills');\n const claudeGlobalSkillsPath = path.join(homeDir, '.claude', 'skills');\n const agentSkillsGlobalPath = path.join(homeDir, '.agents', 'skills');\n\n const paths = [\n ...collectSkillPaths([mastraCodeLocalSkillsPath, claudeLocalSkillsPath, agentSkillsLocalPath], projectPath),\n ...collectSkillPaths([mastraCodeGlobalSkillsPath, claudeGlobalSkillsPath, agentSkillsGlobalPath]),\n ...pluginSkillPaths.flatMap(pluginSkillPath => collectSkillPaths([pluginSkillPath], pluginSkillPath)),\n ];\n\n const seenPaths = new Set<string>();\n return paths.filter(skillPath => {\n let resolved: string;\n try {\n resolved = fs.realpathSync(skillPath);\n } catch {\n resolved = path.resolve(skillPath);\n }\n if (seenPaths.has(resolved)) return false;\n seenPaths.add(resolved);\n return true;\n });\n}\n\nexport interface WorkspaceSkillExtension {\n /** Distinguishes extended workspaces from the default resolver cache. */\n id: string;\n /** Additional read-only skill roots prepended to normal project/global skill roots. */\n paths: string[];\n /** Compose the additional roots with the workspace's normal skill source. */\n createSource: (fallback: SkillSource, fallbackSkillRoots: string[]) => SkillSource;\n}\n\n/**\n * Paths the agent is always allowed to access (in addition to the project root\n * and any per-thread sandboxAllowedPaths). The OS temp directory is included\n * so the agent can use it as a scratchpad without requesting access every time.\n */\nconst DEFAULT_ALLOWED_PATHS: string[] = [os.tmpdir(), '/tmp', getPlansDir()].reduce<string[]>((acc, p) => {\n const resolved = path.resolve(p);\n if (!acc.includes(resolved)) acc.push(resolved);\n return acc;\n}, []);\n\nconst WORKSPACE_ID_PREFIX = 'mastra-code-workspace';\n\n/**\n * Detect the project's package runner from lock files.\n * Used as a fallback packageRunner for LSP when no binary is found locally or on PATH.\n */\nfunction detectPackageRunner(projectPath: string): string | undefined {\n if (existsSync(join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm dlx';\n if (existsSync(join(projectPath, 'bun.lockb')) || existsSync(join(projectPath, 'bun.lock'))) return 'bunx';\n if (existsSync(join(projectPath, 'yarn.lock'))) return 'yarn dlx';\n if (existsSync(join(projectPath, 'package-lock.json'))) return 'npx --yes';\n return 'npx --yes';\n}\n\nexport async function getDynamicWorkspace({\n requestContext,\n mastra,\n skillExtension,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n skillExtension?: WorkspaceSkillExtension;\n}) {\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeState> | undefined;\n const state = ctx?.getState();\n\n const rawProjectPath = state?.projectPath;\n\n if (!rawProjectPath) {\n throw new Error('Project path is required');\n }\n\n const projectPath = path.resolve(rawProjectPath);\n const configDir = state?.configDir ?? DEFAULT_CONFIG_DIR;\n const projectSkillPaths = buildSkillPaths(projectPath, configDir, state?.homeDir, state?.pluginSkillPaths ?? []);\n const skillPaths = [...(skillExtension?.paths ?? []), ...projectSkillPaths];\n const extensionId = skillExtension ? `-${skillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectPath}${extensionId}`;\n const sandboxPaths = state?.sandboxAllowedPaths ?? [];\n const allowedPaths = [\n ...projectSkillPaths,\n ...DEFAULT_ALLOWED_PATHS,\n ...sandboxPaths.map((p: string) => path.resolve(p)),\n ];\n\n // All modes share the same workspace tool configuration. Per-mode tool\n // visibility is enforced at LLM-call time via `availableTools` /\n // `activeTools` on the AgentController, not by mutating workspace capabilities.\n const workspaceTools = MASTRACODE_WORKSPACE_TOOLS;\n\n // Reuse existing workspace if already registered (preserves ProcessManager state)\n let existing: Workspace<LocalFilesystem, LocalSandbox> | undefined;\n try {\n existing = mastra?.getWorkspaceById(workspaceId) as Workspace<LocalFilesystem, LocalSandbox>;\n } catch {\n // Not registered yet\n }\n\n if (existing) {\n existing.filesystem.setAllowedPaths(allowedPaths);\n existing.setToolsConfig(workspaceTools);\n return existing;\n }\n\n // LSP is opt-in. When disabled we pass `false` so core skips the dependency\n // check, the LSP manager, and the `lsp_inspect` tool entirely.\n const userLsp = resolveLspSetting(loadSettings().lsp);\n let lspConfig: LSPConfig | false = false;\n if (userLsp !== false) {\n const mcModulePath = join(dirname(fileURLToPath(import.meta.url)), '..');\n lspConfig = {\n maxOpenClients: 4,\n ...userLsp,\n packageRunner: userLsp.packageRunner || detectPackageRunner(projectPath), // Detected runner is the fallback — user's packageRunner always wins\n searchPaths: [mcModulePath, ...(userLsp.searchPaths ?? [])],\n };\n }\n\n // First call for this project — create the workspace\n const filesystem = new LocalFilesystem({\n basePath: projectPath,\n allowedPaths,\n });\n return new Workspace({\n id: workspaceId,\n name: 'Mastra Code Workspace',\n filesystem,\n sandbox: new LocalSandbox({\n workingDirectory: projectPath,\n env: buildSandboxEnv(),\n }),\n tools: workspaceTools,\n skills: skillPaths,\n ...(skillExtension ? { skillSource: skillExtension.createSource(filesystem, projectSkillPaths) } : {}),\n lsp: lspConfig,\n });\n}\n\n/**\n * Resolver for the agent's `goal.tools` config. Builds the request's workspace\n * (same per-request resolution as the agent's own tools) and returns only the\n * read-only verification subset, remapped to mastracode's tool names (`view`,\n * `search_content`, etc.). Returns `undefined` when no workspace can be resolved\n * (e.g. no project path), keeping the default judge text-only rather than\n * throwing inside the goal step.\n */\nexport async function getGoalJudgeTools({\n requestContext,\n mastra,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n}): Promise<ToolsInput | undefined> {\n let workspace: Workspace;\n try {\n workspace = await getDynamicWorkspace({ requestContext, mastra });\n } catch {\n return undefined;\n }\n\n const allTools = await createWorkspaceTools(workspace, { requestContext, workspace });\n const readonly: ToolsInput = {};\n for (const name of GOAL_JUDGE_READONLY_TOOLS) {\n if (allTools[name]) readonly[name] = allTools[name];\n }\n return Object.keys(readonly).length > 0 ? readonly : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workspace.js","names":[],"sources":["../../src/agents/workspace.ts"],"sourcesContent":["import fs, { existsSync } from 'node:fs';\nimport os from 'node:os';\nimport path, { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ToolsInput } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { Workspace, LocalFilesystem, LocalSandbox, createWorkspaceTools } from '@mastra/core/workspace';\nimport type { LSPConfig, SkillSource } from '@mastra/core/workspace';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport { loadSettings, resolveLspSetting } from '../onboarding/settings.js';\nimport type { MastraCodeState } from '../schema.js';\nimport { isPathWithinRoot } from '../utils/path-security.js';\nimport { getPlansDir } from '../utils/plans.js';\n\nimport { createMastraCodeWorkspaceTools, GOAL_JUDGE_READONLY_TOOLS } from './tool-availability.js';\n\n// =============================================================================\n// Sandbox Environment\n// =============================================================================\n\nfunction buildSandboxEnv(): NodeJS.ProcessEnv {\n return {\n ...process.env,\n // Explicit overrides for non-interactive subprocess execution\n FORCE_COLOR: '1',\n CLICOLOR_FORCE: '1',\n TERM: process.env.TERM || 'xterm-256color',\n CI: 'true',\n NONINTERACTIVE: '1',\n DEBIAN_FRONTEND: 'noninteractive',\n };\n}\n\n// =============================================================================\n// Create Workspace with Skills\n// =============================================================================\n\n// We support multiple skill locations for compatibility:\n// 1. Project-local: <configDir>/skills (project-specific mastracode skills)\n// 2. Project-local: .claude/skills (Claude Code compatible skills)\n// 3. Project-local: .agents/skills (Agent Skills spec compatible)\n// 4. Global: ~/<configDir>/skills (user-wide mastracode skills)\n// 5. Global: ~/.claude/skills (user-wide Claude Code skills)\n// 6. Global: ~/.agents/skills (user-wide Agent Skills spec compatible)\n\n// Mastra's LocalSkillSource.readdir uses Node's Dirent.isDirectory() which\n// returns false for symlinks. Tools like `npx skills add` install skills as\n// symlinks, so we need to resolve them. For each symlinked skill directory,\n// we add the real (resolved) parent path as an additional skill scan path.\nfunction collectSkillPaths(skillsDirs: string[], allowedRoot?: string): string[] {\n const paths: string[] = [];\n const seen = new Set<string>();\n let realAllowedRoot: string | undefined;\n\n if (allowedRoot) {\n try {\n realAllowedRoot = fs.realpathSync(allowedRoot);\n } catch {\n return [];\n }\n }\n\n for (const skillsDir of skillsDirs) {\n const skillsDirExists = fs.existsSync(skillsDir);\n if (skillsDirExists && realAllowedRoot) {\n try {\n const realSkillsDir = fs.realpathSync(skillsDir);\n if (!isPathWithinRoot(realSkillsDir, realAllowedRoot)) continue;\n } catch {\n continue;\n }\n }\n\n const resolved = path.resolve(skillsDir);\n if (!seen.has(resolved)) {\n seen.add(resolved);\n paths.push(skillsDir);\n }\n\n if (!skillsDirExists) continue;\n\n try {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isSymbolicLink()) {\n try {\n const linkPath = path.join(skillsDir, entry.name);\n const realPath = fs.realpathSync(linkPath);\n if (realAllowedRoot && !isPathWithinRoot(realPath, realAllowedRoot)) continue;\n const stat = fs.statSync(realPath);\n if (stat.isDirectory()) {\n const realParent = path.dirname(realPath);\n if (realAllowedRoot && !isPathWithinRoot(realParent, realAllowedRoot)) continue;\n if (!seen.has(realParent)) {\n seen.add(realParent);\n paths.push(realParent);\n }\n }\n } catch {\n continue;\n }\n }\n }\n } catch {\n // Ignore errors during symlink resolution\n }\n }\n\n return paths;\n}\n\n// Build skill paths dynamically based on configDir and projectPath\nexport function buildSkillPaths(\n projectPath: string,\n configDir: string,\n homeDir = os.homedir(),\n pluginSkillPaths: string[] = [],\n): string[] {\n const mastraCodeLocalSkillsPath = path.join(projectPath, configDir, 'skills');\n const claudeLocalSkillsPath = path.join(projectPath, '.claude', 'skills');\n const agentSkillsLocalPath = path.join(projectPath, '.agents', 'skills');\n const mastraCodeGlobalSkillsPath = path.join(homeDir, configDir, 'skills');\n const claudeGlobalSkillsPath = path.join(homeDir, '.claude', 'skills');\n const agentSkillsGlobalPath = path.join(homeDir, '.agents', 'skills');\n\n const paths = [\n ...collectSkillPaths([mastraCodeLocalSkillsPath, claudeLocalSkillsPath, agentSkillsLocalPath], projectPath),\n ...collectSkillPaths([mastraCodeGlobalSkillsPath, claudeGlobalSkillsPath, agentSkillsGlobalPath]),\n ...pluginSkillPaths.flatMap(pluginSkillPath => collectSkillPaths([pluginSkillPath], pluginSkillPath)),\n ];\n\n const seenPaths = new Set<string>();\n return paths.filter(skillPath => {\n let resolved: string;\n try {\n resolved = fs.realpathSync(skillPath);\n } catch {\n resolved = path.resolve(skillPath);\n }\n if (seenPaths.has(resolved)) return false;\n seenPaths.add(resolved);\n return true;\n });\n}\n\nexport interface WorkspaceSkillExtension {\n /** Distinguishes extended workspaces from the default resolver cache. */\n id: string;\n /** Additional read-only skill roots prepended to normal project/global skill roots. */\n paths: string[];\n /** Compose the additional roots with the workspace's normal skill source. */\n createSource: (fallback: SkillSource, fallbackSkillRoots: string[]) => SkillSource;\n}\n\n/**\n * Paths the agent is always allowed to access (in addition to the project root\n * and any per-thread sandboxAllowedPaths). The OS temp directory is included\n * so the agent can use it as a scratchpad without requesting access every time.\n */\nconst DEFAULT_ALLOWED_PATHS: string[] = [os.tmpdir(), '/tmp', getPlansDir()].reduce<string[]>((acc, p) => {\n const resolved = path.resolve(p);\n if (!acc.includes(resolved)) acc.push(resolved);\n return acc;\n}, []);\n\nconst WORKSPACE_ID_PREFIX = 'mastra-code-workspace';\n\n/**\n * Detect the project's package runner from lock files.\n * Used as a fallback packageRunner for LSP when no binary is found locally or on PATH.\n */\nfunction detectPackageRunner(projectPath: string): string | undefined {\n if (existsSync(join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm dlx';\n if (existsSync(join(projectPath, 'bun.lockb')) || existsSync(join(projectPath, 'bun.lock'))) return 'bunx';\n if (existsSync(join(projectPath, 'yarn.lock'))) return 'yarn dlx';\n if (existsSync(join(projectPath, 'package-lock.json'))) return 'npx --yes';\n return 'npx --yes';\n}\n\nexport async function getDynamicWorkspace({\n requestContext,\n mastra,\n skillExtension,\n backgroundToolsEnabled = false,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n skillExtension?: WorkspaceSkillExtension;\n backgroundToolsEnabled?: boolean;\n}) {\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeState> | undefined;\n const state = ctx?.getState();\n\n const rawProjectPath = state?.projectPath;\n\n if (!rawProjectPath) {\n throw new Error('Project path is required');\n }\n\n const projectPath = path.resolve(rawProjectPath);\n const configDir = state?.configDir ?? DEFAULT_CONFIG_DIR;\n const projectSkillPaths = buildSkillPaths(projectPath, configDir, state?.homeDir, state?.pluginSkillPaths ?? []);\n const skillPaths = [...(skillExtension?.paths ?? []), ...projectSkillPaths];\n const extensionId = skillExtension ? `-${skillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectPath}${extensionId}`;\n const sandboxPaths = state?.sandboxAllowedPaths ?? [];\n const allowedPaths = [\n ...projectSkillPaths,\n ...DEFAULT_ALLOWED_PATHS,\n ...sandboxPaths.map((p: string) => path.resolve(p)),\n ];\n\n // All modes share the same workspace tool configuration. Per-mode tool\n // visibility is enforced at LLM-call time via `availableTools` /\n // `activeTools` on the AgentController, not by mutating workspace capabilities.\n const workspaceTools = createMastraCodeWorkspaceTools(backgroundToolsEnabled);\n\n // Reuse existing workspace if already registered (preserves ProcessManager state)\n let existing: Workspace<LocalFilesystem, LocalSandbox> | undefined;\n try {\n existing = mastra?.getWorkspaceById(workspaceId) as Workspace<LocalFilesystem, LocalSandbox>;\n } catch {\n // Not registered yet\n }\n\n if (existing) {\n existing.filesystem.setAllowedPaths(allowedPaths);\n existing.setToolsConfig(workspaceTools);\n return existing;\n }\n\n // LSP is opt-in. When disabled we pass `false` so core skips the dependency\n // check, the LSP manager, and the `lsp_inspect` tool entirely.\n const userLsp = resolveLspSetting(loadSettings().lsp);\n let lspConfig: LSPConfig | false = false;\n if (userLsp !== false) {\n const mcModulePath = join(dirname(fileURLToPath(import.meta.url)), '..');\n lspConfig = {\n maxOpenClients: 4,\n ...userLsp,\n packageRunner: userLsp.packageRunner || detectPackageRunner(projectPath), // Detected runner is the fallback — user's packageRunner always wins\n searchPaths: [mcModulePath, ...(userLsp.searchPaths ?? [])],\n };\n }\n\n // First call for this project — create the workspace\n const filesystem = new LocalFilesystem({\n basePath: projectPath,\n allowedPaths,\n });\n return new Workspace({\n id: workspaceId,\n name: 'Mastra Code Workspace',\n filesystem,\n sandbox: new LocalSandbox({\n workingDirectory: projectPath,\n env: buildSandboxEnv(),\n }),\n tools: workspaceTools,\n skills: skillPaths,\n ...(skillExtension ? { skillSource: skillExtension.createSource(filesystem, projectSkillPaths) } : {}),\n lsp: lspConfig,\n });\n}\n\n/**\n * Resolver for the agent's `goal.tools` config. Builds the request's workspace\n * (same per-request resolution as the agent's own tools) and returns only the\n * read-only verification subset, remapped to mastracode's tool names (`view`,\n * `search_content`, etc.). Returns `undefined` when no workspace can be resolved\n * (e.g. no project path), keeping the default judge text-only rather than\n * throwing inside the goal step.\n */\nexport async function getGoalJudgeTools({\n requestContext,\n mastra,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n}): Promise<ToolsInput | undefined> {\n let workspace: Workspace;\n try {\n workspace = await getDynamicWorkspace({ requestContext, mastra });\n } catch {\n return undefined;\n }\n\n const allTools = await createWorkspaceTools(workspace, { requestContext, workspace });\n const readonly: ToolsInput = {};\n for (const name of GOAL_JUDGE_READONLY_TOOLS) {\n if (allTools[name]) readonly[name] = allTools[name];\n }\n return Object.keys(readonly).length > 0 ? readonly : undefined;\n}\n"],"mappings":"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"}
@@ -9,6 +9,7 @@ import type { RunMCResult } from './types.js';
9
9
  export declare function truncate(s: string, max: number): string;
10
10
  /** Mutable per-stream cursor used by {@link formatHuman} to stream assistant text. */
11
11
  export interface HumanFormatState {
12
+ activeAssistantMessageId?: string;
12
13
  lastTextLength: number;
13
14
  }
14
15
  export declare function createHumanFormatState(): HumanFormatState;
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/headless/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAsC,MAAM,+BAA+B,CAAC;AAE9G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAUD,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAED,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,gBAAgB,GAAG,eAAe,CA4CjG;AAOD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMhF;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAc5D"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/headless/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAE1E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,sBAAsB,IAAI,gBAAgB,CAEzD;AAED,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,oBAAoB,EAAE,KAAK,EAAE,gBAAgB,GAAG,eAAe,CAuCjG;AAOD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMhF;AAED,uFAAuF;AACvF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAE5D;AAED,oEAAoE;AACpE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAc5D"}
@@ -3,10 +3,6 @@
3
3
  function truncate(s, max) {
4
4
  return s.length > max ? s.slice(0, max) + "..." : s;
5
5
  }
6
- /** Concatenate the text of an assistant message's nested content parts. */
7
- function messageText(message) {
8
- return message.content.parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
9
- }
10
6
  function createHumanFormatState() {
11
7
  return { lastTextLength: 0 };
12
8
  }
@@ -21,22 +17,22 @@ function formatHuman(event, state) {
21
17
  case "agent_start":
22
18
  state.lastTextLength = 0;
23
19
  return {};
24
- case "message_update": {
25
- const fullText = messageText(event.message);
26
- if (fullText.length > state.lastTextLength) {
27
- const delta = fullText.slice(state.lastTextLength);
28
- state.lastTextLength = fullText.length;
29
- return { stdout: delta };
20
+ case "message_start":
21
+ if (event.message.role === "assistant") {
22
+ state.activeAssistantMessageId = event.message.id;
23
+ state.lastTextLength = 0;
30
24
  }
31
25
  return {};
32
- }
33
- case "message_end": {
34
- if (event.message.role !== "assistant") return {};
35
- const fullText = messageText(event.message);
36
- const delta = fullText.length > state.lastTextLength ? fullText.slice(state.lastTextLength) : "";
26
+ case "message_update":
27
+ if (event.event.type !== "text-delta" || event.id !== state.activeAssistantMessageId || !event.event.delta) return {};
28
+ state.lastTextLength += event.event.delta.length;
29
+ return { stdout: event.event.delta };
30
+ case "message_end":
31
+ if (event.id !== state.activeAssistantMessageId) return {};
32
+ state.activeAssistantMessageId = void 0;
33
+ if (state.lastTextLength === 0) return {};
37
34
  state.lastTextLength = 0;
38
- return { stdout: delta + "\n" };
39
- }
35
+ return { stdout: "\n" };
40
36
  case "tool_start": return { stderr: `[tool] ${event.toolName}\n` };
41
37
  case "tool_end": return event.isError ? { stderr: `[tool error] ${truncate(String(event.result), 200)}\n` } : {};
42
38
  case "shell_output": return { stderr: event.output };
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../../src/headless/format.ts"],"sourcesContent":["/**\n * Output formatters for headless runs. All functions here are pure — they take\n * an event (or a final result) and return strings / plain objects. They never\n * touch `process.*`; the CLI adapter owns the sinks.\n */\nimport type { AgentControllerEvent, MastraDBMessage, MastraMessagePart } from '@mastra/core/agent-controller';\n\nimport type { RunMCResult } from './types.js';\n\n/** Truncate a string to `max` characters, appending \"...\" if truncated. */\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) + '...' : s;\n}\n\n/** Concatenate the text of an assistant message's nested content parts. */\nfunction messageText(message: MastraDBMessage): string {\n return message.content.parts\n .filter((p): p is MastraMessagePart & { text: string } => p.type === 'text' && typeof p.text === 'string')\n .map(p => p.text)\n .join('');\n}\n\n/** Mutable per-stream cursor used by {@link formatHuman} to stream assistant text. */\nexport interface HumanFormatState {\n lastTextLength: number;\n}\n\nexport function createHumanFormatState(): HumanFormatState {\n return { lastTextLength: 0 };\n}\n\n/** A chunk of formatted output destined for stdout and/or stderr. */\nexport interface FormattedOutput {\n stdout?: string;\n stderr?: string;\n}\n\n/**\n * Human-readable streaming formatter (the historical default). Assistant text\n * streams to stdout; tool/subagent/shell/error activity goes to stderr. The\n * `state` cursor is mutated so repeated `message_update` events only emit the\n * newly-appended text.\n */\nexport function formatHuman(event: AgentControllerEvent, state: HumanFormatState): FormattedOutput {\n switch (event.type) {\n case 'agent_start':\n state.lastTextLength = 0;\n return {};\n case 'message_update': {\n const fullText = messageText(event.message);\n if (fullText.length > state.lastTextLength) {\n const delta = fullText.slice(state.lastTextLength);\n state.lastTextLength = fullText.length;\n return { stdout: delta };\n }\n return {};\n }\n case 'message_end': {\n // Only assistant messages produce stdout. The controller also emits\n // message_end for the echoed user prompt (and system messages); emitting\n // those here would duplicate the prompt to stdout and corrupt the stream.\n if (event.message.role !== 'assistant') return {};\n // Emit any assistant text the message_update stream didn't already cover\n // (e.g. a run that only delivered the final text on message_end), then\n // terminate the line.\n const fullText = messageText(event.message);\n const delta = fullText.length > state.lastTextLength ? fullText.slice(state.lastTextLength) : '';\n state.lastTextLength = 0;\n return { stdout: delta + '\\n' };\n }\n case 'tool_start':\n return { stderr: `[tool] ${event.toolName}\\n` };\n case 'tool_end':\n return event.isError ? { stderr: `[tool error] ${truncate(String(event.result), 200)}\\n` } : {};\n case 'shell_output':\n return { stderr: event.output };\n case 'subagent_start':\n return {\n stderr: `[subagent:${event.forked ? 'forked:' : ''}${event.agentType}] ${truncate(event.task, 100)}\\n`,\n };\n case 'subagent_end':\n return event.isError ? { stderr: `[subagent error] ${truncate(event.result, 200)}\\n` } : {};\n case 'error':\n return { stderr: `[error] ${event.error.message}\\n` };\n default:\n return {};\n }\n}\n\n/** Convert an `Error` instance into a JSON-serializable plain object. */\nfunction serializeError(err: Error): { name: string; message: string; stack?: string } {\n return { name: err.name, message: err.message, stack: err.stack };\n}\n\n/**\n * JSONL (stream-json) formatter — returns a plain object to be `JSON.stringify`'d\n * as one line per event by the sink. `Error` instances are normalized so their\n * `name`/`message`/`stack` survive serialization (`JSON.stringify` turns a raw\n * `Error` into `{}`).\n */\nexport function formatJsonl(event: AgentControllerEvent): Record<string, unknown> {\n const out: Record<string, unknown> = { ...event };\n if ('error' in event && event.error instanceof Error) {\n out.error = serializeError(event.error);\n }\n return out;\n}\n\n/** Render the final result for `--output text`: assistant text, newline-terminated. */\nexport function renderTextResult(result: RunMCResult): string {\n return result.text.endsWith('\\n') ? result.text : result.text + '\\n';\n}\n\n/** Render the final result for `--output json`: one JSON object. */\nexport function renderJsonResult(result: RunMCResult): string {\n return (\n JSON.stringify({\n status: result.status,\n text: result.text,\n finishReason: result.finishReason,\n usage: result.usage,\n toolCalls: result.toolCalls,\n toolResults: result.toolResults,\n error: result.error,\n threadId: result.threadId,\n exitCode: result.exitCode,\n }) + '\\n'\n );\n}\n"],"mappings":";;AAUA,SAAgB,SAAS,GAAW,KAAqB;CACvD,OAAO,EAAE,SAAS,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,QAAQ;AACpD;;AAGA,SAAS,YAAY,SAAkC;CACrD,OAAO,QAAQ,QAAQ,MACpB,QAAQ,MAAiD,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,QAAQ,CAAC,CACzG,KAAI,MAAK,EAAE,IAAI,CAAC,CAChB,KAAK,EAAE;AACZ;AAOA,SAAgB,yBAA2C;CACzD,OAAO,EAAE,gBAAgB,EAAE;AAC7B;;;;;;;AAcA,SAAgB,YAAY,OAA6B,OAA0C;CACjG,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,MAAM,iBAAiB;GACvB,OAAO,CAAC;EACV,KAAK,kBAAkB;GACrB,MAAM,WAAW,YAAY,MAAM,OAAO;GAC1C,IAAI,SAAS,SAAS,MAAM,gBAAgB;IAC1C,MAAM,QAAQ,SAAS,MAAM,MAAM,cAAc;IACjD,MAAM,iBAAiB,SAAS;IAChC,OAAO,EAAE,QAAQ,MAAM;GACzB;GACA,OAAO,CAAC;EACV;EACA,KAAK,eAAe;GAIlB,IAAI,MAAM,QAAQ,SAAS,aAAa,OAAO,CAAC;GAIhD,MAAM,WAAW,YAAY,MAAM,OAAO;GAC1C,MAAM,QAAQ,SAAS,SAAS,MAAM,iBAAiB,SAAS,MAAM,MAAM,cAAc,IAAI;GAC9F,MAAM,iBAAiB;GACvB,OAAO,EAAE,QAAQ,QAAQ,KAAK;EAChC;EACA,KAAK,cACH,OAAO,EAAE,QAAQ,UAAU,MAAM,SAAS,IAAI;EAChD,KAAK,YACH,OAAO,MAAM,UAAU,EAAE,QAAQ,gBAAgB,SAAS,OAAO,MAAM,MAAM,GAAG,GAAG,EAAE,IAAI,IAAI,CAAC;EAChG,KAAK,gBACH,OAAO,EAAE,QAAQ,MAAM,OAAO;EAChC,KAAK,kBACH,OAAO,EACL,QAAQ,aAAa,MAAM,SAAS,YAAY,KAAK,MAAM,UAAU,IAAI,SAAS,MAAM,MAAM,GAAG,EAAE,IACrG;EACF,KAAK,gBACH,OAAO,MAAM,UAAU,EAAE,QAAQ,oBAAoB,SAAS,MAAM,QAAQ,GAAG,EAAE,IAAI,IAAI,CAAC;EAC5F,KAAK,SACH,OAAO,EAAE,QAAQ,WAAW,MAAM,MAAM,QAAQ,IAAI;EACtD,SACE,OAAO,CAAC;CACZ;AACF;;AAGA,SAAS,eAAe,KAA+D;CACrF,OAAO;EAAE,MAAM,IAAI;EAAM,SAAS,IAAI;EAAS,OAAO,IAAI;CAAM;AAClE;;;;;;;AAQA,SAAgB,YAAY,OAAsD;CAChF,MAAM,MAA+B,EAAE,GAAG,MAAM;CAChD,IAAI,WAAW,SAAS,MAAM,iBAAiB,OAC7C,IAAI,QAAQ,eAAe,MAAM,KAAK;CAExC,OAAO;AACT;;AAGA,SAAgB,iBAAiB,QAA6B;CAC5D,OAAO,OAAO,KAAK,SAAS,IAAI,IAAI,OAAO,OAAO,OAAO,OAAO;AAClE;;AAGA,SAAgB,iBAAiB,QAA6B;CAC5D,OACE,KAAK,UAAU;EACb,QAAQ,OAAO;EACf,MAAM,OAAO;EACb,cAAc,OAAO;EACrB,OAAO,OAAO;EACd,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB,OAAO,OAAO;EACd,UAAU,OAAO;EACjB,UAAU,OAAO;CACnB,CAAC,IAAI;AAET"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../src/headless/format.ts"],"sourcesContent":["/**\n * Output formatters for headless runs. All functions here are pure — they take\n * an event (or a final result) and return strings / plain objects. They never\n * touch `process.*`; the CLI adapter owns the sinks.\n */\nimport type { AgentControllerEvent } from '@mastra/core/agent-controller';\n\nimport type { RunMCResult } from './types.js';\n\n/** Truncate a string to `max` characters, appending \"...\" if truncated. */\nexport function truncate(s: string, max: number): string {\n return s.length > max ? s.slice(0, max) + '...' : s;\n}\n\n/** Mutable per-stream cursor used by {@link formatHuman} to stream assistant text. */\nexport interface HumanFormatState {\n activeAssistantMessageId?: string;\n lastTextLength: number;\n}\n\nexport function createHumanFormatState(): HumanFormatState {\n return { lastTextLength: 0 };\n}\n\n/** A chunk of formatted output destined for stdout and/or stderr. */\nexport interface FormattedOutput {\n stdout?: string;\n stderr?: string;\n}\n\n/**\n * Human-readable streaming formatter (the historical default). Assistant text\n * streams to stdout; tool/subagent/shell/error activity goes to stderr. The\n * `state` cursor is mutated so repeated `message_update` events only emit the\n * newly-appended text.\n */\nexport function formatHuman(event: AgentControllerEvent, state: HumanFormatState): FormattedOutput {\n switch (event.type) {\n case 'agent_start':\n state.lastTextLength = 0;\n return {};\n case 'message_start':\n if (event.message.role === 'assistant') {\n state.activeAssistantMessageId = event.message.id;\n state.lastTextLength = 0;\n }\n return {};\n case 'message_update':\n if (event.event.type !== 'text-delta' || event.id !== state.activeAssistantMessageId || !event.event.delta)\n return {};\n state.lastTextLength += event.event.delta.length;\n return { stdout: event.event.delta };\n case 'message_end':\n if (event.id !== state.activeAssistantMessageId) return {};\n state.activeAssistantMessageId = undefined;\n if (state.lastTextLength === 0) return {};\n state.lastTextLength = 0;\n return { stdout: '\\n' };\n case 'tool_start':\n return { stderr: `[tool] ${event.toolName}\\n` };\n case 'tool_end':\n return event.isError ? { stderr: `[tool error] ${truncate(String(event.result), 200)}\\n` } : {};\n case 'shell_output':\n return { stderr: event.output };\n case 'subagent_start':\n return {\n stderr: `[subagent:${event.forked ? 'forked:' : ''}${event.agentType}] ${truncate(event.task, 100)}\\n`,\n };\n case 'subagent_end':\n return event.isError ? { stderr: `[subagent error] ${truncate(event.result, 200)}\\n` } : {};\n case 'error':\n return { stderr: `[error] ${event.error.message}\\n` };\n default:\n return {};\n }\n}\n\n/** Convert an `Error` instance into a JSON-serializable plain object. */\nfunction serializeError(err: Error): { name: string; message: string; stack?: string } {\n return { name: err.name, message: err.message, stack: err.stack };\n}\n\n/**\n * JSONL (stream-json) formatter — returns a plain object to be `JSON.stringify`'d\n * as one line per event by the sink. `Error` instances are normalized so their\n * `name`/`message`/`stack` survive serialization (`JSON.stringify` turns a raw\n * `Error` into `{}`).\n */\nexport function formatJsonl(event: AgentControllerEvent): Record<string, unknown> {\n const out: Record<string, unknown> = { ...event };\n if ('error' in event && event.error instanceof Error) {\n out.error = serializeError(event.error);\n }\n return out;\n}\n\n/** Render the final result for `--output text`: assistant text, newline-terminated. */\nexport function renderTextResult(result: RunMCResult): string {\n return result.text.endsWith('\\n') ? result.text : result.text + '\\n';\n}\n\n/** Render the final result for `--output json`: one JSON object. */\nexport function renderJsonResult(result: RunMCResult): string {\n return (\n JSON.stringify({\n status: result.status,\n text: result.text,\n finishReason: result.finishReason,\n usage: result.usage,\n toolCalls: result.toolCalls,\n toolResults: result.toolResults,\n error: result.error,\n threadId: result.threadId,\n exitCode: result.exitCode,\n }) + '\\n'\n );\n}\n"],"mappings":";;AAUA,SAAgB,SAAS,GAAW,KAAqB;CACvD,OAAO,EAAE,SAAS,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,QAAQ;AACpD;AAQA,SAAgB,yBAA2C;CACzD,OAAO,EAAE,gBAAgB,EAAE;AAC7B;;;;;;;AAcA,SAAgB,YAAY,OAA6B,OAA0C;CACjG,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,MAAM,iBAAiB;GACvB,OAAO,CAAC;EACV,KAAK;GACH,IAAI,MAAM,QAAQ,SAAS,aAAa;IACtC,MAAM,2BAA2B,MAAM,QAAQ;IAC/C,MAAM,iBAAiB;GACzB;GACA,OAAO,CAAC;EACV,KAAK;GACH,IAAI,MAAM,MAAM,SAAS,gBAAgB,MAAM,OAAO,MAAM,4BAA4B,CAAC,MAAM,MAAM,OACnG,OAAO,CAAC;GACV,MAAM,kBAAkB,MAAM,MAAM,MAAM;GAC1C,OAAO,EAAE,QAAQ,MAAM,MAAM,MAAM;EACrC,KAAK;GACH,IAAI,MAAM,OAAO,MAAM,0BAA0B,OAAO,CAAC;GACzD,MAAM,2BAA2B,KAAA;GACjC,IAAI,MAAM,mBAAmB,GAAG,OAAO,CAAC;GACxC,MAAM,iBAAiB;GACvB,OAAO,EAAE,QAAQ,KAAK;EACxB,KAAK,cACH,OAAO,EAAE,QAAQ,UAAU,MAAM,SAAS,IAAI;EAChD,KAAK,YACH,OAAO,MAAM,UAAU,EAAE,QAAQ,gBAAgB,SAAS,OAAO,MAAM,MAAM,GAAG,GAAG,EAAE,IAAI,IAAI,CAAC;EAChG,KAAK,gBACH,OAAO,EAAE,QAAQ,MAAM,OAAO;EAChC,KAAK,kBACH,OAAO,EACL,QAAQ,aAAa,MAAM,SAAS,YAAY,KAAK,MAAM,UAAU,IAAI,SAAS,MAAM,MAAM,GAAG,EAAE,IACrG;EACF,KAAK,gBACH,OAAO,MAAM,UAAU,EAAE,QAAQ,oBAAoB,SAAS,MAAM,QAAQ,GAAG,EAAE,IAAI,IAAI,CAAC;EAC5F,KAAK,SACH,OAAO,EAAE,QAAQ,WAAW,MAAM,MAAM,QAAQ,IAAI;EACtD,SACE,OAAO,CAAC;CACZ;AACF;;AAGA,SAAS,eAAe,KAA+D;CACrF,OAAO;EAAE,MAAM,IAAI;EAAM,SAAS,IAAI;EAAS,OAAO,IAAI;CAAM;AAClE;;;;;;;AAQA,SAAgB,YAAY,OAAsD;CAChF,MAAM,MAA+B,EAAE,GAAG,MAAM;CAChD,IAAI,WAAW,SAAS,MAAM,iBAAiB,OAC7C,IAAI,QAAQ,eAAe,MAAM,KAAK;CAExC,OAAO;AACT;;AAGA,SAAgB,iBAAiB,QAA6B;CAC5D,OAAO,OAAO,KAAK,SAAS,IAAI,IAAI,OAAO,OAAO,OAAO,OAAO;AAClE;;AAGA,SAAgB,iBAAiB,QAA6B;CAC5D,OACE,KAAK,UAAU;EACb,QAAQ,OAAO;EACf,MAAM,OAAO;EACb,cAAc,OAAO;EACrB,OAAO,OAAO;EACd,WAAW,OAAO;EAClB,aAAa,OAAO;EACpB,OAAO,OAAO;EACd,UAAU,OAAO;EACjB,UAAU,OAAO;CACnB,CAAC,IAAI;AAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-mc.d.ts","sourceRoot":"","sources":["../../src/headless/run-mc.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,KAAK,EAAoB,YAAY,EAA4B,MAAM,YAAY,CAAC;AA0IlG;;;GAGG;AACH,wBAAgB,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,KAAK,CA8RlG"}
1
+ {"version":3,"file":"run-mc.d.ts","sourceRoot":"","sources":["../../src/headless/run-mc.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,KAAK,EAAoB,YAAY,EAA4B,MAAM,YAAY,CAAC;AA0IlG;;;GAGG;AACH,wBAAgB,KAAK,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,KAAK,CAoSlG"}
@@ -2,9 +2,6 @@ import { GoalManager } from "../goal-manager.js";
2
2
  import { createGoalReminderSignal } from "../goal-signal.js";
3
3
  import { autoApprovePolicy } from "./policy.js";
4
4
  //#region src/headless/run-mc.ts
5
- function extractAssistantText(message) {
6
- return message.content.parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
7
- }
8
5
  function exitCodeForStatus(status) {
9
6
  switch (status) {
10
7
  case "completed":
@@ -15,8 +12,14 @@ function exitCodeForStatus(status) {
15
12
  }
16
13
  function aggregate(event, acc) {
17
14
  switch (event.type) {
15
+ case "message_start":
16
+ if (event.message.role === "assistant") acc.activeAssistantMessageId = event.message.id;
17
+ break;
18
+ case "message_update":
19
+ if (event.event.type === "text-delta" && event.id === acc.activeAssistantMessageId) acc.text += event.event.delta;
20
+ break;
18
21
  case "message_end":
19
- if (event.message.role === "assistant") acc.text += extractAssistantText(event.message);
22
+ if (event.id === acc.activeAssistantMessageId) acc.activeAssistantMessageId = void 0;
20
23
  break;
21
24
  case "tool_start":
22
25
  acc.toolCalls.push({
@@ -129,6 +132,7 @@ function runMC(options) {
129
132
  let aborted = false;
130
133
  let maxTurnsExceeded = false;
131
134
  let assistantTurns = 0;
135
+ let activeAssistantMessageId;
132
136
  let settled = false;
133
137
  let unsubscribe;
134
138
  let resolveResult;
@@ -242,13 +246,15 @@ function runMC(options) {
242
246
  queue.push(event);
243
247
  return;
244
248
  }
249
+ if (event.type === "message_start" && event.message.role === "assistant") activeAssistantMessageId = event.message.id;
245
250
  aggregate(event, acc);
246
251
  queue.push(event);
247
252
  if (event.type === "error" && goal) {
248
253
  finish("error");
249
254
  return;
250
255
  }
251
- if (event.type === "message_end" && event.message.role === "assistant" && options.maxTurns !== void 0) {
256
+ if (event.type === "message_end" && event.id === activeAssistantMessageId && options.maxTurns !== void 0) {
257
+ activeAssistantMessageId = void 0;
252
258
  assistantTurns += 1;
253
259
  if (assistantTurns >= options.maxTurns && !maxTurnsExceeded) {
254
260
  maxTurnsExceeded = true;
@@ -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 type { AgentControllerEvent, MastraDBMessage, MastraMessagePart, Session } from '@mastra/core/agent-controller';\n\nimport { GoalManager } from '../goal-manager.js';\nimport { createGoalReminderSignal } from '../goal-signal.js';\n\nimport { autoApprovePolicy } from './policy.js';\nimport type { MCRun, ResolutionPolicy, RunMCOptions, RunMCResult, RunMCStatus } from './types.js';\n\nfunction extractAssistantText(message: MastraDBMessage): string {\n return message.content.parts\n .filter((p): p is MastraMessagePart & { text: string } => p.type === 'text' && typeof p.text === 'string')\n .map(p => p.text)\n .join('');\n}\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}\n\nfunction aggregate(event: AgentControllerEvent, acc: MutableResult): void {\n switch (event.type) {\n case 'message_end':\n if (event.message.role === 'assistant') {\n acc.text += extractAssistantText(event.message);\n }\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): Promise<{ threadId: string } | { error: string }> {\n const threads = await session.thread.list();\n\n const byId = threads.find(t => t.id === threadId);\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 settled = false;\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 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 // --- Config resolution (model / mode / thinking) ---\n try {\n if (options.model) {\n const available = await controller.listAvailableModels();\n const match = available.find(m => m.id === options.model);\n if (!match) return fail(`Unknown model: \"${options.model}\"`);\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n return fail(`Model \"${options.model}\" has no API key configured.${keyHint}`);\n }\n await session.model.switch({ modelId: options.model });\n } else if (options.mode) {\n const modelId = options.modeDefaults?.[options.mode];\n if (modelId) {\n const available = await controller.listAvailableModels();\n const match = available.find(m => m.id === modelId);\n if (!match) return fail(`Unknown model \"${modelId}\" configured for mode \"${options.mode}\"`);\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n return fail(`Model \"${modelId}\" (mode: ${options.mode}) has no API key configured.${keyHint}`);\n }\n await session.model.switch({ modelId });\n }\n // No configured model for mode → fall through to default (no failure).\n }\n\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 resolve run config: ${(err as Error).message}`);\n }\n\n // --- Subscribe ---\n unsubscribe = session.subscribe(event => {\n if (settled) return;\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 === 'tool_suspended') {\n let outcome: ReturnType<ResolutionPolicy['onSuspension']>;\n try {\n outcome = policy.onSuspension(event);\n } catch (err) {\n fail(`Resolution policy failed: ${(err as Error).message}`);\n return;\n }\n if ('abort' in outcome) {\n queue.push(event);\n abort();\n return;\n }\n void session.respondToToolSuspension({ toolCallId: event.toolCallId, resumeData: outcome.resumeData });\n queue.push(event);\n return;\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.message.role === 'assistant' && options.maxTurns !== 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 // 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 // --- 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 const thread = options.thread;\n if (thread?.id) {\n const resolved = await resolveThread(session, thread.id);\n if ('error' in resolved) return fail(resolved.error);\n await session.thread.switch({ threadId: resolved.threadId });\n } else if (thread?.continueLatest) {\n const threads = await session.thread.list();\n if (threads.length > 0) {\n const sorted = [...threads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\n await session.thread.switch({ threadId: sorted[0]!.id });\n }\n }\n } catch (err) {\n return fail(`Failed to select thread: ${(err as Error).message}`);\n }\n\n // --- Clone ---\n if (options.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 // --- 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 planStartedGoalId: undefined,\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 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":["#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 type { AgentControllerEvent, Session } from '@mastra/core/agent-controller';\n\nimport { GoalManager } from '../goal-manager.js';\nimport { createGoalReminderSignal } from '../goal-signal.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): Promise<{ threadId: string } | { error: string }> {\n const threads = await session.thread.list();\n\n const byId = threads.find(t => t.id === threadId);\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 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 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 // --- Config resolution (model / mode / thinking) ---\n try {\n if (options.model) {\n const available = await controller.listAvailableModels();\n const match = available.find(m => m.id === options.model);\n if (!match) return fail(`Unknown model: \"${options.model}\"`);\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n return fail(`Model \"${options.model}\" has no API key configured.${keyHint}`);\n }\n await session.model.switch({ modelId: options.model });\n } else if (options.mode) {\n const modelId = options.modeDefaults?.[options.mode];\n if (modelId) {\n const available = await controller.listAvailableModels();\n const match = available.find(m => m.id === modelId);\n if (!match) return fail(`Unknown model \"${modelId}\" configured for mode \"${options.mode}\"`);\n if (!match.hasApiKey) {\n const keyHint = match.apiKeyEnvVar ? ` Set ${match.apiKeyEnvVar} to use this model.` : '';\n return fail(`Model \"${modelId}\" (mode: ${options.mode}) has no API key configured.${keyHint}`);\n }\n await session.model.switch({ modelId });\n }\n // No configured model for mode → fall through to default (no failure).\n }\n\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 resolve run config: ${(err as Error).message}`);\n }\n\n // --- Subscribe ---\n unsubscribe = session.subscribe(event => {\n if (settled) return;\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 === 'tool_suspended') {\n let outcome: ReturnType<ResolutionPolicy['onSuspension']>;\n try {\n outcome = policy.onSuspension(event);\n } catch (err) {\n fail(`Resolution policy failed: ${(err as Error).message}`);\n return;\n }\n if ('abort' in outcome) {\n queue.push(event);\n abort();\n return;\n }\n void session.respondToToolSuspension({ toolCallId: event.toolCallId, resumeData: outcome.resumeData });\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 // 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 // --- 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 const thread = options.thread;\n if (thread?.id) {\n const resolved = await resolveThread(session, thread.id);\n if ('error' in resolved) return fail(resolved.error);\n await session.thread.switch({ threadId: resolved.threadId });\n } else if (thread?.continueLatest) {\n const threads = await session.thread.list();\n if (threads.length > 0) {\n const sorted = [...threads].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());\n await session.thread.switch({ threadId: sorted[0]!.id });\n }\n }\n } catch (err) {\n return fail(`Failed to select thread: ${(err as Error).message}`);\n }\n\n // --- Clone ---\n if (options.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 // --- 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 planStartedGoalId: undefined,\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 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":";;;;AAoBA,SAAS,kBAAkB,QAA6B;CACtD,QAAQ,QAAR;EACE,KAAK;EACL,KAAK,QACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAaA,SAAS,UAAU,OAA6B,KAA0B;CACxE,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,IAAI,MAAM,QAAQ,SAAS,aAAa,IAAI,2BAA2B,MAAM,QAAQ;GACrF;EACF,KAAK;GACH,IAAI,MAAM,MAAM,SAAS,gBAAgB,MAAM,OAAO,IAAI,0BACxD,IAAI,QAAQ,MAAM,MAAM;GAE1B;EACF,KAAK;GACH,IAAI,MAAM,OAAO,IAAI,0BAA0B,IAAI,2BAA2B,KAAA;GAC9E;EACF,KAAK;GACH,IAAI,UAAU,KAAK;IAAE,IAAI,MAAM;IAAY,MAAM,MAAM;IAAU,MAAM,MAAM;GAAK,CAAC;GACnF;EACF,KAAK,YAAY;GACf,MAAM,WAAW,IAAI,UAAU,MAAK,MAAK,EAAE,OAAO,MAAM,UAAU;GAClE,IAAI,YAAY,KAAK;IACnB,IAAI,MAAM;IACV,MAAM,UAAU,QAAQ;IACxB,QAAQ,MAAM;IACd,SAAS,MAAM;GACjB,CAAC;GACD;EACF;EACA,KAAK;GACH,IAAI,QAAQ;IACV,aAAa,MAAM,MAAM;IACzB,cAAc,MAAM,MAAM;IAC1B,aAAa,MAAM,MAAM;GAC3B;GACA;EACF,KAAK;GACH,IAAI,QAAQ;IACV,MAAM,MAAM,MAAM;IAClB,SAAS,MAAM,MAAM;IACrB,OAAO,MAAM,MAAM;GACrB;GACA;CACJ;AACF;;AAGA,eAAe,cACb,SACA,UACmD;CAGnD,MAAM,QAAO,MAFS,QAAQ,OAAO,KAAK,EAAA,CAErB,MAAK,MAAK,EAAE,OAAO,QAAQ;CAChD,IAAI,MAAM,OAAO,EAAE,UAAU,KAAK,GAAG;CAErC,OAAO,EAAE,OAAO,4BAA4B,SAAS,GAAG;AAC1D;;;;;;;;;;;AAYA,IAAM,aAAN,MAAoB;CAClB,UAAe,CAAC;CAChB,aAAoD,CAAC;CACrD,UAAU;CACV;CAEA,YAAY,YAAY,KAAQ;EAC9B,KAAKA,aAAa;CACpB;CAEA,KAAK,OAAgB;EACnB,IAAI,KAAKC,SAAS;EAClB,MAAM,UAAU,KAAKC,WAAW,MAAM;EACtC,IAAI,SAAS;GACX,QAAQ;IAAE;IAAO,MAAM;GAAM,CAAC;GAC9B;EACF;EACA,KAAKC,QAAQ,KAAK,KAAK;EACvB,IAAI,KAAKA,QAAQ,SAAS,KAAKH,YAE7B,KAAKG,QAAQ,MAAM;CAEvB;CAEA,QAAc;EACZ,IAAI,KAAKF,SAAS;EAClB,KAAKA,UAAU;EACf,KAAK,MAAM,WAAW,KAAKC,WAAW,OAAO,CAAC,GAC5C,QAAQ;GAAE,OAAO,KAAA;GAA2B,MAAM;EAAK,CAAC;CAE5D;CAEA,CAAC,OAAO,iBAAmC;EACzC,OAAO,EACL,YAAwC;GACtC,IAAI,KAAKC,QAAQ,SAAS,GACxB,OAAO,QAAQ,QAAQ;IAAE,OAAO,KAAKA,QAAQ,MAAM;IAAI,MAAM;GAAM,CAAC;GAEtE,IAAI,KAAKF,SACP,OAAO,QAAQ,QAAQ;IAAE,OAAO,KAAA;IAA2B,MAAM;GAAK,CAAC;GAEzE,OAAO,IAAI,SAA2B,YAAW,KAAKC,WAAW,KAAK,OAAO,CAAC;EAChF,EACF;CACF;AACF;;;;;AAMA,SAAgB,MAA8C,SAAsC;CAClG,MAAM,EAAE,YAAY,YAAY;CAChC,MAAM,OAAO,QAAQ;CACrB,MAAM,SAA2B,QAAQ,UAAU;CAEnD,MAAM,QAAQ,IAAI,WAAiC;CACnD,MAAM,MAAqB;EAAE,MAAM;EAAI,WAAW,CAAC;EAAG,aAAa,CAAC;CAAE;CAEtE,IAAI;CACJ,IAAI,WAAW;CACf,IAAI,UAAU;CACd,IAAI,mBAAmB;CACvB,IAAI,iBAAiB;CACrB,IAAI;CACJ,IAAI,UAAU;CACd,IAAI;CACJ,IAAI;CAEJ,MAAM,SAAS,IAAI,SAAqB,YAAW;EACjD,gBAAgB;CAClB,CAAC;CAED,SAAS,OAAO,QAAqB,OAA6B,CAAC,GAAS;EAC1E,IAAI,SAAS;EACb,UAAU;EACV,IAAI,WAAW,aAAa,SAAS;EACrC,cAAc;EACd,MAAM,MAAM;EACZ,cAAc;GACZ;GACA,MAAM,IAAI;GACV,cAAc,IAAI;GAClB,OAAO,IAAI;GACX,WAAW,IAAI;GACf,aAAa,IAAI;GACjB,UAAU,IAAI,YAAY,QAAQ,OAAO,MAAM,KAAK,KAAA;GACpD,OAAO,IAAI;GACX,UAAU,kBAAkB,MAAM;GAClC,GAAG;EACL,CAAC;CACH;CAEA,SAAS,KAAK,SAAiB,OAAO,SAAe;EACnD,IAAI,CAAC,IAAI,OAAO,IAAI,QAAQ;GAAE;GAAM;EAAQ;EAC5C,OAAO,OAAO;CAChB;CAEA,SAAS,QAAc;EACrB,IAAI,SAAS;EACb,UAAU;EACV,QAAQ,MAAM;CAChB;CAEA,IAAI,QAAQ,QACV,IAAI,QAAQ,OAAO,SAEjB,qBAAqB,MAAM,CAAC;MAE5B,QAAQ,OAAO,iBAAiB,eAAe,MAAM,GAAG,EAAE,MAAM,KAAK,CAAC;CAI1E,SAAS,aAAmB;EAC1B,IAAI,CAAC,QAAQ,aAAa,SAAS;EACnC,IAAI,WAAW,aAAa,SAAS;EACrC,YAAY,iBAAiB;GAC3B,WAAW;GACX,QAAQ,MAAM;GACd,IAAI,MAAM,OAAO,SAAS;EAC5B,GAAG,QAAQ,SAAS;CACtB;CAGA,CAAM,YAAY;EAChB,WAAW;EAGX,IAAI;GACF,IAAI,QAAQ,OAAO;IAEjB,MAAM,SAAQ,MADU,WAAW,oBAAoB,EAAA,CAC/B,MAAK,MAAK,EAAE,OAAO,QAAQ,KAAK;IACxD,IAAI,CAAC,OAAO,OAAO,KAAK,mBAAmB,QAAQ,MAAM,EAAE;IAC3D,IAAI,CAAC,MAAM,WAAW;KACpB,MAAM,UAAU,MAAM,eAAe,QAAQ,MAAM,aAAa,uBAAuB;KACvF,OAAO,KAAK,UAAU,QAAQ,MAAM,8BAA8B,SAAS;IAC7E;IACA,MAAM,QAAQ,MAAM,OAAO,EAAE,SAAS,QAAQ,MAAM,CAAC;GACvD,OAAO,IAAI,QAAQ,MAAM;IACvB,MAAM,UAAU,QAAQ,eAAe,QAAQ;IAC/C,IAAI,SAAS;KAEX,MAAM,SAAQ,MADU,WAAW,oBAAoB,EAAA,CAC/B,MAAK,MAAK,EAAE,OAAO,OAAO;KAClD,IAAI,CAAC,OAAO,OAAO,KAAK,kBAAkB,QAAQ,yBAAyB,QAAQ,KAAK,EAAE;KAC1F,IAAI,CAAC,MAAM,WAAW;MACpB,MAAM,UAAU,MAAM,eAAe,QAAQ,MAAM,aAAa,uBAAuB;MACvF,OAAO,KAAK,UAAU,QAAQ,WAAW,QAAQ,KAAK,8BAA8B,SAAS;KAC/F;KACA,MAAM,QAAQ,MAAM,OAAO,EAAE,QAAQ,CAAC;IACxC;GAEF;GAEA,IAAI,QAAQ,eACV,MAAM,QAAQ,MAAM,IAAI,EAAE,eAAe,QAAQ,cAAc,CAA+B;EAElG,SAAS,KAAK;GACZ,OAAO,KAAK,iCAAkC,IAAc,SAAS;EACvE;EAGA,cAAc,QAAQ,WAAU,UAAS;GACvC,IAAI,SAAS;GACb,IAAI,MAAM,WAAW;GAErB,IAAI,MAAM,SAAS,0BAA0B;IAC3C,IAAI;IACJ,IAAI;KACF,WAAW,OAAO,eAAe,KAAK;IACxC,SAAS,KAAK;KACZ,KAAK,6BAA8B,IAAc,SAAS;KAC1D;IACF;IACA,QAAQ,sBAAsB;KAC5B,UAAU,aAAa,YAAY,YAAY;KAC/C,YAAY,MAAM;IACpB,CAAC;IACD,MAAM,KAAK,KAAK;IAChB;GACF;GAEA,IAAI,MAAM,SAAS,kBAAkB;IACnC,IAAI;IACJ,IAAI;KACF,UAAU,OAAO,aAAa,KAAK;IACrC,SAAS,KAAK;KACZ,KAAK,6BAA8B,IAAc,SAAS;KAC1D;IACF;IACA,IAAI,WAAW,SAAS;KACtB,MAAM,KAAK,KAAK;KAChB,MAAM;KACN;IACF;IACA,QAAa,wBAAwB;KAAE,YAAY,MAAM;KAAY,YAAY,QAAQ;IAAW,CAAC;IACrG,MAAM,KAAK,KAAK;IAChB;GACF;GAEA,IAAI,MAAM,SAAS,mBAAmB,MAAM,QAAQ,SAAS,aAC3D,2BAA2B,MAAM,QAAQ;GAG3C,UAAU,OAAO,GAAG;GACpB,MAAM,KAAK,KAAK;GAEhB,IAAI,MAAM,SAAS,WAAW,MAAM;IAClC,OAAO,OAAO;IACd;GACF;GAIA,IAAI,MAAM,SAAS,iBAAiB,MAAM,OAAO,4BAA4B,QAAQ,aAAa,KAAA,GAAW;IAC3G,2BAA2B,KAAA;IAC3B,kBAAkB;IAClB,IAAI,kBAAkB,QAAQ,YAAY,CAAC,kBAAkB;KAC3D,mBAAmB;KACnB,MAAM;IACR;GACF;GAEA,IAAI,MAAM,SAAS,qBAAqB,QAAQ,CAAC,MAAM,QAAQ,SAAS;IACtE,IAAI,MAAM,QAAQ,WAAW,UAAU,MAAM,QAAQ,WAAW,UAC9D,OAAO,MAAM,QAAQ,QAAQ;KAC3B,WAAW,KAAK;KAChB,WAAW;KACX,QAAQ,MAAM,QAAQ,UAAU,MAAM,QAAQ,UAAU,EAAE,EAAE;KAC5D,YAAY,MAAM,QAAQ;KAC1B,SAAS,MAAM,QAAQ;IACzB,CAAC;IAEH;GACF;GAEA,IAAI,MAAM,SAAS,aAAa;IAC9B,IAAI,eAAe,MAAM;IAGzB,IAAI,MAAM;IACV,IAAI,UACF,OAAO,SAAS;SACX,IAAI,MAAM,WAAW,SAC1B,OAAO,OAAO;SACT,IAAI,oBAAoB,MAAM,WAAW,YAE9C,OAAO,WAAW;SACb,KAAK,MAAM,WAAW,aAAa,YAAY,CAAC,kBACrD,OAAO,SAAS;SAEhB,OAAO,WAAW;GAEtB;EACF,CAAC;EAGD,IAAI;GACF,IAAI,QAAQ,YACV,MAAM,WAAW,cAAc,SAAS,EAAE,YAAY,QAAQ,WAAW,CAAC;EAE9E,SAAS,KAAK;GACZ,OAAO,KAAK,8BAA+B,IAAc,SAAS;EACpE;EAGA,IAAI;GACF,MAAM,SAAS,QAAQ;GACvB,IAAI,QAAQ,IAAI;IACd,MAAM,WAAW,MAAM,cAAc,SAAS,OAAO,EAAE;IACvD,IAAI,WAAW,UAAU,OAAO,KAAK,SAAS,KAAK;IACnD,MAAM,QAAQ,OAAO,OAAO,EAAE,UAAU,SAAS,SAAS,CAAC;GAC7D,OAAO,IAAI,QAAQ,gBAAgB;IACjC,MAAM,UAAU,MAAM,QAAQ,OAAO,KAAK;IAC1C,IAAI,QAAQ,SAAS,GAAG;KACtB,MAAM,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,QAAQ,IAAI,EAAE,UAAU,QAAQ,CAAC;KACxF,MAAM,QAAQ,OAAO,OAAO,EAAE,UAAU,OAAO,EAAE,CAAE,GAAG,CAAC;IACzD;GACF;EACF,SAAS,KAAK;GACZ,OAAO,KAAK,4BAA6B,IAAc,SAAS;EAClE;EAGA,IAAI,QAAQ,QAAQ,OAClB,IAAI;GACF,MAAM,QAAQ,OAAO,MAAM;EAC7B,SAAS,KAAK;GACZ,OAAO,KAAK,2BAA4B,IAAc,SAAS;EACjE;EAIF,IAAI,QAAQ,OACV,IAAI;GACF,MAAM,QAAQ,OAAO,OAAO,EAAE,OAAO,QAAQ,MAAM,CAAC;EACtD,SAAS,KAAK;GACZ,OAAO,KAAK,+BAAgC,IAAc,SAAS;EACrE;EAIF,IAAI;GACF,IAAI,MAAM;IACR,MAAM,WAAW,QAAQ,OAAO,MAAM;IACtC,IAAI,CAAC,YAAY,CAAE,MAAM,QAAQ,OAAO,QAAQ,EAAE,SAAS,CAAC,GAC1D,MAAM,QAAQ,OAAO,OAAO;IAG9B,MAAM,cAAc,KAAK,eAAe,IAAI,YAAY;IACxD,MAAM,QAAQ;KACZ;KACA;KACA;KACA,kBAAkB;KAClB,mBAAmB,KAAA;IACrB;IACA,MAAM,YAAY,MAAM,YAAY,QAAQ,OAAO,KAAK,WAAW,KAAK,cAAc,KAAK,OAAO;IAClG,IAAI,CAAC,WAAW,OAAO,KAAK,qBAAqB;IAGjD,MAAM,YAAY,MADJ,WAAW,gBAAgB,OACb,CAAC,CAAC,aAAa,EAAE,UAAU,QAAQ,OAAO,MAAM,EAAG,CAAC;IAChF,IAAI,CAAC,aAAa,UAAU,OAAO,UAAU,IAC3C,OAAO,KAAK,8DAA8D;IAG5E,MAAM,YAAY,aAAa,KAAK;IACpC,IAAI,CAAC,SAAS,MAAM,QAAQ,WAAW,yBAAyB,SAAS,CAAC,CAAC,CAAC;GAC9E,OAAO;IACL,IAAI,CAAC,QAAQ,QAAQ,OAAO,KAAK,+BAA+B;IAChE,MAAM,QAAQ,YAAY,EAAE,SAAS,QAAQ,OAAO,CAAC;GACvD;EACF,SAAS,KAAK;GACZ,OAAO,KAAK,wBAAyB,IAAc,SAAS;EAC9D;CACF,EAAA,CAAG;CAEH,OAAO;EACL;EACA;EACA,CAAC,OAAO,iBAAsD;GAC5D,OAAO,MAAM,OAAO,cAAc,CAAC;EACrC;CACF;AACF"}
package/dist/index.d.ts CHANGED
@@ -147,6 +147,8 @@ export declare function createMastraCodeAgentController(config?: MastraCodeConfi
147
147
  builtinOmPacks: import("./onboarding/packs.js").OMPack[];
148
148
  effectiveDefaults: Record<string, string>;
149
149
  githubSignals: GithubSignals | undefined;
150
+ backgroundToolsEnabled: boolean;
151
+ backgroundCompletionEvents: import("./agents/background-completion-events.js").BackgroundCompletionEvents | undefined;
150
152
  sessionId: string;
151
153
  ownerId: string;
152
154
  projectPath: string;
@@ -233,6 +235,8 @@ export declare function bootLocalAgentController(config?: MastraCodeConfig): Pro
233
235
  builtinOmPacks: import("./onboarding/packs.js").OMPack[];
234
236
  effectiveDefaults: Record<string, string>;
235
237
  githubSignals: GithubSignals | undefined;
238
+ backgroundToolsEnabled: boolean;
239
+ backgroundCompletionEvents: import("./agents/background-completion-events.js").BackgroundCompletionEvents | undefined;
236
240
  sessionId: string;
237
241
  ownerId: string;
238
242
  projectPath: string;