@mastra/code-sdk 1.8.1-alpha.2 → 1.8.1-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -0
- package/dist/agents/tools.d.ts.map +1 -1
- package/dist/agents/tools.js +6 -24
- package/dist/agents/tools.js.map +1 -1
- package/dist/goal-manager.d.ts +1 -1
- package/dist/goal-manager.d.ts.map +1 -1
- package/dist/goal-manager.js +12 -13
- package/dist/goal-manager.js.map +1 -1
- package/dist/hooks/config.d.ts.map +1 -1
- package/dist/hooks/config.js +8 -1
- package/dist/hooks/config.js.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -24,6 +24,40 @@ const { mastra, controller } = await mountAgentControllerOnMastra({
|
|
|
24
24
|
});
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
### Plugin background execution
|
|
28
|
+
|
|
29
|
+
With the experimental `backgroundTools.enabled` setting on, plugin tools are eligible for native background execution only when they declare their own configuration:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
background: {
|
|
33
|
+
enabled: true,
|
|
34
|
+
defaultDisposition: 'foreground',
|
|
35
|
+
maxRetries: 0,
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The SDK doesn't infer support from tool names or serialize plugin calls. When the setting is off, it disables plugin-declared background execution without modifying the original tool.
|
|
40
|
+
|
|
41
|
+
Tools that already await their work need no separate execution path. For a tool that returns an acknowledgement while continuing independently, native execution exposes `context.background`. Adopt the existing operation before returning:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
const operation = startResearch(input, context.abortSignal);
|
|
45
|
+
if (context.background) {
|
|
46
|
+
context.background.adopt({
|
|
47
|
+
completion: operation.finished,
|
|
48
|
+
cancel: reason => operation.cancel(reason),
|
|
49
|
+
});
|
|
50
|
+
return { answer: 'Research started' };
|
|
51
|
+
}
|
|
52
|
+
return await operation.finished;
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Here, `startResearch` represents the plugin's own operation API. Its `finished` promise must resolve with the terminal tool result only after work and cleanup finish, or reject on failure. The native task tracks that promise instead of the acknowledgement. Adopt at most one operation, before `execute()` returns. Forward cancellation through the supplied signal or the operation's `cancel` callback. Without adoption, `execute()` must itself await the complete operation.
|
|
56
|
+
|
|
57
|
+
The handle stays in memory and isn't restart-safe. The plugin still owns its conversation-level queue and answer signals. Adoption doesn't suppress host completion notifications; keep progress rendering active until the adopted operation finishes.
|
|
58
|
+
|
|
59
|
+
`defaultDisposition: 'foreground'` preserves normal calls unless the caller explicitly requests `_background.disposition: 'deferred'` or `'awaited'`. Plugins without a declaration remain usable in the foreground, including older versions of `mastra_expert`.
|
|
60
|
+
|
|
27
61
|
## Documentation
|
|
28
62
|
|
|
29
63
|
- [@mastra/code-sdk documentation](https://mastra.ai/reference/code-sdk/mount-agent-controller)
|
|
@@ -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;
|
|
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;AAOxB,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"}
|
package/dist/agents/tools.js
CHANGED
|
@@ -12,32 +12,14 @@ 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
|
-
|
|
16
|
-
|
|
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;
|
|
15
|
+
function configurePluginTool(tool, backgroundToolsEnabled) {
|
|
16
|
+
if (backgroundToolsEnabled || !tool.background) return tool;
|
|
34
17
|
return {
|
|
35
18
|
...tool,
|
|
36
19
|
background: {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
execute: (...args) => executeAlexandriaSerially(tool, args)
|
|
20
|
+
...tool.background,
|
|
21
|
+
enabled: false
|
|
22
|
+
}
|
|
41
23
|
};
|
|
42
24
|
}
|
|
43
25
|
var LazyNotificationsStorage = class extends NotificationsStorage {
|
|
@@ -125,7 +107,7 @@ function createDynamicTools(mcpManager, extraTools, disabledTools, storage, plug
|
|
|
125
107
|
for (const [name, tool] of Object.entries(resolvedExtra)) if (tool && !(name in tools)) tools[name] = tool;
|
|
126
108
|
}
|
|
127
109
|
if (pluginTools) {
|
|
128
|
-
for (const [name, tool] of Object.entries(pluginTools)) if (!(name in tools)) tools[name] = configurePluginTool(
|
|
110
|
+
for (const [name, tool] of Object.entries(pluginTools)) if (!(name in tools)) tools[name] = configurePluginTool(tool, backgroundToolsEnabled);
|
|
129
111
|
}
|
|
130
112
|
if (disabledTools?.length) for (const toolName of disabledTools) delete tools[toolName];
|
|
131
113
|
const permissionRules = state?.permissionRules;
|
package/dist/agents/tools.js.map
CHANGED
|
@@ -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\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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"}
|
|
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\nfunction configurePluginTool(tool: ToolLike, backgroundToolsEnabled: boolean): ToolLike {\n if (backgroundToolsEnabled || !tool.background) return tool;\n return { ...tool, background: { ...tool.background, enabled: false } };\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(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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"}
|
package/dist/goal-manager.d.ts
CHANGED
|
@@ -64,7 +64,7 @@ export declare class GoalManager {
|
|
|
64
64
|
* Load the objective from ThreadState (called on thread switch). Falls back to
|
|
65
65
|
* the legacy thread-metadata goal for threads created before this migration.
|
|
66
66
|
*/
|
|
67
|
-
loadFromThread(state: GoalManagerState): Promise<void>;
|
|
67
|
+
loadFromThread(state: GoalManagerState, isCurrent?: () => boolean): Promise<void>;
|
|
68
68
|
/**
|
|
69
69
|
* Legacy entry point retained for thread-switch call sites that only have the
|
|
70
70
|
* thread metadata available. Hydrates from a previously-persisted GoalState.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goal-manager.d.ts","sourceRoot":"","sources":["../src/goal-manager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAI9E,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChG,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAWpC,qBAAa,WAAW;IACtB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,6BAA6B,CAAS;IAM9C,OAAO,IAAI,SAAS,GAAG,IAAI;IAuB3B,QAAQ,IAAI,OAAO;IAInB,yBAAyB,IAAI,IAAI;IAIjC,gCAAgC,IAAI,OAAO;IAU3C;;;;OAIG;IACG,OAAO,CACX,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAA0B,GACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA0B5B;;;OAGG;IACG,mBAAmB,CACvB,KAAK,EAAE,gBAAgB,EACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAsB5B,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAOxC,MAAM,IAAI,SAAS,GAAG,IAAI;IAO1B,QAAQ,IAAI,IAAI;IAMhB,KAAK,IAAI,IAAI;IAOb;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI;IAUnF;;;;OAIG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgD1D;;;OAGG;IACG,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"goal-manager.d.ts","sourceRoot":"","sources":["../src/goal-manager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAI9E,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChG,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAWpC,qBAAa,WAAW;IACtB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,6BAA6B,CAAS;IAM9C,OAAO,IAAI,SAAS,GAAG,IAAI;IAuB3B,QAAQ,IAAI,OAAO;IAInB,yBAAyB,IAAI,IAAI;IAIjC,gCAAgC,IAAI,OAAO;IAU3C;;;;OAIG;IACG,OAAO,CACX,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAA0B,GACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA0B5B;;;OAGG;IACG,mBAAmB,CACvB,KAAK,EAAE,gBAAgB,EACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAsB5B,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAOxC,MAAM,IAAI,SAAS,GAAG,IAAI;IAO1B,QAAQ,IAAI,IAAI;IAMhB,KAAK,IAAI,IAAI;IAOb;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI;IAUnF;;;;OAIG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgD1D;;;OAGG;IACG,cAAc,CAAC,KAAK,EAAE,gBAAgB,EAAE,SAAS,GAAE,MAAM,OAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBnG;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI;IA0B3E,OAAO,CAAC,QAAQ;IAQhB,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,WAAW;CAmBpB"}
|
package/dist/goal-manager.js
CHANGED
|
@@ -197,24 +197,23 @@ var GoalManager = class {
|
|
|
197
197
|
* Load the objective from ThreadState (called on thread switch). Falls back to
|
|
198
198
|
* the legacy thread-metadata goal for threads created before this migration.
|
|
199
199
|
*/
|
|
200
|
-
async loadFromThread(state) {
|
|
201
|
-
this.persistGoalOnNextThreadCreate = false;
|
|
200
|
+
async loadFromThread(state, isCurrent = () => true) {
|
|
202
201
|
const threadId = state.session.thread.getId();
|
|
203
202
|
const agent = this.getAgent(state);
|
|
204
|
-
|
|
205
|
-
this.agentId = agent?.id;
|
|
203
|
+
let nextRecord = null;
|
|
206
204
|
if (agent && threadId) try {
|
|
207
205
|
const record = await agent.getObjective({ threadId });
|
|
208
|
-
if (record) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
};
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
206
|
+
if (record) nextRecord = {
|
|
207
|
+
...record,
|
|
208
|
+
id: record.id ?? randomUUID(),
|
|
209
|
+
activeDurationMs: normalizeActiveDurationMs(record.activeDurationMs)
|
|
210
|
+
};
|
|
216
211
|
} catch {}
|
|
217
|
-
|
|
212
|
+
if (!isCurrent()) return;
|
|
213
|
+
this.persistGoalOnNextThreadCreate = false;
|
|
214
|
+
this.threadId = threadId ?? void 0;
|
|
215
|
+
this.agentId = agent?.id;
|
|
216
|
+
this.record = nextRecord;
|
|
218
217
|
}
|
|
219
218
|
/**
|
|
220
219
|
* Legacy entry point retained for thread-switch call sites that only have the
|
package/dist/goal-manager.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goal-manager.js","names":[],"sources":["../src/goal-manager.ts"],"sourcesContent":["/**\n * GoalManager — persistent cross-turn goals, backed by the Agent's native goal\n * mechanism.\n *\n * The objective lives in the durable `threadState` `'goal'` slot (via\n * `agent.setObjective`/`getObjective`/`clearObjective`/`updateObjectiveOptions`)\n * and is judged in-loop by the core goal step. This manager is a thin adapter:\n * it keeps a synchronous in-memory view of the current objective for the TUI\n * (status line, modal, keyboard shortcuts) and delegates persistence to the\n * agent. There is no standalone judge agent and no between-turn re-invocation —\n * the core goal step drives continuation and surfaces progress via `goal` stream\n * chunks.\n */\nimport { randomUUID } from 'node:crypto';\nimport { getGoalActivityDurationMs } from '@mastra/core/agent';\nimport type { Agent } from '@mastra/core/agent';\nimport type { AgentController, Session } from '@mastra/core/agent-controller';\nimport type { GoalObjectiveRecord } from '@mastra/core/storage';\nimport { loadSettings } from './onboarding/settings.js';\n\nexport interface GoalManagerState<TState extends Record<string, unknown> = Record<string, unknown>> {\n controller: AgentController<TState>;\n session: Session<TState>;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type GoalStatus = 'active' | 'paused' | 'done';\n\n/**\n * TUI-facing view of a goal. Derived from the durable {@link GoalObjectiveRecord}\n * plus the effective judge/max-runs settings, with display-only timer fields and\n * a stable `id` used to match plan-started goals.\n */\nexport interface GoalState {\n id: string;\n objective: string;\n status: GoalStatus;\n turnsUsed: number;\n maxTurns: number;\n judgeModelId: string;\n startedAt: string;\n activeStartedAt?: string;\n activeDurationMs?: number;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const DEFAULT_MAX_TURNS = 50;\nconst THREAD_GOAL_KEY = 'goal';\n\nfunction normalizeActiveDurationMs(value: number | undefined): number {\n return value !== undefined && Number.isFinite(value) && value >= 0 ? value : 0;\n}\n\n// =============================================================================\n// GoalManager\n// =============================================================================\n\nexport class GoalManager {\n /** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */\n private record: (GoalObjectiveRecord & { id: string }) | null = null;\n private threadId: string | undefined;\n private agentId: string | undefined;\n private persistGoalOnNextThreadCreate = false;\n\n // ---------------------------------------------------------------------------\n // Synchronous TUI surface\n // ---------------------------------------------------------------------------\n\n getGoal(): GoalState | null {\n if (!this.record) return null;\n const { judgeModelId, maxTurns } = this.effectiveSettings(this.record);\n return {\n id: this.record.id,\n objective: this.record.objective,\n status: this.record.status,\n turnsUsed: this.record.runsUsed,\n maxTurns,\n judgeModelId,\n startedAt: new Date(this.record.startedAt).toISOString(),\n activeDurationMs:\n this.agentId && this.threadId\n ? getGoalActivityDurationMs({\n agentId: this.agentId,\n threadId: this.threadId,\n objectiveId: this.record.id,\n activeDurationMs: this.record.activeDurationMs,\n })\n : normalizeActiveDurationMs(this.record.activeDurationMs),\n };\n }\n\n isActive(): boolean {\n return this.record?.status === 'active';\n }\n\n persistOnNextThreadCreate(): void {\n this.persistGoalOnNextThreadCreate = true;\n }\n\n consumePersistOnNextThreadCreate(): boolean {\n if (!this.persistGoalOnNextThreadCreate) return false;\n this.persistGoalOnNextThreadCreate = false;\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Objective lifecycle (ThreadState-backed via the agent)\n // ---------------------------------------------------------------------------\n\n /**\n * Set a new objective. Persists to ThreadState via `agent.setObjective` and\n * updates the in-memory view. Only the provided settings are persisted into\n * the record; unset ones fall back to the agent's `goal` config at read time.\n */\n async setGoal(\n state: GoalManagerState,\n objective: string,\n judgeModelId: string,\n maxTurns: number = DEFAULT_MAX_TURNS,\n ): Promise<GoalState | null> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n const now = Date.now();\n const id = randomUUID();\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n\n if (agent && threadId) {\n const persisted = await agent.setObjective(objective, {\n id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n this.record = persisted\n ? { ...persisted, id: persisted.id ?? id }\n : this.localRecord(objective, judgeModelId, maxTurns, now, id);\n } else {\n this.record = this.localRecord(objective, judgeModelId, maxTurns, now, id);\n }\n\n return this.getGoal();\n }\n\n /**\n * Update the judge model / max-runs defaults. Persists into the active record\n * (so the override is remembered in thread state) when a goal is set.\n */\n async updateJudgeDefaults(\n state: GoalManagerState,\n judgeModelId: string,\n maxTurns: number,\n ): Promise<GoalState | null> {\n if (!this.record) return null;\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n if (agent && threadId) {\n const updated = await agent.updateObjectiveOptions({\n threadId,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n if (updated) this.record = { ...updated, id: this.record.id };\n } else {\n this.record = {\n ...this.record,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n updatedAt: Date.now(),\n };\n }\n return this.getGoal();\n }\n\n pause(reason?: string): GoalState | null {\n if (this.record && this.record.status === 'active') {\n this.record = { ...this.record, status: 'paused', pausedReason: reason, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n resume(): GoalState | null {\n if (this.record && this.record.status === 'paused') {\n this.record = { ...this.record, status: 'active', pausedReason: undefined, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n markDone(): void {\n if (this.record) {\n this.record = { ...this.record, status: 'done', updatedAt: Date.now() };\n }\n }\n\n clear(): void {\n this.record = null;\n this.threadId = undefined;\n this.agentId = undefined;\n this.persistGoalOnNextThreadCreate = false;\n }\n\n /**\n * Sync the latest objective record from ThreadState into the in-memory view.\n * Called from the `goal` stream-chunk handler after each evaluation.\n */\n applyEvaluation(update: { runsUsed: number; status: GoalStatus }): GoalState | null {\n if (!this.record) return null;\n this.record = { ...this.record, runsUsed: update.runsUsed, status: update.status, updatedAt: Date.now() };\n return this.getGoal();\n }\n\n // ---------------------------------------------------------------------------\n // Persistence\n // ---------------------------------------------------------------------------\n\n /**\n * Persist the active objective to ThreadState via the agent. The objective\n * record is the source of truth; the legacy thread-metadata key is cleared so\n * stale state from older sessions does not resurface.\n */\n async saveToThread(state: GoalManagerState): Promise<void> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n try {\n if (agent && threadId) {\n if (this.record) {\n // Push the current status/options into the existing record. If no\n // record is persisted yet (e.g. first save), create one.\n const updated = await agent.updateObjectiveOptions({\n threadId,\n status: this.record.status,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (!updated) {\n // No persisted record yet: create one. `setObjective` always writes\n // `status: 'active'`, so re-apply the in-memory status afterwards if\n // the local goal was already paused/done — otherwise the resumed\n // thread state would no longer match the in-memory state.\n const desiredStatus = this.record.status;\n await agent.setObjective(this.record.objective, {\n id: this.record.id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n activeDurationMs: normalizeActiveDurationMs(this.record.activeDurationMs),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (desiredStatus !== 'active') {\n await agent.updateObjectiveOptions({\n threadId,\n status: desiredStatus,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n });\n }\n }\n } else {\n await agent.clearObjective({ threadId });\n }\n }\n // Clear any legacy thread-metadata goal so it can't shadow the record.\n await state.session.thread.setSetting({ key: THREAD_GOAL_KEY, value: undefined });\n } catch {\n // Persistence is not critical.\n }\n }\n\n /**\n * Load the objective from ThreadState (called on thread switch). Falls back to\n * the legacy thread-metadata goal for threads created before this migration.\n */\n async loadFromThread(state: GoalManagerState): Promise<void> {\n this.persistGoalOnNextThreadCreate = false;\n\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n if (agent && threadId) {\n try {\n const record = await agent.getObjective({ threadId });\n if (record) {\n this.record = {\n ...record,\n id: record.id ?? randomUUID(),\n activeDurationMs: normalizeActiveDurationMs(record.activeDurationMs),\n };\n return;\n }\n } catch {\n // fall through to legacy metadata\n }\n }\n this.record = null;\n }\n\n /**\n * Legacy entry point retained for thread-switch call sites that only have the\n * thread metadata available. Hydrates from a previously-persisted GoalState.\n */\n loadFromThreadMetadata(metadata: Record<string, unknown> | undefined): void {\n const saved = metadata?.[THREAD_GOAL_KEY] as Partial<GoalState> | undefined;\n this.persistGoalOnNextThreadCreate = false;\n this.threadId = undefined;\n this.agentId = undefined;\n if (saved && saved.objective && saved.status) {\n this.record = {\n objective: saved.objective,\n status: saved.status,\n runsUsed: saved.turnsUsed ?? 0,\n activeDurationMs: normalizeActiveDurationMs(saved.activeDurationMs),\n maxRuns: saved.maxTurns ?? DEFAULT_MAX_TURNS,\n judgeModelId: saved.judgeModelId ?? '',\n startedAt: saved.startedAt ? Date.parse(saved.startedAt) || Date.now() : Date.now(),\n updatedAt: Date.now(),\n id: saved.id ?? randomUUID(),\n };\n } else {\n this.record = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private getAgent(state: GoalManagerState): Agent | undefined {\n try {\n return state.controller.getCurrentAgent(state.session);\n } catch {\n return undefined;\n }\n }\n\n /** Resolve effective judge model + max runs (record value → settings default). */\n private effectiveSettings(record: GoalObjectiveRecord): { judgeModelId: string; maxTurns: number } {\n const settings = loadSettings();\n return {\n judgeModelId: record.judgeModelId ?? settings.models.goalJudgeModel ?? '',\n maxTurns: record.maxRuns ?? settings.models.goalMaxTurns ?? DEFAULT_MAX_TURNS,\n };\n }\n\n private localRecord(\n objective: string,\n judgeModelId: string,\n maxTurns: number,\n now: number,\n id: string,\n ): GoalObjectiveRecord & { id: string } {\n return {\n objective,\n status: 'active',\n runsUsed: 0,\n activeDurationMs: 0,\n maxRuns: maxTurns,\n ...(judgeModelId ? { judgeModelId } : {}),\n startedAt: now,\n updatedAt: now,\n id,\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"goal-manager.js","names":[],"sources":["../src/goal-manager.ts"],"sourcesContent":["/**\n * GoalManager — persistent cross-turn goals, backed by the Agent's native goal\n * mechanism.\n *\n * The objective lives in the durable `threadState` `'goal'` slot (via\n * `agent.setObjective`/`getObjective`/`clearObjective`/`updateObjectiveOptions`)\n * and is judged in-loop by the core goal step. This manager is a thin adapter:\n * it keeps a synchronous in-memory view of the current objective for the TUI\n * (status line, modal, keyboard shortcuts) and delegates persistence to the\n * agent. There is no standalone judge agent and no between-turn re-invocation —\n * the core goal step drives continuation and surfaces progress via `goal` stream\n * chunks.\n */\nimport { randomUUID } from 'node:crypto';\nimport { getGoalActivityDurationMs } from '@mastra/core/agent';\nimport type { Agent } from '@mastra/core/agent';\nimport type { AgentController, Session } from '@mastra/core/agent-controller';\nimport type { GoalObjectiveRecord } from '@mastra/core/storage';\nimport { loadSettings } from './onboarding/settings.js';\n\nexport interface GoalManagerState<TState extends Record<string, unknown> = Record<string, unknown>> {\n controller: AgentController<TState>;\n session: Session<TState>;\n}\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type GoalStatus = 'active' | 'paused' | 'done';\n\n/**\n * TUI-facing view of a goal. Derived from the durable {@link GoalObjectiveRecord}\n * plus the effective judge/max-runs settings, with display-only timer fields and\n * a stable `id` used to match plan-started goals.\n */\nexport interface GoalState {\n id: string;\n objective: string;\n status: GoalStatus;\n turnsUsed: number;\n maxTurns: number;\n judgeModelId: string;\n startedAt: string;\n activeStartedAt?: string;\n activeDurationMs?: number;\n}\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const DEFAULT_MAX_TURNS = 50;\nconst THREAD_GOAL_KEY = 'goal';\n\nfunction normalizeActiveDurationMs(value: number | undefined): number {\n return value !== undefined && Number.isFinite(value) && value >= 0 ? value : 0;\n}\n\n// =============================================================================\n// GoalManager\n// =============================================================================\n\nexport class GoalManager {\n /** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */\n private record: (GoalObjectiveRecord & { id: string }) | null = null;\n private threadId: string | undefined;\n private agentId: string | undefined;\n private persistGoalOnNextThreadCreate = false;\n\n // ---------------------------------------------------------------------------\n // Synchronous TUI surface\n // ---------------------------------------------------------------------------\n\n getGoal(): GoalState | null {\n if (!this.record) return null;\n const { judgeModelId, maxTurns } = this.effectiveSettings(this.record);\n return {\n id: this.record.id,\n objective: this.record.objective,\n status: this.record.status,\n turnsUsed: this.record.runsUsed,\n maxTurns,\n judgeModelId,\n startedAt: new Date(this.record.startedAt).toISOString(),\n activeDurationMs:\n this.agentId && this.threadId\n ? getGoalActivityDurationMs({\n agentId: this.agentId,\n threadId: this.threadId,\n objectiveId: this.record.id,\n activeDurationMs: this.record.activeDurationMs,\n })\n : normalizeActiveDurationMs(this.record.activeDurationMs),\n };\n }\n\n isActive(): boolean {\n return this.record?.status === 'active';\n }\n\n persistOnNextThreadCreate(): void {\n this.persistGoalOnNextThreadCreate = true;\n }\n\n consumePersistOnNextThreadCreate(): boolean {\n if (!this.persistGoalOnNextThreadCreate) return false;\n this.persistGoalOnNextThreadCreate = false;\n return true;\n }\n\n // ---------------------------------------------------------------------------\n // Objective lifecycle (ThreadState-backed via the agent)\n // ---------------------------------------------------------------------------\n\n /**\n * Set a new objective. Persists to ThreadState via `agent.setObjective` and\n * updates the in-memory view. Only the provided settings are persisted into\n * the record; unset ones fall back to the agent's `goal` config at read time.\n */\n async setGoal(\n state: GoalManagerState,\n objective: string,\n judgeModelId: string,\n maxTurns: number = DEFAULT_MAX_TURNS,\n ): Promise<GoalState | null> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n const now = Date.now();\n const id = randomUUID();\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n\n if (agent && threadId) {\n const persisted = await agent.setObjective(objective, {\n id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n this.record = persisted\n ? { ...persisted, id: persisted.id ?? id }\n : this.localRecord(objective, judgeModelId, maxTurns, now, id);\n } else {\n this.record = this.localRecord(objective, judgeModelId, maxTurns, now, id);\n }\n\n return this.getGoal();\n }\n\n /**\n * Update the judge model / max-runs defaults. Persists into the active record\n * (so the override is remembered in thread state) when a goal is set.\n */\n async updateJudgeDefaults(\n state: GoalManagerState,\n judgeModelId: string,\n maxTurns: number,\n ): Promise<GoalState | null> {\n if (!this.record) return null;\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n if (agent && threadId) {\n const updated = await agent.updateObjectiveOptions({\n threadId,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n });\n if (updated) this.record = { ...updated, id: this.record.id };\n } else {\n this.record = {\n ...this.record,\n ...(judgeModelId ? { judgeModelId } : {}),\n maxRuns: maxTurns,\n updatedAt: Date.now(),\n };\n }\n return this.getGoal();\n }\n\n pause(reason?: string): GoalState | null {\n if (this.record && this.record.status === 'active') {\n this.record = { ...this.record, status: 'paused', pausedReason: reason, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n resume(): GoalState | null {\n if (this.record && this.record.status === 'paused') {\n this.record = { ...this.record, status: 'active', pausedReason: undefined, updatedAt: Date.now() };\n }\n return this.getGoal();\n }\n\n markDone(): void {\n if (this.record) {\n this.record = { ...this.record, status: 'done', updatedAt: Date.now() };\n }\n }\n\n clear(): void {\n this.record = null;\n this.threadId = undefined;\n this.agentId = undefined;\n this.persistGoalOnNextThreadCreate = false;\n }\n\n /**\n * Sync the latest objective record from ThreadState into the in-memory view.\n * Called from the `goal` stream-chunk handler after each evaluation.\n */\n applyEvaluation(update: { runsUsed: number; status: GoalStatus }): GoalState | null {\n if (!this.record) return null;\n this.record = { ...this.record, runsUsed: update.runsUsed, status: update.status, updatedAt: Date.now() };\n return this.getGoal();\n }\n\n // ---------------------------------------------------------------------------\n // Persistence\n // ---------------------------------------------------------------------------\n\n /**\n * Persist the active objective to ThreadState via the agent. The objective\n * record is the source of truth; the legacy thread-metadata key is cleared so\n * stale state from older sessions does not resurface.\n */\n async saveToThread(state: GoalManagerState): Promise<void> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n try {\n if (agent && threadId) {\n if (this.record) {\n // Push the current status/options into the existing record. If no\n // record is persisted yet (e.g. first save), create one.\n const updated = await agent.updateObjectiveOptions({\n threadId,\n status: this.record.status,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (!updated) {\n // No persisted record yet: create one. `setObjective` always writes\n // `status: 'active'`, so re-apply the in-memory status afterwards if\n // the local goal was already paused/done — otherwise the resumed\n // thread state would no longer match the in-memory state.\n const desiredStatus = this.record.status;\n await agent.setObjective(this.record.objective, {\n id: this.record.id,\n threadId,\n resourceId: state.session.identity.getResourceId(),\n activeDurationMs: normalizeActiveDurationMs(this.record.activeDurationMs),\n ...(this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {}),\n ...(this.record.maxRuns !== undefined ? { maxRuns: this.record.maxRuns } : {}),\n });\n if (desiredStatus !== 'active') {\n await agent.updateObjectiveOptions({\n threadId,\n status: desiredStatus,\n ...(this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}),\n });\n }\n }\n } else {\n await agent.clearObjective({ threadId });\n }\n }\n // Clear any legacy thread-metadata goal so it can't shadow the record.\n await state.session.thread.setSetting({ key: THREAD_GOAL_KEY, value: undefined });\n } catch {\n // Persistence is not critical.\n }\n }\n\n /**\n * Load the objective from ThreadState (called on thread switch). Falls back to\n * the legacy thread-metadata goal for threads created before this migration.\n */\n async loadFromThread(state: GoalManagerState, isCurrent: () => boolean = () => true): Promise<void> {\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n let nextRecord: typeof this.record = null;\n if (agent && threadId) {\n try {\n const record = await agent.getObjective({ threadId });\n if (record) {\n nextRecord = {\n ...record,\n id: record.id ?? randomUUID(),\n activeDurationMs: normalizeActiveDurationMs(record.activeDurationMs),\n };\n }\n } catch {\n // fall through to legacy metadata\n }\n }\n if (!isCurrent()) return;\n this.persistGoalOnNextThreadCreate = false;\n this.threadId = threadId ?? undefined;\n this.agentId = agent?.id;\n this.record = nextRecord;\n }\n\n /**\n * Legacy entry point retained for thread-switch call sites that only have the\n * thread metadata available. Hydrates from a previously-persisted GoalState.\n */\n loadFromThreadMetadata(metadata: Record<string, unknown> | undefined): void {\n const saved = metadata?.[THREAD_GOAL_KEY] as Partial<GoalState> | undefined;\n this.persistGoalOnNextThreadCreate = false;\n this.threadId = undefined;\n this.agentId = undefined;\n if (saved && saved.objective && saved.status) {\n this.record = {\n objective: saved.objective,\n status: saved.status,\n runsUsed: saved.turnsUsed ?? 0,\n activeDurationMs: normalizeActiveDurationMs(saved.activeDurationMs),\n maxRuns: saved.maxTurns ?? DEFAULT_MAX_TURNS,\n judgeModelId: saved.judgeModelId ?? '',\n startedAt: saved.startedAt ? Date.parse(saved.startedAt) || Date.now() : Date.now(),\n updatedAt: Date.now(),\n id: saved.id ?? randomUUID(),\n };\n } else {\n this.record = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private\n // ---------------------------------------------------------------------------\n\n private getAgent(state: GoalManagerState): Agent | undefined {\n try {\n return state.controller.getCurrentAgent(state.session);\n } catch {\n return undefined;\n }\n }\n\n /** Resolve effective judge model + max runs (record value → settings default). */\n private effectiveSettings(record: GoalObjectiveRecord): { judgeModelId: string; maxTurns: number } {\n const settings = loadSettings();\n return {\n judgeModelId: record.judgeModelId ?? settings.models.goalJudgeModel ?? '',\n maxTurns: record.maxRuns ?? settings.models.goalMaxTurns ?? DEFAULT_MAX_TURNS,\n };\n }\n\n private localRecord(\n objective: string,\n judgeModelId: string,\n maxTurns: number,\n now: number,\n id: string,\n ): GoalObjectiveRecord & { id: string } {\n return {\n objective,\n status: 'active',\n runsUsed: 0,\n activeDurationMs: 0,\n maxRuns: maxTurns,\n ...(judgeModelId ? { judgeModelId } : {}),\n startedAt: now,\n updatedAt: now,\n id,\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/hooks/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAkB,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7E,eAAO,MAAM,YAAY,EAAE,aAAa,EAevC,CAAC;AAEF,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,SAAqB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/hooks/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAkB,aAAa,EAAE,MAAM,YAAY,CAAC;AAE7E,eAAO,MAAM,YAAY,EAAE,aAAa,EAevC,CAAC;AAEF,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,SAAqB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAUrH;AAUD,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,SAAqB,GAAG,MAAM,CAElG;AAED,wBAAgB,kBAAkB,CAAC,aAAa,SAAqB,EAAE,OAAO,SAAe,GAAG,MAAM,CAErG"}
|
package/dist/hooks/config.js
CHANGED
|
@@ -27,7 +27,14 @@ const VALID_EVENTS = [
|
|
|
27
27
|
function loadHooksConfig(projectDir, configDirName = DEFAULT_CONFIG_DIR, homeDir) {
|
|
28
28
|
const globalPath = getGlobalHooksPath(configDirName, homeDir);
|
|
29
29
|
const projectPath = getProjectHooksPath(projectDir, configDirName);
|
|
30
|
-
return mergeConfigs(loadSingleConfig(globalPath), loadSingleConfig(projectPath));
|
|
30
|
+
return mergeConfigs(loadSingleConfig(globalPath), isSameFile(globalPath, projectPath) ? {} : loadSingleConfig(projectPath));
|
|
31
|
+
}
|
|
32
|
+
function isSameFile(a, b) {
|
|
33
|
+
try {
|
|
34
|
+
return fs$1.realpathSync(a) === fs$1.realpathSync(b);
|
|
35
|
+
} catch {
|
|
36
|
+
return path$1.resolve(a) === path$1.resolve(b);
|
|
37
|
+
}
|
|
31
38
|
}
|
|
32
39
|
function getProjectHooksPath(projectDir, configDirName = DEFAULT_CONFIG_DIR) {
|
|
33
40
|
return path$1.join(projectDir, configDirName, "hooks.json");
|
package/dist/hooks/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","names":["
|
|
1
|
+
{"version":3,"file":"config.js","names":["fs","path","os"],"sources":["../../src/hooks/config.ts"],"sourcesContent":["/**\n * Hook configuration loading from filesystem.\n * Loads from global (~/.mastracode/hooks.json) and project (.mastracode/hooks.json).\n * Global hooks run first, project hooks append.\n */\n\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport type { HooksConfig, HookDefinition, HookEventName } from './types.js';\n\nexport const VALID_EVENTS: HookEventName[] = [\n 'PreToolUse',\n 'PostToolUse',\n 'Stop',\n 'UserPromptSubmit',\n 'SessionStart',\n 'SessionEnd',\n 'Notification',\n 'AgentStart',\n 'AgentEnd',\n 'PermissionRequest',\n 'PermissionResult',\n 'Interrupt',\n 'SubagentStart',\n 'SubagentEnd',\n];\n\nexport function loadHooksConfig(projectDir: string, configDirName = DEFAULT_CONFIG_DIR, homeDir?: string): HooksConfig {\n const globalPath = getGlobalHooksPath(configDirName, homeDir);\n const projectPath = getProjectHooksPath(projectDir, configDirName);\n\n const globalConfig = loadSingleConfig(globalPath);\n // A session started in the home directory resolves both paths to the same\n // file; loading it twice would register (and run) every hook twice.\n const projectConfig = isSameFile(globalPath, projectPath) ? {} : loadSingleConfig(projectPath);\n\n return mergeConfigs(globalConfig, projectConfig);\n}\n\nfunction isSameFile(a: string, b: string): boolean {\n try {\n return fs.realpathSync(a) === fs.realpathSync(b);\n } catch {\n return path.resolve(a) === path.resolve(b);\n }\n}\n\nexport function getProjectHooksPath(projectDir: string, configDirName = DEFAULT_CONFIG_DIR): string {\n return path.join(projectDir, configDirName, 'hooks.json');\n}\n\nexport function getGlobalHooksPath(configDirName = DEFAULT_CONFIG_DIR, homeDir = os.homedir()): string {\n return path.join(homeDir, configDirName, 'hooks.json');\n}\n\nfunction loadSingleConfig(filePath: string): HooksConfig {\n try {\n if (!fs.existsSync(filePath)) return {};\n const raw = fs.readFileSync(filePath, 'utf-8');\n return validateConfig(JSON.parse(raw));\n } catch {\n return {};\n }\n}\n\nfunction validateConfig(raw: unknown): HooksConfig {\n if (!raw || typeof raw !== 'object') return {};\n\n const config: HooksConfig = {};\n const obj = raw as Record<string, unknown>;\n\n for (const event of VALID_EVENTS) {\n if (Array.isArray(obj[event])) {\n const hooks = (obj[event] as unknown[]).filter(isValidHook);\n if (hooks.length > 0) {\n config[event] = hooks;\n }\n }\n }\n\n return config;\n}\n\nfunction isValidHook(raw: unknown): raw is HookDefinition {\n if (!raw || typeof raw !== 'object') return false;\n const obj = raw as Record<string, unknown>;\n return obj.type === 'command' && typeof obj.command === 'string';\n}\n\nfunction mergeConfigs(global: HooksConfig, project: HooksConfig): HooksConfig {\n const merged: HooksConfig = {};\n\n for (const event of VALID_EVENTS) {\n const combined = [...(global[event] ?? []), ...(project[event] ?? [])];\n if (combined.length > 0) {\n merged[event] = combined;\n }\n }\n\n return merged;\n}\n"],"mappings":";;;;;;;;;;AAYA,MAAa,eAAgC;CAC3C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,gBAAgB,YAAoB,gBAAgB,oBAAoB,SAA+B;CACrH,MAAM,aAAa,mBAAmB,eAAe,OAAO;CAC5D,MAAM,cAAc,oBAAoB,YAAY,aAAa;CAOjE,OAAO,aALc,iBAAiB,UAKP,GAFT,WAAW,YAAY,WAAW,IAAI,CAAC,IAAI,iBAAiB,WAAW,CAE9C;AACjD;AAEA,SAAS,WAAW,GAAW,GAAoB;CACjD,IAAI;EACF,OAAOA,KAAG,aAAa,CAAC,MAAMA,KAAG,aAAa,CAAC;CACjD,QAAQ;EACN,OAAOC,OAAK,QAAQ,CAAC,MAAMA,OAAK,QAAQ,CAAC;CAC3C;AACF;AAEA,SAAgB,oBAAoB,YAAoB,gBAAgB,oBAA4B;CAClG,OAAOA,OAAK,KAAK,YAAY,eAAe,YAAY;AAC1D;AAEA,SAAgB,mBAAmB,gBAAgB,oBAAoB,UAAUC,KAAG,QAAQ,GAAW;CACrG,OAAOD,OAAK,KAAK,SAAS,eAAe,YAAY;AACvD;AAEA,SAAS,iBAAiB,UAA+B;CACvD,IAAI;EACF,IAAI,CAACD,KAAG,WAAW,QAAQ,GAAG,OAAO,CAAC;EACtC,MAAM,MAAMA,KAAG,aAAa,UAAU,OAAO;EAC7C,OAAO,eAAe,KAAK,MAAM,GAAG,CAAC;CACvC,QAAQ;EACN,OAAO,CAAC;CACV;AACF;AAEA,SAAS,eAAe,KAA2B;CACjD,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,OAAO,CAAC;CAE7C,MAAM,SAAsB,CAAC;CAC7B,MAAM,MAAM;CAEZ,KAAK,MAAM,SAAS,cAClB,IAAI,MAAM,QAAQ,IAAI,MAAM,GAAG;EAC7B,MAAM,QAAS,IAAI,MAAM,CAAe,OAAO,WAAW;EAC1D,IAAI,MAAM,SAAS,GACjB,OAAO,SAAS;CAEpB;CAGF,OAAO;AACT;AAEA,SAAS,YAAY,KAAqC;CACxD,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,OAAO;CAC5C,MAAM,MAAM;CACZ,OAAO,IAAI,SAAS,aAAa,OAAO,IAAI,YAAY;AAC1D;AAEA,SAAS,aAAa,QAAqB,SAAmC;CAC5E,MAAM,SAAsB,CAAC;CAE7B,KAAK,MAAM,SAAS,cAAc;EAChC,MAAM,WAAW,CAAC,GAAI,OAAO,UAAU,CAAC,GAAI,GAAI,QAAQ,UAAU,CAAC,CAAE;EACrE,IAAI,SAAS,SAAS,GACpB,OAAO,SAAS;CAEpB;CAEA,OAAO;AACT"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/code-sdk",
|
|
3
|
-
"version": "1.8.1-alpha.
|
|
3
|
+
"version": "1.8.1-alpha.3",
|
|
4
4
|
"description": "Mastra Code SDK: the agent core behind Mastra Code (everything except the TUI) — build your own UIs and surfaces on top of it",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -57,18 +57,18 @@
|
|
|
57
57
|
"yaml": "^2.7.1",
|
|
58
58
|
"zod": "^4.6.4",
|
|
59
59
|
"@mastra/agent-browser": "0.5.3",
|
|
60
|
-
"@mastra/core": "1.69.0-alpha.
|
|
61
|
-
"@mastra/fastembed": "1.3.1",
|
|
62
|
-
"@mastra/github-signals": "0.5.0",
|
|
60
|
+
"@mastra/core": "1.69.0-alpha.3",
|
|
63
61
|
"@mastra/duckdb": "1.10.0",
|
|
64
|
-
"@mastra/mcp": "2.0.0",
|
|
65
62
|
"@mastra/libsql": "1.23.1",
|
|
66
|
-
"@mastra/
|
|
63
|
+
"@mastra/mcp": "2.0.0",
|
|
67
64
|
"@mastra/memory": "1.31.0",
|
|
65
|
+
"@mastra/fastembed": "1.3.1",
|
|
66
|
+
"@mastra/observability": "1.17.9",
|
|
68
67
|
"@mastra/parallel": "0.1.2",
|
|
68
|
+
"@mastra/stagehand": "0.3.5",
|
|
69
69
|
"@mastra/pg": "1.26.0",
|
|
70
|
+
"@mastra/github-signals": "0.5.0",
|
|
70
71
|
"@mastra/schema-compat": "1.3.11",
|
|
71
|
-
"@mastra/stagehand": "0.3.5",
|
|
72
72
|
"@mastra/tavily": "1.1.2"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"typescript-eslint": "^8.57.0",
|
|
82
82
|
"vitest": "4.1.11",
|
|
83
83
|
"@internal/lint": "0.0.134",
|
|
84
|
-
"@internal/
|
|
85
|
-
"@internal/
|
|
84
|
+
"@internal/workspace-test-utils": "0.0.78",
|
|
85
|
+
"@internal/types-builder": "0.0.109"
|
|
86
86
|
},
|
|
87
87
|
"engines": {
|
|
88
88
|
"node": ">=22.19.0"
|