@mastra/code-sdk 0.2.0-alpha.9 → 1.0.0-alpha.14
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 +142 -0
- package/README.md +20 -0
- package/dist/agents/workspace.d.ts.map +1 -1
- package/dist/agents/workspace.js +4 -4
- package/dist/agents/workspace.js.map +1 -1
- package/dist/auth/storage.js +2 -2
- package/dist/auth/storage.js.map +1 -1
- package/dist/constants.js +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/goal-manager.d.ts +2 -7
- package/dist/goal-manager.d.ts.map +1 -1
- package/dist/goal-manager.js +28 -37
- package/dist/goal-manager.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/config.d.ts +21 -1
- package/dist/mcp/config.d.ts.map +1 -1
- package/dist/mcp/config.js +30 -6
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/manager.d.ts +22 -0
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +193 -80
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/types.d.ts +24 -2
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/onboarding/packs.js +4 -4
- package/dist/onboarding/packs.js.map +1 -1
- package/dist/plugins/manager.d.ts +4 -0
- package/dist/plugins/manager.d.ts.map +1 -1
- package/dist/plugins/manager.js +18 -6
- package/dist/plugins/manager.js.map +1 -1
- package/dist/schema.d.ts +6 -3
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +2 -1
- package/dist/schema.js.map +1 -1
- package/package.json +15 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,147 @@
|
|
|
1
1
|
# @mastra/code-sdk
|
|
2
2
|
|
|
3
|
+
## 1.0.0-alpha.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Added a session notification when a GitHub plugin is automatically updated to its latest version ([#19943](https://github.com/mastra-ai/mastra/pull/19943))
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const unsubscribe = pluginManager.onGithubPluginsUpdated(pluginNames => {
|
|
11
|
+
console.log(`Updated plugins: ${pluginNames.join(', ')}`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Call during shutdown.
|
|
15
|
+
unsubscribe();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [[`d7385ad`](https://github.com/mastra-ai/mastra/commit/d7385ad9e88f9e4f33d15c0ec0bfebedde0cbc2e), [`3d6e539`](https://github.com/mastra-ai/mastra/commit/3d6e539272eb2ea0407034605ee1906b3be06b39), [`35865a5`](https://github.com/mastra-ai/mastra/commit/35865a53e194aa9634d6a70a97010e7a6b9d58b1), [`70687f7`](https://github.com/mastra-ai/mastra/commit/70687f7e495a322a02070b4a67cb0c77a5ca91ec), [`3d6e539`](https://github.com/mastra-ai/mastra/commit/3d6e539272eb2ea0407034605ee1906b3be06b39)]:
|
|
19
|
+
- @mastra/core@1.52.0-alpha.12
|
|
20
|
+
|
|
21
|
+
## 1.0.0-alpha.13
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies [[`c9e3521`](https://github.com/mastra-ai/mastra/commit/c9e3521628422db84e00a5ff1dea7426c8cce537)]:
|
|
26
|
+
- @mastra/pg@1.17.0-alpha.3
|
|
27
|
+
|
|
28
|
+
## 1.0.0-alpha.12
|
|
29
|
+
|
|
30
|
+
### Minor Changes
|
|
31
|
+
|
|
32
|
+
- Added an input processor extension for embedding surfaces while preserving Mastra Code's required processors. ([#19702](https://github.com/mastra-ai/mastra/pull/19702))
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- Improved local database safety by using rollback journals and closing storage during shutdown. ([#19901](https://github.com/mastra-ai/mastra/pull/19901))
|
|
37
|
+
|
|
38
|
+
- Updated dependencies [[`c7d30cd`](https://github.com/mastra-ai/mastra/commit/c7d30cd86009c407df91105591f03cd6e3d2854d), [`ef03fbc`](https://github.com/mastra-ai/mastra/commit/ef03fbcc556bcbc04c9b3d06fab88771ecaa043c), [`6193d6d`](https://github.com/mastra-ai/mastra/commit/6193d6d4ae62ad68daaaf450992198e9e49493f1), [`a7bbe77`](https://github.com/mastra-ai/mastra/commit/a7bbe773577f60bc4761b534ef7ec6b476332dad), [`a7bbe77`](https://github.com/mastra-ai/mastra/commit/a7bbe773577f60bc4761b534ef7ec6b476332dad), [`4e68363`](https://github.com/mastra-ai/mastra/commit/4e683634f94ebd062d26a3bb6093a8dfc7263d37), [`9251370`](https://github.com/mastra-ai/mastra/commit/9251370ad413af464aa22d7566338bec5613e8de)]:
|
|
39
|
+
- @mastra/core@1.52.0-alpha.11
|
|
40
|
+
- @mastra/libsql@1.17.0-alpha.3
|
|
41
|
+
|
|
42
|
+
## 1.0.0-alpha.11
|
|
43
|
+
|
|
44
|
+
### Minor Changes
|
|
45
|
+
|
|
46
|
+
- Add browser-based OAuth authentication for HTTP MCP servers to Mastra Code. ([#19467](https://github.com/mastra-ai/mastra/pull/19467))
|
|
47
|
+
|
|
48
|
+
When an HTTP MCP server rejects a connection with an authorization error, the
|
|
49
|
+
`/mcp` selector now shows a "needs auth" badge and an **Authenticate** action.
|
|
50
|
+
Choosing it opens the provider's consent page in the browser and completes the
|
|
51
|
+
OAuth 2.1 authorization-code flow (PKCE + Dynamic Client Registration) over a
|
|
52
|
+
loopback callback server, persists the tokens, and reconnects — no manual
|
|
53
|
+
configuration required for a bare `{ "url": ... }` server entry. A **Cancel
|
|
54
|
+
authentication** action aborts an in-flight flow and returns the server to the
|
|
55
|
+
needs-auth state.
|
|
56
|
+
|
|
57
|
+
The server manager gains `authenticateServer(name)` and
|
|
58
|
+
`cancelServerAuthentication(name)`, `McpServerStatus` gains an optional
|
|
59
|
+
`needsAuth` flag, and the OAuth `redirectUrl` in MCP server config is now
|
|
60
|
+
optional (it defaults to a stable loopback URL). The config also accepts
|
|
61
|
+
`callbackPort` as a shorthand that synthesizes
|
|
62
|
+
`http://localhost:<callbackPort>/callback`, the Claude Code / Codex
|
|
63
|
+
convention, so configs written for those clients (like Slack's official MCP
|
|
64
|
+
plugin config) work verbatim. `callbackPort` and `redirectUrl` are mutually
|
|
65
|
+
exclusive.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const server = manager.getServerStatuses().find(s => s.name === 'supabase');
|
|
69
|
+
if (server?.needsAuth) {
|
|
70
|
+
// Opens the consent page in the browser, completes the OAuth flow, and
|
|
71
|
+
// resolves with the reconnected server status.
|
|
72
|
+
const status = await manager.authenticateServer('supabase', {
|
|
73
|
+
onAuthorizationUrl: url => openInBrowser(url),
|
|
74
|
+
});
|
|
75
|
+
console.log(status.connected);
|
|
76
|
+
|
|
77
|
+
// Abort an abandoned browser flow and return the server to needs-auth:
|
|
78
|
+
// await manager.cancelServerAuthentication('supabase')
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 1.0.0-alpha.10
|
|
83
|
+
|
|
84
|
+
### Major Changes
|
|
85
|
+
|
|
86
|
+
- Replaced GitHub-specific Mastra Code session state with Factory project and linked-repository identities. This lets SDK consumers represent sessions independently of a source-control provider and select a repository explicitly when sandbox execution is required. ([#19849](https://github.com/mastra-ai/mastra/pull/19849))
|
|
87
|
+
|
|
88
|
+
Updated Mastra Code onboarding to be Factory-first: create a Factory by name, then link repositories from your connected source-control installations in a separate step. A Factory is valid with zero linked repositories, and the Board, Metrics, and Audit pages stay available for any server-backed Factory. Factory pages keep project-scoped data separate from repository-scoped intake and provide a repository selector when a Factory has multiple linked repositories. Creating a Factory from a local folder remains available as a secondary option.
|
|
89
|
+
|
|
90
|
+
**Before**
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const state = { githubProjectId: 'project-1', sandboxId, sandboxWorkdir };
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**After**
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const state = {
|
|
100
|
+
factoryProjectId: 'factory-project-1',
|
|
101
|
+
projectRepositoryId: 'project-repository-1',
|
|
102
|
+
sandboxId,
|
|
103
|
+
sandboxWorkdir,
|
|
104
|
+
};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Minor Changes
|
|
108
|
+
|
|
109
|
+
- Moved model packs in Mastra Code web to database-backed storage and refreshed the built-in packs. ([#19849](https://github.com/mastra-ai/mastra/pull/19849))
|
|
110
|
+
|
|
111
|
+
**Model packs are now stored in the Factory database**
|
|
112
|
+
|
|
113
|
+
When running with a Factory backend, custom model packs are saved in a new model-packs storage domain scoped to your organization instead of the local settings.json file. Local (non-tenant) mode keeps the file-backed behavior.
|
|
114
|
+
|
|
115
|
+
**Pick from available models**
|
|
116
|
+
|
|
117
|
+
The settings Model tab now loads the list of available models from a new /web/config/models endpoint, so the Factory default model picker and model pack editor only offer models you actually have credentials for. Model pickers are searchable comboboxes instead of plain dropdowns, and pack activation now resolves the correct scoped session so packs can be activated from settings.
|
|
118
|
+
|
|
119
|
+
**Default packs updated to the latest model releases**
|
|
120
|
+
|
|
121
|
+
- Anthropic: build and plan anthropic/claude-fable-5, fast anthropic/claude-haiku-4-5
|
|
122
|
+
- OpenAI: build and plan openai/gpt-5.6
|
|
123
|
+
- Observational memory default model is now google/gemini-3.5-flash
|
|
124
|
+
|
|
125
|
+
- Added a Factory default model for server-backed Factories in Mastra Code web. Set it in Settings under the Model tab and every factory run (like issue triage) starts on that model. The Model tab now also hosts model packs, replacing the separate Packs tab — packs stay session-scoped while the default model is stored on the Factory project itself. ([#19849](https://github.com/mastra-ai/mastra/pull/19849))
|
|
126
|
+
|
|
127
|
+
### Patch Changes
|
|
128
|
+
|
|
129
|
+
- dependencies updates: ([#19813](https://github.com/mastra-ai/mastra/pull/19813))
|
|
130
|
+
- Updated dependency [`@ai-sdk/amazon-bedrock@^3.0.107` ↗︎](https://www.npmjs.com/package/@ai-sdk/amazon-bedrock/v/3.0.107) (from `^3.0.105`, in `dependencies`)
|
|
131
|
+
- Updated dependency [`@ai-sdk/anthropic@^3.0.98` ↗︎](https://www.npmjs.com/package/@ai-sdk/anthropic/v/3.0.98) (from `^3.0.96`, in `dependencies`)
|
|
132
|
+
- Updated dependency [`@ai-sdk/openai@^3.0.86` ↗︎](https://www.npmjs.com/package/@ai-sdk/openai/v/3.0.86) (from `^3.0.84`, in `dependencies`)
|
|
133
|
+
- Updated dependency [`@ai-sdk/openai-compatible@^2.0.62` ↗︎](https://www.npmjs.com/package/@ai-sdk/openai-compatible/v/2.0.62) (from `^2.0.59`, in `dependencies`)
|
|
134
|
+
- Updated dependency [`ai@^6.0.230` ↗︎](https://www.npmjs.com/package/ai/v/6.0.230) (from `^6.0.225`, in `dependencies`)
|
|
135
|
+
|
|
136
|
+
- Fixed goal duration so it persists across pauses and process restarts. ([#19837](https://github.com/mastra-ai/mastra/pull/19837))
|
|
137
|
+
|
|
138
|
+
- Updated dependencies [[`41a5392`](https://github.com/mastra-ai/mastra/commit/41a5392d9f6c5e18d6b227f0fc0ddf49c50774e9), [`675fbff`](https://github.com/mastra-ai/mastra/commit/675fbff84d3274391b33e852f76083c38a5514e5), [`da009e1`](https://github.com/mastra-ai/mastra/commit/da009e1aacd89ed94b8d1b2af09c9d4fe7c4db49), [`35c2181`](https://github.com/mastra-ai/mastra/commit/35c2181e6a50e47c90ba36260db7c9723d54696f), [`b4b7ea8`](https://github.com/mastra-ai/mastra/commit/b4b7ea8733f033fc441ea47ed03f6afb17ec2248), [`675fbff`](https://github.com/mastra-ai/mastra/commit/675fbff84d3274391b33e852f76083c38a5514e5), [`6deac4a`](https://github.com/mastra-ai/mastra/commit/6deac4a520750d807a2154333bf1b91a2df958a5), [`c328769`](https://github.com/mastra-ai/mastra/commit/c3287698ff8ef98dba86d415faa566fa3e5f4d56), [`232fcbc`](https://github.com/mastra-ai/mastra/commit/232fcbc14fce625dd672ba043329c0b732c62be2), [`3491666`](https://github.com/mastra-ai/mastra/commit/34916663c4fdd43b48c21f4ab2d5fb6dcccc94f9)]:
|
|
139
|
+
- @mastra/core@1.52.0-alpha.10
|
|
140
|
+
- @mastra/libsql@1.17.0-alpha.2
|
|
141
|
+
- @mastra/pg@1.17.0-alpha.2
|
|
142
|
+
- @mastra/observability@1.16.2-alpha.1
|
|
143
|
+
- @mastra/mcp@1.15.0-alpha.0
|
|
144
|
+
|
|
3
145
|
## 0.2.0-alpha.9
|
|
4
146
|
|
|
5
147
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -37,6 +37,26 @@ export const mastra = new Mastra(prepared.mastraArgs);
|
|
|
37
37
|
await prepared.finalize();
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
### Add input processors
|
|
41
|
+
|
|
42
|
+
Embedding surfaces can prepend stateless input processors without replacing Mastra Code's required policy and compatibility processors:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const phaseProcessor = {
|
|
46
|
+
id: 'current-phase',
|
|
47
|
+
async processInputStep({ messages }) {
|
|
48
|
+
await reconcileCompletedTools(messages);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const prepared = await prepareAgentControllerMount({
|
|
53
|
+
cwd: process.cwd(),
|
|
54
|
+
inputProcessors: [phaseProcessor],
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Configured processors run before Mastra Code's built-in input processors. Keep processor instances stateless because the mounted agent shares them across sessions and runs.
|
|
59
|
+
|
|
40
60
|
Deep modules are available as subpath imports, e.g.:
|
|
41
61
|
|
|
42
62
|
```ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/agents/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AACxG,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,wBAAwB,CAAC;AA0GrE,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,SAAe,EACtB,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,EAAE,CA0BV;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,uFAAuF;IACvF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,6EAA6E;IAC7E,YAAY,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,WAAW,CAAC;CACpF;AAyFD,wBAAsB,mBAAmB,CAAC,EACxC,cAAc,EACd,MAAM,EACN,cAAc,GACf,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,uBAAuB,CAAC;CAC1C,
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/agents/workspace.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AACxG,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,wBAAwB,CAAC;AA0GrE,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,SAAe,EACtB,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,EAAE,CA0BV;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,uFAAuF;IACvF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,6EAA6E;IAC7E,YAAY,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,EAAE,KAAK,WAAW,CAAC;CACpF;AAyFD,wBAAsB,mBAAmB,CAAC,EACxC,cAAc,EACd,MAAM,EACN,cAAc,GACf,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,uBAAuB,CAAC;CAC1C,wNAoFA;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,cAAc,EACd,MAAM,GACP,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAclC"}
|
package/dist/agents/workspace.js
CHANGED
|
@@ -115,7 +115,7 @@ function detectPackageRunner(projectPath) {
|
|
|
115
115
|
return "npx --yes";
|
|
116
116
|
}
|
|
117
117
|
async function getSandboxWorkspace({
|
|
118
|
-
|
|
118
|
+
projectRepositoryId,
|
|
119
119
|
sandboxId,
|
|
120
120
|
workdir,
|
|
121
121
|
worktreePath,
|
|
@@ -125,7 +125,7 @@ async function getSandboxWorkspace({
|
|
|
125
125
|
}) {
|
|
126
126
|
const boundWorkdir = worktreePath || workdir;
|
|
127
127
|
const extensionId = skillExtension ? `-${skillExtension.id}` : "";
|
|
128
|
-
const workspaceId = `${WORKSPACE_ID_PREFIX}-
|
|
128
|
+
const workspaceId = `${WORKSPACE_ID_PREFIX}-repository-${projectRepositoryId}-${sandboxId}-${boundWorkdir}${extensionId}`;
|
|
129
129
|
try {
|
|
130
130
|
const existing = mastra?.getWorkspaceById(workspaceId);
|
|
131
131
|
if (existing) {
|
|
@@ -155,9 +155,9 @@ async function getDynamicWorkspace({
|
|
|
155
155
|
}) {
|
|
156
156
|
const ctx = requestContext.get("controller");
|
|
157
157
|
const state = ctx?.getState();
|
|
158
|
-
if (state?.
|
|
158
|
+
if (state?.projectRepositoryId && state.sandboxId && state.sandboxWorkdir) {
|
|
159
159
|
return getSandboxWorkspace({
|
|
160
|
-
|
|
160
|
+
projectRepositoryId: state.projectRepositoryId,
|
|
161
161
|
sandboxId: state.sandboxId,
|
|
162
162
|
workdir: state.sandboxWorkdir,
|
|
163
163
|
worktreePath: state.worktreePath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/agents/workspace.ts"],"sourcesContent":["import fs, { existsSync } from 'node:fs';\nimport os from 'node:os';\nimport path, { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ToolsInput } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { Workspace, LocalFilesystem, LocalSandbox, createWorkspaceTools } from '@mastra/core/workspace';\nimport type { LSPConfig, SkillSource } from '@mastra/core/workspace';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport { loadSettings } from '../onboarding/settings.js';\nimport type { MastraCodeState } from '../schema.js';\nimport { isPathWithinRoot } from '../utils/path-security.js';\nimport { getPlansDir } from '../utils/plans.js';\nimport { SandboxFilesystem } from './sandbox-filesystem.js';\nimport { reattachProjectSandbox } from './sandbox-reattach.js';\nimport { GOAL_JUDGE_READONLY_TOOLS, MASTRACODE_WORKSPACE_TOOLS } from './tool-availability.js';\n\n// =============================================================================\n// Sandbox Environment\n// =============================================================================\n\nfunction buildSandboxEnv(): NodeJS.ProcessEnv {\n return {\n ...process.env,\n // Explicit overrides for non-interactive subprocess execution\n FORCE_COLOR: '1',\n CLICOLOR_FORCE: '1',\n TERM: process.env.TERM || 'xterm-256color',\n CI: 'true',\n NONINTERACTIVE: '1',\n DEBIAN_FRONTEND: 'noninteractive',\n };\n}\n\n// =============================================================================\n// Create Workspace with Skills\n// =============================================================================\n\n// We support multiple skill locations for compatibility:\n// 1. Project-local: <configDir>/skills (project-specific mastracode skills)\n// 2. Project-local: .claude/skills (Claude Code compatible skills)\n// 3. Project-local: .agents/skills (Agent Skills spec compatible)\n// 4. Global: ~/<configDir>/skills (user-wide mastracode skills)\n// 5. Global: ~/.claude/skills (user-wide Claude Code skills)\n// 6. Global: ~/.agents/skills (user-wide Agent Skills spec compatible)\n\n// Mastra's LocalSkillSource.readdir uses Node's Dirent.isDirectory() which\n// returns false for symlinks. Tools like `npx skills add` install skills as\n// symlinks, so we need to resolve them. For each symlinked skill directory,\n// we add the real (resolved) parent path as an additional skill scan path.\nfunction collectSkillPaths(skillsDirs: string[], allowedRoot?: string): string[] {\n const paths: string[] = [];\n const seen = new Set<string>();\n let realAllowedRoot: string | undefined;\n\n if (allowedRoot) {\n try {\n realAllowedRoot = fs.realpathSync(allowedRoot);\n } catch {\n return [];\n }\n }\n\n for (const skillsDir of skillsDirs) {\n const skillsDirExists = fs.existsSync(skillsDir);\n if (skillsDirExists && realAllowedRoot) {\n try {\n const realSkillsDir = fs.realpathSync(skillsDir);\n if (!isPathWithinRoot(realSkillsDir, realAllowedRoot)) continue;\n } catch {\n continue;\n }\n }\n\n const resolved = path.resolve(skillsDir);\n if (!seen.has(resolved)) {\n seen.add(resolved);\n paths.push(skillsDir);\n }\n\n if (!skillsDirExists) continue;\n\n try {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isSymbolicLink()) {\n try {\n const linkPath = path.join(skillsDir, entry.name);\n const realPath = fs.realpathSync(linkPath);\n if (realAllowedRoot && !isPathWithinRoot(realPath, realAllowedRoot)) continue;\n const stat = fs.statSync(realPath);\n if (stat.isDirectory()) {\n const realParent = path.dirname(realPath);\n if (realAllowedRoot && !isPathWithinRoot(realParent, realAllowedRoot)) continue;\n if (!seen.has(realParent)) {\n seen.add(realParent);\n paths.push(realParent);\n }\n }\n } catch {\n continue;\n }\n }\n }\n } catch {\n // Ignore errors during symlink resolution\n }\n }\n\n return paths;\n}\n\n// Build skill paths dynamically based on configDir and projectPath\nexport function buildSkillPaths(\n projectPath: string,\n configDir: string,\n homeDir = os.homedir(),\n pluginSkillPaths: string[] = [],\n): string[] {\n const mastraCodeLocalSkillsPath = path.join(projectPath, configDir, 'skills');\n const claudeLocalSkillsPath = path.join(projectPath, '.claude', 'skills');\n const agentSkillsLocalPath = path.join(projectPath, '.agents', 'skills');\n const mastraCodeGlobalSkillsPath = path.join(homeDir, configDir, 'skills');\n const claudeGlobalSkillsPath = path.join(homeDir, '.claude', 'skills');\n const agentSkillsGlobalPath = path.join(homeDir, '.agents', 'skills');\n\n const paths = [\n ...collectSkillPaths([mastraCodeLocalSkillsPath, claudeLocalSkillsPath, agentSkillsLocalPath], projectPath),\n ...collectSkillPaths([mastraCodeGlobalSkillsPath, claudeGlobalSkillsPath, agentSkillsGlobalPath]),\n ...pluginSkillPaths.flatMap(pluginSkillPath => collectSkillPaths([pluginSkillPath], pluginSkillPath)),\n ];\n\n const seenPaths = new Set<string>();\n return paths.filter(skillPath => {\n let resolved: string;\n try {\n resolved = fs.realpathSync(skillPath);\n } catch {\n resolved = path.resolve(skillPath);\n }\n if (seenPaths.has(resolved)) return false;\n seenPaths.add(resolved);\n return true;\n });\n}\n\nexport interface WorkspaceSkillExtension {\n /** Distinguishes extended workspaces from the default resolver cache. */\n id: string;\n /** Additional read-only skill roots prepended to normal project/global skill roots. */\n paths: string[];\n /** Compose the additional roots with the workspace's normal skill source. */\n createSource: (fallback: SkillSource, fallbackSkillRoots: string[]) => SkillSource;\n}\n\n/**\n * Paths the agent is always allowed to access (in addition to the project root\n * and any per-thread sandboxAllowedPaths). The OS temp directory is included\n * so the agent can use it as a scratchpad without requesting access every time.\n */\nconst DEFAULT_ALLOWED_PATHS: string[] = [os.tmpdir(), '/tmp', getPlansDir()].reduce<string[]>((acc, p) => {\n const resolved = path.resolve(p);\n if (!acc.includes(resolved)) acc.push(resolved);\n return acc;\n}, []);\n\nconst WORKSPACE_ID_PREFIX = 'mastra-code-workspace';\n\n/**\n * Detect the project's package runner from lock files.\n * Used as a fallback packageRunner for LSP when no binary is found locally or on PATH.\n */\nfunction detectPackageRunner(projectPath: string): string | undefined {\n if (existsSync(join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm dlx';\n if (existsSync(join(projectPath, 'bun.lockb')) || existsSync(join(projectPath, 'bun.lock'))) return 'bunx';\n if (existsSync(join(projectPath, 'yarn.lock'))) return 'yarn dlx';\n if (existsSync(join(projectPath, 'package-lock.json'))) return 'npx --yes';\n return 'npx --yes';\n}\n\n/**\n * Build (or reuse) a sandbox-backed Workspace for a GitHub project. The sandbox\n * is reattached by its persisted provider id and a `SandboxFilesystem` is layered\n * over the in-sandbox checkout so file tools and command tools share one VM.\n */\nasync function getSandboxWorkspace({\n githubProjectId,\n sandboxId,\n workdir,\n worktreePath,\n configDir,\n mastra,\n skillExtension,\n}: {\n githubProjectId: string;\n sandboxId: string;\n workdir: string;\n worktreePath?: string;\n configDir: string;\n mastra?: Mastra;\n skillExtension?: WorkspaceSkillExtension;\n}): Promise<Workspace> {\n // Bind the workspace to the active worktree when one is set, so file tools and\n // command tools operate inside the feature branch's working tree rather than\n // the base checkout. Falls back to the repo root when no worktree is active.\n const boundWorkdir = worktreePath || workdir;\n\n // Include the sandbox id *and* worktree path in the reuse key: a new sandbox\n // (e.g. the previous one expired) or a different worktree must each get a\n // fresh Workspace/ProcessManager instead of reusing one bound to a stale\n // sandbox or the wrong working tree.\n const extensionId = skillExtension ? `-${skillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-gh-${githubProjectId}-${sandboxId}-${boundWorkdir}${extensionId}`;\n\n // Reuse the existing remote workspace if already registered (preserves the\n // reattached sandbox + ProcessManager state across re-opens).\n try {\n const existing = mastra?.getWorkspaceById(workspaceId) as Workspace | undefined;\n if (existing) {\n existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);\n return existing;\n }\n } catch {\n // Not registered yet.\n }\n\n const sandbox = await reattachProjectSandbox(sandboxId);\n const filesystem = new SandboxFilesystem({ sandbox, workdir: boundWorkdir });\n const projectSkillPaths = [path.join(configDir, 'skills'), '.claude/skills', '.agents/skills'];\n const skillPaths = [...(skillExtension?.paths ?? []), ...projectSkillPaths];\n\n return new Workspace({\n id: workspaceId,\n name: 'Mastra Code Sandbox Workspace',\n filesystem,\n sandbox: sandbox as unknown as ConstructorParameters<typeof Workspace>[0]['sandbox'],\n tools: MASTRACODE_WORKSPACE_TOOLS,\n skills: skillPaths,\n skillSource: skillExtension?.createSource(filesystem, projectSkillPaths) ?? filesystem,\n });\n}\n\nexport async function getDynamicWorkspace({\n requestContext,\n mastra,\n skillExtension,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n skillExtension?: WorkspaceSkillExtension;\n}) {\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeState> | undefined;\n const state = ctx?.getState();\n\n // GitHub/cloud-sandbox-backed project: the repo lives inside a remote sandbox,\n // not on the server host. Reattach to the already-provisioned + materialized\n // sandbox (the SPA called `.../ensure` first, persisting sandboxId/workdir on\n // controller state) and build a sandbox-backed Workspace. Optional embedders\n // may add read-only skill roots while project skills remain sandbox-backed.\n if (state?.githubProjectId && state.sandboxId && state.sandboxWorkdir) {\n return getSandboxWorkspace({\n githubProjectId: state.githubProjectId,\n sandboxId: state.sandboxId,\n workdir: state.sandboxWorkdir,\n worktreePath: state.worktreePath,\n configDir: state.configDir ?? DEFAULT_CONFIG_DIR,\n mastra,\n skillExtension,\n });\n }\n\n const rawProjectPath = state?.projectPath;\n\n if (!rawProjectPath) {\n throw new Error('Project path is required');\n }\n\n const projectPath = path.resolve(rawProjectPath);\n const configDir = state?.configDir ?? DEFAULT_CONFIG_DIR;\n const projectSkillPaths = buildSkillPaths(projectPath, configDir, state?.homeDir, state?.pluginSkillPaths ?? []);\n const skillPaths = [...(skillExtension?.paths ?? []), ...projectSkillPaths];\n const extensionId = skillExtension ? `-${skillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectPath}${extensionId}`;\n const sandboxPaths = state?.sandboxAllowedPaths ?? [];\n const allowedPaths = [\n ...projectSkillPaths,\n ...DEFAULT_ALLOWED_PATHS,\n ...sandboxPaths.map((p: string) => path.resolve(p)),\n ];\n\n // All modes share the same workspace tool configuration. Per-mode tool\n // visibility is enforced at LLM-call time via `availableTools` /\n // `activeTools` on the AgentController, not by mutating workspace capabilities.\n const workspaceTools = MASTRACODE_WORKSPACE_TOOLS;\n\n // Reuse existing workspace if already registered (preserves ProcessManager state)\n let existing: Workspace<LocalFilesystem, LocalSandbox> | undefined;\n try {\n existing = mastra?.getWorkspaceById(workspaceId) as Workspace<LocalFilesystem, LocalSandbox>;\n } catch {\n // Not registered yet\n }\n\n if (existing) {\n existing.filesystem.setAllowedPaths(allowedPaths);\n existing.setToolsConfig(workspaceTools);\n return existing;\n }\n\n const userLsp = loadSettings().lsp ?? {};\n const mcModulePath = join(dirname(fileURLToPath(import.meta.url)), '..');\n const lspConfig: LSPConfig = {\n ...userLsp,\n packageRunner: userLsp.packageRunner || detectPackageRunner(projectPath), // Detected runner is the fallback — user's packageRunner always wins\n searchPaths: [mcModulePath, ...(userLsp.searchPaths ?? [])],\n };\n\n // First call for this project — create the workspace\n const filesystem = new LocalFilesystem({\n basePath: projectPath,\n allowedPaths,\n });\n return new Workspace({\n id: workspaceId,\n name: 'Mastra Code Workspace',\n filesystem,\n sandbox: new LocalSandbox({\n workingDirectory: projectPath,\n env: buildSandboxEnv(),\n }),\n tools: workspaceTools,\n skills: skillPaths,\n ...(skillExtension ? { skillSource: skillExtension.createSource(filesystem, projectSkillPaths) } : {}),\n lsp: lspConfig,\n });\n}\n\n/**\n * Resolver for the agent's `goal.tools` config. Builds the request's workspace\n * (same per-request resolution as the agent's own tools) and returns only the\n * read-only verification subset, remapped to mastracode's tool names (`view`,\n * `search_content`, etc.). Returns `undefined` when no workspace can be resolved\n * (e.g. no project path), keeping the default judge text-only rather than\n * throwing inside the goal step.\n */\nexport async function getGoalJudgeTools({\n requestContext,\n mastra,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n}): Promise<ToolsInput | undefined> {\n let workspace: Workspace;\n try {\n workspace = await getDynamicWorkspace({ requestContext, mastra });\n } catch {\n return undefined;\n }\n\n const allTools = await createWorkspaceTools(workspace, { requestContext, workspace });\n const readonly: ToolsInput = {};\n for (const name of GOAL_JUDGE_READONLY_TOOLS) {\n if (allTools[name]) readonly[name] = allTools[name];\n }\n return Object.keys(readonly).length > 0 ? readonly : undefined;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/agents/workspace.ts"],"sourcesContent":["import fs, { existsSync } from 'node:fs';\nimport os from 'node:os';\nimport path, { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ToolsInput } from '@mastra/core/agent';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { Mastra } from '@mastra/core/mastra';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { Workspace, LocalFilesystem, LocalSandbox, createWorkspaceTools } from '@mastra/core/workspace';\nimport type { LSPConfig, SkillSource } from '@mastra/core/workspace';\nimport { DEFAULT_CONFIG_DIR } from '../constants.js';\nimport { loadSettings } from '../onboarding/settings.js';\nimport type { MastraCodeState } from '../schema.js';\nimport { isPathWithinRoot } from '../utils/path-security.js';\nimport { getPlansDir } from '../utils/plans.js';\nimport { SandboxFilesystem } from './sandbox-filesystem.js';\nimport { reattachProjectSandbox } from './sandbox-reattach.js';\nimport { GOAL_JUDGE_READONLY_TOOLS, MASTRACODE_WORKSPACE_TOOLS } from './tool-availability.js';\n\n// =============================================================================\n// Sandbox Environment\n// =============================================================================\n\nfunction buildSandboxEnv(): NodeJS.ProcessEnv {\n return {\n ...process.env,\n // Explicit overrides for non-interactive subprocess execution\n FORCE_COLOR: '1',\n CLICOLOR_FORCE: '1',\n TERM: process.env.TERM || 'xterm-256color',\n CI: 'true',\n NONINTERACTIVE: '1',\n DEBIAN_FRONTEND: 'noninteractive',\n };\n}\n\n// =============================================================================\n// Create Workspace with Skills\n// =============================================================================\n\n// We support multiple skill locations for compatibility:\n// 1. Project-local: <configDir>/skills (project-specific mastracode skills)\n// 2. Project-local: .claude/skills (Claude Code compatible skills)\n// 3. Project-local: .agents/skills (Agent Skills spec compatible)\n// 4. Global: ~/<configDir>/skills (user-wide mastracode skills)\n// 5. Global: ~/.claude/skills (user-wide Claude Code skills)\n// 6. Global: ~/.agents/skills (user-wide Agent Skills spec compatible)\n\n// Mastra's LocalSkillSource.readdir uses Node's Dirent.isDirectory() which\n// returns false for symlinks. Tools like `npx skills add` install skills as\n// symlinks, so we need to resolve them. For each symlinked skill directory,\n// we add the real (resolved) parent path as an additional skill scan path.\nfunction collectSkillPaths(skillsDirs: string[], allowedRoot?: string): string[] {\n const paths: string[] = [];\n const seen = new Set<string>();\n let realAllowedRoot: string | undefined;\n\n if (allowedRoot) {\n try {\n realAllowedRoot = fs.realpathSync(allowedRoot);\n } catch {\n return [];\n }\n }\n\n for (const skillsDir of skillsDirs) {\n const skillsDirExists = fs.existsSync(skillsDir);\n if (skillsDirExists && realAllowedRoot) {\n try {\n const realSkillsDir = fs.realpathSync(skillsDir);\n if (!isPathWithinRoot(realSkillsDir, realAllowedRoot)) continue;\n } catch {\n continue;\n }\n }\n\n const resolved = path.resolve(skillsDir);\n if (!seen.has(resolved)) {\n seen.add(resolved);\n paths.push(skillsDir);\n }\n\n if (!skillsDirExists) continue;\n\n try {\n const entries = fs.readdirSync(skillsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isSymbolicLink()) {\n try {\n const linkPath = path.join(skillsDir, entry.name);\n const realPath = fs.realpathSync(linkPath);\n if (realAllowedRoot && !isPathWithinRoot(realPath, realAllowedRoot)) continue;\n const stat = fs.statSync(realPath);\n if (stat.isDirectory()) {\n const realParent = path.dirname(realPath);\n if (realAllowedRoot && !isPathWithinRoot(realParent, realAllowedRoot)) continue;\n if (!seen.has(realParent)) {\n seen.add(realParent);\n paths.push(realParent);\n }\n }\n } catch {\n continue;\n }\n }\n }\n } catch {\n // Ignore errors during symlink resolution\n }\n }\n\n return paths;\n}\n\n// Build skill paths dynamically based on configDir and projectPath\nexport function buildSkillPaths(\n projectPath: string,\n configDir: string,\n homeDir = os.homedir(),\n pluginSkillPaths: string[] = [],\n): string[] {\n const mastraCodeLocalSkillsPath = path.join(projectPath, configDir, 'skills');\n const claudeLocalSkillsPath = path.join(projectPath, '.claude', 'skills');\n const agentSkillsLocalPath = path.join(projectPath, '.agents', 'skills');\n const mastraCodeGlobalSkillsPath = path.join(homeDir, configDir, 'skills');\n const claudeGlobalSkillsPath = path.join(homeDir, '.claude', 'skills');\n const agentSkillsGlobalPath = path.join(homeDir, '.agents', 'skills');\n\n const paths = [\n ...collectSkillPaths([mastraCodeLocalSkillsPath, claudeLocalSkillsPath, agentSkillsLocalPath], projectPath),\n ...collectSkillPaths([mastraCodeGlobalSkillsPath, claudeGlobalSkillsPath, agentSkillsGlobalPath]),\n ...pluginSkillPaths.flatMap(pluginSkillPath => collectSkillPaths([pluginSkillPath], pluginSkillPath)),\n ];\n\n const seenPaths = new Set<string>();\n return paths.filter(skillPath => {\n let resolved: string;\n try {\n resolved = fs.realpathSync(skillPath);\n } catch {\n resolved = path.resolve(skillPath);\n }\n if (seenPaths.has(resolved)) return false;\n seenPaths.add(resolved);\n return true;\n });\n}\n\nexport interface WorkspaceSkillExtension {\n /** Distinguishes extended workspaces from the default resolver cache. */\n id: string;\n /** Additional read-only skill roots prepended to normal project/global skill roots. */\n paths: string[];\n /** Compose the additional roots with the workspace's normal skill source. */\n createSource: (fallback: SkillSource, fallbackSkillRoots: string[]) => SkillSource;\n}\n\n/**\n * Paths the agent is always allowed to access (in addition to the project root\n * and any per-thread sandboxAllowedPaths). The OS temp directory is included\n * so the agent can use it as a scratchpad without requesting access every time.\n */\nconst DEFAULT_ALLOWED_PATHS: string[] = [os.tmpdir(), '/tmp', getPlansDir()].reduce<string[]>((acc, p) => {\n const resolved = path.resolve(p);\n if (!acc.includes(resolved)) acc.push(resolved);\n return acc;\n}, []);\n\nconst WORKSPACE_ID_PREFIX = 'mastra-code-workspace';\n\n/**\n * Detect the project's package runner from lock files.\n * Used as a fallback packageRunner for LSP when no binary is found locally or on PATH.\n */\nfunction detectPackageRunner(projectPath: string): string | undefined {\n if (existsSync(join(projectPath, 'pnpm-lock.yaml'))) return 'pnpm dlx';\n if (existsSync(join(projectPath, 'bun.lockb')) || existsSync(join(projectPath, 'bun.lock'))) return 'bunx';\n if (existsSync(join(projectPath, 'yarn.lock'))) return 'yarn dlx';\n if (existsSync(join(projectPath, 'package-lock.json'))) return 'npx --yes';\n return 'npx --yes';\n}\n\n/**\n * Build (or reuse) a sandbox-backed Workspace for a linked project repository. The sandbox\n * is reattached by its persisted provider id and a `SandboxFilesystem` is layered\n * over the in-sandbox checkout so file tools and command tools share one VM.\n */\nasync function getSandboxWorkspace({\n projectRepositoryId,\n sandboxId,\n workdir,\n worktreePath,\n configDir,\n mastra,\n skillExtension,\n}: {\n projectRepositoryId: string;\n sandboxId: string;\n workdir: string;\n worktreePath?: string;\n configDir: string;\n mastra?: Mastra;\n skillExtension?: WorkspaceSkillExtension;\n}): Promise<Workspace> {\n // Bind the workspace to the active worktree when one is set, so file tools and\n // command tools operate inside the feature branch's working tree rather than\n // the base checkout. Falls back to the repo root when no worktree is active.\n const boundWorkdir = worktreePath || workdir;\n\n // Include the sandbox id *and* worktree path in the reuse key: a new sandbox\n // (e.g. the previous one expired) or a different worktree must each get a\n // fresh Workspace/ProcessManager instead of reusing one bound to a stale\n // sandbox or the wrong working tree.\n const extensionId = skillExtension ? `-${skillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-repository-${projectRepositoryId}-${sandboxId}-${boundWorkdir}${extensionId}`;\n\n // Reuse the existing remote workspace if already registered (preserves the\n // reattached sandbox + ProcessManager state across re-opens).\n try {\n const existing = mastra?.getWorkspaceById(workspaceId) as Workspace | undefined;\n if (existing) {\n existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);\n return existing;\n }\n } catch {\n // Not registered yet.\n }\n\n const sandbox = await reattachProjectSandbox(sandboxId);\n const filesystem = new SandboxFilesystem({ sandbox, workdir: boundWorkdir });\n const projectSkillPaths = [path.join(configDir, 'skills'), '.claude/skills', '.agents/skills'];\n const skillPaths = [...(skillExtension?.paths ?? []), ...projectSkillPaths];\n\n return new Workspace({\n id: workspaceId,\n name: 'Mastra Code Sandbox Workspace',\n filesystem,\n sandbox: sandbox as unknown as ConstructorParameters<typeof Workspace>[0]['sandbox'],\n tools: MASTRACODE_WORKSPACE_TOOLS,\n skills: skillPaths,\n skillSource: skillExtension?.createSource(filesystem, projectSkillPaths) ?? filesystem,\n });\n}\n\nexport async function getDynamicWorkspace({\n requestContext,\n mastra,\n skillExtension,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n skillExtension?: WorkspaceSkillExtension;\n}) {\n const ctx = requestContext.get('controller') as AgentControllerRequestContext<MastraCodeState> | undefined;\n const state = ctx?.getState();\n\n // Repository-backed project: the repo lives inside a remote sandbox, not on\n // the server host. Reattach to the already-provisioned + materialized sandbox\n // and build a sandbox-backed Workspace. Optional embedders may add read-only\n // skill roots while project skills remain sandbox-backed.\n if (state?.projectRepositoryId && state.sandboxId && state.sandboxWorkdir) {\n return getSandboxWorkspace({\n projectRepositoryId: state.projectRepositoryId,\n sandboxId: state.sandboxId,\n workdir: state.sandboxWorkdir,\n worktreePath: state.worktreePath,\n configDir: state.configDir ?? DEFAULT_CONFIG_DIR,\n mastra,\n skillExtension,\n });\n }\n\n const rawProjectPath = state?.projectPath;\n\n if (!rawProjectPath) {\n throw new Error('Project path is required');\n }\n\n const projectPath = path.resolve(rawProjectPath);\n const configDir = state?.configDir ?? DEFAULT_CONFIG_DIR;\n const projectSkillPaths = buildSkillPaths(projectPath, configDir, state?.homeDir, state?.pluginSkillPaths ?? []);\n const skillPaths = [...(skillExtension?.paths ?? []), ...projectSkillPaths];\n const extensionId = skillExtension ? `-${skillExtension.id}` : '';\n const workspaceId = `${WORKSPACE_ID_PREFIX}-${projectPath}${extensionId}`;\n const sandboxPaths = state?.sandboxAllowedPaths ?? [];\n const allowedPaths = [\n ...projectSkillPaths,\n ...DEFAULT_ALLOWED_PATHS,\n ...sandboxPaths.map((p: string) => path.resolve(p)),\n ];\n\n // All modes share the same workspace tool configuration. Per-mode tool\n // visibility is enforced at LLM-call time via `availableTools` /\n // `activeTools` on the AgentController, not by mutating workspace capabilities.\n const workspaceTools = MASTRACODE_WORKSPACE_TOOLS;\n\n // Reuse existing workspace if already registered (preserves ProcessManager state)\n let existing: Workspace<LocalFilesystem, LocalSandbox> | undefined;\n try {\n existing = mastra?.getWorkspaceById(workspaceId) as Workspace<LocalFilesystem, LocalSandbox>;\n } catch {\n // Not registered yet\n }\n\n if (existing) {\n existing.filesystem.setAllowedPaths(allowedPaths);\n existing.setToolsConfig(workspaceTools);\n return existing;\n }\n\n const userLsp = loadSettings().lsp ?? {};\n const mcModulePath = join(dirname(fileURLToPath(import.meta.url)), '..');\n const lspConfig: LSPConfig = {\n ...userLsp,\n packageRunner: userLsp.packageRunner || detectPackageRunner(projectPath), // Detected runner is the fallback — user's packageRunner always wins\n searchPaths: [mcModulePath, ...(userLsp.searchPaths ?? [])],\n };\n\n // First call for this project — create the workspace\n const filesystem = new LocalFilesystem({\n basePath: projectPath,\n allowedPaths,\n });\n return new Workspace({\n id: workspaceId,\n name: 'Mastra Code Workspace',\n filesystem,\n sandbox: new LocalSandbox({\n workingDirectory: projectPath,\n env: buildSandboxEnv(),\n }),\n tools: workspaceTools,\n skills: skillPaths,\n ...(skillExtension ? { skillSource: skillExtension.createSource(filesystem, projectSkillPaths) } : {}),\n lsp: lspConfig,\n });\n}\n\n/**\n * Resolver for the agent's `goal.tools` config. Builds the request's workspace\n * (same per-request resolution as the agent's own tools) and returns only the\n * read-only verification subset, remapped to mastracode's tool names (`view`,\n * `search_content`, etc.). Returns `undefined` when no workspace can be resolved\n * (e.g. no project path), keeping the default judge text-only rather than\n * throwing inside the goal step.\n */\nexport async function getGoalJudgeTools({\n requestContext,\n mastra,\n}: {\n requestContext: RequestContext;\n mastra?: Mastra;\n}): Promise<ToolsInput | undefined> {\n let workspace: Workspace;\n try {\n workspace = await getDynamicWorkspace({ requestContext, mastra });\n } catch {\n return undefined;\n }\n\n const allTools = await createWorkspaceTools(workspace, { requestContext, workspace });\n const readonly: ToolsInput = {};\n for (const name of GOAL_JUDGE_READONLY_TOOLS) {\n if (allTools[name]) readonly[name] = allTools[name];\n }\n return Object.keys(readonly).length > 0 ? readonly : undefined;\n}\n"],"mappings":"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","names":[]}
|
package/dist/auth/storage.js
CHANGED
|
@@ -6,8 +6,8 @@ import { githubCopilotOAuthProvider } from "./providers/github-copilot.js";
|
|
|
6
6
|
import { openaiCodexOAuthProvider } from "./providers/openai-codex.js";
|
|
7
7
|
import { xaiOAuthProvider } from "./providers/xai.js";
|
|
8
8
|
const PROVIDER_DEFAULT_MODELS = {
|
|
9
|
-
anthropic: "anthropic/claude-
|
|
10
|
-
"openai-codex": "openai/gpt-5.
|
|
9
|
+
anthropic: "anthropic/claude-fable-5",
|
|
10
|
+
"openai-codex": "openai/gpt-5.6",
|
|
11
11
|
// gpt-4.1 routes through `/chat/completions` (which our OpenAI-compatible
|
|
12
12
|
// adapter handles); Anthropic-shaped Copilot models (Claude on `/v1/messages`)
|
|
13
13
|
// are not yet wired up, so picking one as the post-login default would error.
|
package/dist/auth/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/storage.ts"],"sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getAppDataDir } from '../utils/project.js';\nimport { anthropicOAuthProvider } from './providers/anthropic.js';\nimport { githubCopilotOAuthProvider } from './providers/github-copilot.js';\nimport { openaiCodexOAuthProvider } from './providers/openai-codex.js';\nimport { xaiOAuthProvider } from './providers/xai.js';\nimport type {\n AuthCredential,\n AuthStorageData,\n OAuthLoginCallbacks,\n OAuthProviderId,\n OAuthProviderInterface,\n} from './types.js';\n\n/**\n * Best/default models for each OAuth provider.\n * Used when auto-selecting a model after login.\n */\nexport const PROVIDER_DEFAULT_MODELS: Record<OAuthProviderId, string> = {\n anthropic: 'anthropic/claude-opus-4-6',\n 'openai-codex': 'openai/gpt-5.5',\n // gpt-4.1 routes through `/chat/completions` (which our OpenAI-compatible\n // adapter handles); Anthropic-shaped Copilot models (Claude on `/v1/messages`)\n // are not yet wired up, so picking one as the post-login default would error.\n 'github-copilot': 'github-copilot/gpt-4.1',\n xai: 'xai/grok-4.5',\n};\n\n// Provider registry\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>([\n [anthropicOAuthProvider.id, anthropicOAuthProvider],\n [openaiCodexOAuthProvider.id, openaiCodexOAuthProvider],\n [githubCopilotOAuthProvider.id, githubCopilotOAuthProvider],\n [xaiOAuthProvider.id, xaiOAuthProvider],\n]);\n\n/**\n * Get an OAuth provider by ID\n */\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n return oauthProviderRegistry.get(id);\n}\n\n/**\n * Get all registered OAuth providers\n */\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n return Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n private data: AuthStorageData = {};\n\n constructor(private authPath: string = join(getAppDataDir(), 'auth.json')) {\n this.reload();\n }\n\n /**\n * Reload credentials from disk.\n */\n reload(): void {\n if (!existsSync(this.authPath)) {\n this.data = {};\n return;\n }\n try {\n this.data = JSON.parse(readFileSync(this.authPath, 'utf-8'));\n } catch {\n this.data = {};\n }\n }\n\n /**\n * Save credentials to disk.\n */\n private save(): void {\n const dir = dirname(this.authPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n writeFileSync(this.authPath, JSON.stringify(this.data, null, 2), 'utf-8');\n chmodSync(this.authPath, 0o600);\n }\n\n /**\n * Get credential for a provider.\n */\n get(provider: string): AuthCredential | undefined {\n return this.data[provider] ?? undefined;\n }\n\n /**\n * Set credential for a provider.\n */\n set(provider: string, credential: AuthCredential): void {\n this.data[provider] = credential;\n this.save();\n }\n\n /**\n * Remove credential for a provider.\n */\n remove(provider: string): void {\n delete this.data[provider];\n this.save();\n }\n\n /**\n * List all providers with credentials.\n */\n list(): string[] {\n return Object.keys(this.data);\n }\n\n /**\n * Check if credentials exist for a provider.\n */\n has(provider: string): boolean {\n return provider in this.data;\n }\n\n /**\n * Check if logged in via OAuth for a provider.\n */\n isLoggedIn(provider: string): boolean {\n const cred = this.data[provider];\n return cred?.type === 'oauth';\n }\n\n /**\n * Check if a stored API key exists for a provider.\n * Keys are stored under `apikey:<provider>` in auth.json.\n */\n hasStoredApiKey(provider: string): boolean {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0;\n }\n\n /**\n * Get a stored API key for a provider, if any.\n */\n getStoredApiKey(provider: string): string | undefined {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0 ? cred.key : undefined;\n }\n\n /**\n * Store an API key for a provider.\n * Also sets the corresponding environment variable so model resolution can find it.\n */\n setStoredApiKey(provider: string, key: string, envVar?: string): void {\n this.set(`apikey:${provider}`, { type: 'api_key', key });\n if (envVar) {\n process.env[envVar] = key;\n }\n }\n\n /**\n * Load all stored API keys into process.env.\n * Called at startup so model resolution can find stored keys.\n * Only sets env vars that aren't already set (env vars take precedence).\n */\n loadStoredApiKeysIntoEnv(providerEnvVars: Record<string, string | undefined>): void {\n for (const [key, cred] of Object.entries(this.data)) {\n if (!key.startsWith('apikey:') || cred.type !== 'api_key' || !cred.key) continue;\n const provider = key.substring('apikey:'.length);\n const envVar = providerEnvVars[provider];\n if (envVar && !process.env[envVar]) {\n process.env[envVar] = cred.key;\n }\n }\n }\n\n /**\n * Login to an OAuth provider.\n */\n async login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n const provider = getOAuthProvider(providerId);\n if (!provider) {\n throw new Error(`Unknown OAuth provider: ${providerId}`);\n }\n\n const credentials = await provider.login(callbacks);\n this.set(providerId, { type: 'oauth', ...credentials });\n }\n\n /**\n * Logout from a provider.\n */\n logout(provider: string): void {\n this.remove(provider);\n }\n\n /**\n * Get API key for a provider, auto-refreshing OAuth tokens if needed.\n */\n async getApiKey(providerId: string): Promise<string | undefined> {\n const cred = this.data[providerId];\n\n if (cred?.type === 'api_key') {\n return cred.key;\n }\n\n if (cred?.type === 'oauth') {\n const provider = getOAuthProvider(providerId);\n if (!provider) {\n return undefined;\n }\n\n // Check if token needs refresh\n if (Date.now() >= cred.expires) {\n try {\n const newCreds = await provider.refreshToken(cred);\n this.set(providerId, { type: 'oauth', ...newCreds });\n return provider.getApiKey(newCreds);\n } catch {\n // Refresh failed - user needs to re-login\n return undefined;\n }\n }\n\n return provider.getApiKey(cred);\n }\n\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/auth/storage.ts"],"sourcesContent":["/**\n * Credential storage for API keys and OAuth tokens.\n * Handles loading, saving, and refreshing credentials from auth.json.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getAppDataDir } from '../utils/project.js';\nimport { anthropicOAuthProvider } from './providers/anthropic.js';\nimport { githubCopilotOAuthProvider } from './providers/github-copilot.js';\nimport { openaiCodexOAuthProvider } from './providers/openai-codex.js';\nimport { xaiOAuthProvider } from './providers/xai.js';\nimport type {\n AuthCredential,\n AuthStorageData,\n OAuthLoginCallbacks,\n OAuthProviderId,\n OAuthProviderInterface,\n} from './types.js';\n\n/**\n * Best/default models for each OAuth provider.\n * Used when auto-selecting a model after login.\n */\nexport const PROVIDER_DEFAULT_MODELS: Record<OAuthProviderId, string> = {\n anthropic: 'anthropic/claude-fable-5',\n 'openai-codex': 'openai/gpt-5.6',\n // gpt-4.1 routes through `/chat/completions` (which our OpenAI-compatible\n // adapter handles); Anthropic-shaped Copilot models (Claude on `/v1/messages`)\n // are not yet wired up, so picking one as the post-login default would error.\n 'github-copilot': 'github-copilot/gpt-4.1',\n xai: 'xai/grok-4.5',\n};\n\n// Provider registry\nconst oauthProviderRegistry = new Map<string, OAuthProviderInterface>([\n [anthropicOAuthProvider.id, anthropicOAuthProvider],\n [openaiCodexOAuthProvider.id, openaiCodexOAuthProvider],\n [githubCopilotOAuthProvider.id, githubCopilotOAuthProvider],\n [xaiOAuthProvider.id, xaiOAuthProvider],\n]);\n\n/**\n * Get an OAuth provider by ID\n */\nexport function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined {\n return oauthProviderRegistry.get(id);\n}\n\n/**\n * Get all registered OAuth providers\n */\nexport function getOAuthProviders(): OAuthProviderInterface[] {\n return Array.from(oauthProviderRegistry.values());\n}\n\n/**\n * Credential storage backed by a JSON file.\n */\nexport class AuthStorage {\n private data: AuthStorageData = {};\n\n constructor(private authPath: string = join(getAppDataDir(), 'auth.json')) {\n this.reload();\n }\n\n /**\n * Reload credentials from disk.\n */\n reload(): void {\n if (!existsSync(this.authPath)) {\n this.data = {};\n return;\n }\n try {\n this.data = JSON.parse(readFileSync(this.authPath, 'utf-8'));\n } catch {\n this.data = {};\n }\n }\n\n /**\n * Save credentials to disk.\n */\n private save(): void {\n const dir = dirname(this.authPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n writeFileSync(this.authPath, JSON.stringify(this.data, null, 2), 'utf-8');\n chmodSync(this.authPath, 0o600);\n }\n\n /**\n * Get credential for a provider.\n */\n get(provider: string): AuthCredential | undefined {\n return this.data[provider] ?? undefined;\n }\n\n /**\n * Set credential for a provider.\n */\n set(provider: string, credential: AuthCredential): void {\n this.data[provider] = credential;\n this.save();\n }\n\n /**\n * Remove credential for a provider.\n */\n remove(provider: string): void {\n delete this.data[provider];\n this.save();\n }\n\n /**\n * List all providers with credentials.\n */\n list(): string[] {\n return Object.keys(this.data);\n }\n\n /**\n * Check if credentials exist for a provider.\n */\n has(provider: string): boolean {\n return provider in this.data;\n }\n\n /**\n * Check if logged in via OAuth for a provider.\n */\n isLoggedIn(provider: string): boolean {\n const cred = this.data[provider];\n return cred?.type === 'oauth';\n }\n\n /**\n * Check if a stored API key exists for a provider.\n * Keys are stored under `apikey:<provider>` in auth.json.\n */\n hasStoredApiKey(provider: string): boolean {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0;\n }\n\n /**\n * Get a stored API key for a provider, if any.\n */\n getStoredApiKey(provider: string): string | undefined {\n const cred = this.data[`apikey:${provider}`];\n return cred?.type === 'api_key' && cred.key.length > 0 ? cred.key : undefined;\n }\n\n /**\n * Store an API key for a provider.\n * Also sets the corresponding environment variable so model resolution can find it.\n */\n setStoredApiKey(provider: string, key: string, envVar?: string): void {\n this.set(`apikey:${provider}`, { type: 'api_key', key });\n if (envVar) {\n process.env[envVar] = key;\n }\n }\n\n /**\n * Load all stored API keys into process.env.\n * Called at startup so model resolution can find stored keys.\n * Only sets env vars that aren't already set (env vars take precedence).\n */\n loadStoredApiKeysIntoEnv(providerEnvVars: Record<string, string | undefined>): void {\n for (const [key, cred] of Object.entries(this.data)) {\n if (!key.startsWith('apikey:') || cred.type !== 'api_key' || !cred.key) continue;\n const provider = key.substring('apikey:'.length);\n const envVar = providerEnvVars[provider];\n if (envVar && !process.env[envVar]) {\n process.env[envVar] = cred.key;\n }\n }\n }\n\n /**\n * Login to an OAuth provider.\n */\n async login(providerId: OAuthProviderId, callbacks: OAuthLoginCallbacks): Promise<void> {\n const provider = getOAuthProvider(providerId);\n if (!provider) {\n throw new Error(`Unknown OAuth provider: ${providerId}`);\n }\n\n const credentials = await provider.login(callbacks);\n this.set(providerId, { type: 'oauth', ...credentials });\n }\n\n /**\n * Logout from a provider.\n */\n logout(provider: string): void {\n this.remove(provider);\n }\n\n /**\n * Get API key for a provider, auto-refreshing OAuth tokens if needed.\n */\n async getApiKey(providerId: string): Promise<string | undefined> {\n const cred = this.data[providerId];\n\n if (cred?.type === 'api_key') {\n return cred.key;\n }\n\n if (cred?.type === 'oauth') {\n const provider = getOAuthProvider(providerId);\n if (!provider) {\n return undefined;\n }\n\n // Check if token needs refresh\n if (Date.now() >= cred.expires) {\n try {\n const newCreds = await provider.refreshToken(cred);\n this.set(providerId, { type: 'oauth', ...newCreds });\n return provider.getApiKey(newCreds);\n } catch {\n // Refresh failed - user needs to re-login\n return undefined;\n }\n }\n\n return provider.getApiKey(cred);\n }\n\n return undefined;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/constants.js
CHANGED
|
@@ -10,7 +10,7 @@ function validateConfigDirName(configDirName) {
|
|
|
10
10
|
);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
|
-
const DEFAULT_OM_MODEL_ID = process.env.DEFAULT_OM_MODEL_ID ?? "google/gemini-
|
|
13
|
+
const DEFAULT_OM_MODEL_ID = process.env.DEFAULT_OM_MODEL_ID ?? "google/gemini-3.5-flash";
|
|
14
14
|
const DEFAULT_OBS_THRESHOLD = 3e4;
|
|
15
15
|
const DEFAULT_REF_THRESHOLD = 4e4;
|
|
16
16
|
export {
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["import * as path from 'node:path';\n\n// Default config directory name used for all project-level and global config paths.\nexport const DEFAULT_CONFIG_DIR = '.mastracode';\n\n/**\n * Validate that a configDirName is a safe single directory name.\n * Rejects absolute paths, path separators, and traversal components.\n */\nexport function validateConfigDirName(configDirName: string): void {\n if (configDirName.trim().length === 0) {\n throw new Error('configDirName must be a non-empty directory name');\n }\n\n if (\n path.isAbsolute(configDirName) ||\n configDirName.includes('/') ||\n configDirName.includes('\\\\') ||\n configDirName === '..' ||\n configDirName === '.'\n ) {\n throw new Error(\n `configDirName must be a single directory name without path separators or traversal components, got: \"${configDirName}\"`,\n );\n }\n}\n\n// Default OM model - using gemini-
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts"],"sourcesContent":["import * as path from 'node:path';\n\n// Default config directory name used for all project-level and global config paths.\nexport const DEFAULT_CONFIG_DIR = '.mastracode';\n\n/**\n * Validate that a configDirName is a safe single directory name.\n * Rejects absolute paths, path separators, and traversal components.\n */\nexport function validateConfigDirName(configDirName: string): void {\n if (configDirName.trim().length === 0) {\n throw new Error('configDirName must be a non-empty directory name');\n }\n\n if (\n path.isAbsolute(configDirName) ||\n configDirName.includes('/') ||\n configDirName.includes('\\\\') ||\n configDirName === '..' ||\n configDirName === '.'\n ) {\n throw new Error(\n `configDirName must be a single directory name without path separators or traversal components, got: \"${configDirName}\"`,\n );\n }\n}\n\n// Default OM model - using gemini-3.5-flash for efficiency\nexport const DEFAULT_OM_MODEL_ID = process.env.DEFAULT_OM_MODEL_ID ?? 'google/gemini-3.5-flash';\n\n// Default OM thresholds — per-thread overrides are loaded from thread metadata\nexport const DEFAULT_OBS_THRESHOLD = 30_000;\nexport const DEFAULT_REF_THRESHOLD = 40_000;\n"],"mappings":"AAAA,YAAY,UAAU;AAGf,MAAM,qBAAqB;AAM3B,SAAS,sBAAsB,eAA6B;AACjE,MAAI,cAAc,KAAK,EAAE,WAAW,GAAG;AACrC,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,MACE,KAAK,WAAW,aAAa,KAC7B,cAAc,SAAS,GAAG,KAC1B,cAAc,SAAS,IAAI,KAC3B,kBAAkB,QAClB,kBAAkB,KAClB;AACA,UAAM,IAAI;AAAA,MACR,wGAAwG,aAAa;AAAA,IACvH;AAAA,EACF;AACF;AAGO,MAAM,sBAAsB,QAAQ,IAAI,uBAAuB;AAG/D,MAAM,wBAAwB;AAC9B,MAAM,wBAAwB;","names":[]}
|
package/dist/goal-manager.d.ts
CHANGED
|
@@ -24,18 +24,13 @@ export declare const DEFAULT_MAX_TURNS = 50;
|
|
|
24
24
|
export declare class GoalManager {
|
|
25
25
|
/** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */
|
|
26
26
|
private record;
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
private activeDurationMs;
|
|
27
|
+
private threadId;
|
|
28
|
+
private agentId;
|
|
30
29
|
private persistGoalOnNextThreadCreate;
|
|
31
30
|
getGoal(): GoalState | null;
|
|
32
31
|
isActive(): boolean;
|
|
33
32
|
persistOnNextThreadCreate(): void;
|
|
34
33
|
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
34
|
/**
|
|
40
35
|
* Set a new objective. Persists to ThreadState via `agent.setObjective` and
|
|
41
36
|
* updates the in-memory view. Only the provided settings are persisted into
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goal-manager.d.ts","sourceRoot":"","sources":["../src/goal-manager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"goal-manager.d.ts","sourceRoot":"","sources":["../src/goal-manager.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAI9E,MAAM,WAAW,gBAAgB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChG,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1B;AAMD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAWpC,qBAAa,WAAW;IACtB,kGAAkG;IAClG,OAAO,CAAC,MAAM,CAAuD;IACrE,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,6BAA6B,CAAS;IAM9C,OAAO,IAAI,SAAS,GAAG,IAAI;IAuB3B,QAAQ,IAAI,OAAO;IAInB,yBAAyB,IAAI,IAAI;IAIjC,gCAAgC,IAAI,OAAO;IAU3C;;;;OAIG;IACG,OAAO,CACX,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,QAAQ,GAAE,MAA0B,GACnC,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA0B5B;;;OAGG;IACG,mBAAmB,CACvB,KAAK,EAAE,gBAAgB,EACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAsB5B,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAOxC,MAAM,IAAI,SAAS,GAAG,IAAI;IAO1B,QAAQ,IAAI,IAAI;IAMhB,KAAK,IAAI,IAAI;IAOb;;;OAGG;IACH,eAAe,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAE,GAAG,SAAS,GAAG,IAAI;IAUnF;;;;OAIG;IACG,YAAY,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgD1D;;;OAGG;IACG,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB5D;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI;IA0B3E,OAAO,CAAC,QAAQ;IAQhB,kFAAkF;IAClF,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,WAAW;CAmBpB"}
|
package/dist/goal-manager.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { getGoalActivityDurationMs } from "@mastra/core/agent";
|
|
2
3
|
import { loadSettings } from "./onboarding/settings.js";
|
|
3
4
|
const DEFAULT_MAX_TURNS = 50;
|
|
4
5
|
const THREAD_GOAL_KEY = "goal";
|
|
6
|
+
function normalizeActiveDurationMs(value) {
|
|
7
|
+
return value !== void 0 && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
8
|
+
}
|
|
5
9
|
class GoalManager {
|
|
6
10
|
/** Synchronous in-memory view of the active objective record (source of truth is ThreadState). */
|
|
7
11
|
record = null;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
activeDurationMs = 0;
|
|
12
|
+
threadId;
|
|
13
|
+
agentId;
|
|
11
14
|
persistGoalOnNextThreadCreate = false;
|
|
12
15
|
// ---------------------------------------------------------------------------
|
|
13
16
|
// Synchronous TUI surface
|
|
@@ -23,8 +26,12 @@ class GoalManager {
|
|
|
23
26
|
maxTurns,
|
|
24
27
|
judgeModelId,
|
|
25
28
|
startedAt: new Date(this.record.startedAt).toISOString(),
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
activeDurationMs: this.agentId && this.threadId ? getGoalActivityDurationMs({
|
|
30
|
+
agentId: this.agentId,
|
|
31
|
+
threadId: this.threadId,
|
|
32
|
+
objectiveId: this.record.id,
|
|
33
|
+
activeDurationMs: this.record.activeDurationMs
|
|
34
|
+
}) : normalizeActiveDurationMs(this.record.activeDurationMs)
|
|
28
35
|
};
|
|
29
36
|
}
|
|
30
37
|
isActive() {
|
|
@@ -38,24 +45,6 @@ class GoalManager {
|
|
|
38
45
|
this.persistGoalOnNextThreadCreate = false;
|
|
39
46
|
return true;
|
|
40
47
|
}
|
|
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
48
|
// ---------------------------------------------------------------------------
|
|
60
49
|
// Objective lifecycle (ThreadState-backed via the agent)
|
|
61
50
|
// ---------------------------------------------------------------------------
|
|
@@ -69,6 +58,8 @@ class GoalManager {
|
|
|
69
58
|
const agent = this.getAgent(state);
|
|
70
59
|
const now = Date.now();
|
|
71
60
|
const id = randomUUID();
|
|
61
|
+
this.threadId = threadId ?? void 0;
|
|
62
|
+
this.agentId = agent?.id;
|
|
72
63
|
if (agent && threadId) {
|
|
73
64
|
const persisted = await agent.setObjective(objective, {
|
|
74
65
|
id,
|
|
@@ -81,8 +72,6 @@ class GoalManager {
|
|
|
81
72
|
} else {
|
|
82
73
|
this.record = this.localRecord(objective, judgeModelId, maxTurns, now, id);
|
|
83
74
|
}
|
|
84
|
-
this.activeStartedAt = new Date(now).toISOString();
|
|
85
|
-
this.activeDurationMs = 0;
|
|
86
75
|
return this.getGoal();
|
|
87
76
|
}
|
|
88
77
|
/**
|
|
@@ -112,7 +101,6 @@ class GoalManager {
|
|
|
112
101
|
}
|
|
113
102
|
pause(reason) {
|
|
114
103
|
if (this.record && this.record.status === "active") {
|
|
115
|
-
this.stopActiveTimer();
|
|
116
104
|
this.record = { ...this.record, status: "paused", pausedReason: reason, updatedAt: Date.now() };
|
|
117
105
|
}
|
|
118
106
|
return this.getGoal();
|
|
@@ -120,20 +108,18 @@ class GoalManager {
|
|
|
120
108
|
resume() {
|
|
121
109
|
if (this.record && this.record.status === "paused") {
|
|
122
110
|
this.record = { ...this.record, status: "active", pausedReason: void 0, updatedAt: Date.now() };
|
|
123
|
-
this.startActiveTimer();
|
|
124
111
|
}
|
|
125
112
|
return this.getGoal();
|
|
126
113
|
}
|
|
127
114
|
markDone() {
|
|
128
115
|
if (this.record) {
|
|
129
|
-
this.stopActiveTimer();
|
|
130
116
|
this.record = { ...this.record, status: "done", updatedAt: Date.now() };
|
|
131
117
|
}
|
|
132
118
|
}
|
|
133
119
|
clear() {
|
|
134
120
|
this.record = null;
|
|
135
|
-
this.
|
|
136
|
-
this.
|
|
121
|
+
this.threadId = void 0;
|
|
122
|
+
this.agentId = void 0;
|
|
137
123
|
this.persistGoalOnNextThreadCreate = false;
|
|
138
124
|
}
|
|
139
125
|
/**
|
|
@@ -143,7 +129,6 @@ class GoalManager {
|
|
|
143
129
|
applyEvaluation(update) {
|
|
144
130
|
if (!this.record) return null;
|
|
145
131
|
this.record = { ...this.record, runsUsed: update.runsUsed, status: update.status, updatedAt: Date.now() };
|
|
146
|
-
if (update.status !== "active") this.stopActiveTimer();
|
|
147
132
|
return this.getGoal();
|
|
148
133
|
}
|
|
149
134
|
// ---------------------------------------------------------------------------
|
|
@@ -173,6 +158,7 @@ class GoalManager {
|
|
|
173
158
|
id: this.record.id,
|
|
174
159
|
threadId,
|
|
175
160
|
resourceId: state.session.identity.getResourceId(),
|
|
161
|
+
activeDurationMs: normalizeActiveDurationMs(this.record.activeDurationMs),
|
|
176
162
|
...this.record.judgeModelId ? { judgeModelId: this.record.judgeModelId } : {},
|
|
177
163
|
...this.record.maxRuns !== void 0 ? { maxRuns: this.record.maxRuns } : {}
|
|
178
164
|
});
|
|
@@ -198,15 +184,19 @@ class GoalManager {
|
|
|
198
184
|
*/
|
|
199
185
|
async loadFromThread(state) {
|
|
200
186
|
this.persistGoalOnNextThreadCreate = false;
|
|
201
|
-
this.activeStartedAt = null;
|
|
202
|
-
this.activeDurationMs = 0;
|
|
203
187
|
const threadId = state.session.thread.getId();
|
|
204
188
|
const agent = this.getAgent(state);
|
|
189
|
+
this.threadId = threadId ?? void 0;
|
|
190
|
+
this.agentId = agent?.id;
|
|
205
191
|
if (agent && threadId) {
|
|
206
192
|
try {
|
|
207
193
|
const record = await agent.getObjective({ threadId });
|
|
208
194
|
if (record) {
|
|
209
|
-
this.record = {
|
|
195
|
+
this.record = {
|
|
196
|
+
...record,
|
|
197
|
+
id: record.id ?? randomUUID(),
|
|
198
|
+
activeDurationMs: normalizeActiveDurationMs(record.activeDurationMs)
|
|
199
|
+
};
|
|
210
200
|
return;
|
|
211
201
|
}
|
|
212
202
|
} catch {
|
|
@@ -221,20 +211,20 @@ class GoalManager {
|
|
|
221
211
|
loadFromThreadMetadata(metadata) {
|
|
222
212
|
const saved = metadata?.[THREAD_GOAL_KEY];
|
|
223
213
|
this.persistGoalOnNextThreadCreate = false;
|
|
224
|
-
this.
|
|
225
|
-
this.
|
|
214
|
+
this.threadId = void 0;
|
|
215
|
+
this.agentId = void 0;
|
|
226
216
|
if (saved && saved.objective && saved.status) {
|
|
227
217
|
this.record = {
|
|
228
218
|
objective: saved.objective,
|
|
229
219
|
status: saved.status,
|
|
230
220
|
runsUsed: saved.turnsUsed ?? 0,
|
|
221
|
+
activeDurationMs: normalizeActiveDurationMs(saved.activeDurationMs),
|
|
231
222
|
maxRuns: saved.maxTurns ?? DEFAULT_MAX_TURNS,
|
|
232
223
|
judgeModelId: saved.judgeModelId ?? "",
|
|
233
224
|
startedAt: saved.startedAt ? Date.parse(saved.startedAt) || Date.now() : Date.now(),
|
|
234
225
|
updatedAt: Date.now(),
|
|
235
226
|
id: saved.id ?? randomUUID()
|
|
236
227
|
};
|
|
237
|
-
this.activeDurationMs = saved.activeDurationMs ?? 0;
|
|
238
228
|
} else {
|
|
239
229
|
this.record = null;
|
|
240
230
|
}
|
|
@@ -262,6 +252,7 @@ class GoalManager {
|
|
|
262
252
|
objective,
|
|
263
253
|
status: "active",
|
|
264
254
|
runsUsed: 0,
|
|
255
|
+
activeDurationMs: 0,
|
|
265
256
|
maxRuns: maxTurns,
|
|
266
257
|
...judgeModelId ? { judgeModelId } : {},
|
|
267
258
|
startedAt: now,
|