@agentproto/adapter-mastracode-inprocess 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 agentproto contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: mastracode-inprocess
3
+ id: mastracode-inprocess
4
+ description: Mastra Code driven in-process via the mastracode SDK — no subprocess spawn.
5
+ version: 0.1.0
6
+ bin: in-process
7
+ install:
8
+ - method: npm
9
+ package: "@agentproto/adapter-mastracode-inprocess"
10
+ version_check:
11
+ cmd: npm view mastracode version
12
+ parse: '(\d+\.\d+\.\d+)'
13
+ range: ">=0.27.0"
14
+ timeout_ms: 15000
15
+ sandbox:
16
+ model: in-process
17
+ note: >-
18
+ Runs inside the host process — no subprocess boundary. File/shell tool
19
+ access is whatever Mastra Code's own tool policy grants for the session,
20
+ with the host process's OS permissions.
21
+ protocol: proprietary
22
+ adapter: "@agentproto/adapter-mastracode-inprocess"
23
+ tags: ["mastracode", "mastra", "proprietary", "in-process", "agent-runtime", "coding"]
24
+ ---
25
+
26
+ # Mastra Code (in-process) adapter
27
+
28
+ This adapter drives Mastra Code directly through its `mastracode` /
29
+ `mastracode/headless` SDK (`createMastraCode` + `runMC`) instead of spawning
30
+ `npx mastracode` as a subprocess. It implements the AIP-45 `protocol:
31
+ "proprietary"` arm contract: `createAgentCliRuntime` skips the subprocess
32
+ spawn entirely for this manifest and dynamic-imports this package, calling
33
+ its exported `createAgentCliClient(definition)` factory (see
34
+ `src/client.ts`).
35
+
36
+ ## Why a separate package instead of extending `@agentproto/adapter-mastracode`
37
+
38
+ `@agentproto/adapter-mastracode` is a `protocol: "print"` arm: every turn
39
+ spawns a fresh `npx mastracode --prompt ... --output jsonl` process and
40
+ resumes prior turns via `--thread <id>`. This package wraps the *same*
41
+ underlying agent but through its SDK surface, in the host's own process.
42
+ Those are genuinely different runtime shapes, not a config toggle on one
43
+ package:
44
+
45
+ - **Dependency surface.** The print arm has zero npm dependency on
46
+ `mastracode`/`@mastra/core` — it only needs the CLI binary on `PATH` (or
47
+ fetched on demand via `npx`). This package takes `mastracode` and
48
+ `@mastra/core` as real `dependencies`, pinned to versions this adapter is
49
+ tested against. Merging them would force every consumer of the print arm
50
+ to pull in the full SDK even when they never use it, or force every
51
+ in-process consumer onto whatever CLI version happens to be on `PATH`.
52
+ - **Process lifecycle.** The print arm's `AgentCliClient` has no live
53
+ connection between turns — `connect()`/`close()` are no-ops and each
54
+ `send()` spawns and tears down its own child. This package's client holds
55
+ a long-lived `MastraCodeHandle` (`controller` + `session`) across
56
+ `connect()` → many `send()`s → `close()`, and that handle's lifecycle
57
+ rules (in particular the resourceId/threadId binding documented below)
58
+ don't map onto a stateless one-shot-per-turn model at all.
59
+ - **Session-id shape.** The print arm's resumable id is a bare Mastra
60
+ thread id, passed straight through `--thread <id>`. This package's
61
+ `sessionId` is a composite `"<resourceId>:<threadId>"` (see below) — a
62
+ format specific to how `runMC()` resolves threads for a live, in-process
63
+ `AgentController`. Encoding two incompatible id formats behind one
64
+ `protocol` field would require a runtime discriminator the AIP-45 spec
65
+ doesn't have a slot for, and would silently break resume for whichever
66
+ shape wasn't the one actually in use.
67
+ - **Independent versioning/failure domain.** The print arm can keep
68
+ shipping fixes to its subprocess/argv handling without ever touching (or
69
+ re-testing) the in-process SDK surface, and vice versa. A shared package
70
+ would couple their release cadence and blast radius for no shared code —
71
+ `mapMastraEvent`/`createMastraMapperState` (the only genuinely shared
72
+ logic, the wire-event → `StreamEvent` mapper) is exported from
73
+ `@agentproto/driver-agent-cli` precisely so both packages can reuse it
74
+ without merging everything else around it.
75
+
76
+ A new adapter slug (`mastracode-inprocess`) keeps both discoverable
77
+ independently via `adapter_list` / `agentproto install`, lets a host pick
78
+ whichever runtime shape it actually wants (subprocess isolation vs.
79
+ in-process, no `npx` cold-start), and keeps this package's `dependencies`
80
+ honest about what it actually needs.
81
+
82
+ ## The composite sessionId
83
+
84
+ A single Mastra Code `AgentController` auto-binds a session to whatever
85
+ thread `session.thread` currently points at, and (verified against
86
+ mastracode 0.27.0 / @mastra/core 1.48.0) `runMC()` only rebinds that
87
+ pointer when a thread is explicitly passed. Two unrelated conversations
88
+ sharing this arm's dedicated storage file would otherwise silently land on
89
+ the same thread, because Mastra Code resolves the initial thread lookup by
90
+ `resourceId` and every conversation was defaulting to the same one.
91
+
92
+ The fix: a distinct `resourceId` per *conversation* (not per process, not
93
+ via a global env var, which would race across concurrent in-process
94
+ conversations in the same daemon). A fresh conversation mints a random
95
+ `resourceId`; resuming one must reuse the exact `resourceId` the thread was
96
+ created under, since thread-by-id resolution is scoped to the session's
97
+ current `resourceId`. So this arm's public `sessionId` is
98
+ `"<resourceId>:<threadId>"` — hosts only need to persist and echo back
99
+ whatever string `sessionId` returns, per the generic `AgentCliClient`
100
+ contract; the composite format is this arm's own implementation detail.
101
+
102
+ Cross-process resume (kill the process, start a new one, reattach via the
103
+ same composite `sessionId` against the same dedicated storage file) has
104
+ been verified to preserve conversation memory end-to-end.
105
+
106
+ ## Isolation
107
+
108
+ - **Storage**: a dedicated libsql file under
109
+ `$AGENTPROTO_HOME/mastracode-inprocess/storage.db` (default
110
+ `~/.agentproto/...`), never the ambient global `mastracode` storage a
111
+ developer's own interactive CLI use would touch.
112
+ - **Home dir**: a dedicated, empty home directory for Mastra Code's global
113
+ config discovery (`~/.claude/skills`, `~/.mastracode/*`, `~/.agents/skills`),
114
+ so this arm never implicitly inherits an operator's personal Claude
115
+ Code / Mastra Code setup. A target repo's own project-local
116
+ `.claude/skills` (relative to `cwd`) is unaffected — that's the repo's
117
+ own content, not the operator's personal config.
118
+
119
+ ## Modes and options
120
+
121
+ Mode selection has no argv to append to (there's no spawn) — modes patch
122
+ env instead, the one channel `composeSpawn` threads through for every
123
+ protocol regardless of whether a subprocess exists. `plan` / `build` /
124
+ `fast` set `AGENTPROTO_MASTRACODE_MODE`, which `client.ts` reads and
125
+ applies via `session.mode.switch(...)` before the next turn. `model` and
126
+ `effort` are declared as options so a host's generic
127
+ `agent_start({ model, effort })` passthrough works without the runtime
128
+ config validator rejecting them as unknown.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # @agentproto/adapter-mastracode-inprocess
2
+
3
+ Mastra Code driven **in-process** via its `mastracode` / `mastracode/headless`
4
+ SDK, instead of spawning `npx mastracode` as a subprocess. Implements the
5
+ AIP-45 `protocol: "proprietary"` arm contract directly — no child process,
6
+ no argv, no JSONL pipe.
7
+
8
+ ```ts
9
+ import { mastracodeInprocessRuntime } from "@agentproto/adapter-mastracode-inprocess"
10
+
11
+ const runtime = mastracodeInprocessRuntime()
12
+ const session = await runtime.start({ cwd: process.cwd() })
13
+
14
+ for await (const evt of session.send("Fix the failing test in src/foo.ts")) {
15
+ console.log(evt)
16
+ }
17
+ await session.close()
18
+
19
+ // Resume later — same process or a fresh one — via the sessionId the
20
+ // first session returned:
21
+ const resumed = await runtime.start({ resumeSessionId: session.sessionId })
22
+ ```
23
+
24
+ See `@agentproto/driver-agent-cli`'s `createAgentCliRuntime` for the generic
25
+ `AgentCliRuntime`/`AgentCliClient` contract this package implements.
26
+
27
+ ## How it works
28
+
29
+ ```
30
+ manifest (src/index.ts, protocol: "proprietary")
31
+ │ createAgentCliRuntime skips the subprocess spawn
32
+
33
+ createProprietaryProtocolArm ──dynamic import──▶ this package
34
+ │ │
35
+ │ createAgentCliClient(definition)
36
+ ▼ ▼
37
+ AgentCliClient ◀── src/client.ts ── createMastraCode() + runMC()
38
+
39
+ │ mapMastraEvent (reused from @agentproto/driver-agent-cli's print arm)
40
+
41
+ StreamEvent
42
+ ```
43
+
44
+ ## Why this is a separate package from `@agentproto/adapter-mastracode`
45
+
46
+ That package spawns the CLI (`protocol: "print"`) per turn; this one holds
47
+ a live in-process `AgentController` across turns and never shells out.
48
+ Different dependency surface, different process lifecycle, different
49
+ session-id shape — see **MASTRACODE-INPROCESS.md** for the full rationale.
50
+
51
+ ## Session ids
52
+
53
+ `sessionId` is a composite `"<resourceId>:<threadId>"` string — an
54
+ implementation detail of how Mastra Code resolves threads for a live
55
+ `AgentController`, documented in full in **MASTRACODE-INPROCESS.md**. Hosts
56
+ should treat it as an opaque string: persist whatever `sessionId` returns
57
+ and pass it back as `resumeSessionId` to resume, including across a
58
+ process restart.
59
+
60
+ ## Isolation
61
+
62
+ Storage and Mastra Code's global home-dir config discovery are both
63
+ redirected to a dedicated location under `$AGENTPROTO_HOME` (default
64
+ `~/.agentproto/mastracode-inprocess/`), so this arm's sessions never
65
+ collide with — or leak into — a developer's own interactive `mastracode`
66
+ usage. See MASTRACODE-INPROCESS.md for specifics and a known limitation
67
+ around `homeDir` propagation.
68
+
69
+ ## Modes and options
70
+
71
+ `modes` (`plan` / `build` / `fast`) and `options` (`model` / `effort`) are
72
+ declared on the manifest; see MASTRACODE-INPROCESS.md for how they reach
73
+ `client.ts` (env-carried mode selection, direct model/effort passthrough).
74
+
75
+ ## Spec
76
+
77
+ See [AIP-45](https://agentproto.sh/docs/aip-45).
@@ -0,0 +1,67 @@
1
+ import { AgentCliHandle, AgentCliClient, AgentCliRuntime } from '@agentproto/driver-agent-cli';
2
+ export { AgentCliHandle, AgentCliRuntime } from '@agentproto/driver-agent-cli';
3
+
4
+ /**
5
+ * `AgentCliClient` implementation wrapping Mastra Code's in-process SDK
6
+ * (`mastracode` / `mastracode/headless`) instead of spawning a subprocess.
7
+ *
8
+ * ## Why a composite sessionId
9
+ *
10
+ * A single Mastra Code `AgentController` instance auto-binds a *fresh*
11
+ * session to whatever thread `session.thread` currently points at, and
12
+ * (empirically verified against mastracode 0.27.0 / @mastra/core 1.48.0)
13
+ * `runMC()` only rebinds that pointer when `thread.id` / `thread.continueLatest`
14
+ * is explicitly passed. Two unrelated conversations that share both a
15
+ * `cwd` and this arm's dedicated storage file would otherwise silently
16
+ * land on the SAME thread — Mastra Code resolves the initial thread by
17
+ * `resourceId`, and every conversation was defaulting to the same one.
18
+ *
19
+ * The fix (also verified live): pass a distinct `resourceId` to `runMC()`
20
+ * per *conversation*, not per process and not globally via
21
+ * `MASTRA_RESOURCE_ID` (a global env var would race across concurrent
22
+ * in-process conversations in the same daemon). A fresh conversation
23
+ * mints a random resourceId; a resumed one must reuse the EXACT
24
+ * resourceId the thread was created under, because `session.thread.list()`
25
+ * — which `runMC`'s thread-by-id resolution calls internally — is scoped
26
+ * to the session's current resourceId by default. So this arm's public
27
+ * `sessionId` is `"<resourceId>:<threadId>"`, and resume splits it back
28
+ * apart. This composite format is this arm's own implementation detail;
29
+ * hosts only ever need to persist and echo back whatever string
30
+ * `sessionId` returns, per the generic AgentCliClient contract.
31
+ *
32
+ * Cross-process resume (kill the process, start a new one, reattach via
33
+ * the same composite sessionId against the same dedicated storage file)
34
+ * has been verified to preserve conversation memory end-to-end.
35
+ */
36
+
37
+ declare function createAgentCliClient(definition: AgentCliHandle): AgentCliClient;
38
+
39
+ /**
40
+ * @agentproto/adapter-mastracode-inprocess — AIP-45 adapter for Mastra Code,
41
+ * driven in-process via the `mastracode` / `mastracode/headless` SDK
42
+ * instead of spawning a subprocess.
43
+ *
44
+ * This is a `protocol: "proprietary"` manifest: `createAgentCliRuntime`
45
+ * skips the subprocess spawn entirely for this protocol and instead
46
+ * dynamic-imports `adapter` (this very package) and calls its exported
47
+ * `createAgentCliClient(definition)` factory — see
48
+ * `createProprietaryProtocolArm` in `@agentproto/driver-agent-cli`. The
49
+ * factory itself lives in `./client.ts`; this file only builds and
50
+ * exports the manifest handle.
51
+ *
52
+ * import { mastracodeInprocess, mastracodeInprocessRuntime } from "@agentproto/adapter-mastracode-inprocess"
53
+ * const session = await mastracodeInprocessRuntime().start()
54
+ * for await (const evt of session.send({ role: "user", content: "..." })) {
55
+ * console.log(evt)
56
+ * }
57
+ * await session.close()
58
+ *
59
+ * Why a separate package from `@agentproto/adapter-mastracode` (the
60
+ * `print` arm) rather than a second export of it — see
61
+ * MASTRACODE-INPROCESS.md.
62
+ */
63
+
64
+ declare const mastracodeInprocess: AgentCliHandle;
65
+ declare function mastracodeInprocessRuntime(): AgentCliRuntime;
66
+
67
+ export { createAgentCliClient, mastracodeInprocess, mastracodeInprocessRuntime };
package/dist/index.mjs ADDED
@@ -0,0 +1,272 @@
1
+ import { defineAgentCli, createMastraMapperState, mapMastraEvent, toFileBasedMcpServers, createAgentCliRuntime } from '@agentproto/driver-agent-cli';
2
+ import { randomUUID } from 'crypto';
3
+ import { homedir } from 'os';
4
+ import { dirname, join } from 'path';
5
+ import { mkdirSync } from 'fs';
6
+ import { createMastraCode } from 'mastracode';
7
+ import { runMC } from 'mastracode/headless';
8
+
9
+ /**
10
+ * @agentproto/adapter-mastracode-inprocess v0.1.0
11
+ * AIP-45 proprietary protocol arm for Mastra Code, driven in-process
12
+ * via the mastracode SDK instead of a spawned subprocess.
13
+ */
14
+
15
+ var MODE_ENV_KEY = "AGENTPROTO_MASTRACODE_MODE";
16
+ var VALID_THINKING_LEVELS = /* @__PURE__ */ new Set(["off", "low", "medium", "high", "xhigh"]);
17
+ var VALID_RUN_MODES = /* @__PURE__ */ new Set(["build", "plan", "fast"]);
18
+ function resolveAgentprotoHome() {
19
+ return process.env.AGENTPROTO_HOME ?? join(homedir(), ".agentproto");
20
+ }
21
+ function resolveStoragePath() {
22
+ return join(resolveAgentprotoHome(), "mastracode-inprocess", "storage.db");
23
+ }
24
+ function resolveIsolatedHomeDir() {
25
+ return join(resolveAgentprotoHome(), "mastracode-inprocess", "home");
26
+ }
27
+ function parseSessionId(sessionId) {
28
+ const idx = sessionId.indexOf(":");
29
+ if (idx <= 0 || idx === sessionId.length - 1) {
30
+ throw new Error(
31
+ `mastracode-inprocess: malformed resumeSessionId '${sessionId}' \u2014 expected "<resourceId>:<threadId>", the format this arm's own sessionId getter produces.`
32
+ );
33
+ }
34
+ return { resourceId: sessionId.slice(0, idx), threadId: sessionId.slice(idx + 1) };
35
+ }
36
+ function createAgentCliClient(definition) {
37
+ let handle;
38
+ let resourceId;
39
+ let threadId;
40
+ let connectEnv = {};
41
+ let connectModel;
42
+ let connectEffort;
43
+ const pendingByTurn = /* @__PURE__ */ new Map();
44
+ return {
45
+ get sessionId() {
46
+ return resourceId && threadId ? `${resourceId}:${threadId}` : void 0;
47
+ },
48
+ async connect(opts) {
49
+ const isolatedHome = resolveIsolatedHomeDir();
50
+ const storagePath = resolveStoragePath();
51
+ mkdirSync(isolatedHome, { recursive: true });
52
+ mkdirSync(dirname(storagePath), { recursive: true });
53
+ handle = await createMastraCode({
54
+ cwd: opts.cwd,
55
+ homeDir: isolatedHome,
56
+ initialState: { homeDir: isolatedHome },
57
+ disablePlugins: true,
58
+ storage: { backend: "libsql", url: `file:${storagePath}`, isRemote: false },
59
+ ...opts.mcpServers?.length ? { mcpServers: toFileBasedMcpServers(opts.mcpServers) } : {}
60
+ });
61
+ if (handle.mcpManager?.hasServers()) {
62
+ try {
63
+ await handle.mcpManager.initInBackground();
64
+ } catch {
65
+ }
66
+ }
67
+ connectEnv = opts.env;
68
+ connectModel = opts.model;
69
+ connectEffort = opts.effort;
70
+ if (opts.resumeSessionId) {
71
+ ({ resourceId, threadId } = parseSessionId(opts.resumeSessionId));
72
+ } else {
73
+ resourceId = `agentproto-mastracode-inprocess-${randomUUID()}`;
74
+ threadId = void 0;
75
+ }
76
+ },
77
+ async send(turnId, message) {
78
+ if (!handle || !resourceId) {
79
+ throw new Error("mastracode-inprocess: send() called before connect()");
80
+ }
81
+ const prompt = extractPromptText(message);
82
+ const requestedMode = connectEnv[MODE_ENV_KEY];
83
+ if (requestedMode && VALID_RUN_MODES.has(requestedMode)) {
84
+ if (handle.session.mode.get() !== requestedMode) {
85
+ await handle.session.mode.switch({ modeId: requestedMode });
86
+ }
87
+ }
88
+ const thinkingLevel = connectEffort && VALID_THINKING_LEVELS.has(connectEffort) ? connectEffort : void 0;
89
+ const run = runMC({
90
+ controller: handle.controller,
91
+ session: handle.session,
92
+ prompt,
93
+ resourceId,
94
+ thread: threadId ? { id: threadId } : void 0,
95
+ ...connectModel ? { model: connectModel } : {},
96
+ ...thinkingLevel ? { thinkingLevel } : {},
97
+ ...definition.session?.max_turns ? { maxTurns: definition.session.max_turns } : {}
98
+ });
99
+ pendingByTurn.set(turnId, run);
100
+ },
101
+ async *events() {
102
+ const run = Array.from(pendingByTurn.values()).at(-1);
103
+ if (!run) return;
104
+ const state = createMastraMapperState();
105
+ for await (const evt of run) {
106
+ if (evt.type === "thread_created") threadId = evt.thread.id;
107
+ else if (evt.type === "thread_changed") threadId = evt.threadId;
108
+ const sid = resourceId && threadId ? `${resourceId}:${threadId}` : "";
109
+ const mapped = mapMastraEvent(
110
+ evt,
111
+ sid,
112
+ [],
113
+ state
114
+ );
115
+ if (mapped) yield mapped;
116
+ }
117
+ const result = await run.result;
118
+ if (result.threadId) threadId = result.threadId;
119
+ },
120
+ async cancel(turnId) {
121
+ pendingByTurn.get(turnId)?.abort();
122
+ },
123
+ async close() {
124
+ await handle?.controller.destroy();
125
+ }
126
+ };
127
+ }
128
+ function extractPromptText(message) {
129
+ if (typeof message === "string") return message;
130
+ if (Array.isArray(message)) {
131
+ return message.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n");
132
+ }
133
+ if (message !== null && typeof message === "object") {
134
+ const m = message;
135
+ if (typeof m.text === "string") return m.text;
136
+ }
137
+ return JSON.stringify(message);
138
+ }
139
+
140
+ // src/index.ts
141
+ var mastracodeInprocess = defineAgentCli({
142
+ name: "Mastra Code (in-process)",
143
+ id: "mastracode-inprocess",
144
+ description: "Mastra Code terminal coding agent driven in-process via the `mastracode` SDK (createMastraCode + runMC) instead of spawning the CLI as a subprocess. Same underlying agent as @agentproto/adapter-mastracode, no child process.",
145
+ version: "0.1.0",
146
+ // No binary is ever spawned for a proprietary arm — `bin` is required by
147
+ // the AIP-45 schema regardless, so this is a documented placeholder
148
+ // rather than a real executable. `createAgentCliRuntime` checks
149
+ // `protocol === "proprietary"` and never reads this field.
150
+ bin: "in-process",
151
+ install: [{ method: "npm", package: "@agentproto/adapter-mastracode-inprocess" }],
152
+ version_check: {
153
+ cmd: "npm view mastracode version",
154
+ parse: "(\\d+\\.\\d+\\.\\d+)",
155
+ // Matches this package's pinned `mastracode` dependency (see package.json).
156
+ range: ">=0.27.0",
157
+ timeout_ms: 15e3
158
+ },
159
+ auth: {
160
+ state: {
161
+ env: [
162
+ "ANTHROPIC_API_KEY",
163
+ "OPENAI_API_KEY",
164
+ "OPENROUTER_API_KEY",
165
+ "GOOGLE_GENERATIVE_AI_API_KEY"
166
+ ]
167
+ }
168
+ },
169
+ sandbox: {
170
+ model: "in-process",
171
+ note: "Runs inside the host process \u2014 there is no subprocess boundary to sandbox. File and shell tool access is whatever Mastra Code's own tool policy grants for this session, with the host process's OS permissions (not a separately confined child). Storage and home dir ARE isolated per MASTRACODE-INPROCESS.md so this arm's sessions never collide with a developer's own interactive mastracode usage, but that isolation is not a security sandbox."
172
+ },
173
+ protocol: "proprietary",
174
+ adapter: "@agentproto/adapter-mastracode-inprocess",
175
+ session: {
176
+ mode: "resumable",
177
+ idle_timeout_ms: 18e5,
178
+ context_carryover: true
179
+ },
180
+ models: {
181
+ default: "anthropic/claude-sonnet-4-5",
182
+ allowed: [
183
+ "anthropic/claude-sonnet-4-5",
184
+ "anthropic/claude-sonnet-4-6",
185
+ "openai/gpt-5.1",
186
+ "openai/gpt-5.1-mini",
187
+ "google/gemini-2.5-flash",
188
+ "openrouter/anthropic/claude-sonnet-4-6"
189
+ ],
190
+ env: {
191
+ anthropic: "ANTHROPIC_API_KEY",
192
+ openai: "OPENAI_API_KEY",
193
+ openrouter: "OPENROUTER_API_KEY",
194
+ google: "GOOGLE_GENERATIVE_AI_API_KEY"
195
+ }
196
+ },
197
+ capabilities: {
198
+ streaming: true,
199
+ tool_calls: true,
200
+ sub_agents: true,
201
+ file_io: true,
202
+ multimodal: false,
203
+ // The composite "<resourceId>:<threadId>" sessionId round-trips through
204
+ // `client.ts`'s connect(resumeSessionId) and has been verified to
205
+ // recall prior-turn context across a fresh process (see README.md).
206
+ resumable: true,
207
+ bidirectional: false
208
+ },
209
+ // Mode selection has no argv to append to (no spawn) — modes patch the
210
+ // one channel `composeSpawn` threads through for every protocol
211
+ // regardless: env. `client.ts` reads `AGENTPROTO_MASTRACODE_MODE` off
212
+ // the composed env and switches the live session's mode before send().
213
+ modes: [
214
+ {
215
+ id: "default",
216
+ description: "Default headless mode. Mastra Code defaults to build mode when omitted."
217
+ },
218
+ {
219
+ id: "plan",
220
+ description: "Plan-only mode \u2014 analyze architecture and propose implementation plans before writing code.",
221
+ env: { AGENTPROTO_MASTRACODE_MODE: "plan" }
222
+ },
223
+ {
224
+ id: "build",
225
+ description: "Build mode \u2014 file edits and shell commands use Mastra Code's configured permissions.",
226
+ env: { AGENTPROTO_MASTRACODE_MODE: "build" }
227
+ },
228
+ {
229
+ id: "fast",
230
+ description: "Fast mode \u2014 lower-latency answers and small edits.",
231
+ env: { AGENTPROTO_MASTRACODE_MODE: "fast" }
232
+ }
233
+ ],
234
+ // `model`/`effort` here are the two ids `createAgentCliRuntime` always
235
+ // reads off `config.options` for the generic model/effort passthrough
236
+ // (see `define-agent-cli.ts`) — declaring them is what lets a host pass
237
+ // `agent_start({ model, effort })` without composeSpawn rejecting the
238
+ // config as an unknown option. Both travel to `client.ts`'s connect()
239
+ // directly (not via bin_args/env), so no `bin_args_template`/`env` patch
240
+ // is declared on either.
241
+ options: [
242
+ {
243
+ id: "model",
244
+ type: "string",
245
+ description: "Provider/model override for this operator binding (e.g. `anthropic/claude-sonnet-4-5`)."
246
+ },
247
+ {
248
+ id: "effort",
249
+ type: "enum",
250
+ enum: ["off", "low", "medium", "high", "xhigh"],
251
+ description: "Thinking level, mapped 1:1 to Mastra Code's `thinkingLevel` run option."
252
+ }
253
+ ],
254
+ continuation: {
255
+ default: "none",
256
+ supported: ["none", "transcript", "native-resume"]
257
+ },
258
+ metadata: {
259
+ proprietary: {
260
+ checked: "2026-07-02",
261
+ result: "Verified in-process against mastracode 0.27.0 / @mastra/core 1.48.0: a turn returns real streamed content, and cross-process resume (kill the process, start a fresh one, reattach via the composite sessionId) recalls prior-turn context. See MASTRACODE-INPROCESS.md for the composite sessionId format."
262
+ }
263
+ },
264
+ tags: ["mastracode", "mastra", "proprietary", "in-process", "agent-runtime", "coding"]
265
+ });
266
+ function mastracodeInprocessRuntime() {
267
+ return createAgentCliRuntime(mastracodeInprocess);
268
+ }
269
+
270
+ export { createAgentCliClient, mastracodeInprocess, mastracodeInprocessRuntime };
271
+ //# sourceMappingURL=index.mjs.map
272
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * `AgentCliClient` implementation wrapping Mastra Code's in-process SDK\n * (`mastracode` / `mastracode/headless`) instead of spawning a subprocess.\n *\n * ## Why a composite sessionId\n *\n * A single Mastra Code `AgentController` instance auto-binds a *fresh*\n * session to whatever thread `session.thread` currently points at, and\n * (empirically verified against mastracode 0.27.0 / @mastra/core 1.48.0)\n * `runMC()` only rebinds that pointer when `thread.id` / `thread.continueLatest`\n * is explicitly passed. Two unrelated conversations that share both a\n * `cwd` and this arm's dedicated storage file would otherwise silently\n * land on the SAME thread — Mastra Code resolves the initial thread by\n * `resourceId`, and every conversation was defaulting to the same one.\n *\n * The fix (also verified live): pass a distinct `resourceId` to `runMC()`\n * per *conversation*, not per process and not globally via\n * `MASTRA_RESOURCE_ID` (a global env var would race across concurrent\n * in-process conversations in the same daemon). A fresh conversation\n * mints a random resourceId; a resumed one must reuse the EXACT\n * resourceId the thread was created under, because `session.thread.list()`\n * — which `runMC`'s thread-by-id resolution calls internally — is scoped\n * to the session's current resourceId by default. So this arm's public\n * `sessionId` is `\"<resourceId>:<threadId>\"`, and resume splits it back\n * apart. This composite format is this arm's own implementation detail;\n * hosts only ever need to persist and echo back whatever string\n * `sessionId` returns, per the generic AgentCliClient contract.\n *\n * Cross-process resume (kill the process, start a new one, reattach via\n * the same composite sessionId against the same dedicated storage file)\n * has been verified to preserve conversation memory end-to-end.\n */\n\nimport { randomUUID } from \"node:crypto\"\nimport { homedir } from \"node:os\"\nimport { join, dirname } from \"node:path\"\nimport { mkdirSync } from \"node:fs\"\nimport { createMastraCode } from \"mastracode\"\nimport { runMC, type MCRun } from \"mastracode/headless\"\nimport {\n mapMastraEvent,\n createMastraMapperState,\n toFileBasedMcpServers,\n type AgentCliClient,\n type AgentCliConnectOptions,\n type AgentCliHandle,\n type StreamEvent,\n} from \"@agentproto/driver-agent-cli\"\n\n/** Env var carrying a manifest `modes[].env` patch through to `send()` —\n * there's no argv for a `proprietary` arm to append `bin_args_append`\n * to, so mode selection travels the one channel `composeSpawn` already\n * threads through for every protocol: env. See `../MASTRACODE-INPROCESS.md`. */\nconst MODE_ENV_KEY = \"AGENTPROTO_MASTRACODE_MODE\"\n\nconst VALID_THINKING_LEVELS = new Set([\"off\", \"low\", \"medium\", \"high\", \"xhigh\"])\nconst VALID_RUN_MODES = new Set([\"build\", \"plan\", \"fast\"])\n\nfunction resolveAgentprotoHome(): string {\n return process.env.AGENTPROTO_HOME ?? join(homedir(), \".agentproto\")\n}\n\n/** Dedicated per-machine storage — NOT the ambient global mastracode\n * default (which is shared across every mastracode invocation on the\n * host, subprocess or not). Isolating it here means this arm's threads\n * never collide with a developer's own interactive `mastracode` usage. */\nfunction resolveStoragePath(): string {\n return join(resolveAgentprotoHome(), \"mastracode-inprocess\", \"storage.db\")\n}\n\n/** Dedicated, empty home directory so Mastra Code's global config\n * discovery (`~/.claude/skills`, `~/.mastracode/{hooks,commands,skills}`,\n * `~/.agents/skills`) does not implicitly inherit the operator's own\n * personal Claude Code / Mastra Code setup.\n *\n * KNOWN LIMITATION (verified against 0.27.0, not assumed): passing\n * `homeDir` at the top level of `MastraCodeConfig` is NOT sufficient —\n * `createMastraCodeAgentController` resolves `homeDir` for hooks/plugin\n * wiring but does not copy it into `initialState`, and the workspace's\n * skill-path builder reads `state.homeDir`, falling back to the real\n * `os.homedir()` when that's unset. Both `homeDir` AND\n * `initialState: { homeDir }` must be passed for the override to\n * actually reach skill discovery. This does not affect a target repo's\n * own PROJECT-LOCAL `.claude/skills` (relative to `cwd`) — that's the\n * repo's own content, not the operator's personal config, and is\n * intentionally left reachable (mirrors the ACP arm reading a target\n * repo's own `.claude/settings.json`). */\nfunction resolveIsolatedHomeDir(): string {\n return join(resolveAgentprotoHome(), \"mastracode-inprocess\", \"home\")\n}\n\nfunction parseSessionId(sessionId: string): { resourceId: string; threadId: string } {\n const idx = sessionId.indexOf(\":\")\n if (idx <= 0 || idx === sessionId.length - 1) {\n throw new Error(\n `mastracode-inprocess: malformed resumeSessionId '${sessionId}' — expected ` +\n `\"<resourceId>:<threadId>\", the format this arm's own sessionId getter produces.`,\n )\n }\n return { resourceId: sessionId.slice(0, idx), threadId: sessionId.slice(idx + 1) }\n}\n\n/** Narrow shape of `createMastraCode`'s resolved return value this arm\n * actually uses — kept local (not re-exported from `mastracode`) so this\n * file only depends on what it reads. */\ninterface MastraCodeHandle {\n controller: { destroy(): Promise<void> }\n session: {\n thread: { getId(): string | null }\n mode: { get(): string; switch(args: { modeId: string }): Promise<void> }\n }\n /** Present when `bootLocalAgentController` builds an MCP manager (i.e.\n * MCP servers were configured). `createMastraCode` does NOT initialize\n * it automatically — verified against mastracode's own headless CLI\n * entrypoint (`runMCCli` in `dist/chunk-HADE25EX.cjs`), which explicitly\n * calls `mcpManager.initInBackground()` itself right after boot, before\n * running any prompt. Skipping this step (as an earlier version of this\n * arm did) leaves `mcpServers` connected-but-never-initialized: the\n * config is accepted, but no MCP tools ever reach the agent's toolset. */\n mcpManager?: {\n hasServers(): boolean\n initInBackground(): Promise<unknown>\n }\n}\n\nexport function createAgentCliClient(definition: AgentCliHandle): AgentCliClient {\n let handle: MastraCodeHandle | undefined\n let resourceId: string | undefined\n let threadId: string | undefined\n let connectEnv: Record<string, string> = {}\n let connectModel: string | undefined\n let connectEffort: string | undefined\n const pendingByTurn = new Map<string, MCRun>()\n\n return {\n get sessionId(): string | undefined {\n return resourceId && threadId ? `${resourceId}:${threadId}` : undefined\n },\n\n async connect(opts: AgentCliConnectOptions): Promise<void> {\n const isolatedHome = resolveIsolatedHomeDir()\n const storagePath = resolveStoragePath()\n mkdirSync(isolatedHome, { recursive: true })\n mkdirSync(dirname(storagePath), { recursive: true })\n\n handle = (await createMastraCode({\n cwd: opts.cwd,\n homeDir: isolatedHome,\n initialState: { homeDir: isolatedHome },\n disablePlugins: true,\n storage: { backend: \"libsql\", url: `file:${storagePath}`, isRemote: false },\n ...(opts.mcpServers?.length\n ? { mcpServers: toFileBasedMcpServers(opts.mcpServers) }\n : {}),\n })) as unknown as MastraCodeHandle\n\n // `createMastraCode` builds the MCP manager but does not initialize\n // it — mirror mastracode's own headless CLI (`runMCCli`), which\n // explicitly awaits this before running any prompt. Without it, MCP\n // servers are configured but never connected, so their tools never\n // reach the agent. Best-effort: a failed MCP connection shouldn't\n // fail the whole session (matches the CLI's own warn-and-continue).\n if (handle.mcpManager?.hasServers()) {\n try {\n await handle.mcpManager.initInBackground()\n } catch {\n // Best-effort — the session still starts on its non-MCP tools.\n }\n }\n\n connectEnv = opts.env\n connectModel = opts.model\n connectEffort = opts.effort\n\n if (opts.resumeSessionId) {\n ;({ resourceId, threadId } = parseSessionId(opts.resumeSessionId))\n } else {\n resourceId = `agentproto-mastracode-inprocess-${randomUUID()}`\n threadId = undefined\n }\n },\n\n async send(turnId: string, message: unknown): Promise<void> {\n if (!handle || !resourceId) {\n throw new Error(\"mastracode-inprocess: send() called before connect()\")\n }\n const prompt = extractPromptText(message)\n\n const requestedMode = connectEnv[MODE_ENV_KEY]\n if (requestedMode && VALID_RUN_MODES.has(requestedMode)) {\n if (handle.session.mode.get() !== requestedMode) {\n await handle.session.mode.switch({ modeId: requestedMode })\n }\n }\n\n const thinkingLevel =\n connectEffort && VALID_THINKING_LEVELS.has(connectEffort)\n ? (connectEffort as \"off\" | \"low\" | \"medium\" | \"high\" | \"xhigh\")\n : undefined\n\n const run = runMC({\n controller: handle.controller as never,\n session: handle.session as never,\n prompt,\n resourceId,\n thread: threadId ? { id: threadId } : undefined,\n ...(connectModel ? { model: connectModel } : {}),\n ...(thinkingLevel ? { thinkingLevel } : {}),\n ...(definition.session?.max_turns\n ? { maxTurns: definition.session.max_turns }\n : {}),\n })\n pendingByTurn.set(turnId, run)\n },\n\n async *events(): AsyncIterable<StreamEvent> {\n const run = Array.from(pendingByTurn.values()).at(-1)\n if (!run) return\n const state = createMastraMapperState()\n for await (const evt of run) {\n // Track the thread id live from in-band events, synchronously\n // with the loop that's about to map+yield them — no race against\n // `run.result` resolving on a separate microtask.\n if (evt.type === \"thread_created\") threadId = evt.thread.id\n else if (evt.type === \"thread_changed\") threadId = evt.threadId\n const sid = resourceId && threadId ? `${resourceId}:${threadId}` : \"\"\n const mapped = mapMastraEvent(\n evt as unknown as Record<string, unknown>,\n sid,\n [],\n state,\n )\n if (mapped) yield mapped\n }\n // Fallback for the (unverified) case where a fresh thread's first\n // binding happens inside `sendMessage()` without an in-band\n // `thread_created`/`thread_changed` event — `result.threadId` is\n // always populated once the run settles, so this is authoritative\n // even if the loop above never fired.\n const result = await run.result\n if (result.threadId) threadId = result.threadId\n },\n\n async cancel(turnId: string): Promise<void> {\n pendingByTurn.get(turnId)?.abort()\n },\n\n async close(): Promise<void> {\n await handle?.controller.destroy()\n },\n }\n}\n\nfunction extractPromptText(message: unknown): string {\n if (typeof message === \"string\") return message\n if (Array.isArray(message)) {\n return (message as Array<Record<string, unknown>>)\n .filter(b => b.type === \"text\" && typeof b.text === \"string\")\n .map(b => b.text as string)\n .join(\"\\n\")\n }\n if (message !== null && typeof message === \"object\") {\n const m = message as Record<string, unknown>\n if (typeof m.text === \"string\") return m.text\n }\n return JSON.stringify(message)\n}\n","/**\n * @agentproto/adapter-mastracode-inprocess — AIP-45 adapter for Mastra Code,\n * driven in-process via the `mastracode` / `mastracode/headless` SDK\n * instead of spawning a subprocess.\n *\n * This is a `protocol: \"proprietary\"` manifest: `createAgentCliRuntime`\n * skips the subprocess spawn entirely for this protocol and instead\n * dynamic-imports `adapter` (this very package) and calls its exported\n * `createAgentCliClient(definition)` factory — see\n * `createProprietaryProtocolArm` in `@agentproto/driver-agent-cli`. The\n * factory itself lives in `./client.ts`; this file only builds and\n * exports the manifest handle.\n *\n * import { mastracodeInprocess, mastracodeInprocessRuntime } from \"@agentproto/adapter-mastracode-inprocess\"\n * const session = await mastracodeInprocessRuntime().start()\n * for await (const evt of session.send({ role: \"user\", content: \"...\" })) {\n * console.log(evt)\n * }\n * await session.close()\n *\n * Why a separate package from `@agentproto/adapter-mastracode` (the\n * `print` arm) rather than a second export of it — see\n * MASTRACODE-INPROCESS.md.\n */\n\nimport {\n createAgentCliRuntime,\n defineAgentCli,\n type AgentCliHandle,\n type AgentCliRuntime,\n} from \"@agentproto/driver-agent-cli\"\n\nexport const mastracodeInprocess: AgentCliHandle = defineAgentCli({\n name: \"Mastra Code (in-process)\",\n id: \"mastracode-inprocess\",\n description:\n \"Mastra Code terminal coding agent driven in-process via the `mastracode` SDK \" +\n \"(createMastraCode + runMC) instead of spawning the CLI as a subprocess. Same \" +\n \"underlying agent as @agentproto/adapter-mastracode, no child process.\",\n version: \"0.1.0\",\n // No binary is ever spawned for a proprietary arm — `bin` is required by\n // the AIP-45 schema regardless, so this is a documented placeholder\n // rather than a real executable. `createAgentCliRuntime` checks\n // `protocol === \"proprietary\"` and never reads this field.\n bin: \"in-process\",\n install: [{ method: \"npm\", package: \"@agentproto/adapter-mastracode-inprocess\" }],\n version_check: {\n cmd: \"npm view mastracode version\",\n parse: \"(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)\",\n // Matches this package's pinned `mastracode` dependency (see package.json).\n range: \">=0.27.0\",\n timeout_ms: 15_000,\n },\n auth: {\n state: {\n env: [\n \"ANTHROPIC_API_KEY\",\n \"OPENAI_API_KEY\",\n \"OPENROUTER_API_KEY\",\n \"GOOGLE_GENERATIVE_AI_API_KEY\",\n ],\n },\n },\n sandbox: {\n model: \"in-process\",\n note:\n \"Runs inside the host process — there is no subprocess boundary to sandbox. \" +\n \"File and shell tool access is whatever Mastra Code's own tool policy grants \" +\n \"for this session, with the host process's OS permissions (not a separately \" +\n \"confined child). Storage and home dir ARE isolated per MASTRACODE-INPROCESS.md \" +\n \"so this arm's sessions never collide with a developer's own interactive \" +\n \"mastracode usage, but that isolation is not a security sandbox.\",\n },\n protocol: \"proprietary\",\n adapter: \"@agentproto/adapter-mastracode-inprocess\",\n session: {\n mode: \"resumable\",\n idle_timeout_ms: 1_800_000,\n context_carryover: true,\n },\n models: {\n default: \"anthropic/claude-sonnet-4-5\",\n allowed: [\n \"anthropic/claude-sonnet-4-5\",\n \"anthropic/claude-sonnet-4-6\",\n \"openai/gpt-5.1\",\n \"openai/gpt-5.1-mini\",\n \"google/gemini-2.5-flash\",\n \"openrouter/anthropic/claude-sonnet-4-6\",\n ],\n env: {\n anthropic: \"ANTHROPIC_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n },\n },\n capabilities: {\n streaming: true,\n tool_calls: true,\n sub_agents: true,\n file_io: true,\n multimodal: false,\n // The composite \"<resourceId>:<threadId>\" sessionId round-trips through\n // `client.ts`'s connect(resumeSessionId) and has been verified to\n // recall prior-turn context across a fresh process (see README.md).\n resumable: true,\n bidirectional: false,\n },\n // Mode selection has no argv to append to (no spawn) — modes patch the\n // one channel `composeSpawn` threads through for every protocol\n // regardless: env. `client.ts` reads `AGENTPROTO_MASTRACODE_MODE` off\n // the composed env and switches the live session's mode before send().\n modes: [\n {\n id: \"default\",\n description: \"Default headless mode. Mastra Code defaults to build mode when omitted.\",\n },\n {\n id: \"plan\",\n description:\n \"Plan-only mode — analyze architecture and propose implementation plans before writing code.\",\n env: { AGENTPROTO_MASTRACODE_MODE: \"plan\" },\n },\n {\n id: \"build\",\n description:\n \"Build mode — file edits and shell commands use Mastra Code's configured permissions.\",\n env: { AGENTPROTO_MASTRACODE_MODE: \"build\" },\n },\n {\n id: \"fast\",\n description: \"Fast mode — lower-latency answers and small edits.\",\n env: { AGENTPROTO_MASTRACODE_MODE: \"fast\" },\n },\n ],\n // `model`/`effort` here are the two ids `createAgentCliRuntime` always\n // reads off `config.options` for the generic model/effort passthrough\n // (see `define-agent-cli.ts`) — declaring them is what lets a host pass\n // `agent_start({ model, effort })` without composeSpawn rejecting the\n // config as an unknown option. Both travel to `client.ts`'s connect()\n // directly (not via bin_args/env), so no `bin_args_template`/`env` patch\n // is declared on either.\n options: [\n {\n id: \"model\",\n type: \"string\",\n description:\n \"Provider/model override for this operator binding (e.g. `anthropic/claude-sonnet-4-5`).\",\n },\n {\n id: \"effort\",\n type: \"enum\",\n enum: [\"off\", \"low\", \"medium\", \"high\", \"xhigh\"],\n description:\n \"Thinking level, mapped 1:1 to Mastra Code's `thinkingLevel` run option.\",\n },\n ],\n continuation: {\n default: \"none\",\n supported: [\"none\", \"transcript\", \"native-resume\"],\n },\n metadata: {\n proprietary: {\n checked: \"2026-07-02\",\n result:\n \"Verified in-process against mastracode 0.27.0 / @mastra/core 1.48.0: a turn \" +\n \"returns real streamed content, and cross-process resume (kill the process, \" +\n \"start a fresh one, reattach via the composite sessionId) recalls prior-turn \" +\n \"context. See MASTRACODE-INPROCESS.md for the composite sessionId format.\",\n },\n },\n tags: [\"mastracode\", \"mastra\", \"proprietary\", \"in-process\", \"agent-runtime\", \"coding\"],\n})\n\nexport function mastracodeInprocessRuntime(): AgentCliRuntime {\n return createAgentCliRuntime(mastracodeInprocess)\n}\n\nexport { createAgentCliClient } from \"./client.js\"\nexport type { AgentCliHandle, AgentCliRuntime }\n"]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@agentproto/adapter-mastracode-inprocess",
3
+ "version": "0.2.0",
4
+ "description": "@agentproto/adapter-mastracode-inprocess — AIP-45 AGENT-CLI adapter for Mastra Code driven in-process via the `mastracode`/`mastracode/headless` SDK, instead of spawning a subprocess. Implements the `protocol: \"proprietary\"` arm contract directly.",
5
+ "keywords": [
6
+ "agentproto",
7
+ "aip-45",
8
+ "mastracode",
9
+ "mastra",
10
+ "proprietary",
11
+ "in-process",
12
+ "agent-cli",
13
+ "adapter"
14
+ ],
15
+ "homepage": "https://agentproto.sh/docs/aip-45",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/agentproto/ts",
19
+ "directory": "adapters/mastracode-inprocess"
20
+ },
21
+ "license": "MIT",
22
+ "type": "module",
23
+ "main": "dist/index.mjs",
24
+ "module": "dist/index.mjs",
25
+ "types": "dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.mjs",
30
+ "default": "./dist/index.mjs"
31
+ },
32
+ "./MASTRACODE-INPROCESS.md": "./MASTRACODE-INPROCESS.md",
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "MASTRACODE-INPROCESS.md",
38
+ "README.md",
39
+ "LICENSE"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "@mastra/core": "1.48.0",
46
+ "mastracode": "0.27.0",
47
+ "@agentproto/driver-agent-cli": "0.3.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^25.6.2",
51
+ "tsup": "^8.5.1",
52
+ "typescript": "^5.9.3",
53
+ "vitest": "^3.2.4",
54
+ "@agentproto/tooling": "0.1.0-alpha.0"
55
+ },
56
+ "scripts": {
57
+ "dev": "tsup --watch",
58
+ "build": "tsup",
59
+ "clean": "rm -rf dist",
60
+ "check-types": "tsc --noEmit",
61
+ "test": "vitest run --passWithNoTests",
62
+ "test:watch": "vitest"
63
+ }
64
+ }