@mastra/code-sdk 0.1.1-alpha.1 → 0.2.0-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/CHANGELOG.md +40 -0
- package/dist/acp/event-mapper.d.ts.map +1 -1
- package/dist/acp/event-mapper.js +25 -2
- package/dist/acp/event-mapper.js.map +1 -1
- package/dist/goal-manager.d.ts +83 -0
- package/dist/goal-manager.d.ts.map +1 -0
- package/dist/goal-manager.js +277 -0
- package/dist/goal-manager.js.map +1 -0
- package/dist/goal-signal.d.ts +14 -0
- package/dist/goal-signal.d.ts.map +1 -0
- package/dist/goal-signal.js +16 -0
- package/dist/goal-signal.js.map +1 -0
- package/dist/headless/format.d.ts.map +1 -1
- package/dist/headless/format.js +5 -2
- package/dist/headless/format.js.map +1 -1
- package/dist/headless/index.d.ts +1 -1
- package/dist/headless/index.d.ts.map +1 -1
- package/dist/headless/index.js.map +1 -1
- package/dist/headless/run-mc.d.ts.map +1 -1
- package/dist/headless/run-mc.js +64 -12
- package/dist/headless/run-mc.js.map +1 -1
- package/dist/headless/types.d.ts +29 -5
- package/dist/headless/types.d.ts.map +1 -1
- package/dist/headless/types.js.map +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# @mastra/code-sdk
|
|
2
2
|
|
|
3
|
+
## 0.2.0-alpha.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed ACP clients dropping standalone signal messages such as system reminders and notification summaries, while preserving assistant text deltas across interleaved signals without inserting separators. ([#18783](https://github.com/mastra-ai/mastra/pull/18783))
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`1426af2`](https://github.com/mastra-ai/mastra/commit/1426af24975879c000d13ac75673f630fcc970c1), [`975295d`](https://github.com/mastra-ai/mastra/commit/975295d418552f0d46a59edfef4c3ee555f9930a), [`85e4fb5`](https://github.com/mastra-ai/mastra/commit/85e4fb50087a81c74df3a762f53b56373db0b912), [`19881f5`](https://github.com/mastra-ai/mastra/commit/19881f5d6a09437cf5b947d2e8be3bd8745df767), [`ef03c0c`](https://github.com/mastra-ai/mastra/commit/ef03c0cfc62367a458e4cc56462e2148b35681c5), [`4fb4d88`](https://github.com/mastra-ai/mastra/commit/4fb4d881bc107acee13890ad4d78661016c510ed), [`4eba27a`](https://github.com/mastra-ai/mastra/commit/4eba27adcf60f991df0e62f94b3e75b4e67f3b4b), [`c701be3`](https://github.com/mastra-ai/mastra/commit/c701be32d7d9aa94a66da8c6cc38dcac6856f464)]:
|
|
10
|
+
- @mastra/core@1.52.0-alpha.3
|
|
11
|
+
- @mastra/pg@1.16.1-alpha.0
|
|
12
|
+
|
|
13
|
+
## 0.2.0-alpha.2
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- Add goal execution to the headless `runMC` API. Goal runs use the same GoalManager and system-reminder signal path as the TUI and resolve on terminal `goal_evaluation` events without manual continuation messages. ([#19441](https://github.com/mastra-ai/mastra/pull/19441))
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
const run = runMC({
|
|
21
|
+
controller,
|
|
22
|
+
session,
|
|
23
|
+
goal: {
|
|
24
|
+
objective: 'Implement and verify the requested change',
|
|
25
|
+
judgeModelId: 'openai/gpt-5-mini',
|
|
26
|
+
maxRuns: 20,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
for await (const event of run) {
|
|
31
|
+
console.log(event.type);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const result = await run.result;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- Updated dependencies [[`8b20926`](https://github.com/mastra-ai/mastra/commit/8b20926cd59e2ba3d66458e062fa0e6e2ada3e68), [`74faf8b`](https://github.com/mastra-ai/mastra/commit/74faf8bd9c1018f2492653c06b1e25fc8300e9e6), [`1fadac4`](https://github.com/mastra-ai/mastra/commit/1fadac44537caeefe81f9f775ae2f2f3d94e9069), [`970c032`](https://github.com/mastra-ai/mastra/commit/970c032502751ee5dd4d0b603331d9838cb538fc), [`792ec9a`](https://github.com/mastra-ai/mastra/commit/792ec9a0869bab8274cf5e0ed2840738737a1607), [`712b864`](https://github.com/mastra-ai/mastra/commit/712b864aa1ed12b14c54390ec17b69de163c37f7), [`8f7a5de`](https://github.com/mastra-ai/mastra/commit/8f7a5dedc246cdc938bb65516703cf9b27b03756), [`c0bec73`](https://github.com/mastra-ai/mastra/commit/c0bec732c93d1a22ae5e51ed66cf8cacca8bd6a6)]:
|
|
40
|
+
- @mastra/core@1.52.0-alpha.2
|
|
41
|
+
- @mastra/mcp@1.15.0-alpha.0
|
|
42
|
+
|
|
3
43
|
## 0.1.1-alpha.1
|
|
4
44
|
|
|
5
45
|
### Patch Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-mapper.d.ts","sourceRoot":"","sources":["../../src/acp/event-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiD,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnH,OAAO,KAAK,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"event-mapper.d.ts","sourceRoot":"","sources":["../../src/acp/event-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiD,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACnH,OAAO,KAAK,EAAE,oBAAoB,EAAmB,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAyBhH;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEnD;AA4BD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,KAAK,IAAI,CAAC;CAC3E;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,oBAAoB,EAC3B,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,UAAU,EAAE,mBAAmB,EAC/B,OAAO,EAAE,OAAO,GACf,IAAI,CAkFN"}
|
package/dist/acp/event-mapper.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import { mastraDBMessageToSignal } from "@mastra/core/signals";
|
|
2
|
+
function getMessageText(message) {
|
|
3
|
+
const content = message.content;
|
|
4
|
+
if (typeof content === "string" || !content?.parts) return "";
|
|
5
|
+
return content.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
6
|
+
}
|
|
7
|
+
function getSignalText(message) {
|
|
8
|
+
const contents = mastraDBMessageToSignal(message).contents;
|
|
9
|
+
if (typeof contents === "string") return contents;
|
|
10
|
+
if (!Array.isArray(contents)) return "";
|
|
11
|
+
return contents.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
12
|
+
}
|
|
1
13
|
let autoApprove = false;
|
|
2
14
|
function setAutoApprove(value) {
|
|
3
15
|
autoApprove = value;
|
|
@@ -22,9 +34,20 @@ function handleAgentControllerEvent(event, state, connection, session) {
|
|
|
22
34
|
case "agent_start":
|
|
23
35
|
state.lastTextLength = 0;
|
|
24
36
|
break;
|
|
37
|
+
case "message_start": {
|
|
38
|
+
if (event.message.role !== "signal") break;
|
|
39
|
+
const text = getSignalText(event.message);
|
|
40
|
+
if (text) {
|
|
41
|
+
sendUpdate(connection, state.sessionId, {
|
|
42
|
+
sessionUpdate: "agent_message_chunk",
|
|
43
|
+
content: { type: "text", text }
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
25
48
|
case "message_update": {
|
|
26
49
|
if (event.message.role !== "assistant") break;
|
|
27
|
-
const fullText = event.message
|
|
50
|
+
const fullText = getMessageText(event.message);
|
|
28
51
|
if (fullText.length > state.lastTextLength) {
|
|
29
52
|
const delta = fullText.slice(state.lastTextLength);
|
|
30
53
|
state.lastTextLength = fullText.length;
|
|
@@ -36,7 +59,7 @@ function handleAgentControllerEvent(event, state, connection, session) {
|
|
|
36
59
|
break;
|
|
37
60
|
}
|
|
38
61
|
case "message_end":
|
|
39
|
-
state.lastTextLength = 0;
|
|
62
|
+
if (event.message.role === "assistant") state.lastTextLength = 0;
|
|
40
63
|
break;
|
|
41
64
|
case "tool_start":
|
|
42
65
|
sendUpdate(connection, state.sessionId, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/acp/event-mapper.ts"],"sourcesContent":["import type { SessionNotification, RequestPermissionRequest, AgentSideConnection } from '@agentclientprotocol/sdk';\nimport type { AgentControllerEvent, Session, TokenUsage } from '@mastra/core/agent-controller';\n\nlet autoApprove = false;\n\n/**\n * Enable or disable automatic tool approval (set via --dangerous-auto-approve CLI flag).\n */\nexport function setAutoApprove(value: boolean): void {\n autoApprove = value;\n}\n\n/**\n * Map a mastracode tool name to an ACP ToolKind.\n */\nfunction mapToolKind(\n toolName: string,\n): 'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'think' | 'fetch' | 'other' {\n const name = toolName.toLowerCase();\n if (name.includes('edit') || name.includes('write') || name.includes('replace') || name.includes('patch'))\n return 'edit';\n if (name.includes('read') || name.includes('view') || name.includes('list') || name.includes('find')) return 'read';\n if (name.includes('delete') || name.includes('remove')) return 'delete';\n if (name.includes('search') || name.includes('grep') || name.includes('query')) return 'search';\n if (name.includes('execute') || name.includes('run') || name.includes('command') || name.includes('shell'))\n return 'execute';\n if (\n name.includes('fetch') ||\n name.includes('curl') ||\n name.includes('http') ||\n name.includes('browse') ||\n name.includes('navigate')\n )\n return 'fetch';\n if (name.includes('think') || name.includes('reason')) return 'think';\n return 'other';\n}\n\n/**\n * Accumulated state for an active prompt turn.\n */\nexport interface PromptState {\n sessionId: string;\n lastTextLength: number;\n usage: TokenUsage;\n resolve: (reason: 'complete' | 'aborted' | 'error' | 'suspended') => void;\n}\n\n/**\n * Translate an AgentControllerEvent into an ACP SessionNotification and send it\n * via the provided connection. Returns null for events that don't produce\n * a session update.\n */\nexport function handleAgentControllerEvent(\n event: AgentControllerEvent,\n state: PromptState | null,\n connection: AgentSideConnection,\n session: Session,\n): void {\n if (!state) return;\n\n switch (event.type) {\n case 'agent_start':\n state.lastTextLength = 0;\n break;\n\n case 'message_update': {\n if (event.message.role !== 'assistant') break;\n const fullText = event.message.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map(p => p.text)\n .join('');\n if (fullText.length > state.lastTextLength) {\n const delta = fullText.slice(state.lastTextLength);\n state.lastTextLength = fullText.length;\n sendUpdate(connection, state.sessionId, {\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text: delta },\n });\n }\n break;\n }\n\n case 'message_end':\n state.lastTextLength = 0;\n break;\n\n case 'tool_start':\n sendUpdate(connection, state.sessionId, {\n sessionUpdate: 'tool_call',\n toolCallId: event.toolCallId,\n title: event.toolName,\n kind: mapToolKind(event.toolName),\n status: 'in_progress',\n rawInput: JSON.stringify(event.args),\n });\n break;\n\n case 'tool_end':\n sendUpdate(connection, state.sessionId, {\n sessionUpdate: 'tool_call_update',\n toolCallId: event.toolCallId,\n status: event.isError ? 'failed' : 'completed',\n rawOutput: typeof event.result === 'string' ? event.result : JSON.stringify(event.result),\n });\n break;\n\n case 'tool_approval_required':\n void handleToolApproval(state, connection, session, event).catch(err => {\n process.stderr.write(`[acp] handleToolApproval error: ${err}\\n`);\n });\n break;\n\n case 'tool_suspended':\n void handleToolSuspended(state, connection, session, event).catch(err => {\n process.stderr.write(`[acp] handleToolSuspended error: ${err}\\n`);\n });\n break;\n\n case 'usage_update':\n accumulateUsage(state.usage, event.usage);\n break;\n\n case 'agent_end':\n state.resolve(event.reason ?? 'complete');\n break;\n\n // Ignored in v1: om_*, subagent_*, workspace_*, task_updated, etc.\n default:\n break;\n }\n}\n\nfunction sendUpdate(connection: AgentSideConnection, sessionId: string, update: SessionNotification['update']): void {\n connection.sessionUpdate({ sessionId, update }).catch(err => {\n process.stderr.write(`[acp] sessionUpdate error: ${err}\\n`);\n });\n}\n\nasync function handleToolApproval(\n state: PromptState,\n connection: AgentSideConnection,\n session: Session,\n event: Extract<AgentControllerEvent, { type: 'tool_approval_required' }>,\n): Promise<void> {\n // Auto-approve if --dangerous-auto-approve flag is set\n if (autoApprove) {\n session.respondToToolApproval({ decision: 'approve' });\n return;\n }\n\n const req: RequestPermissionRequest = {\n sessionId: state.sessionId,\n toolCall: {\n toolCallId: event.toolCallId,\n title: event.toolName,\n rawInput: JSON.stringify(event.args),\n },\n options: [\n { optionId: 'approve', name: 'Allow', kind: 'allow_once' },\n { optionId: 'reject', name: 'Reject', kind: 'reject_once' },\n ],\n };\n\n try {\n const resp = await connection.requestPermission(req);\n if (resp.outcome.outcome === 'selected') {\n const decision = resp.outcome.optionId === 'approve' ? 'approve' : 'decline';\n session.respondToToolApproval({ decision });\n } else {\n session.respondToToolApproval({ decision: 'decline' });\n }\n } catch (err) {\n process.stderr.write(`[acp] requestPermission error: ${err}\\n`);\n session.respondToToolApproval({ decision: 'decline' });\n }\n}\n\nasync function handleToolSuspended(\n state: PromptState,\n connection: AgentSideConnection,\n session: Session,\n event: Extract<AgentControllerEvent, { type: 'tool_suspended' }>,\n): Promise<void> {\n const { toolCallId, toolName, args, suspendPayload } = event;\n\n // Auto-resolve certain suspensions (mirrors headless.ts autoResolve)\n if (toolName === 'request_access' || (suspendPayload as any)?.kind === 'sandbox_access_request') {\n void session.respondToToolSuspension({ toolCallId, resumeData: 'Yes' });\n return;\n }\n\n if (toolName === 'submit_plan') {\n // Request permission for plan approval\n const req: RequestPermissionRequest = {\n sessionId: state.sessionId,\n toolCall: {\n toolCallId,\n title: toolName,\n rawInput: JSON.stringify(args),\n },\n options: [\n { optionId: 'approve', name: 'Approve Plan', kind: 'allow_once' },\n { optionId: 'reject', name: 'Reject Plan', kind: 'reject_once' },\n ],\n };\n\n try {\n const resp = await connection.requestPermission(req);\n const action =\n resp.outcome.outcome === 'selected' && resp.outcome.optionId === 'approve' ? 'approved' : 'rejected';\n void session.respondToToolSuspension({ toolCallId, resumeData: { action } });\n } catch {\n void session.respondToToolSuspension({ toolCallId, resumeData: { action: 'rejected' } });\n }\n return;\n }\n\n // For ask_user and other suspensions, auto-resolve\n void session.respondToToolSuspension({\n toolCallId,\n resumeData: 'Proceed with your best judgment. Do not ask further questions.',\n });\n}\n\nfunction accumulateUsage(target: TokenUsage, usage: TokenUsage): void {\n target.promptTokens += usage.promptTokens;\n target.completionTokens += usage.completionTokens;\n target.totalTokens += usage.totalTokens;\n if (usage.reasoningTokens) target.reasoningTokens = (target.reasoningTokens ?? 0) + usage.reasoningTokens;\n if (usage.cachedInputTokens) target.cachedInputTokens = (target.cachedInputTokens ?? 0) + usage.cachedInputTokens;\n if (usage.cacheCreationInputTokens) {\n target.cacheCreationInputTokens = (target.cacheCreationInputTokens ?? 0) + usage.cacheCreationInputTokens;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/acp/event-mapper.ts"],"sourcesContent":["import type { SessionNotification, RequestPermissionRequest, AgentSideConnection } from '@agentclientprotocol/sdk';\nimport type { AgentControllerEvent, MastraDBMessage, Session, TokenUsage } from '@mastra/core/agent-controller';\nimport { mastraDBMessageToSignal } from '@mastra/core/signals';\n\n/** Concatenate the text of all `text` parts on a DB-native assistant message. */\nfunction getMessageText(message: MastraDBMessage): string {\n const content = message.content;\n if (typeof content === 'string' || !content?.parts) return '';\n return content.parts\n .filter((part): part is Extract<(typeof content.parts)[number], { type: 'text' }> => part.type === 'text')\n .map(part => part.text)\n .join('');\n}\n\nfunction getSignalText(message: MastraDBMessage): string {\n const contents = mastraDBMessageToSignal(message).contents;\n if (typeof contents === 'string') return contents;\n if (!Array.isArray(contents)) return '';\n return contents\n .filter((part): part is Extract<(typeof contents)[number], { type: 'text' }> => part.type === 'text')\n .map(part => part.text)\n .join('\\n');\n}\n\nlet autoApprove = false;\n\n/**\n * Enable or disable automatic tool approval (set via --dangerous-auto-approve CLI flag).\n */\nexport function setAutoApprove(value: boolean): void {\n autoApprove = value;\n}\n\n/**\n * Map a mastracode tool name to an ACP ToolKind.\n */\nfunction mapToolKind(\n toolName: string,\n): 'read' | 'edit' | 'delete' | 'move' | 'search' | 'execute' | 'think' | 'fetch' | 'other' {\n const name = toolName.toLowerCase();\n if (name.includes('edit') || name.includes('write') || name.includes('replace') || name.includes('patch'))\n return 'edit';\n if (name.includes('read') || name.includes('view') || name.includes('list') || name.includes('find')) return 'read';\n if (name.includes('delete') || name.includes('remove')) return 'delete';\n if (name.includes('search') || name.includes('grep') || name.includes('query')) return 'search';\n if (name.includes('execute') || name.includes('run') || name.includes('command') || name.includes('shell'))\n return 'execute';\n if (\n name.includes('fetch') ||\n name.includes('curl') ||\n name.includes('http') ||\n name.includes('browse') ||\n name.includes('navigate')\n )\n return 'fetch';\n if (name.includes('think') || name.includes('reason')) return 'think';\n return 'other';\n}\n\n/**\n * Accumulated state for an active prompt turn.\n */\nexport interface PromptState {\n sessionId: string;\n lastTextLength: number;\n usage: TokenUsage;\n resolve: (reason: 'complete' | 'aborted' | 'error' | 'suspended') => void;\n}\n\n/**\n * Translate an AgentControllerEvent into an ACP SessionNotification and send it\n * via the provided connection. Returns null for events that don't produce\n * a session update.\n */\nexport function handleAgentControllerEvent(\n event: AgentControllerEvent,\n state: PromptState | null,\n connection: AgentSideConnection,\n session: Session,\n): void {\n if (!state) return;\n\n switch (event.type) {\n case 'agent_start':\n state.lastTextLength = 0;\n break;\n\n case 'message_start': {\n if (event.message.role !== 'signal') break;\n const text = getSignalText(event.message);\n if (text) {\n sendUpdate(connection, state.sessionId, {\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text },\n });\n }\n break;\n }\n\n case 'message_update': {\n if (event.message.role !== 'assistant') break;\n const fullText = getMessageText(event.message);\n if (fullText.length > state.lastTextLength) {\n const delta = fullText.slice(state.lastTextLength);\n state.lastTextLength = fullText.length;\n sendUpdate(connection, state.sessionId, {\n sessionUpdate: 'agent_message_chunk',\n content: { type: 'text', text: delta },\n });\n }\n break;\n }\n\n case 'message_end':\n if (event.message.role === 'assistant') state.lastTextLength = 0;\n break;\n\n case 'tool_start':\n sendUpdate(connection, state.sessionId, {\n sessionUpdate: 'tool_call',\n toolCallId: event.toolCallId,\n title: event.toolName,\n kind: mapToolKind(event.toolName),\n status: 'in_progress',\n rawInput: JSON.stringify(event.args),\n });\n break;\n\n case 'tool_end':\n sendUpdate(connection, state.sessionId, {\n sessionUpdate: 'tool_call_update',\n toolCallId: event.toolCallId,\n status: event.isError ? 'failed' : 'completed',\n rawOutput: typeof event.result === 'string' ? event.result : JSON.stringify(event.result),\n });\n break;\n\n case 'tool_approval_required':\n void handleToolApproval(state, connection, session, event).catch(err => {\n process.stderr.write(`[acp] handleToolApproval error: ${err}\\n`);\n });\n break;\n\n case 'tool_suspended':\n void handleToolSuspended(state, connection, session, event).catch(err => {\n process.stderr.write(`[acp] handleToolSuspended error: ${err}\\n`);\n });\n break;\n\n case 'usage_update':\n accumulateUsage(state.usage, event.usage);\n break;\n\n case 'agent_end':\n state.resolve(event.reason ?? 'complete');\n break;\n\n // Ignored in v1: om_*, subagent_*, workspace_*, task_updated, etc.\n default:\n break;\n }\n}\n\nfunction sendUpdate(connection: AgentSideConnection, sessionId: string, update: SessionNotification['update']): void {\n connection.sessionUpdate({ sessionId, update }).catch(err => {\n process.stderr.write(`[acp] sessionUpdate error: ${err}\\n`);\n });\n}\n\nasync function handleToolApproval(\n state: PromptState,\n connection: AgentSideConnection,\n session: Session,\n event: Extract<AgentControllerEvent, { type: 'tool_approval_required' }>,\n): Promise<void> {\n // Auto-approve if --dangerous-auto-approve flag is set\n if (autoApprove) {\n session.respondToToolApproval({ decision: 'approve' });\n return;\n }\n\n const req: RequestPermissionRequest = {\n sessionId: state.sessionId,\n toolCall: {\n toolCallId: event.toolCallId,\n title: event.toolName,\n rawInput: JSON.stringify(event.args),\n },\n options: [\n { optionId: 'approve', name: 'Allow', kind: 'allow_once' },\n { optionId: 'reject', name: 'Reject', kind: 'reject_once' },\n ],\n };\n\n try {\n const resp = await connection.requestPermission(req);\n if (resp.outcome.outcome === 'selected') {\n const decision = resp.outcome.optionId === 'approve' ? 'approve' : 'decline';\n session.respondToToolApproval({ decision });\n } else {\n session.respondToToolApproval({ decision: 'decline' });\n }\n } catch (err) {\n process.stderr.write(`[acp] requestPermission error: ${err}\\n`);\n session.respondToToolApproval({ decision: 'decline' });\n }\n}\n\nasync function handleToolSuspended(\n state: PromptState,\n connection: AgentSideConnection,\n session: Session,\n event: Extract<AgentControllerEvent, { type: 'tool_suspended' }>,\n): Promise<void> {\n const { toolCallId, toolName, args, suspendPayload } = event;\n\n // Auto-resolve certain suspensions (mirrors headless.ts autoResolve)\n if (toolName === 'request_access' || (suspendPayload as any)?.kind === 'sandbox_access_request') {\n void session.respondToToolSuspension({ toolCallId, resumeData: 'Yes' });\n return;\n }\n\n if (toolName === 'submit_plan') {\n // Request permission for plan approval\n const req: RequestPermissionRequest = {\n sessionId: state.sessionId,\n toolCall: {\n toolCallId,\n title: toolName,\n rawInput: JSON.stringify(args),\n },\n options: [\n { optionId: 'approve', name: 'Approve Plan', kind: 'allow_once' },\n { optionId: 'reject', name: 'Reject Plan', kind: 'reject_once' },\n ],\n };\n\n try {\n const resp = await connection.requestPermission(req);\n const action =\n resp.outcome.outcome === 'selected' && resp.outcome.optionId === 'approve' ? 'approved' : 'rejected';\n void session.respondToToolSuspension({ toolCallId, resumeData: { action } });\n } catch {\n void session.respondToToolSuspension({ toolCallId, resumeData: { action: 'rejected' } });\n }\n return;\n }\n\n // For ask_user and other suspensions, auto-resolve\n void session.respondToToolSuspension({\n toolCallId,\n resumeData: 'Proceed with your best judgment. Do not ask further questions.',\n });\n}\n\nfunction accumulateUsage(target: TokenUsage, usage: TokenUsage): void {\n target.promptTokens += usage.promptTokens;\n target.completionTokens += usage.completionTokens;\n target.totalTokens += usage.totalTokens;\n if (usage.reasoningTokens) target.reasoningTokens = (target.reasoningTokens ?? 0) + usage.reasoningTokens;\n if (usage.cachedInputTokens) target.cachedInputTokens = (target.cachedInputTokens ?? 0) + usage.cachedInputTokens;\n if (usage.cacheCreationInputTokens) {\n target.cacheCreationInputTokens = (target.cacheCreationInputTokens ?? 0) + usage.cacheCreationInputTokens;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { AgentController, Session } from '@mastra/core/agent-controller';
|
|
2
|
+
export interface GoalManagerState<TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
3
|
+
controller: AgentController<TState>;
|
|
4
|
+
session: Session<TState>;
|
|
5
|
+
}
|
|
6
|
+
export type GoalStatus = 'active' | 'paused' | 'done';
|
|
7
|
+
/**
|
|
8
|
+
* TUI-facing view of a goal. Derived from the durable {@link GoalObjectiveRecord}
|
|
9
|
+
* plus the effective judge/max-runs settings, with display-only timer fields and
|
|
10
|
+
* a stable `id` used to match plan-started goals.
|
|
11
|
+
*/
|
|
12
|
+
export interface GoalState {
|
|
13
|
+
id: string;
|
|
14
|
+
objective: string;
|
|
15
|
+
status: GoalStatus;
|
|
16
|
+
turnsUsed: number;
|
|
17
|
+
maxTurns: number;
|
|
18
|
+
judgeModelId: string;
|
|
19
|
+
startedAt: string;
|
|
20
|
+
activeStartedAt?: string;
|
|
21
|
+
activeDurationMs?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare const DEFAULT_MAX_TURNS = 50;
|
|
24
|
+
export declare class GoalManager {
|
|
25
|
+
/** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */
|
|
26
|
+
private record;
|
|
27
|
+
/** Display-only active-timer accounting (not persisted to the objective record). */
|
|
28
|
+
private activeStartedAt;
|
|
29
|
+
private activeDurationMs;
|
|
30
|
+
private persistGoalOnNextThreadCreate;
|
|
31
|
+
getGoal(): GoalState | null;
|
|
32
|
+
isActive(): boolean;
|
|
33
|
+
persistOnNextThreadCreate(): void;
|
|
34
|
+
consumePersistOnNextThreadCreate(): boolean;
|
|
35
|
+
startActiveTimer(): void;
|
|
36
|
+
stopActiveTimer(): void;
|
|
37
|
+
/** Reset active-timer accounting to zero (e.g. for an untriggered plan goal). */
|
|
38
|
+
resetActiveTimer(): void;
|
|
39
|
+
/**
|
|
40
|
+
* Set a new objective. Persists to ThreadState via `agent.setObjective` and
|
|
41
|
+
* updates the in-memory view. Only the provided settings are persisted into
|
|
42
|
+
* the record; unset ones fall back to the agent's `goal` config at read time.
|
|
43
|
+
*/
|
|
44
|
+
setGoal(state: GoalManagerState, objective: string, judgeModelId: string, maxTurns?: number): Promise<GoalState | null>;
|
|
45
|
+
/**
|
|
46
|
+
* Update the judge model / max-runs defaults. Persists into the active record
|
|
47
|
+
* (so the override is remembered in thread state) when a goal is set.
|
|
48
|
+
*/
|
|
49
|
+
updateJudgeDefaults(state: GoalManagerState, judgeModelId: string, maxTurns: number): Promise<GoalState | null>;
|
|
50
|
+
pause(reason?: string): GoalState | null;
|
|
51
|
+
resume(): GoalState | null;
|
|
52
|
+
markDone(): void;
|
|
53
|
+
clear(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Sync the latest objective record from ThreadState into the in-memory view.
|
|
56
|
+
* Called from the `goal` stream-chunk handler after each evaluation.
|
|
57
|
+
*/
|
|
58
|
+
applyEvaluation(update: {
|
|
59
|
+
runsUsed: number;
|
|
60
|
+
status: GoalStatus;
|
|
61
|
+
}): GoalState | null;
|
|
62
|
+
/**
|
|
63
|
+
* Persist the active objective to ThreadState via the agent. The objective
|
|
64
|
+
* record is the source of truth; the legacy thread-metadata key is cleared so
|
|
65
|
+
* stale state from older sessions does not resurface.
|
|
66
|
+
*/
|
|
67
|
+
saveToThread(state: GoalManagerState): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Load the objective from ThreadState (called on thread switch). Falls back to
|
|
70
|
+
* the legacy thread-metadata goal for threads created before this migration.
|
|
71
|
+
*/
|
|
72
|
+
loadFromThread(state: GoalManagerState): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Legacy entry point retained for thread-switch call sites that only have the
|
|
75
|
+
* thread metadata available. Hydrates from a previously-persisted GoalState.
|
|
76
|
+
*/
|
|
77
|
+
loadFromThreadMetadata(metadata: Record<string, unknown> | undefined): void;
|
|
78
|
+
private getAgent;
|
|
79
|
+
/** Resolve effective judge model + max runs (record value → settings default). */
|
|
80
|
+
private effectiveSettings;
|
|
81
|
+
private localRecord;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=goal-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-manager.d.ts","sourceRoot":"","sources":["../src/goal-manager.ts"],"names":[],"mappings":"AAeA,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;AAOpC,qBAAa,WAAW;IACtB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAuD;IACrE,oFAAoF;IACpF,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,6BAA6B,CAAS;IAM9C,OAAO,IAAI,SAAS,GAAG,IAAI;IAgB3B,QAAQ,IAAI,OAAO;IAInB,yBAAyB,IAAI,IAAI;IAIjC,gCAAgC,IAAI,OAAO;IAM3C,gBAAgB,IAAI,IAAI;IAMxB,eAAe,IAAI,IAAI;IASvB,iFAAiF;IACjF,gBAAgB,IAAI,IAAI;IASxB;;;;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;IAQxC,MAAM,IAAI,SAAS,GAAG,IAAI;IAQ1B,QAAQ,IAAI,IAAI;IAOhB,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;IAWnF;;;;OAIG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+C1D;;;OAGG;IACG,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB5D;;;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;CAkBpB"}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { loadSettings } from "./onboarding/settings.js";
|
|
3
|
+
const DEFAULT_MAX_TURNS = 50;
|
|
4
|
+
const THREAD_GOAL_KEY = "goal";
|
|
5
|
+
class GoalManager {
|
|
6
|
+
/** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */
|
|
7
|
+
record = null;
|
|
8
|
+
/** Display-only active-timer accounting (not persisted to the objective record). */
|
|
9
|
+
activeStartedAt = null;
|
|
10
|
+
activeDurationMs = 0;
|
|
11
|
+
persistGoalOnNextThreadCreate = false;
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Synchronous TUI surface
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
getGoal() {
|
|
16
|
+
if (!this.record) return null;
|
|
17
|
+
const { judgeModelId, maxTurns } = this.effectiveSettings(this.record);
|
|
18
|
+
return {
|
|
19
|
+
id: this.record.id,
|
|
20
|
+
objective: this.record.objective,
|
|
21
|
+
status: this.record.status,
|
|
22
|
+
turnsUsed: this.record.runsUsed,
|
|
23
|
+
maxTurns,
|
|
24
|
+
judgeModelId,
|
|
25
|
+
startedAt: new Date(this.record.startedAt).toISOString(),
|
|
26
|
+
activeStartedAt: this.activeStartedAt ?? void 0,
|
|
27
|
+
activeDurationMs: this.activeDurationMs
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
isActive() {
|
|
31
|
+
return this.record?.status === "active";
|
|
32
|
+
}
|
|
33
|
+
persistOnNextThreadCreate() {
|
|
34
|
+
this.persistGoalOnNextThreadCreate = true;
|
|
35
|
+
}
|
|
36
|
+
consumePersistOnNextThreadCreate() {
|
|
37
|
+
if (!this.persistGoalOnNextThreadCreate) return false;
|
|
38
|
+
this.persistGoalOnNextThreadCreate = false;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
startActiveTimer() {
|
|
42
|
+
if (this.record?.status === "active" && !this.activeStartedAt) {
|
|
43
|
+
this.activeStartedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
stopActiveTimer() {
|
|
47
|
+
if (!this.activeStartedAt) return;
|
|
48
|
+
const startedMs = Date.parse(this.activeStartedAt);
|
|
49
|
+
if (Number.isFinite(startedMs)) {
|
|
50
|
+
this.activeDurationMs += Math.max(0, Date.now() - startedMs);
|
|
51
|
+
}
|
|
52
|
+
this.activeStartedAt = null;
|
|
53
|
+
}
|
|
54
|
+
/** Reset active-timer accounting to zero (e.g. for an untriggered plan goal). */
|
|
55
|
+
resetActiveTimer() {
|
|
56
|
+
this.activeStartedAt = null;
|
|
57
|
+
this.activeDurationMs = 0;
|
|
58
|
+
}
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Objective lifecycle (ThreadState-backed via the agent)
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
/**
|
|
63
|
+
* Set a new objective. Persists to ThreadState via `agent.setObjective` and
|
|
64
|
+
* updates the in-memory view. Only the provided settings are persisted into
|
|
65
|
+
* the record; unset ones fall back to the agent's `goal` config at read time.
|
|
66
|
+
*/
|
|
67
|
+
async setGoal(state, objective, judgeModelId, maxTurns = DEFAULT_MAX_TURNS) {
|
|
68
|
+
const threadId = state.session.thread.getId();
|
|
69
|
+
const agent = this.getAgent(state);
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
const id = randomUUID();
|
|
72
|
+
if (agent && threadId) {
|
|
73
|
+
const persisted = await agent.setObjective(objective, {
|
|
74
|
+
id,
|
|
75
|
+
threadId,
|
|
76
|
+
resourceId: state.session.identity.getResourceId(),
|
|
77
|
+
...judgeModelId ? { judgeModelId } : {},
|
|
78
|
+
maxRuns: maxTurns
|
|
79
|
+
});
|
|
80
|
+
this.record = persisted ? { ...persisted, id: persisted.id ?? id } : this.localRecord(objective, judgeModelId, maxTurns, now, id);
|
|
81
|
+
} else {
|
|
82
|
+
this.record = this.localRecord(objective, judgeModelId, maxTurns, now, id);
|
|
83
|
+
}
|
|
84
|
+
this.activeStartedAt = new Date(now).toISOString();
|
|
85
|
+
this.activeDurationMs = 0;
|
|
86
|
+
return this.getGoal();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Update the judge model / max-runs defaults. Persists into the active record
|
|
90
|
+
* (so the override is remembered in thread state) when a goal is set.
|
|
91
|
+
*/
|
|
92
|
+
async updateJudgeDefaults(state, judgeModelId, maxTurns) {
|
|
93
|
+
if (!this.record) return null;
|
|
94
|
+
const threadId = state.session.thread.getId();
|
|
95
|
+
const agent = this.getAgent(state);
|
|
96
|
+
if (agent && threadId) {
|
|
97
|
+
const updated = await agent.updateObjectiveOptions({
|
|
98
|
+
threadId,
|
|
99
|
+
...judgeModelId ? { judgeModelId } : {},
|
|
100
|
+
maxRuns: maxTurns
|
|
101
|
+
});
|
|
102
|
+
if (updated) this.record = { ...updated, id: this.record.id };
|
|
103
|
+
} else {
|
|
104
|
+
this.record = {
|
|
105
|
+
...this.record,
|
|
106
|
+
...judgeModelId ? { judgeModelId } : {},
|
|
107
|
+
maxRuns: maxTurns,
|
|
108
|
+
updatedAt: Date.now()
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return this.getGoal();
|
|
112
|
+
}
|
|
113
|
+
pause(reason) {
|
|
114
|
+
if (this.record && this.record.status === "active") {
|
|
115
|
+
this.stopActiveTimer();
|
|
116
|
+
this.record = { ...this.record, status: "paused", pausedReason: reason, updatedAt: Date.now() };
|
|
117
|
+
}
|
|
118
|
+
return this.getGoal();
|
|
119
|
+
}
|
|
120
|
+
resume() {
|
|
121
|
+
if (this.record && this.record.status === "paused") {
|
|
122
|
+
this.record = { ...this.record, status: "active", pausedReason: void 0, updatedAt: Date.now() };
|
|
123
|
+
this.startActiveTimer();
|
|
124
|
+
}
|
|
125
|
+
return this.getGoal();
|
|
126
|
+
}
|
|
127
|
+
markDone() {
|
|
128
|
+
if (this.record) {
|
|
129
|
+
this.stopActiveTimer();
|
|
130
|
+
this.record = { ...this.record, status: "done", updatedAt: Date.now() };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
clear() {
|
|
134
|
+
this.record = null;
|
|
135
|
+
this.activeStartedAt = null;
|
|
136
|
+
this.activeDurationMs = 0;
|
|
137
|
+
this.persistGoalOnNextThreadCreate = false;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Sync the latest objective record from ThreadState into the in-memory view.
|
|
141
|
+
* Called from the `goal` stream-chunk handler after each evaluation.
|
|
142
|
+
*/
|
|
143
|
+
applyEvaluation(update) {
|
|
144
|
+
if (!this.record) return null;
|
|
145
|
+
this.record = { ...this.record, runsUsed: update.runsUsed, status: update.status, updatedAt: Date.now() };
|
|
146
|
+
if (update.status !== "active") this.stopActiveTimer();
|
|
147
|
+
return this.getGoal();
|
|
148
|
+
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Persistence
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
/**
|
|
153
|
+
* Persist the active objective to ThreadState via the agent. The objective
|
|
154
|
+
* record is the source of truth; the legacy thread-metadata key is cleared so
|
|
155
|
+
* stale state from older sessions does not resurface.
|
|
156
|
+
*/
|
|
157
|
+
async saveToThread(state) {
|
|
158
|
+
const threadId = state.session.thread.getId();
|
|
159
|
+
const agent = this.getAgent(state);
|
|
160
|
+
try {
|
|
161
|
+
if (agent && threadId) {
|
|
162
|
+
if (this.record) {
|
|
163
|
+
const updated = await agent.updateObjectiveOptions({
|
|
164
|
+
threadId,
|
|
165
|
+
status: this.record.status,
|
|
166
|
+
...this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {},
|
|
167
|
+
...this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {},
|
|
168
|
+
...this.record.maxRuns !== void 0 ? { maxRuns: this.record.maxRuns } : {}
|
|
169
|
+
});
|
|
170
|
+
if (!updated) {
|
|
171
|
+
const desiredStatus = this.record.status;
|
|
172
|
+
await agent.setObjective(this.record.objective, {
|
|
173
|
+
id: this.record.id,
|
|
174
|
+
threadId,
|
|
175
|
+
resourceId: state.session.identity.getResourceId(),
|
|
176
|
+
...this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {},
|
|
177
|
+
...this.record.maxRuns !== void 0 ? { maxRuns: this.record.maxRuns } : {}
|
|
178
|
+
});
|
|
179
|
+
if (desiredStatus !== "active") {
|
|
180
|
+
await agent.updateObjectiveOptions({
|
|
181
|
+
threadId,
|
|
182
|
+
status: desiredStatus,
|
|
183
|
+
...this.record.pausedReason ? { pausedReason: this.record.pausedReason } : {}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
await agent.clearObjective({ threadId });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
await state.session.thread.setSetting({ key: THREAD_GOAL_KEY, value: void 0 });
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Load the objective from ThreadState (called on thread switch). Falls back to
|
|
197
|
+
* the legacy thread-metadata goal for threads created before this migration.
|
|
198
|
+
*/
|
|
199
|
+
async loadFromThread(state) {
|
|
200
|
+
this.persistGoalOnNextThreadCreate = false;
|
|
201
|
+
this.activeStartedAt = null;
|
|
202
|
+
this.activeDurationMs = 0;
|
|
203
|
+
const threadId = state.session.thread.getId();
|
|
204
|
+
const agent = this.getAgent(state);
|
|
205
|
+
if (agent && threadId) {
|
|
206
|
+
try {
|
|
207
|
+
const record = await agent.getObjective({ threadId });
|
|
208
|
+
if (record) {
|
|
209
|
+
this.record = { ...record, id: record.id ?? randomUUID() };
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
} catch {
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
this.record = null;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Legacy entry point retained for thread-switch call sites that only have the
|
|
219
|
+
* thread metadata available. Hydrates from a previously-persisted GoalState.
|
|
220
|
+
*/
|
|
221
|
+
loadFromThreadMetadata(metadata) {
|
|
222
|
+
const saved = metadata?.[THREAD_GOAL_KEY];
|
|
223
|
+
this.persistGoalOnNextThreadCreate = false;
|
|
224
|
+
this.activeStartedAt = null;
|
|
225
|
+
this.activeDurationMs = 0;
|
|
226
|
+
if (saved && saved.objective && saved.status) {
|
|
227
|
+
this.record = {
|
|
228
|
+
objective: saved.objective,
|
|
229
|
+
status: saved.status,
|
|
230
|
+
runsUsed: saved.turnsUsed ?? 0,
|
|
231
|
+
maxRuns: saved.maxTurns ?? DEFAULT_MAX_TURNS,
|
|
232
|
+
judgeModelId: saved.judgeModelId ?? "",
|
|
233
|
+
startedAt: saved.startedAt ? Date.parse(saved.startedAt) || Date.now() : Date.now(),
|
|
234
|
+
updatedAt: Date.now(),
|
|
235
|
+
id: saved.id ?? randomUUID()
|
|
236
|
+
};
|
|
237
|
+
this.activeDurationMs = saved.activeDurationMs ?? 0;
|
|
238
|
+
} else {
|
|
239
|
+
this.record = null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
// Private
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
getAgent(state) {
|
|
246
|
+
try {
|
|
247
|
+
return state.controller.getCurrentAgent(state.session);
|
|
248
|
+
} catch {
|
|
249
|
+
return void 0;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/** Resolve effective judge model + max runs (record value → settings default). */
|
|
253
|
+
effectiveSettings(record) {
|
|
254
|
+
const settings = loadSettings();
|
|
255
|
+
return {
|
|
256
|
+
judgeModelId: record.judgeModelId ?? settings.models.goalJudgeModel ?? "",
|
|
257
|
+
maxTurns: record.maxRuns ?? settings.models.goalMaxTurns ?? DEFAULT_MAX_TURNS
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
localRecord(objective, judgeModelId, maxTurns, now, id) {
|
|
261
|
+
return {
|
|
262
|
+
objective,
|
|
263
|
+
status: "active",
|
|
264
|
+
runsUsed: 0,
|
|
265
|
+
maxRuns: maxTurns,
|
|
266
|
+
...judgeModelId ? { judgeModelId } : {},
|
|
267
|
+
startedAt: now,
|
|
268
|
+
updatedAt: now,
|
|
269
|
+
id
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
export {
|
|
274
|
+
DEFAULT_MAX_TURNS,
|
|
275
|
+
GoalManager
|
|
276
|
+
};
|
|
277
|
+
//# sourceMappingURL=goal-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/goal-manager.ts"],"sourcesContent":["/**\n * GoalManager — persistent cross-turn goals, backed by the Agent's native goal\n * mechanism.\n *\n * The objective lives in the durable `threadState` `'goal'` slot (via\n * `agent.setObjective`/`getObjective`/`clearObjective`/`updateObjectiveOptions`)\n * and is judged in-loop by the core goal step. This manager is a thin adapter:\n * it keeps a synchronous in-memory view of the current objective for the TUI\n * (status line, modal, keyboard shortcuts) and delegates persistence to the\n * agent. There is no standalone judge agent and no between-turn re-invocation —\n * the core goal step drives continuation and surfaces progress via `goal` stream\n * chunks.\n */\nimport { randomUUID } from 'node:crypto';\nimport 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\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 /** Display-only active-timer accounting (not persisted to the objective record). */\n private activeStartedAt: string | null = null;\n private activeDurationMs = 0;\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 activeStartedAt: this.activeStartedAt ?? undefined,\n activeDurationMs: this.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 startActiveTimer(): void {\n if (this.record?.status === 'active' && !this.activeStartedAt) {\n this.activeStartedAt = new Date().toISOString();\n }\n }\n\n stopActiveTimer(): void {\n if (!this.activeStartedAt) return;\n const startedMs = Date.parse(this.activeStartedAt);\n if (Number.isFinite(startedMs)) {\n this.activeDurationMs += Math.max(0, Date.now() - startedMs);\n }\n this.activeStartedAt = null;\n }\n\n /** Reset active-timer accounting to zero (e.g. for an untriggered plan goal). */\n resetActiveTimer(): void {\n this.activeStartedAt = null;\n this.activeDurationMs = 0;\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\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 this.activeStartedAt = new Date(now).toISOString();\n this.activeDurationMs = 0;\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.stopActiveTimer();\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 this.startActiveTimer();\n }\n return this.getGoal();\n }\n\n markDone(): void {\n if (this.record) {\n this.stopActiveTimer();\n this.record = { ...this.record, status: 'done', updatedAt: Date.now() };\n }\n }\n\n clear(): void {\n this.record = null;\n this.activeStartedAt = null;\n this.activeDurationMs = 0;\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 if (update.status !== 'active') this.stopActiveTimer();\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 ...(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 this.activeStartedAt = null;\n this.activeDurationMs = 0;\n\n const threadId = state.session.thread.getId();\n const agent = this.getAgent(state);\n if (agent && threadId) {\n try {\n const record = await agent.getObjective({ threadId });\n if (record) {\n this.record = { ...record, id: record.id ?? randomUUID() };\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.activeStartedAt = null;\n this.activeDurationMs = 0;\n if (saved && saved.objective && saved.status) {\n this.record = {\n objective: saved.objective,\n status: saved.status,\n runsUsed: saved.turnsUsed ?? 0,\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 this.activeDurationMs = saved.activeDurationMs ?? 0;\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 maxRuns: maxTurns,\n ...(judgeModelId ? { judgeModelId } : {}),\n startedAt: now,\n updatedAt: now,\n id,\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GoalState } from './goal-manager.js';
|
|
2
|
+
export declare function createGoalReminderSignal(goal: GoalState): {
|
|
3
|
+
type: "system-reminder";
|
|
4
|
+
contents: string;
|
|
5
|
+
attributes: {
|
|
6
|
+
type: string;
|
|
7
|
+
};
|
|
8
|
+
metadata: {
|
|
9
|
+
goalId: string;
|
|
10
|
+
maxTurns: number;
|
|
11
|
+
judgeModelId: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=goal-signal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-signal.d.ts","sourceRoot":"","sources":["../src/goal-signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,SAAS;;;;;;;;;;;EAWvD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function createGoalReminderSignal(goal) {
|
|
2
|
+
return {
|
|
3
|
+
type: "system-reminder",
|
|
4
|
+
contents: goal.objective,
|
|
5
|
+
attributes: { type: "goal" },
|
|
6
|
+
metadata: {
|
|
7
|
+
goalId: goal.id,
|
|
8
|
+
maxTurns: goal.maxTurns,
|
|
9
|
+
judgeModelId: goal.judgeModelId
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
createGoalReminderSignal
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=goal-signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/goal-signal.ts"],"sourcesContent":["import type { GoalState } from './goal-manager.js';\n\nexport function createGoalReminderSignal(goal: GoalState) {\n return {\n type: 'system-reminder' as const,\n contents: goal.objective,\n attributes: { type: 'goal' },\n metadata: {\n goalId: goal.id,\n maxTurns: goal.maxTurns,\n judgeModelId: goal.judgeModelId,\n },\n };\n}\n"],"mappings":"AAEO,SAAS,yBAAyB,MAAiB;AACxD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,KAAK;AAAA,IACf,YAAY,EAAE,MAAM,OAAO;AAAA,IAC3B,UAAU;AAAA,MACR,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf,cAAc,KAAK;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/headless/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,oBAAoB,
|
|
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"}
|
package/dist/headless/format.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
function truncate(s, max) {
|
|
2
2
|
return s.length > max ? s.slice(0, max) + "..." : s;
|
|
3
3
|
}
|
|
4
|
+
function messageText(message) {
|
|
5
|
+
return message.content.parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
6
|
+
}
|
|
4
7
|
function createHumanFormatState() {
|
|
5
8
|
return { lastTextLength: 0 };
|
|
6
9
|
}
|
|
@@ -10,7 +13,7 @@ function formatHuman(event, state) {
|
|
|
10
13
|
state.lastTextLength = 0;
|
|
11
14
|
return {};
|
|
12
15
|
case "message_update": {
|
|
13
|
-
const fullText = event.message
|
|
16
|
+
const fullText = messageText(event.message);
|
|
14
17
|
if (fullText.length > state.lastTextLength) {
|
|
15
18
|
const delta = fullText.slice(state.lastTextLength);
|
|
16
19
|
state.lastTextLength = fullText.length;
|
|
@@ -20,7 +23,7 @@ function formatHuman(event, state) {
|
|
|
20
23
|
}
|
|
21
24
|
case "message_end": {
|
|
22
25
|
if (event.message.role !== "assistant") return {};
|
|
23
|
-
const fullText = event.message
|
|
26
|
+
const fullText = messageText(event.message);
|
|
24
27
|
const delta = fullText.length > state.lastTextLength ? fullText.slice(state.lastTextLength) : "";
|
|
25
28
|
state.lastTextLength = 0;
|
|
26
29
|
return { stdout: delta + "\n" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"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 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 = event.message
|
|
1
|
+
{"version":3,"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":"AAUO,SAAS,SAAS,GAAW,KAAqB;AACvD,SAAO,EAAE,SAAS,MAAM,EAAE,MAAM,GAAG,GAAG,IAAI,QAAQ;AACpD;AAGA,SAAS,YAAY,SAAkC;AACrD,SAAO,QAAQ,QAAQ,MACpB,OAAO,CAAC,MAAiD,EAAE,SAAS,UAAU,OAAO,EAAE,SAAS,QAAQ,EACxG,IAAI,OAAK,EAAE,IAAI,EACf,KAAK,EAAE;AACZ;AAOO,SAAS,yBAA2C;AACzD,SAAO,EAAE,gBAAgB,EAAE;AAC7B;AAcO,SAAS,YAAY,OAA6B,OAA0C;AACjG,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,YAAM,iBAAiB;AACvB,aAAO,CAAC;AAAA,IACV,KAAK,kBAAkB;AACrB,YAAM,WAAW,YAAY,MAAM,OAAO;AAC1C,UAAI,SAAS,SAAS,MAAM,gBAAgB;AAC1C,cAAM,QAAQ,SAAS,MAAM,MAAM,cAAc;AACjD,cAAM,iBAAiB,SAAS;AAChC,eAAO,EAAE,QAAQ,MAAM;AAAA,MACzB;AACA,aAAO,CAAC;AAAA,IACV;AAAA,IACA,KAAK,eAAe;AAIlB,UAAI,MAAM,QAAQ,SAAS,YAAa,QAAO,CAAC;AAIhD,YAAM,WAAW,YAAY,MAAM,OAAO;AAC1C,YAAM,QAAQ,SAAS,SAAS,MAAM,iBAAiB,SAAS,MAAM,MAAM,cAAc,IAAI;AAC9F,YAAM,iBAAiB;AACvB,aAAO,EAAE,QAAQ,QAAQ,KAAK;AAAA,IAChC;AAAA,IACA,KAAK;AACH,aAAO,EAAE,QAAQ,UAAU,MAAM,QAAQ;AAAA,EAAK;AAAA,IAChD,KAAK;AACH,aAAO,MAAM,UAAU,EAAE,QAAQ,gBAAgB,SAAS,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC;AAAA,EAAK,IAAI,CAAC;AAAA,IAChG,KAAK;AACH,aAAO,EAAE,QAAQ,MAAM,OAAO;AAAA,IAChC,KAAK;AACH,aAAO;AAAA,QACL,QAAQ,aAAa,MAAM,SAAS,YAAY,EAAE,GAAG,MAAM,SAAS,KAAK,SAAS,MAAM,MAAM,GAAG,CAAC;AAAA;AAAA,MACpG;AAAA,IACF,KAAK;AACH,aAAO,MAAM,UAAU,EAAE,QAAQ,oBAAoB,SAAS,MAAM,QAAQ,GAAG,CAAC;AAAA,EAAK,IAAI,CAAC;AAAA,IAC5F,KAAK;AACH,aAAO,EAAE,QAAQ,WAAW,MAAM,MAAM,OAAO;AAAA,EAAK;AAAA,IACtD;AACE,aAAO,CAAC;AAAA,EACZ;AACF;AAGA,SAAS,eAAe,KAA+D;AACrF,SAAO,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,SAAS,OAAO,IAAI,MAAM;AAClE;AAQO,SAAS,YAAY,OAAsD;AAChF,QAAM,MAA+B,EAAE,GAAG,MAAM;AAChD,MAAI,WAAW,SAAS,MAAM,iBAAiB,OAAO;AACpD,QAAI,QAAQ,eAAe,MAAM,KAAK;AAAA,EACxC;AACA,SAAO;AACT;AAGO,SAAS,iBAAiB,QAA6B;AAC5D,SAAO,OAAO,KAAK,SAAS,IAAI,IAAI,OAAO,OAAO,OAAO,OAAO;AAClE;AAGO,SAAS,iBAAiB,QAA6B;AAC5D,SACE,KAAK,UAAU;AAAA,IACb,QAAQ,OAAO;AAAA,IACf,MAAM,OAAO;AAAA,IACb,cAAc,OAAO;AAAA,IACrB,OAAO,OAAO;AAAA,IACd,WAAW,OAAO;AAAA,IAClB,aAAa,OAAO;AAAA,IACpB,OAAO,OAAO;AAAA,IACd,UAAU,OAAO;AAAA,IACjB,UAAU,OAAO;AAAA,EACnB,CAAC,IAAI;AAET;","names":[]}
|
package/dist/headless/index.d.ts
CHANGED
|
@@ -20,6 +20,6 @@ export { formatHuman, formatJsonl, renderTextResult, renderJsonResult, createHum
|
|
|
20
20
|
export type { FormattedOutput, HumanFormatState } from './format.js';
|
|
21
21
|
export { runMCCli, hasHeadlessFlag, parseHeadlessArgs, printHeadlessUsage } from './cli.js';
|
|
22
22
|
export type { HeadlessArgs, OutputMode } from './cli.js';
|
|
23
|
-
export type { RunMCOptions, RunMCResult, RunMCStatus, RunMCUsage, RunMCToolCall, RunMCToolResult, RunMCError, RunMCThreadOptions, MCRun, ResolutionPolicy, RunMode, ThinkingLevel, PermissionMode, } from './types.js';
|
|
23
|
+
export type { RunMCOptions, RunMCResult, RunMCStatus, RunMCUsage, RunMCToolCall, RunMCToolResult, RunMCError, RunMCThreadOptions, MCRun, RunMCGoalOptions, ResolutionPolicy, RunMode, ThinkingLevel, PermissionMode, } from './types.js';
|
|
24
24
|
export { VALID_MODES, VALID_THINKING_LEVELS, VALID_PERMISSION_MODES } from './types.js';
|
|
25
25
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/headless/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGpF,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGrE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC5F,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGzD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,KAAK,EACL,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/headless/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAGpF,OAAO,EACL,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,sBAAsB,EACtB,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGrE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC5F,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGzD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,KAAK,EACL,gBAAgB,EAChB,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/headless/index.ts"],"sourcesContent":["/**\n * Public headless / programmatic API for MastraCode.\n *\n * Programmatic (CI / Node) usage:\n * ```ts\n * import { createMastraCode } from 'mastracode';\n * import { runMC } from 'mastracode/headless';\n *\n * const { controller, session } = await createMastraCode({ settingsPath });\n * const run = runMC({ controller, session, prompt: 'Fix the bug' });\n * for await (const event of run) { ... } // optional live events\n * const result = await run.result; // typed RunMCResult\n * ```\n *\n * The CLI adapter (`runMCCli`) wraps the same `createMastraCode` → `runMC` flow.\n */\n\n// Core runner\nexport { runMC } from './run-mc.js';\n\n// Resolution policy\nexport { autoApprovePolicy, denyPolicy, permissionModeToPolicy } from './policy.js';\n\n// Formatters (pure, sink-agnostic)\nexport {\n formatHuman,\n formatJsonl,\n renderTextResult,\n renderJsonResult,\n createHumanFormatState,\n truncate,\n} from './format.js';\nexport type { FormattedOutput, HumanFormatState } from './format.js';\n\n// CLI adapter\nexport { runMCCli, hasHeadlessFlag, parseHeadlessArgs, printHeadlessUsage } from './cli.js';\nexport type { HeadlessArgs, OutputMode } from './cli.js';\n\n// Shared types\nexport type {\n RunMCOptions,\n RunMCResult,\n RunMCStatus,\n RunMCUsage,\n RunMCToolCall,\n RunMCToolResult,\n RunMCError,\n RunMCThreadOptions,\n MCRun,\n ResolutionPolicy,\n RunMode,\n ThinkingLevel,\n PermissionMode,\n} from './types.js';\nexport { VALID_MODES, VALID_THINKING_LEVELS, VALID_PERMISSION_MODES } from './types.js';\n"],"mappings":"AAkBA,SAAS,aAAa;AAGtB,SAAS,mBAAmB,YAAY,8BAA8B;AAGtE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,SAAS,UAAU,iBAAiB,mBAAmB,0BAA0B;
|
|
1
|
+
{"version":3,"sources":["../../src/headless/index.ts"],"sourcesContent":["/**\n * Public headless / programmatic API for MastraCode.\n *\n * Programmatic (CI / Node) usage:\n * ```ts\n * import { createMastraCode } from 'mastracode';\n * import { runMC } from 'mastracode/headless';\n *\n * const { controller, session } = await createMastraCode({ settingsPath });\n * const run = runMC({ controller, session, prompt: 'Fix the bug' });\n * for await (const event of run) { ... } // optional live events\n * const result = await run.result; // typed RunMCResult\n * ```\n *\n * The CLI adapter (`runMCCli`) wraps the same `createMastraCode` → `runMC` flow.\n */\n\n// Core runner\nexport { runMC } from './run-mc.js';\n\n// Resolution policy\nexport { autoApprovePolicy, denyPolicy, permissionModeToPolicy } from './policy.js';\n\n// Formatters (pure, sink-agnostic)\nexport {\n formatHuman,\n formatJsonl,\n renderTextResult,\n renderJsonResult,\n createHumanFormatState,\n truncate,\n} from './format.js';\nexport type { FormattedOutput, HumanFormatState } from './format.js';\n\n// CLI adapter\nexport { runMCCli, hasHeadlessFlag, parseHeadlessArgs, printHeadlessUsage } from './cli.js';\nexport type { HeadlessArgs, OutputMode } from './cli.js';\n\n// Shared types\nexport type {\n RunMCOptions,\n RunMCResult,\n RunMCStatus,\n RunMCUsage,\n RunMCToolCall,\n RunMCToolResult,\n RunMCError,\n RunMCThreadOptions,\n MCRun,\n RunMCGoalOptions,\n ResolutionPolicy,\n RunMode,\n ThinkingLevel,\n PermissionMode,\n} from './types.js';\nexport { VALID_MODES, VALID_THINKING_LEVELS, VALID_PERMISSION_MODES } from './types.js';\n"],"mappings":"AAkBA,SAAS,aAAa;AAGtB,SAAS,mBAAmB,YAAY,8BAA8B;AAGtE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,SAAS,UAAU,iBAAiB,mBAAmB,0BAA0B;AAoBjF,SAAS,aAAa,uBAAuB,8BAA8B;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-mc.d.ts","sourceRoot":"","sources":["../../src/headless/run-mc.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/headless/run-mc.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import { GoalManager } from "../goal-manager.js";
|
|
2
|
+
import { createGoalReminderSignal } from "../goal-signal.js";
|
|
1
3
|
import { autoApprovePolicy } from "./policy.js";
|
|
2
4
|
function extractAssistantText(message) {
|
|
3
|
-
return message.content.filter((
|
|
5
|
+
return message.content.parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
4
6
|
}
|
|
5
7
|
function exitCodeForStatus(status) {
|
|
6
8
|
switch (status) {
|
|
7
9
|
case "completed":
|
|
10
|
+
case "done":
|
|
8
11
|
return 0;
|
|
9
12
|
case "timeout":
|
|
10
13
|
return 2;
|
|
@@ -96,7 +99,8 @@ class EventQueue {
|
|
|
96
99
|
}
|
|
97
100
|
}
|
|
98
101
|
function runMC(options) {
|
|
99
|
-
const { controller, session
|
|
102
|
+
const { controller, session } = options;
|
|
103
|
+
const goal = options.goal;
|
|
100
104
|
const policy = options.policy ?? autoApprovePolicy;
|
|
101
105
|
const queue = new EventQueue();
|
|
102
106
|
const acc = { text: "", toolCalls: [], toolResults: [] };
|
|
@@ -111,7 +115,7 @@ function runMC(options) {
|
|
|
111
115
|
const result = new Promise((resolve) => {
|
|
112
116
|
resolveResult = resolve;
|
|
113
117
|
});
|
|
114
|
-
function finish(status) {
|
|
118
|
+
function finish(status, data = {}) {
|
|
115
119
|
if (settled) return;
|
|
116
120
|
settled = true;
|
|
117
121
|
if (timeoutId) clearTimeout(timeoutId);
|
|
@@ -126,7 +130,8 @@ function runMC(options) {
|
|
|
126
130
|
toolResults: acc.toolResults,
|
|
127
131
|
threadId: acc.threadId ?? session.thread.getId() ?? void 0,
|
|
128
132
|
error: acc.error,
|
|
129
|
-
exitCode: exitCodeForStatus(status)
|
|
133
|
+
exitCode: exitCodeForStatus(status),
|
|
134
|
+
...data
|
|
130
135
|
});
|
|
131
136
|
}
|
|
132
137
|
function fail(message, name = "Error") {
|
|
@@ -145,13 +150,17 @@ function runMC(options) {
|
|
|
145
150
|
options.signal.addEventListener("abort", () => abort(), { once: true });
|
|
146
151
|
}
|
|
147
152
|
}
|
|
153
|
+
function armTimeout() {
|
|
154
|
+
if (!options.timeoutMs || settled) return;
|
|
155
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
156
|
+
timeoutId = setTimeout(() => {
|
|
157
|
+
timedOut = true;
|
|
158
|
+
session.abort();
|
|
159
|
+
if (goal) finish("timeout");
|
|
160
|
+
}, options.timeoutMs);
|
|
161
|
+
}
|
|
148
162
|
void (async () => {
|
|
149
|
-
|
|
150
|
-
timeoutId = setTimeout(() => {
|
|
151
|
-
timedOut = true;
|
|
152
|
-
session.abort();
|
|
153
|
-
}, options.timeoutMs);
|
|
154
|
-
}
|
|
163
|
+
armTimeout();
|
|
155
164
|
try {
|
|
156
165
|
if (options.model) {
|
|
157
166
|
const available = await controller.listAvailableModels();
|
|
@@ -183,6 +192,7 @@ function runMC(options) {
|
|
|
183
192
|
}
|
|
184
193
|
unsubscribe = session.subscribe((event) => {
|
|
185
194
|
if (settled) return;
|
|
195
|
+
if (goal) armTimeout();
|
|
186
196
|
if (event.type === "tool_approval_required") {
|
|
187
197
|
let decision;
|
|
188
198
|
try {
|
|
@@ -217,6 +227,10 @@ function runMC(options) {
|
|
|
217
227
|
}
|
|
218
228
|
aggregate(event, acc);
|
|
219
229
|
queue.push(event);
|
|
230
|
+
if (event.type === "error" && goal) {
|
|
231
|
+
finish("error");
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
220
234
|
if (event.type === "message_end" && event.message.role === "assistant" && options.maxTurns !== void 0) {
|
|
221
235
|
assistantTurns += 1;
|
|
222
236
|
if (assistantTurns >= options.maxTurns && !maxTurnsExceeded) {
|
|
@@ -224,8 +238,21 @@ function runMC(options) {
|
|
|
224
238
|
abort();
|
|
225
239
|
}
|
|
226
240
|
}
|
|
241
|
+
if (event.type === "goal_evaluation" && goal && !event.payload.pending) {
|
|
242
|
+
if (event.payload.status === "done" || event.payload.status === "paused") {
|
|
243
|
+
finish(event.payload.status, {
|
|
244
|
+
objective: goal.objective,
|
|
245
|
+
goalEvent: event,
|
|
246
|
+
reason: event.payload.reason ?? event.payload.results?.[0]?.reason,
|
|
247
|
+
iterations: event.payload.iteration,
|
|
248
|
+
maxRuns: event.payload.maxRuns
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
227
253
|
if (event.type === "agent_end") {
|
|
228
254
|
acc.finishReason = event.reason;
|
|
255
|
+
if (goal) return;
|
|
229
256
|
if (timedOut) {
|
|
230
257
|
finish("timeout");
|
|
231
258
|
} else if (event.reason === "error") {
|
|
@@ -277,9 +304,34 @@ function runMC(options) {
|
|
|
277
304
|
}
|
|
278
305
|
}
|
|
279
306
|
try {
|
|
280
|
-
|
|
307
|
+
if (goal) {
|
|
308
|
+
const threadId = session.thread.getId();
|
|
309
|
+
if (!threadId || !await session.thread.getById({ threadId })) {
|
|
310
|
+
await session.thread.create();
|
|
311
|
+
}
|
|
312
|
+
const goalManager = goal.goalManager ?? new GoalManager();
|
|
313
|
+
const state = {
|
|
314
|
+
controller,
|
|
315
|
+
session,
|
|
316
|
+
goalManager,
|
|
317
|
+
pendingNewThread: false,
|
|
318
|
+
planStartedGoalId: void 0
|
|
319
|
+
};
|
|
320
|
+
const objective = await goalManager.setGoal(state, goal.objective, goal.judgeModelId, goal.maxRuns);
|
|
321
|
+
if (!objective) return fail("Failed to set goal.");
|
|
322
|
+
const agent = controller.getCurrentAgent(session);
|
|
323
|
+
const persisted = await agent.getObjective({ threadId: session.thread.getId() });
|
|
324
|
+
if (!persisted || persisted.id !== objective.id) {
|
|
325
|
+
return fail("Failed to persist goal objective before sending goal signal.");
|
|
326
|
+
}
|
|
327
|
+
await goalManager.saveToThread(state);
|
|
328
|
+
if (!settled) await session.sendSignal(createGoalReminderSignal(objective)).accepted;
|
|
329
|
+
} else {
|
|
330
|
+
if (!options.prompt) return fail("A prompt or goal is required.");
|
|
331
|
+
await session.sendMessage({ content: options.prompt });
|
|
332
|
+
}
|
|
281
333
|
} catch (err) {
|
|
282
|
-
return fail(`Failed to
|
|
334
|
+
return fail(`Failed to start run: ${err.message}`);
|
|
283
335
|
}
|
|
284
336
|
})();
|
|
285
337
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"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, AgentControllerMessage, Session } from '@mastra/core/agent-controller';\n\nimport { autoApprovePolicy } from './policy.js';\nimport type { MCRun, ResolutionPolicy, RunMCOptions, RunMCResult, RunMCStatus } from './types.js';\n\nfunction extractAssistantText(message: AgentControllerMessage): string {\n return message.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map(c => c.text)\n .join('');\n}\n\nfunction exitCodeForStatus(status: RunMCStatus): number {\n switch (status) {\n case 'completed':\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, prompt } = options;\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): 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 });\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 // Kick off the run asynchronously so the MCRun handle is returned synchronously.\n void (async () => {\n if (options.timeoutMs) {\n timeoutId = setTimeout(() => {\n timedOut = true;\n session.abort();\n }, options.timeoutMs);\n }\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\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 // 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 === 'agent_end') {\n acc.finishReason = event.reason;\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 // --- Send ---\n try {\n await session.sendMessage({ content: prompt });\n } catch (err) {\n return fail(`Failed to send message: ${(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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","names":[]}
|
|
1
|
+
{"version":3,"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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","names":[]}
|
package/dist/headless/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* `process.*` access so the core API is usable from CI / Node code.
|
|
7
7
|
*/
|
|
8
8
|
import type { AgentController, AgentControllerEvent, Session } from '@mastra/core/agent-controller';
|
|
9
|
+
import type { GoalManager } from '../goal-manager.js';
|
|
9
10
|
export type RunMode = 'build' | 'plan' | 'fast';
|
|
10
11
|
export type ThinkingLevel = 'off' | 'low' | 'medium' | 'high' | 'xhigh';
|
|
11
12
|
export declare const VALID_MODES: readonly ["build", "plan", "fast"];
|
|
@@ -48,13 +49,11 @@ export interface RunMCThreadOptions {
|
|
|
48
49
|
/** Clone the resolved (or current) thread before running — work on a copy. */
|
|
49
50
|
clone?: boolean;
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
interface RunMCBaseOptions<TState extends Record<string, unknown> = Record<string, unknown>> {
|
|
52
53
|
/** Controller built via `createMastraCode(...)`. */
|
|
53
54
|
controller: AgentController<TState>;
|
|
54
55
|
/** Session built via `createMastraCode(...)`. */
|
|
55
56
|
session: Session<TState>;
|
|
56
|
-
/** The task to run. */
|
|
57
|
-
prompt: string;
|
|
58
57
|
/** Explicit model id override. Takes precedence over `mode`. */
|
|
59
58
|
model?: string;
|
|
60
59
|
/** Execution mode; resolves a model from `modeDefaults` when `model` is absent. */
|
|
@@ -69,7 +68,7 @@ export interface RunMCOptions<TState extends Record<string, unknown> = Record<st
|
|
|
69
68
|
resourceId?: string;
|
|
70
69
|
/** Set or rename the thread title before running. */
|
|
71
70
|
title?: string;
|
|
72
|
-
/** Abort with `status: 'timeout'` (exit code 2)
|
|
71
|
+
/** Abort with `status: 'timeout'` (exit code 2) after this many ms without an event. */
|
|
73
72
|
timeoutMs?: number;
|
|
74
73
|
/**
|
|
75
74
|
* Maximum number of agentic turns (assistant responses). When the limit is
|
|
@@ -82,7 +81,22 @@ export interface RunMCOptions<TState extends Record<string, unknown> = Record<st
|
|
|
82
81
|
/** External abort signal; aborting it aborts the run. */
|
|
83
82
|
signal?: AbortSignal;
|
|
84
83
|
}
|
|
85
|
-
export
|
|
84
|
+
export interface RunMCGoalOptions {
|
|
85
|
+
objective: string;
|
|
86
|
+
judgeModelId: string;
|
|
87
|
+
maxRuns: number;
|
|
88
|
+
goalManager?: GoalManager;
|
|
89
|
+
}
|
|
90
|
+
export type RunMCOptions<TState extends Record<string, unknown> = Record<string, unknown>> = RunMCBaseOptions<TState> & ({
|
|
91
|
+
/** The task to run as a regular headless turn. */
|
|
92
|
+
prompt: string;
|
|
93
|
+
goal?: never;
|
|
94
|
+
} | {
|
|
95
|
+
prompt?: never;
|
|
96
|
+
/** Run a persisted goal instead of a regular prompt. */
|
|
97
|
+
goal: RunMCGoalOptions;
|
|
98
|
+
});
|
|
99
|
+
export type RunMCStatus = 'completed' | 'done' | 'paused' | 'error' | 'aborted' | 'timeout' | 'max_turns';
|
|
86
100
|
export interface RunMCUsage {
|
|
87
101
|
inputTokens?: number;
|
|
88
102
|
outputTokens?: number;
|
|
@@ -108,6 +122,15 @@ export interface RunMCResult {
|
|
|
108
122
|
status: RunMCStatus;
|
|
109
123
|
/** Aggregated assistant text across all message_end events. */
|
|
110
124
|
text: string;
|
|
125
|
+
/** Goal objective when this was a goal run. */
|
|
126
|
+
objective?: string;
|
|
127
|
+
/** Terminal goal evaluation when this was a goal run. */
|
|
128
|
+
goalEvent?: Extract<AgentControllerEvent, {
|
|
129
|
+
type: 'goal_evaluation';
|
|
130
|
+
}>;
|
|
131
|
+
reason?: string;
|
|
132
|
+
iterations?: number;
|
|
133
|
+
maxRuns?: number;
|
|
111
134
|
/** Underlying finish reason from `agent_end`, when the run finished normally. */
|
|
112
135
|
finishReason?: string;
|
|
113
136
|
usage?: RunMCUsage;
|
|
@@ -131,4 +154,5 @@ export interface MCRun extends AsyncIterable<AgentControllerEvent> {
|
|
|
131
154
|
/** Abort the in-flight run. */
|
|
132
155
|
abort(): void;
|
|
133
156
|
}
|
|
157
|
+
export {};
|
|
134
158
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/headless/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAEpG,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAChD,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAExE,eAAO,MAAM,WAAW,oCAAqC,CAAC;AAC9D,eAAO,MAAM,qBAAqB,oDAAqD,CAAC;AAExF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;AAC7C,eAAO,MAAM,sBAAsB,2BAA4B,CAAC;AAEhE,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC,oBAAoB,EAAE;QAAE,IAAI,EAAE,wBAAwB,CAAA;KAAE,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7G;;;OAGG;IACH,YAAY,CACV,KAAK,EAAE,OAAO,CAAC,oBAAoB,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAA;KAAE,CAAC,GAC/D;QAAE,UAAU,EAAE,OAAO,CAAA;KAAE,GAAG;QAAE,KAAK,EAAE,IAAI,CAAA;KAAE,CAAC;CAC9C;AAED,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/headless/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/headless/types.ts"],"sourcesContent":["/**\n * Shared types for the headless / programmatic MastraCode API.\n *\n * These types are consumed by the core runner (`runMC`), the resolution policy,\n * the output formatters, and the CLI adapter. They are intentionally free of any\n * `process.*` access so the core API is usable from CI / Node code.\n */\nimport type { AgentController, AgentControllerEvent, Session } from '@mastra/core/agent-controller';\n\nexport type RunMode = 'build' | 'plan' | 'fast';\nexport type ThinkingLevel = 'off' | 'low' | 'medium' | 'high' | 'xhigh';\n\nexport const VALID_MODES = ['build', 'plan', 'fast'] as const;\nexport const VALID_THINKING_LEVELS = ['off', 'low', 'medium', 'high', 'xhigh'] as const;\n\n/**\n * Named permission modes for non-interactive runs. Maps to a built-in\n * {@link ResolutionPolicy}:\n * - `auto` — approve every tool and auto-resolve suspensions (the default).\n * - `deny` — refuse every tool approval and abort on any suspension.\n */\nexport type PermissionMode = 'auto' | 'deny';\nexport const VALID_PERMISSION_MODES = ['auto', 'deny'] as const;\n\n/** How `runMC` should resume `tool_approval_required` and `tool_suspended` events. */\nexport interface ResolutionPolicy {\n /**\n * Called for every `tool_approval_required` event. Return the decision; the\n * runner forwards it to `session.respondToToolApproval`.\n */\n onToolApproval(event: Extract<AgentControllerEvent, { type: 'tool_approval_required' }>): 'approve' | 'deny';\n /**\n * Called for every `tool_suspended` event. Return `resumeData` to resume the\n * tool, or `{ abort: true }` to abort the run.\n */\n onSuspension(\n event: Extract<AgentControllerEvent, { type: 'tool_suspended' }>,\n ): { resumeData: unknown } | { abort: true };\n}\n\n/** Thread selection / mutation options resolved up front by `runMC`. */\nexport interface RunMCThreadOptions {\n /** Resume a specific thread by its exact id. */\n id?: string;\n /** Resume the most recently updated thread instead of creating a new one. */\n continueLatest?: boolean;\n /** Clone the resolved (or current) thread before running — work on a copy. */\n clone?: boolean;\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../src/headless/types.ts"],"sourcesContent":["/**\n * Shared types for the headless / programmatic MastraCode API.\n *\n * These types are consumed by the core runner (`runMC`), the resolution policy,\n * the output formatters, and the CLI adapter. They are intentionally free of any\n * `process.*` access so the core API is usable from CI / Node code.\n */\nimport type { AgentController, AgentControllerEvent, Session } from '@mastra/core/agent-controller';\n\nimport type { GoalManager } from '../goal-manager.js';\n\nexport type RunMode = 'build' | 'plan' | 'fast';\nexport type ThinkingLevel = 'off' | 'low' | 'medium' | 'high' | 'xhigh';\n\nexport const VALID_MODES = ['build', 'plan', 'fast'] as const;\nexport const VALID_THINKING_LEVELS = ['off', 'low', 'medium', 'high', 'xhigh'] as const;\n\n/**\n * Named permission modes for non-interactive runs. Maps to a built-in\n * {@link ResolutionPolicy}:\n * - `auto` — approve every tool and auto-resolve suspensions (the default).\n * - `deny` — refuse every tool approval and abort on any suspension.\n */\nexport type PermissionMode = 'auto' | 'deny';\nexport const VALID_PERMISSION_MODES = ['auto', 'deny'] as const;\n\n/** How `runMC` should resume `tool_approval_required` and `tool_suspended` events. */\nexport interface ResolutionPolicy {\n /**\n * Called for every `tool_approval_required` event. Return the decision; the\n * runner forwards it to `session.respondToToolApproval`.\n */\n onToolApproval(event: Extract<AgentControllerEvent, { type: 'tool_approval_required' }>): 'approve' | 'deny';\n /**\n * Called for every `tool_suspended` event. Return `resumeData` to resume the\n * tool, or `{ abort: true }` to abort the run.\n */\n onSuspension(\n event: Extract<AgentControllerEvent, { type: 'tool_suspended' }>,\n ): { resumeData: unknown } | { abort: true };\n}\n\n/** Thread selection / mutation options resolved up front by `runMC`. */\nexport interface RunMCThreadOptions {\n /** Resume a specific thread by its exact id. */\n id?: string;\n /** Resume the most recently updated thread instead of creating a new one. */\n continueLatest?: boolean;\n /** Clone the resolved (or current) thread before running — work on a copy. */\n clone?: boolean;\n}\n\ninterface RunMCBaseOptions<TState extends Record<string, unknown> = Record<string, unknown>> {\n /** Controller built via `createMastraCode(...)`. */\n controller: AgentController<TState>;\n /** Session built via `createMastraCode(...)`. */\n session: Session<TState>;\n\n /** Explicit model id override. Takes precedence over `mode`. */\n model?: string;\n /** Execution mode; resolves a model from `modeDefaults` when `model` is absent. */\n mode?: RunMode;\n /** Per-mode default model ids (resolved from settings at startup). */\n modeDefaults?: Record<string, string>;\n /** Thinking-effort level. */\n thinkingLevel?: ThinkingLevel;\n /** Thread selection / mutation. */\n thread?: RunMCThreadOptions;\n /** Resource id for thread scoping. */\n resourceId?: string;\n /** Set or rename the thread title before running. */\n title?: string;\n /** Abort with `status: 'timeout'` (exit code 2) after this many ms without an event. */\n timeoutMs?: number;\n /**\n * Maximum number of agentic turns (assistant responses). When the limit is\n * reached the run aborts with `status: 'max_turns'` (exit code 1). No limit\n * by default.\n */\n maxTurns?: number;\n /** How approvals / suspensions are resolved. Defaults to {@link autoApprovePolicy}. */\n policy?: ResolutionPolicy;\n /** External abort signal; aborting it aborts the run. */\n signal?: AbortSignal;\n}\n\nexport interface RunMCGoalOptions {\n objective: string;\n judgeModelId: string;\n maxRuns: number;\n goalManager?: GoalManager;\n}\n\nexport type RunMCOptions<TState extends Record<string, unknown> = Record<string, unknown>> = RunMCBaseOptions<TState> &\n (\n | {\n /** The task to run as a regular headless turn. */\n prompt: string;\n goal?: never;\n }\n | {\n prompt?: never;\n /** Run a persisted goal instead of a regular prompt. */\n goal: RunMCGoalOptions;\n }\n );\n\nexport type RunMCStatus = 'completed' | 'done' | 'paused' | 'error' | 'aborted' | 'timeout' | 'max_turns';\n\nexport interface RunMCUsage {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n}\n\nexport interface RunMCToolCall {\n id: string;\n name: string;\n args: unknown;\n}\n\nexport interface RunMCToolResult {\n id: string;\n name: string;\n result: unknown;\n isError: boolean;\n}\n\nexport interface RunMCError {\n name: string;\n message: string;\n stack?: string;\n}\n\nexport interface RunMCResult {\n status: RunMCStatus;\n /** Aggregated assistant text across all message_end events. */\n text: string;\n /** Goal objective when this was a goal run. */\n objective?: string;\n /** Terminal goal evaluation when this was a goal run. */\n goalEvent?: Extract<AgentControllerEvent, { type: 'goal_evaluation' }>;\n reason?: string;\n iterations?: number;\n maxRuns?: number;\n /** Underlying finish reason from `agent_end`, when the run finished normally. */\n finishReason?: string;\n usage?: RunMCUsage;\n toolCalls: RunMCToolCall[];\n toolResults: RunMCToolResult[];\n threadId?: string;\n error?: RunMCError;\n /** 0 success, 1 error/aborted/max_turns, 2 timeout. */\n exitCode: number;\n}\n\n/**\n * A handle to an in-flight `runMC` run. It is async-iterable over controller\n * events and also resolves to a final {@link RunMCResult} via `result`.\n *\n * Both `for await (const e of run)` and `await run.result` work on the same run;\n * awaiting `result` without iterating still drains events internally.\n */\nexport interface MCRun extends AsyncIterable<AgentControllerEvent> {\n /** Resolves once the run completes, times out, errors, or is aborted. */\n result: Promise<RunMCResult>;\n /** Abort the in-flight run. */\n abort(): void;\n}\n"],"mappings":"AAcO,MAAM,cAAc,CAAC,SAAS,QAAQ,MAAM;AAC5C,MAAM,wBAAwB,CAAC,OAAO,OAAO,UAAU,QAAQ,OAAO;AAStE,MAAM,yBAAyB,CAAC,QAAQ,MAAM;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/code-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-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,15 +57,15 @@
|
|
|
57
57
|
"yaml": "^2.7.1",
|
|
58
58
|
"zod": "^4.3.6",
|
|
59
59
|
"@mastra/agent-browser": "0.4.1",
|
|
60
|
-
"@mastra/fastembed": "1.2.0",
|
|
61
60
|
"@mastra/duckdb": "1.5.1",
|
|
62
|
-
"@mastra/
|
|
63
|
-
"@mastra/
|
|
64
|
-
"@mastra/memory": "1.23.1-alpha.0",
|
|
61
|
+
"@mastra/fastembed": "1.2.0",
|
|
62
|
+
"@mastra/core": "1.52.0-alpha.3",
|
|
65
63
|
"@mastra/libsql": "1.16.0",
|
|
66
|
-
"@mastra/
|
|
64
|
+
"@mastra/github-signals": "0.2.2",
|
|
65
|
+
"@mastra/mcp": "1.15.0-alpha.0",
|
|
67
66
|
"@mastra/observability": "1.16.1",
|
|
68
|
-
"@mastra/
|
|
67
|
+
"@mastra/memory": "1.23.1-alpha.0",
|
|
68
|
+
"@mastra/pg": "1.16.1-alpha.0",
|
|
69
69
|
"@mastra/schema-compat": "1.3.4",
|
|
70
70
|
"@mastra/stagehand": "0.3.1-alpha.0",
|
|
71
71
|
"@mastra/tavily": "1.1.0"
|