@agentproto/adapter-mastra-agent 0.1.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,16 @@
1
+ # MASTRA-AGENT.md
2
+
3
+ AIP-45 manifest companion for `@agentproto/adapter-mastra-agent` — the
4
+ first-party agentproto agent. See `src/index.ts` for the authoritative
5
+ `defineAgentCli` handle.
6
+
7
+ Unlike the other adapters, this package does not wrap an external agent CLI:
8
+ it **is** the agent. An AIP-42 `AGENT.md` is parsed (`@agentproto/agent`), built
9
+ into a live Mastra agent (`@agentproto/mastra`), and served over AIP-44 ACP.
10
+
11
+ - **bin:** `node <dist>/cli.mjs acp` (self-locating) — also published as the
12
+ `agentproto-mastra` bin.
13
+ - **protocol:** `acp` (stdio JSON-RPC).
14
+ - **models:** any Mastra-routable `provider/model` id; default
15
+ `openrouter/z-ai/glm-5.2`. Provider key comes from the spawn env.
16
+ - **session:** persistent, 30-min idle timeout, context carryover.
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @agentproto/adapter-mastra-agent
2
+
3
+ The **first-party agentproto agent**. Every other adapter
4
+ (`codex`, `hermes`, `claude-code`, `opencode`) wraps an *external* agent CLI.
5
+ This one is ours end to end: an AIP-42 `AGENT.md` is run as a live
6
+ [Mastra](https://mastra.ai) agent behind an AIP-44 ACP server — our loop, our
7
+ models, no third-party CLI.
8
+
9
+ ```bash
10
+ # Standalone — drive it from any ACP-speaking host over stdio:
11
+ agentproto-mastra acp --model anthropic/claude-opus-4-8
12
+
13
+ # Or let the agentproto daemon spawn it like any other arm:
14
+ # start_agent_session({ adapter: "mastra-agent", model: "openrouter/z-ai/glm-5.2" })
15
+ ```
16
+
17
+ ## How it works
18
+
19
+ ```
20
+ AGENT.md ──parseAgentManifest──▶ AgentHandle
21
+ │ buildMastraAgent (@agentproto/mastra)
22
+
23
+ Mastra Agent.stream()
24
+ │ text deltas
25
+
26
+ MastraAcpAgent (src/acp-host.ts) ──agent_message_chunk──▶ ACP client
27
+
28
+ @agentclientprotocol/sdk AgentSideConnection (stdio)
29
+ ```
30
+
31
+ - **Model** — any `provider/model` string Mastra's gateway can route
32
+ (`anthropic/…`, `openrouter/…`, `openai/…`). The provider key is read from the
33
+ spawn environment. Default: `openrouter/z-ai/glm-5.2`.
34
+ - **Agent** — pass `--agent ./path/AGENT.md` (or `AGENTPROTO_MASTRA_AGENT_FILE`)
35
+ to run a custom agent; omit for a built-in coding default.
36
+
37
+ ## Workspace tools
38
+
39
+ The default agent is granted a workspace toolset, all **confined to the session
40
+ cwd** (path-traversal guarded):
41
+
42
+ | Tool | Does |
43
+ | --- | --- |
44
+ | `list_dir` | List a directory. |
45
+ | `read_file` | Read a UTF-8 file. |
46
+ | `write_file` | Create/overwrite a file (mkdir -p). |
47
+ | `edit_file` | Replace a unique substring. |
48
+ | `run_command` | Run a shell command (cwd-scoped, timeout). |
49
+
50
+ `run_command` is on by default; set `AGENTPROTO_MASTRA_NO_EXEC=1` to withhold it.
51
+
52
+ ## Memory
53
+
54
+ Per-conversation memory via Mastra's LibSQL (SQLite) store. Each ACP session is
55
+ a memory **thread**, so the agent recalls earlier turns within a session. The db
56
+ is a single SQLite file under `~/.agentproto/mastra-agent/memory.db` (persistent
57
+ across spawns), overridable with `AGENTPROTO_MASTRA_MEMORY_DB`. A custom
58
+ `AGENT.md` tunes it via the `memory:` block (`scope`, `retention_turns`).
59
+
60
+ ## Status
61
+
62
+ Streaming conversation + workspace tools (edit/run) + SQLite memory, with tool
63
+ activity surfaced live: each tool the agent runs is relayed to the host as an
64
+ ACP `tool_call` (status `in_progress`) and then a `tool_call_update` (status
65
+ `completed`/`failed` with the raw output) — read off Mastra's `fullStream` and
66
+ mapped in `src/tool-call-map.ts`. So a host (codex/claude-code/an IDE) shows the
67
+ "🔧 run_command: …" feed, not just the final prose.
@@ -0,0 +1,476 @@
1
+ import { mkdirSync, promises } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join, resolve, isAbsolute, relative, sep } from 'path';
4
+ import { LibSQLStore } from '@mastra/libsql';
5
+ import { Memory } from '@mastra/memory';
6
+ import { exec } from 'child_process';
7
+ import { promisify } from 'util';
8
+ import { createTool } from '@mastra/core/tools';
9
+ import { z } from 'zod';
10
+ import { readFile } from 'fs/promises';
11
+ import { parseAgentManifest, agentFromManifest } from '@agentproto/agent';
12
+ import { buildMastraAgent } from '@agentproto/mastra';
13
+ import { PROTOCOL_VERSION, ndJsonStream, AgentSideConnection } from '@agentclientprotocol/sdk';
14
+ import { Writable, Readable } from 'stream';
15
+
16
+ /**
17
+ * @agentproto/adapter-mastra-agent v0.1.0-alpha
18
+ * First-party agentproto agent: AGENT.md -> Mastra agent -> ACP server.
19
+ */
20
+
21
+ function resolveMemoryDbPath(env = process.env) {
22
+ const override = env.AGENTPROTO_MASTRA_MEMORY_DB;
23
+ if (override) return override;
24
+ const dir = join(homedir(), ".agentproto", "mastra-agent");
25
+ mkdirSync(dir, { recursive: true });
26
+ return join(dir, "memory.db");
27
+ }
28
+ function buildSqliteMemory(config, env = process.env) {
29
+ if (config?.scope === "none") return void 0;
30
+ const dbPath = resolveMemoryDbPath(env);
31
+ const lastMessages = typeof config?.retention_turns === "number" && config.retention_turns > 0 ? config.retention_turns : 20;
32
+ return new Memory({
33
+ storage: new LibSQLStore({ id: "mastra-agent-memory", url: `file:${dbPath}` }),
34
+ options: {
35
+ lastMessages,
36
+ semanticRecall: false,
37
+ workingMemory: { enabled: false }
38
+ }
39
+ });
40
+ }
41
+
42
+ // src/model-resolver.ts
43
+ var PROVIDER_ENV = {
44
+ openai: "OPENAI_API_KEY",
45
+ anthropic: "ANTHROPIC_API_KEY",
46
+ openrouter: "OPENROUTER_API_KEY",
47
+ google: "GOOGLE_GENERATIVE_AI_API_KEY",
48
+ groq: "GROQ_API_KEY",
49
+ xai: "XAI_API_KEY",
50
+ mistral: "MISTRAL_API_KEY",
51
+ deepseek: "DEEPSEEK_API_KEY"
52
+ };
53
+ function modelRefToString(ref) {
54
+ if (typeof ref === "string") return ref.trim();
55
+ if (ref && typeof ref === "object" && typeof ref.ref === "string") {
56
+ return ref.ref.trim();
57
+ }
58
+ throw new Error(
59
+ "mastra-agent: AGENT.md `model` must be a `provider/model` string (or { ref }); inline model objects are not supported by this adapter."
60
+ );
61
+ }
62
+ function providerOf(modelId) {
63
+ const slash = modelId.indexOf("/");
64
+ return slash > 0 ? modelId.slice(0, slash) : modelId;
65
+ }
66
+ function resolveMastraModel(ref, env = process.env) {
67
+ const modelId = modelRefToString(ref);
68
+ if (!modelId) {
69
+ throw new Error("mastra-agent: empty `model` ref.");
70
+ }
71
+ const provider = providerOf(modelId);
72
+ const envKey = PROVIDER_ENV[provider];
73
+ if (envKey && !env[envKey]) {
74
+ throw new Error(
75
+ `mastra-agent: model '${modelId}' needs ${envKey} in the environment (provider '${provider}'). Set it on the spawn env or export it.`
76
+ );
77
+ }
78
+ return modelId;
79
+ }
80
+ var execAsync = promisify(exec);
81
+ function resolveInCwd(cwd, p) {
82
+ const base = resolve(cwd);
83
+ const target = isAbsolute(p) ? resolve(p) : resolve(base, p);
84
+ const rel = relative(base, target);
85
+ if (rel === "") return target;
86
+ if (rel.startsWith("..") || isAbsolute(rel) && !target.startsWith(base + sep)) {
87
+ throw new Error(
88
+ `path '${p}' escapes the workspace (resolved to '${target}', outside '${base}').`
89
+ );
90
+ }
91
+ return target;
92
+ }
93
+ function makeWorkspaceTools(opts) {
94
+ const cwd = resolve(opts.cwd);
95
+ const allowExec = opts.allowExec ?? true;
96
+ const execTimeoutMs = opts.execTimeoutMs ?? 12e4;
97
+ const list_dir = createTool({
98
+ id: "list_dir",
99
+ description: "List the entries of a directory in the workspace. Returns names with a trailing '/' for directories. Path is relative to the workspace root (default '.').",
100
+ inputSchema: z.object({
101
+ path: z.string().default(".").describe("Directory path, relative to the workspace root.")
102
+ }),
103
+ outputSchema: z.object({ entries: z.array(z.string()) }),
104
+ execute: async (input) => {
105
+ const dir = resolveInCwd(cwd, input.path ?? ".");
106
+ const dirents = await promises.readdir(dir, { withFileTypes: true });
107
+ return {
108
+ entries: dirents.map((d) => d.isDirectory() ? `${d.name}/` : d.name).sort()
109
+ };
110
+ }
111
+ });
112
+ const read_file = createTool({
113
+ id: "read_file",
114
+ description: "Read a UTF-8 text file from the workspace. Path is relative to the workspace root.",
115
+ inputSchema: z.object({
116
+ path: z.string().describe("File path, relative to the workspace root.")
117
+ }),
118
+ outputSchema: z.object({ content: z.string() }),
119
+ execute: async (input) => {
120
+ const file = resolveInCwd(cwd, input.path);
121
+ return { content: await promises.readFile(file, "utf8") };
122
+ }
123
+ });
124
+ const write_file = createTool({
125
+ id: "write_file",
126
+ description: "Write (creating or overwriting) a UTF-8 text file in the workspace. Creates parent directories as needed. Path is relative to the workspace root.",
127
+ inputSchema: z.object({
128
+ path: z.string().describe("File path, relative to the workspace root."),
129
+ content: z.string().describe("Full file contents to write.")
130
+ }),
131
+ outputSchema: z.object({ path: z.string(), bytes: z.number() }),
132
+ execute: async (input) => {
133
+ const file = resolveInCwd(cwd, input.path);
134
+ await promises.mkdir(resolve(file, ".."), { recursive: true });
135
+ await promises.writeFile(file, input.content, "utf8");
136
+ return { path: input.path, bytes: Buffer.byteLength(input.content, "utf8") };
137
+ }
138
+ });
139
+ const edit_file = createTool({
140
+ id: "edit_file",
141
+ description: "Replace an exact substring in a workspace file. `old_string` must occur exactly once. Use for targeted edits instead of rewriting the whole file.",
142
+ inputSchema: z.object({
143
+ path: z.string().describe("File path, relative to the workspace root."),
144
+ old_string: z.string().describe("Exact text to replace (must be unique in the file)."),
145
+ new_string: z.string().describe("Replacement text.")
146
+ }),
147
+ outputSchema: z.object({ path: z.string(), replaced: z.boolean() }),
148
+ execute: async (input) => {
149
+ const file = resolveInCwd(cwd, input.path);
150
+ const current = await promises.readFile(file, "utf8");
151
+ const count = current.split(input.old_string).length - 1;
152
+ if (count === 0) throw new Error(`old_string not found in '${input.path}'.`);
153
+ if (count > 1) {
154
+ throw new Error(`old_string occurs ${count}\xD7 in '${input.path}' \u2014 make it unique.`);
155
+ }
156
+ await promises.writeFile(file, current.replace(input.old_string, input.new_string), "utf8");
157
+ return { path: input.path, replaced: true };
158
+ }
159
+ });
160
+ const tools = {
161
+ list_dir,
162
+ read_file,
163
+ write_file,
164
+ edit_file
165
+ };
166
+ if (allowExec) {
167
+ tools.run_command = createTool({
168
+ id: "run_command",
169
+ description: "Run a shell command in the workspace directory and return its stdout/stderr/exit code. Runs with a timeout; use for builds, tests, git, etc.",
170
+ inputSchema: z.object({
171
+ command: z.string().describe("The shell command to run (executed in the workspace root).")
172
+ }),
173
+ outputSchema: z.object({
174
+ stdout: z.string(),
175
+ stderr: z.string(),
176
+ exitCode: z.number()
177
+ }),
178
+ execute: async (input) => {
179
+ try {
180
+ const { stdout, stderr } = await execAsync(input.command, {
181
+ cwd,
182
+ timeout: execTimeoutMs,
183
+ maxBuffer: 10 * 1024 * 1024
184
+ });
185
+ return { stdout, stderr, exitCode: 0 };
186
+ } catch (err) {
187
+ const e = err;
188
+ return {
189
+ stdout: e.stdout ?? "",
190
+ stderr: e.stderr ?? e.message ?? String(err),
191
+ exitCode: typeof e.code === "number" ? e.code : 1
192
+ };
193
+ }
194
+ }
195
+ });
196
+ }
197
+ return tools;
198
+ }
199
+ var DEFAULT_MODEL = "openrouter/z-ai/glm-5.2";
200
+ var DEFAULT_TOOL_IDS = [
201
+ "list_dir",
202
+ "read_file",
203
+ "write_file",
204
+ "edit_file",
205
+ "run_command"
206
+ ];
207
+ function defaultAgentManifest(model) {
208
+ return [
209
+ "---",
210
+ "schema: agent/v1",
211
+ "id: mastra-agent",
212
+ "description: A first-party agentproto agent powered by Mastra.",
213
+ `model: ${model}`,
214
+ "version: 0.1.0",
215
+ "tools:",
216
+ ...DEFAULT_TOOL_IDS.map((id) => ` - ${id}`),
217
+ "memory:",
218
+ " scope: per-conversation",
219
+ " retention_turns: 20",
220
+ "---",
221
+ "",
222
+ "You are a capable, concise coding agent operating inside a workspace ",
223
+ "directory. You can list, read, write, and edit files and run shell ",
224
+ "commands there using your tools. Do exactly what the user asks \u2014 when ",
225
+ "asked to reply with an exact string, reply with only that string.",
226
+ ""
227
+ ].join("\n");
228
+ }
229
+ function toolRefId(ref) {
230
+ if (typeof ref === "string") return ref;
231
+ if (ref && typeof ref === "object" && typeof ref.ref === "string") {
232
+ return ref.ref;
233
+ }
234
+ return void 0;
235
+ }
236
+ async function resolveAgentSource(opts = {}) {
237
+ if (opts.agentFile) return readFile(opts.agentFile, "utf8");
238
+ return defaultAgentManifest(opts.model ?? DEFAULT_MODEL);
239
+ }
240
+ function makeAgentFactory(opts = {}) {
241
+ return async () => {
242
+ const source = await resolveAgentSource(opts);
243
+ const { frontmatter, body } = parseAgentManifest(source);
244
+ const handle = agentFromManifest({ frontmatter, body });
245
+ const cwd = opts.cwd ?? process.cwd();
246
+ const workspaceTools = makeWorkspaceTools({ cwd, allowExec: opts.allowExec });
247
+ const { agent } = await buildMastraAgent(handle, {
248
+ resolveModel: (ref) => resolveMastraModel(ref),
249
+ // Match each declared tool ref against the workspace toolset by id.
250
+ resolveTool: (ref) => {
251
+ const id = toolRefId(ref);
252
+ const tool = id ? workspaceTools[id] : void 0;
253
+ return tool ? { name: id, tool } : void 0;
254
+ },
255
+ buildMemory: (config) => buildSqliteMemory(config),
256
+ // The markdown body is the agent's primary system prompt (AIP-42).
257
+ body
258
+ });
259
+ return agent;
260
+ };
261
+ }
262
+
263
+ // src/tool-call-map.ts
264
+ function toolKindFor(toolName) {
265
+ switch (toolName) {
266
+ case "read_file":
267
+ case "list_dir":
268
+ return "read";
269
+ case "write_file":
270
+ case "edit_file":
271
+ return "edit";
272
+ case "run_command":
273
+ return "execute";
274
+ default:
275
+ return "other";
276
+ }
277
+ }
278
+ function toolCallTitle(toolName, args) {
279
+ if (args && typeof args === "object") {
280
+ const a = args;
281
+ const hint = typeof a.command === "string" && a.command || typeof a.path === "string" && a.path || typeof a.file === "string" && a.file || "";
282
+ if (hint) return `${toolName}: ${hint}`;
283
+ }
284
+ return toolName;
285
+ }
286
+ function chunkToSessionUpdate(chunk) {
287
+ switch (chunk.type) {
288
+ case "text-delta": {
289
+ const text = chunk.payload?.text;
290
+ if (!text) return null;
291
+ return {
292
+ sessionUpdate: "agent_message_chunk",
293
+ content: { type: "text", text }
294
+ };
295
+ }
296
+ case "tool-call": {
297
+ const p = chunk.payload;
298
+ if (!p?.toolCallId) return null;
299
+ const toolName = p.toolName ?? "tool";
300
+ return {
301
+ sessionUpdate: "tool_call",
302
+ toolCallId: p.toolCallId,
303
+ title: toolCallTitle(toolName, p.args),
304
+ kind: toolKindFor(toolName),
305
+ status: "in_progress",
306
+ rawInput: p.args
307
+ };
308
+ }
309
+ case "tool-result": {
310
+ const p = chunk.payload;
311
+ if (!p?.toolCallId) return null;
312
+ return {
313
+ sessionUpdate: "tool_call_update",
314
+ toolCallId: p.toolCallId,
315
+ status: p.isError ? "failed" : "completed",
316
+ rawOutput: p.result
317
+ };
318
+ }
319
+ default:
320
+ return null;
321
+ }
322
+ }
323
+ function promptText(params) {
324
+ const blocks = Array.isArray(params.prompt) ? params.prompt : [];
325
+ return blocks.filter(
326
+ (b) => Boolean(b) && b.type === "text" && typeof b.text === "string"
327
+ ).map((b) => b.text).join("").trim();
328
+ }
329
+ var MastraAcpAgent = class {
330
+ #conn;
331
+ #buildAgent;
332
+ #resource;
333
+ #sessions = /* @__PURE__ */ new Map();
334
+ #agent = null;
335
+ constructor(conn, buildAgent, resource = "mastra-agent") {
336
+ this.#conn = conn;
337
+ this.#buildAgent = buildAgent;
338
+ this.#resource = resource;
339
+ }
340
+ async initialize(_params) {
341
+ return {
342
+ protocolVersion: PROTOCOL_VERSION,
343
+ agentCapabilities: {
344
+ // Stateless per-prompt for now; no resume/replay surface.
345
+ loadSession: false
346
+ }
347
+ };
348
+ }
349
+ async authenticate(_params) {
350
+ return {};
351
+ }
352
+ async newSession(_params) {
353
+ const sessionId = randomId();
354
+ this.#sessions.set(sessionId, { prompt: null });
355
+ return { sessionId };
356
+ }
357
+ async prompt(params) {
358
+ const session = this.#sessions.get(params.sessionId);
359
+ if (!session) throw new Error(`unknown session ${params.sessionId}`);
360
+ session.prompt?.abort();
361
+ const ac = new AbortController();
362
+ session.prompt = ac;
363
+ const text = promptText(params);
364
+ try {
365
+ const agent = await this.#ensureAgent();
366
+ const result = await agent.stream(text, {
367
+ abortSignal: ac.signal,
368
+ memory: { thread: params.sessionId, resource: this.#resource }
369
+ });
370
+ if (result.fullStream) {
371
+ await this.#pumpFullStream(params.sessionId, result.fullStream, ac);
372
+ } else if (result.textStream) {
373
+ await this.#pumpTextStream(params.sessionId, result.textStream, ac);
374
+ }
375
+ } catch (err) {
376
+ if (ac.signal.aborted) return { stopReason: "cancelled" };
377
+ await this.#conn.sessionUpdate({
378
+ sessionId: params.sessionId,
379
+ update: {
380
+ sessionUpdate: "agent_message_chunk",
381
+ content: {
382
+ type: "text",
383
+ text: `
384
+ [mastra-agent error] ${err.message}
385
+ `
386
+ }
387
+ }
388
+ });
389
+ session.prompt = null;
390
+ return { stopReason: "refusal" };
391
+ }
392
+ const cancelled = ac.signal.aborted;
393
+ session.prompt = null;
394
+ return { stopReason: cancelled ? "cancelled" : "end_turn" };
395
+ }
396
+ async cancel(params) {
397
+ this.#sessions.get(params.sessionId)?.prompt?.abort();
398
+ }
399
+ /**
400
+ * The host applies the `model` (and other operator options) as a `--model`
401
+ * spawn arg via the manifest `bin_args_template`, then ALSO calls this ACP
402
+ * config hook (the daemon's default "config" apply path). The model is
403
+ * already in effect, so this is a no-op that just reports our (empty) set of
404
+ * runtime-configurable options. Without it the spawn fails with
405
+ * "Method not found: session/set_config_option".
406
+ */
407
+ async setSessionConfigOption(_params) {
408
+ return { configOptions: [] };
409
+ }
410
+ /** No agent-specific modes; accept and ignore so a host that sets one
411
+ * doesn't error. */
412
+ async setSessionMode(_params) {
413
+ return {};
414
+ }
415
+ /** Drain Mastra's typed `fullStream`, mapping each chunk to an ACP
416
+ * `session/update` (text deltas + tool_call / tool_call_update). */
417
+ async #pumpFullStream(sessionId, stream, ac) {
418
+ const reader = stream.getReader();
419
+ try {
420
+ for (; ; ) {
421
+ const { value, done } = await reader.read();
422
+ if (done || ac.signal.aborted) break;
423
+ if (!value) continue;
424
+ const update = chunkToSessionUpdate(value);
425
+ if (update) await this.#conn.sessionUpdate({ sessionId, update });
426
+ }
427
+ } finally {
428
+ reader.releaseLock();
429
+ }
430
+ }
431
+ /** Fallback drain for an agent exposing only a plain text stream. */
432
+ async #pumpTextStream(sessionId, stream, ac) {
433
+ const reader = stream.getReader();
434
+ try {
435
+ for (; ; ) {
436
+ const { value, done } = await reader.read();
437
+ if (done || ac.signal.aborted) break;
438
+ if (value) {
439
+ await this.#conn.sessionUpdate({
440
+ sessionId,
441
+ update: {
442
+ sessionUpdate: "agent_message_chunk",
443
+ content: { type: "text", text: value }
444
+ }
445
+ });
446
+ }
447
+ }
448
+ } finally {
449
+ reader.releaseLock();
450
+ }
451
+ }
452
+ async #ensureAgent() {
453
+ if (!this.#agent) this.#agent = await this.#buildAgent();
454
+ return this.#agent;
455
+ }
456
+ };
457
+ function randomId() {
458
+ const bytes = new Uint8Array(16);
459
+ crypto.getRandomValues(bytes);
460
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
461
+ }
462
+ function runAcpOverStdio(buildAgent) {
463
+ const toClient = Writable.toWeb(process.stdout);
464
+ const fromClient = Readable.toWeb(
465
+ process.stdin
466
+ );
467
+ const stream = ndJsonStream(toClient, fromClient);
468
+ return new AgentSideConnection(
469
+ (conn) => new MastraAcpAgent(conn, buildAgent),
470
+ stream
471
+ );
472
+ }
473
+
474
+ export { DEFAULT_MODEL, DEFAULT_TOOL_IDS, MastraAcpAgent, buildSqliteMemory, chunkToSessionUpdate, defaultAgentManifest, makeAgentFactory, makeWorkspaceTools, modelRefToString, promptText, providerOf, resolveInCwd, resolveMastraModel, resolveMemoryDbPath, runAcpOverStdio, toolCallTitle, toolKindFor };
475
+ //# sourceMappingURL=chunk-7SWAWNDG.mjs.map
476
+ //# sourceMappingURL=chunk-7SWAWNDG.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/memory.ts","../src/model-resolver.ts","../src/workspace-tools.ts","../src/default-agent.ts","../src/tool-call-map.ts","../src/acp-host.ts","../src/run.ts"],"names":["fs"],"mappings":"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","file":"chunk-7SWAWNDG.mjs","sourcesContent":["/**\n * SQLite-backed memory for the agent, via Mastra's LibSQL store.\n *\n * Each ACP session is a memory *thread* (see acp-host.ts), so the agent recalls\n * earlier turns within a session. The db is a single SQLite file (LibSQL is a\n * SQLite fork) under `~/.agentproto/mastra-agent/` by default — persistent\n * across spawns — overridable with `AGENTPROTO_MASTRA_MEMORY_DB`.\n */\n\nimport { mkdirSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\nimport { LibSQLStore } from \"@mastra/libsql\"\nimport { Memory } from \"@mastra/memory\"\nimport type { MemoryConfig } from \"@agentproto/agent\"\n\n/** A built Mastra memory — structural, matching `@agentproto/mastra`'s\n * `MastraMemoryLike` expectation without importing the exact type. */\nexport type MastraMemoryLike = Memory\n\n/** Resolve the SQLite file path, creating the parent dir. `AGENTPROTO_MASTRA_MEMORY_DB`\n * wins; otherwise `~/.agentproto/mastra-agent/memory.db`. */\nexport function resolveMemoryDbPath(\n env: Record<string, string | undefined> = process.env,\n): string {\n const override = env.AGENTPROTO_MASTRA_MEMORY_DB\n if (override) return override\n const dir = join(homedir(), \".agentproto\", \"mastra-agent\")\n mkdirSync(dir, { recursive: true })\n return join(dir, \"memory.db\")\n}\n\n/**\n * Build a Mastra `Memory` from an AIP-42 `memory:` config. Returns `undefined`\n * when memory is disabled (`scope: \"none\"`) so `buildMastraAgent` attaches none.\n *\n * - `retention_turns` → `options.lastMessages` (how many recent messages to\n * replay into context). Defaults to 20.\n * - Semantic recall is left off (it needs an embedder + vector index) — this is\n * conversation-history memory, the SQLite ask.\n */\nexport function buildSqliteMemory(\n config?: MemoryConfig,\n env: Record<string, string | undefined> = process.env,\n): MastraMemoryLike | undefined {\n if (config?.scope === \"none\") return undefined\n const dbPath = resolveMemoryDbPath(env)\n const lastMessages =\n typeof config?.retention_turns === \"number\" && config.retention_turns > 0\n ? config.retention_turns\n : 20\n return new Memory({\n storage: new LibSQLStore({ id: \"mastra-agent-memory\", url: `file:${dbPath}` }),\n options: {\n lastMessages,\n semanticRecall: false,\n workingMemory: { enabled: false },\n },\n })\n}\n","/**\n * WP1 — AIP-42 `model:` ref -> a model Mastra's `Agent` can run.\n *\n * Mastra 1.x ships a model router (`models.dev` gateway) that accepts a bare\n * `provider/model` string (e.g. `anthropic/claude-opus-4-8`,\n * `openrouter/z-ai/glm-5.2`) and resolves it to a live ai-sdk model, reading\n * the provider's key from the environment. So the resolver is a thin,\n * *validated* pass-through: extract the ref string, sanity-check the shape,\n * surface a friendly error when the obvious provider key is missing, and hand\n * the string to Mastra. No bespoke provider wiring, no extra ai-sdk deps.\n */\n\nimport type { ModelRef } from \"@agentproto/agent\"\n\n/** Best-effort provider -> env var map, used only for a friendly preflight\n * error. Mastra's gateway is the source of truth; this list just turns the\n * common \"forgot the key\" case into a clear message instead of a deep ai-sdk\n * stack trace. Providers not listed here skip the preflight check. */\nconst PROVIDER_ENV: Record<string, string> = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n groq: \"GROQ_API_KEY\",\n xai: \"XAI_API_KEY\",\n mistral: \"MISTRAL_API_KEY\",\n deepseek: \"DEEPSEEK_API_KEY\",\n}\n\n/** Pull the model id string out of an AIP-42 `ModelRef` (string | { ref }). */\nexport function modelRefToString(ref: ModelRef): string {\n if (typeof ref === \"string\") return ref.trim()\n if (ref && typeof ref === \"object\" && typeof ref.ref === \"string\") {\n return ref.ref.trim()\n }\n throw new Error(\n \"mastra-agent: AGENT.md `model` must be a `provider/model` string \" +\n \"(or { ref }); inline model objects are not supported by this adapter.\",\n )\n}\n\n/** The provider segment is everything before the first `/`. */\nexport function providerOf(modelId: string): string {\n const slash = modelId.indexOf(\"/\")\n return slash > 0 ? modelId.slice(0, slash) : modelId\n}\n\n/**\n * Resolve an AIP-42 model ref to the value Mastra's `Agent` constructor takes.\n * Returns the `provider/model` string — Mastra routes it. `env` defaults to\n * `process.env`; injectable for tests.\n */\nexport function resolveMastraModel(\n ref: ModelRef,\n env: Record<string, string | undefined> = process.env,\n): string {\n const modelId = modelRefToString(ref)\n if (!modelId) {\n throw new Error(\"mastra-agent: empty `model` ref.\")\n }\n const provider = providerOf(modelId)\n const envKey = PROVIDER_ENV[provider]\n if (envKey && !env[envKey]) {\n throw new Error(\n `mastra-agent: model '${modelId}' needs ${envKey} in the environment ` +\n `(provider '${provider}'). Set it on the spawn env or export it.`,\n )\n }\n return modelId\n}\n","/**\n * Workspace toolset — gives the Mastra agent the ability to inspect, edit, and\n * run commands inside its session working directory, like a coding agent.\n *\n * SAFETY: every file path is resolved against the session `cwd` and rejected if\n * it escapes (no `../` traversal, no absolute paths outside cwd). Command\n * execution runs with `cwd` and a timeout, and is gated by `allowExec` (the CLI\n * sets it from `AGENTPROTO_MASTRA_NO_EXEC`). The agent only ever touches the\n * directory the daemon spawned it in.\n */\n\nimport { exec } from \"node:child_process\"\nimport { promises as fs } from \"node:fs\"\nimport { isAbsolute, relative, resolve, sep } from \"node:path\"\nimport { promisify } from \"node:util\"\nimport { createTool } from \"@mastra/core/tools\"\nimport { z } from \"zod\"\n\nconst execAsync = promisify(exec)\n\n/** A Mastra tool (structural — avoids coupling to a @mastra/core type name). */\nexport interface WorkspaceTool {\n id: string\n}\n\nexport interface WorkspaceToolsOptions {\n /** Absolute path the agent is confined to (the spawn cwd). */\n cwd: string\n /** When false, `run_command` is omitted from the toolset. Default true. */\n allowExec?: boolean\n /** Per-command timeout (ms). Default 120_000. */\n execTimeoutMs?: number\n}\n\n/** Resolve `p` against `cwd`, throwing if the result escapes the workspace. */\nexport function resolveInCwd(cwd: string, p: string): string {\n const base = resolve(cwd)\n const target = isAbsolute(p) ? resolve(p) : resolve(base, p)\n const rel = relative(base, target)\n if (rel === \"\" ) return target // the workspace root itself\n if (rel.startsWith(\"..\") || (isAbsolute(rel) && !target.startsWith(base + sep))) {\n throw new Error(\n `path '${p}' escapes the workspace (resolved to '${target}', outside '${base}').`,\n )\n }\n return target\n}\n\n/**\n * Build the workspace toolset, all confined to `cwd`. Returns a record keyed by\n * tool id (also the AGENT.md tool ref the resolver matches).\n */\nexport function makeWorkspaceTools(\n opts: WorkspaceToolsOptions,\n): Record<string, ReturnType<typeof createTool>> {\n const cwd = resolve(opts.cwd)\n const allowExec = opts.allowExec ?? true\n const execTimeoutMs = opts.execTimeoutMs ?? 120_000\n\n const list_dir = createTool({\n id: \"list_dir\",\n description:\n \"List the entries of a directory in the workspace. Returns names with a trailing '/' for directories. Path is relative to the workspace root (default '.').\",\n inputSchema: z.object({\n path: z.string().default(\".\").describe(\"Directory path, relative to the workspace root.\"),\n }),\n outputSchema: z.object({ entries: z.array(z.string()) }),\n execute: async (input: { path?: string }) => {\n const dir = resolveInCwd(cwd, input.path ?? \".\")\n const dirents = await fs.readdir(dir, { withFileTypes: true })\n return {\n entries: dirents.map((d) => (d.isDirectory() ? `${d.name}/` : d.name)).sort(),\n }\n },\n })\n\n const read_file = createTool({\n id: \"read_file\",\n description:\n \"Read a UTF-8 text file from the workspace. Path is relative to the workspace root.\",\n inputSchema: z.object({\n path: z.string().describe(\"File path, relative to the workspace root.\"),\n }),\n outputSchema: z.object({ content: z.string() }),\n execute: async (input: { path: string }) => {\n const file = resolveInCwd(cwd, input.path)\n return { content: await fs.readFile(file, \"utf8\") }\n },\n })\n\n const write_file = createTool({\n id: \"write_file\",\n description:\n \"Write (creating or overwriting) a UTF-8 text file in the workspace. Creates parent directories as needed. Path is relative to the workspace root.\",\n inputSchema: z.object({\n path: z.string().describe(\"File path, relative to the workspace root.\"),\n content: z.string().describe(\"Full file contents to write.\"),\n }),\n outputSchema: z.object({ path: z.string(), bytes: z.number() }),\n execute: async (input: { path: string; content: string }) => {\n const file = resolveInCwd(cwd, input.path)\n await fs.mkdir(resolve(file, \"..\"), { recursive: true })\n await fs.writeFile(file, input.content, \"utf8\")\n return { path: input.path, bytes: Buffer.byteLength(input.content, \"utf8\") }\n },\n })\n\n const edit_file = createTool({\n id: \"edit_file\",\n description:\n \"Replace an exact substring in a workspace file. `old_string` must occur exactly once. Use for targeted edits instead of rewriting the whole file.\",\n inputSchema: z.object({\n path: z.string().describe(\"File path, relative to the workspace root.\"),\n old_string: z.string().describe(\"Exact text to replace (must be unique in the file).\"),\n new_string: z.string().describe(\"Replacement text.\"),\n }),\n outputSchema: z.object({ path: z.string(), replaced: z.boolean() }),\n execute: async (input: { path: string; old_string: string; new_string: string }) => {\n const file = resolveInCwd(cwd, input.path)\n const current = await fs.readFile(file, \"utf8\")\n const count = current.split(input.old_string).length - 1\n if (count === 0) throw new Error(`old_string not found in '${input.path}'.`)\n if (count > 1) {\n throw new Error(`old_string occurs ${count}× in '${input.path}' — make it unique.`)\n }\n await fs.writeFile(file, current.replace(input.old_string, input.new_string), \"utf8\")\n return { path: input.path, replaced: true }\n },\n })\n\n const tools: Record<string, ReturnType<typeof createTool>> = {\n list_dir,\n read_file,\n write_file,\n edit_file,\n }\n\n if (allowExec) {\n tools.run_command = createTool({\n id: \"run_command\",\n description:\n \"Run a shell command in the workspace directory and return its stdout/stderr/exit code. Runs with a timeout; use for builds, tests, git, etc.\",\n inputSchema: z.object({\n command: z.string().describe(\"The shell command to run (executed in the workspace root).\"),\n }),\n outputSchema: z.object({\n stdout: z.string(),\n stderr: z.string(),\n exitCode: z.number(),\n }),\n execute: async (input: { command: string }) => {\n try {\n const { stdout, stderr } = await execAsync(input.command, {\n cwd,\n timeout: execTimeoutMs,\n maxBuffer: 10 * 1024 * 1024,\n })\n return { stdout, stderr, exitCode: 0 }\n } catch (err) {\n const e = err as { stdout?: string; stderr?: string; code?: number; message?: string }\n return {\n stdout: e.stdout ?? \"\",\n stderr: e.stderr ?? e.message ?? String(err),\n exitCode: typeof e.code === \"number\" ? e.code : 1,\n }\n }\n },\n })\n }\n\n return tools\n}\n","/**\n * Builds a runnable Mastra agent from an AIP-42 AGENT.md — either a caller's\n * file or a zero-config built-in default — wiring the model, the markdown body\n * as instructions, the SQLite memory, and the workspace toolset.\n */\n\nimport { readFile } from \"node:fs/promises\"\nimport { agentFromManifest, parseAgentManifest } from \"@agentproto/agent\"\nimport { buildMastraAgent } from \"@agentproto/mastra\"\nimport type { MastraLike } from \"./acp-host.js\"\nimport { buildSqliteMemory } from \"./memory.js\"\nimport { resolveMastraModel } from \"./model-resolver.js\"\nimport { makeWorkspaceTools } from \"./workspace-tools.js\"\n\n/** Cheap OpenRouter coder by default — this is the budget first-party arm,\n * same rationale as the hermes default. Override with --model / env. */\nexport const DEFAULT_MODEL = \"openrouter/z-ai/glm-5.2\"\n\n/** Tool ids the built-in default agent is granted (the workspace toolset). */\nexport const DEFAULT_TOOL_IDS = [\n \"list_dir\",\n \"read_file\",\n \"write_file\",\n \"edit_file\",\n \"run_command\",\n] as const\n\nexport interface AgentSourceOptions {\n /** Path to an AGENT.md. When omitted, the built-in default agent is used. */\n agentFile?: string\n /** Model id for the default agent (ignored when agentFile carries a model). */\n model?: string\n /** Workspace dir the tools are confined to. Defaults to `process.cwd()`. */\n cwd?: string\n /** When false, the `run_command` tool is withheld. Default true. */\n allowExec?: boolean\n}\n\n/** The built-in AGENT.md used when no file is supplied. */\nexport function defaultAgentManifest(model: string): string {\n return [\n \"---\",\n \"schema: agent/v1\",\n \"id: mastra-agent\",\n \"description: A first-party agentproto agent powered by Mastra.\",\n `model: ${model}`,\n \"version: 0.1.0\",\n \"tools:\",\n ...DEFAULT_TOOL_IDS.map((id) => ` - ${id}`),\n \"memory:\",\n \" scope: per-conversation\",\n \" retention_turns: 20\",\n \"---\",\n \"\",\n \"You are a capable, concise coding agent operating inside a workspace \",\n \"directory. You can list, read, write, and edit files and run shell \",\n \"commands there using your tools. Do exactly what the user asks — when \",\n \"asked to reply with an exact string, reply with only that string.\",\n \"\",\n ].join(\"\\n\")\n}\n\n/** Pull the id string out of an AIP-42 tool ref (string | { ref }). */\nfunction toolRefId(ref: unknown): string | undefined {\n if (typeof ref === \"string\") return ref\n if (ref && typeof ref === \"object\" && typeof (ref as { ref?: unknown }).ref === \"string\") {\n return (ref as { ref: string }).ref\n }\n return undefined\n}\n\n/** Resolve the AGENT.md source for these options (file or built-in default). */\nexport async function resolveAgentSource(\n opts: AgentSourceOptions = {},\n): Promise<string> {\n if (opts.agentFile) return readFile(opts.agentFile, \"utf8\")\n return defaultAgentManifest(opts.model ?? DEFAULT_MODEL)\n}\n\n/**\n * A lazy factory: parses the AGENT.md, builds the Mastra agent with the model\n * resolver, SQLite memory, the markdown body as instructions, and the\n * workspace toolset (matched by tool id), then returns it as the structural\n * `MastraLike` the ACP host needs.\n */\nexport function makeAgentFactory(\n opts: AgentSourceOptions = {},\n): () => Promise<MastraLike> {\n return async () => {\n const source = await resolveAgentSource(opts)\n const { frontmatter, body } = parseAgentManifest(source)\n const handle = agentFromManifest({ frontmatter, body })\n\n const cwd = opts.cwd ?? process.cwd()\n const workspaceTools = makeWorkspaceTools({ cwd, allowExec: opts.allowExec })\n\n const { agent } = await buildMastraAgent(handle, {\n resolveModel: (ref) => resolveMastraModel(ref),\n // Match each declared tool ref against the workspace toolset by id.\n resolveTool: (ref) => {\n const id = toolRefId(ref)\n const tool = id ? workspaceTools[id] : undefined\n return tool ? { name: id as string, tool } : undefined\n },\n buildMemory: (config) => buildSqliteMemory(config),\n // The markdown body is the agent's primary system prompt (AIP-42).\n body,\n })\n return agent as unknown as MastraLike\n }\n}\n","/**\n * Pure mapping from Mastra `fullStream` chunks to AIP-44 ACP `session/update`\n * payloads. Kept dependency-light (no SDK import beyond types) and side-effect\n * free so it is straightforward to unit-test; the ACP host (acp-host.ts) owns\n * the wire/IO and just forwards whatever this returns.\n *\n * Mastra emits a typed chunk union on `agent.stream(...).fullStream`. We care\n * about three kinds:\n * - `text-delta` → ACP `agent_message_chunk` (assistant prose)\n * - `tool-call` → ACP `tool_call` (a tool started, status in_progress)\n * - `tool-result` → ACP `tool_call_update` (that tool finished/failed)\n * Everything else (reasoning, step boundaries, finish, …) is not surfaced.\n */\n\nimport type { SessionUpdate, ToolKind } from \"@agentclientprotocol/sdk\"\n\n/** The narrow slice of a Mastra fullStream chunk we read. Typed structurally\n * so we don't couple to a specific @mastra/core version. */\nexport type MastraStreamChunk =\n | { type: \"text-delta\"; payload?: { text?: string } }\n | {\n type: \"tool-call\"\n payload?: { toolCallId?: string; toolName?: string; args?: unknown }\n }\n | {\n type: \"tool-result\"\n payload?: {\n toolCallId?: string\n toolName?: string\n result?: unknown\n isError?: boolean\n }\n }\n | { type: string; payload?: unknown }\n\n/** Map a workspace tool id to the ACP {@link ToolKind} that drives client\n * icon/UI treatment. Unknown ids fall back to \"other\". */\nexport function toolKindFor(toolName: string): ToolKind {\n switch (toolName) {\n case \"read_file\":\n case \"list_dir\":\n return \"read\"\n case \"write_file\":\n case \"edit_file\":\n return \"edit\"\n case \"run_command\":\n return \"execute\"\n default:\n return \"other\"\n }\n}\n\n/** A short, human-readable title for a tool call, e.g. `run_command: ls -la`\n * or `read_file: src/index.ts`. Falls back to the bare tool name. */\nexport function toolCallTitle(toolName: string, args: unknown): string {\n if (args && typeof args === \"object\") {\n const a = args as Record<string, unknown>\n const hint =\n (typeof a.command === \"string\" && a.command) ||\n (typeof a.path === \"string\" && a.path) ||\n (typeof a.file === \"string\" && a.file) ||\n \"\"\n if (hint) return `${toolName}: ${hint}`\n }\n return toolName\n}\n\n/**\n * Translate one Mastra chunk into an ACP `session/update` payload, or `null`\n * when the chunk has no ACP surface (or is missing the ids we need). The ACP\n * host wraps the result with the `sessionId`.\n */\nexport function chunkToSessionUpdate(\n chunk: MastraStreamChunk,\n): SessionUpdate | null {\n switch (chunk.type) {\n case \"text-delta\": {\n const text = (chunk.payload as { text?: string } | undefined)?.text\n if (!text) return null\n return {\n sessionUpdate: \"agent_message_chunk\",\n content: { type: \"text\", text },\n }\n }\n case \"tool-call\": {\n const p = chunk.payload as\n | { toolCallId?: string; toolName?: string; args?: unknown }\n | undefined\n if (!p?.toolCallId) return null\n const toolName = p.toolName ?? \"tool\"\n return {\n sessionUpdate: \"tool_call\",\n toolCallId: p.toolCallId,\n title: toolCallTitle(toolName, p.args),\n kind: toolKindFor(toolName),\n status: \"in_progress\",\n rawInput: p.args,\n }\n }\n case \"tool-result\": {\n const p = chunk.payload as\n | { toolCallId?: string; result?: unknown; isError?: boolean }\n | undefined\n if (!p?.toolCallId) return null\n return {\n sessionUpdate: \"tool_call_update\",\n toolCallId: p.toolCallId,\n status: p.isError ? \"failed\" : \"completed\",\n rawOutput: p.result,\n }\n }\n default:\n return null\n }\n}\n","/**\n * WP2 — the agent side of AIP-44 ACP, backed by a live Mastra agent.\n *\n * Implements the `@agentclientprotocol/sdk` `Agent` interface: handles the\n * session lifecycle and, on `session/prompt`, drives a Mastra agent's\n * `stream()` — relaying each text delta as an `agent_message_chunk`\n * `session/update`, exactly as an IDE/host expects from codex or claude-code.\n *\n * No Mastra-specific protocol knowledge leaks past this file; everything above\n * is the standard ACP wire, so the daemon spawns this like any other arm.\n */\n\nimport type {\n AgentSideConnection,\n Agent as AcpAgent,\n AuthenticateRequest,\n CancelNotification,\n InitializeRequest,\n InitializeResponse,\n NewSessionRequest,\n NewSessionResponse,\n PromptRequest,\n PromptResponse,\n SetSessionConfigOptionRequest,\n SetSessionConfigOptionResponse,\n SetSessionModeRequest,\n} from \"@agentclientprotocol/sdk\"\nimport { PROTOCOL_VERSION } from \"@agentclientprotocol/sdk\"\nimport { type MastraStreamChunk, chunkToSessionUpdate } from \"./tool-call-map.js\"\n\n/** A built Mastra agent — only the surface we need (its `stream`). Typed\n * structurally so we don't couple to a specific @mastra/core version.\n *\n * We read `fullStream` (the typed chunk union: text deltas AND tool-call /\n * tool-result events) so tool activity surfaces as ACP `tool_call` updates,\n * not only the final prose. `textStream` is kept as an optional fallback for\n * a stripped agent that exposes only text. */\nexport interface MastraLike {\n stream(\n input: string,\n options?: {\n abortSignal?: AbortSignal\n /** Memory threading — `thread` scopes recall to one ACP session. */\n memory?: { thread?: string; resource?: string }\n },\n ): Promise<{\n fullStream?: ReadableStream<MastraStreamChunk>\n textStream?: ReadableStream<string>\n text?: Promise<string>\n }>\n}\n\n/** Lazily builds the Mastra agent (so model/key errors surface on the first\n * prompt with a clear message, not at process spawn). */\nexport type AgentFactory = () => Promise<MastraLike>\n\ninterface SessionState {\n prompt: AbortController | null\n}\n\n/** Pull the user's text out of an ACP prompt (its `text` content blocks). */\nexport function promptText(params: PromptRequest): string {\n const blocks = Array.isArray(params.prompt) ? params.prompt : []\n return blocks\n .filter((b): b is { type: \"text\"; text: string } =>\n Boolean(b) && (b as { type?: string }).type === \"text\" &&\n typeof (b as { text?: unknown }).text === \"string\",\n )\n .map((b) => b.text)\n .join(\"\")\n .trim()\n}\n\nexport class MastraAcpAgent implements AcpAgent {\n readonly #conn: AgentSideConnection\n readonly #buildAgent: AgentFactory\n readonly #resource: string\n readonly #sessions = new Map<string, SessionState>()\n #agent: MastraLike | null = null\n\n constructor(\n conn: AgentSideConnection,\n buildAgent: AgentFactory,\n resource = \"mastra-agent\",\n ) {\n this.#conn = conn\n this.#buildAgent = buildAgent\n // `resource` groups a user's threads in Mastra memory; one per agent here.\n this.#resource = resource\n }\n\n async initialize(_params: InitializeRequest): Promise<InitializeResponse> {\n return {\n protocolVersion: PROTOCOL_VERSION,\n agentCapabilities: {\n // Stateless per-prompt for now; no resume/replay surface.\n loadSession: false,\n },\n }\n }\n\n async authenticate(\n _params: AuthenticateRequest,\n ): Promise<Record<string, never>> {\n // The provider key is read from the spawn env by the model gateway — no\n // ACP-level auth handshake needed.\n return {}\n }\n\n async newSession(_params: NewSessionRequest): Promise<NewSessionResponse> {\n const sessionId = randomId()\n this.#sessions.set(sessionId, { prompt: null })\n return { sessionId }\n }\n\n async prompt(params: PromptRequest): Promise<PromptResponse> {\n const session = this.#sessions.get(params.sessionId)\n if (!session) throw new Error(`unknown session ${params.sessionId}`)\n\n // Cancel any in-flight turn for this session before starting a new one.\n session.prompt?.abort()\n const ac = new AbortController()\n session.prompt = ac\n\n const text = promptText(params)\n try {\n const agent = await this.#ensureAgent()\n // Thread = ACP session id → recall is scoped to this session's history.\n const result = await agent.stream(text, {\n abortSignal: ac.signal,\n memory: { thread: params.sessionId, resource: this.#resource },\n })\n if (result.fullStream) {\n // Preferred path: the typed chunk stream carries text deltas AND\n // tool-call / tool-result events, so tool activity surfaces live.\n await this.#pumpFullStream(params.sessionId, result.fullStream, ac)\n } else if (result.textStream) {\n // Fallback: a text-only agent — relay prose deltas, no tool surface.\n await this.#pumpTextStream(params.sessionId, result.textStream, ac)\n }\n } catch (err) {\n if (ac.signal.aborted) return { stopReason: \"cancelled\" }\n // Surface the failure to the client as a message chunk, then end the\n // turn — better UX than a bare JSON-RPC error the host may swallow.\n await this.#conn.sessionUpdate({\n sessionId: params.sessionId,\n update: {\n sessionUpdate: \"agent_message_chunk\",\n content: {\n type: \"text\",\n text: `\\n[mastra-agent error] ${(err as Error).message}\\n`,\n },\n },\n })\n session.prompt = null\n return { stopReason: \"refusal\" }\n }\n\n const cancelled = ac.signal.aborted\n session.prompt = null\n return { stopReason: cancelled ? \"cancelled\" : \"end_turn\" }\n }\n\n async cancel(params: CancelNotification): Promise<void> {\n this.#sessions.get(params.sessionId)?.prompt?.abort()\n }\n\n /**\n * The host applies the `model` (and other operator options) as a `--model`\n * spawn arg via the manifest `bin_args_template`, then ALSO calls this ACP\n * config hook (the daemon's default \"config\" apply path). The model is\n * already in effect, so this is a no-op that just reports our (empty) set of\n * runtime-configurable options. Without it the spawn fails with\n * \"Method not found: session/set_config_option\".\n */\n async setSessionConfigOption(\n _params: SetSessionConfigOptionRequest,\n ): Promise<SetSessionConfigOptionResponse> {\n return { configOptions: [] }\n }\n\n /** No agent-specific modes; accept and ignore so a host that sets one\n * doesn't error. */\n async setSessionMode(\n _params: SetSessionModeRequest,\n ): Promise<Record<string, never>> {\n return {}\n }\n\n /** Drain Mastra's typed `fullStream`, mapping each chunk to an ACP\n * `session/update` (text deltas + tool_call / tool_call_update). */\n async #pumpFullStream(\n sessionId: string,\n stream: ReadableStream<MastraStreamChunk>,\n ac: AbortController,\n ): Promise<void> {\n const reader = stream.getReader()\n try {\n for (;;) {\n const { value, done } = await reader.read()\n if (done || ac.signal.aborted) break\n if (!value) continue\n const update = chunkToSessionUpdate(value)\n if (update) await this.#conn.sessionUpdate({ sessionId, update })\n }\n } finally {\n reader.releaseLock()\n }\n }\n\n /** Fallback drain for an agent exposing only a plain text stream. */\n async #pumpTextStream(\n sessionId: string,\n stream: ReadableStream<string>,\n ac: AbortController,\n ): Promise<void> {\n const reader = stream.getReader()\n try {\n for (;;) {\n const { value, done } = await reader.read()\n if (done || ac.signal.aborted) break\n if (value) {\n await this.#conn.sessionUpdate({\n sessionId,\n update: {\n sessionUpdate: \"agent_message_chunk\",\n content: { type: \"text\", text: value },\n },\n })\n }\n }\n } finally {\n reader.releaseLock()\n }\n }\n\n async #ensureAgent(): Promise<MastraLike> {\n if (!this.#agent) this.#agent = await this.#buildAgent()\n return this.#agent\n }\n}\n\n/** 16 random bytes as hex — matches the SDK example's session id shape. */\nfunction randomId(): string {\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\")\n}\n","/**\n * Boots the ACP server over stdio — the standard wiring for a spawned ACP\n * agent (mirrors the @agentclientprotocol/sdk agent example): the agent writes\n * JSON-RPC to stdout and reads from stdin.\n */\n\nimport { Readable, Writable } from \"node:stream\"\nimport { AgentSideConnection, ndJsonStream } from \"@agentclientprotocol/sdk\"\nimport { MastraAcpAgent, type AgentFactory } from \"./acp-host.js\"\n\nexport function runAcpOverStdio(buildAgent: AgentFactory): AgentSideConnection {\n // ndJsonStream(writable, readable): outgoing bytes -> stdout, incoming <- stdin.\n const toClient = Writable.toWeb(process.stdout) as WritableStream<Uint8Array>\n const fromClient = Readable.toWeb(\n process.stdin,\n ) as unknown as ReadableStream<Uint8Array>\n const stream = ndJsonStream(toClient, fromClient)\n return new AgentSideConnection(\n (conn) => new MastraAcpAgent(conn, buildAgent),\n stream,\n )\n}\n"]}
package/dist/cli.mjs ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import { runAcpOverStdio, makeAgentFactory } from './chunk-7SWAWNDG.mjs';
3
+
4
+ /**
5
+ * @agentproto/adapter-mastra-agent v0.1.0-alpha
6
+ * First-party agentproto agent: AGENT.md -> Mastra agent -> ACP server.
7
+ */
8
+
9
+ // src/cli.ts
10
+ function parseArgs(argv) {
11
+ const rest = argv.slice(2);
12
+ const out = { cmd: rest[0] };
13
+ for (let i = 1; i < rest.length; i++) {
14
+ const arg = rest[i];
15
+ if (arg === "--model") out.model = rest[++i];
16
+ else if (arg === "--agent") out.agentFile = rest[++i];
17
+ }
18
+ return out;
19
+ }
20
+ var USAGE = "usage: agentproto-mastra acp [--model <provider/model>] [--agent <AGENT.md>]\n";
21
+ function main() {
22
+ const { cmd, model, agentFile } = parseArgs(process.argv);
23
+ if (cmd !== "acp") {
24
+ process.stderr.write(USAGE);
25
+ process.exit(cmd ? 1 : 0);
26
+ }
27
+ runAcpOverStdio(
28
+ makeAgentFactory({
29
+ model: model ?? process.env.AGENTPROTO_MASTRA_MODEL,
30
+ agentFile: agentFile ?? process.env.AGENTPROTO_MASTRA_AGENT_FILE,
31
+ // Tools are confined to the dir the daemon spawned us in.
32
+ cwd: process.cwd(),
33
+ // run_command is on unless explicitly disabled.
34
+ allowExec: !process.env.AGENTPROTO_MASTRA_NO_EXEC
35
+ })
36
+ );
37
+ }
38
+ main();
39
+ //# sourceMappingURL=cli.mjs.map
40
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;AAqBA,SAAS,UAAU,IAAA,EAA4B;AAC7C,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA;AACzB,EAAA,MAAM,GAAA,GAAkB,EAAE,GAAA,EAAK,IAAA,CAAK,CAAC,CAAA,EAAE;AACvC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,QAAQ,CAAA,EAAA,EAAK;AACpC,IAAA,MAAM,GAAA,GAAM,KAAK,CAAC,CAAA;AAClB,IAAA,IAAI,QAAQ,SAAA,EAAW,GAAA,CAAI,KAAA,GAAQ,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,SAAA,IAClC,QAAQ,SAAA,EAAW,GAAA,CAAI,SAAA,GAAY,IAAA,CAAK,EAAE,CAAC,CAAA;AAAA,EACtD;AACA,EAAA,OAAO,GAAA;AACT;AAEA,IAAM,KAAA,GACJ,gFAAA;AAEF,SAAS,IAAA,GAAa;AACpB,EAAA,MAAM,EAAE,GAAA,EAAK,KAAA,EAAO,WAAU,GAAI,SAAA,CAAU,QAAQ,IAAI,CAAA;AACxD,EAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,IAAA,OAAA,CAAQ,MAAA,CAAO,MAAM,KAAK,CAAA;AAC1B,IAAA,OAAA,CAAQ,IAAA,CAAK,GAAA,GAAM,CAAA,GAAI,CAAC,CAAA;AAAA,EAC1B;AAGA,EAAA,eAAA;AAAA,IACE,gBAAA,CAAiB;AAAA,MACf,KAAA,EAAO,KAAA,IAAS,OAAA,CAAQ,GAAA,CAAI,uBAAA;AAAA,MAC5B,SAAA,EAAW,SAAA,IAAa,OAAA,CAAQ,GAAA,CAAI,4BAAA;AAAA;AAAA,MAEpC,GAAA,EAAK,QAAQ,GAAA,EAAI;AAAA;AAAA,MAEjB,SAAA,EAAW,CAAC,OAAA,CAAQ,GAAA,CAAI;AAAA,KACzB;AAAA,GACH;AACF;AAEA,IAAA,EAAK","file":"cli.mjs","sourcesContent":["#!/usr/bin/env node\n/**\n * Standalone CLI for the first-party Mastra agent.\n *\n * agentproto-mastra acp [--model <id>] [--agent <AGENT.md>]\n *\n * `acp` boots the ACP server over stdio — this is both what the agentproto\n * daemon spawns (as the `mastra-agent` arm) and what a user runs directly to\n * drive the agent from any ACP-speaking host, with no other agent CLI in the\n * loop. Our loop, our models.\n */\n\nimport { makeAgentFactory } from \"./default-agent.js\"\nimport { runAcpOverStdio } from \"./run.js\"\n\ninterface ParsedArgs {\n cmd?: string\n model?: string\n agentFile?: string\n}\n\nfunction parseArgs(argv: string[]): ParsedArgs {\n const rest = argv.slice(2)\n const out: ParsedArgs = { cmd: rest[0] }\n for (let i = 1; i < rest.length; i++) {\n const arg = rest[i]\n if (arg === \"--model\") out.model = rest[++i]\n else if (arg === \"--agent\") out.agentFile = rest[++i]\n }\n return out\n}\n\nconst USAGE =\n \"usage: agentproto-mastra acp [--model <provider/model>] [--agent <AGENT.md>]\\n\"\n\nfunction main(): void {\n const { cmd, model, agentFile } = parseArgs(process.argv)\n if (cmd !== \"acp\") {\n process.stderr.write(USAGE)\n process.exit(cmd ? 1 : 0)\n }\n // The connection keeps the process alive (it holds stdin open); no explicit\n // wait loop needed.\n runAcpOverStdio(\n makeAgentFactory({\n model: model ?? process.env.AGENTPROTO_MASTRA_MODEL,\n agentFile: agentFile ?? process.env.AGENTPROTO_MASTRA_AGENT_FILE,\n // Tools are confined to the dir the daemon spawned us in.\n cwd: process.cwd(),\n // run_command is on unless explicitly disabled.\n allowExec: !process.env.AGENTPROTO_MASTRA_NO_EXEC,\n }),\n )\n}\n\nmain()\n"]}
@@ -0,0 +1,251 @@
1
+ import { AgentCliHandle, AgentCliRuntime } from '@agentproto/driver-agent-cli';
2
+ export { AgentCliHandle, AgentCliRuntime } from '@agentproto/driver-agent-cli';
3
+ import { SessionUpdate, ToolKind, Agent, AgentSideConnection, InitializeRequest, InitializeResponse, AuthenticateRequest, NewSessionRequest, NewSessionResponse, PromptRequest, PromptResponse, CancelNotification, SetSessionConfigOptionRequest, SetSessionConfigOptionResponse, SetSessionModeRequest } from '@agentclientprotocol/sdk';
4
+ import { ModelRef, MemoryConfig } from '@agentproto/agent';
5
+ import { createTool } from '@mastra/core/tools';
6
+ import { Memory } from '@mastra/memory';
7
+
8
+ /**
9
+ * Pure mapping from Mastra `fullStream` chunks to AIP-44 ACP `session/update`
10
+ * payloads. Kept dependency-light (no SDK import beyond types) and side-effect
11
+ * free so it is straightforward to unit-test; the ACP host (acp-host.ts) owns
12
+ * the wire/IO and just forwards whatever this returns.
13
+ *
14
+ * Mastra emits a typed chunk union on `agent.stream(...).fullStream`. We care
15
+ * about three kinds:
16
+ * - `text-delta` → ACP `agent_message_chunk` (assistant prose)
17
+ * - `tool-call` → ACP `tool_call` (a tool started, status in_progress)
18
+ * - `tool-result` → ACP `tool_call_update` (that tool finished/failed)
19
+ * Everything else (reasoning, step boundaries, finish, …) is not surfaced.
20
+ */
21
+
22
+ /** The narrow slice of a Mastra fullStream chunk we read. Typed structurally
23
+ * so we don't couple to a specific @mastra/core version. */
24
+ type MastraStreamChunk = {
25
+ type: "text-delta";
26
+ payload?: {
27
+ text?: string;
28
+ };
29
+ } | {
30
+ type: "tool-call";
31
+ payload?: {
32
+ toolCallId?: string;
33
+ toolName?: string;
34
+ args?: unknown;
35
+ };
36
+ } | {
37
+ type: "tool-result";
38
+ payload?: {
39
+ toolCallId?: string;
40
+ toolName?: string;
41
+ result?: unknown;
42
+ isError?: boolean;
43
+ };
44
+ } | {
45
+ type: string;
46
+ payload?: unknown;
47
+ };
48
+ /** Map a workspace tool id to the ACP {@link ToolKind} that drives client
49
+ * icon/UI treatment. Unknown ids fall back to "other". */
50
+ declare function toolKindFor(toolName: string): ToolKind;
51
+ /** A short, human-readable title for a tool call, e.g. `run_command: ls -la`
52
+ * or `read_file: src/index.ts`. Falls back to the bare tool name. */
53
+ declare function toolCallTitle(toolName: string, args: unknown): string;
54
+ /**
55
+ * Translate one Mastra chunk into an ACP `session/update` payload, or `null`
56
+ * when the chunk has no ACP surface (or is missing the ids we need). The ACP
57
+ * host wraps the result with the `sessionId`.
58
+ */
59
+ declare function chunkToSessionUpdate(chunk: MastraStreamChunk): SessionUpdate | null;
60
+
61
+ /**
62
+ * WP2 — the agent side of AIP-44 ACP, backed by a live Mastra agent.
63
+ *
64
+ * Implements the `@agentclientprotocol/sdk` `Agent` interface: handles the
65
+ * session lifecycle and, on `session/prompt`, drives a Mastra agent's
66
+ * `stream()` — relaying each text delta as an `agent_message_chunk`
67
+ * `session/update`, exactly as an IDE/host expects from codex or claude-code.
68
+ *
69
+ * No Mastra-specific protocol knowledge leaks past this file; everything above
70
+ * is the standard ACP wire, so the daemon spawns this like any other arm.
71
+ */
72
+
73
+ /** A built Mastra agent — only the surface we need (its `stream`). Typed
74
+ * structurally so we don't couple to a specific @mastra/core version.
75
+ *
76
+ * We read `fullStream` (the typed chunk union: text deltas AND tool-call /
77
+ * tool-result events) so tool activity surfaces as ACP `tool_call` updates,
78
+ * not only the final prose. `textStream` is kept as an optional fallback for
79
+ * a stripped agent that exposes only text. */
80
+ interface MastraLike {
81
+ stream(input: string, options?: {
82
+ abortSignal?: AbortSignal;
83
+ /** Memory threading — `thread` scopes recall to one ACP session. */
84
+ memory?: {
85
+ thread?: string;
86
+ resource?: string;
87
+ };
88
+ }): Promise<{
89
+ fullStream?: ReadableStream<MastraStreamChunk>;
90
+ textStream?: ReadableStream<string>;
91
+ text?: Promise<string>;
92
+ }>;
93
+ }
94
+ /** Lazily builds the Mastra agent (so model/key errors surface on the first
95
+ * prompt with a clear message, not at process spawn). */
96
+ type AgentFactory = () => Promise<MastraLike>;
97
+ /** Pull the user's text out of an ACP prompt (its `text` content blocks). */
98
+ declare function promptText(params: PromptRequest): string;
99
+ declare class MastraAcpAgent implements Agent {
100
+ #private;
101
+ constructor(conn: AgentSideConnection, buildAgent: AgentFactory, resource?: string);
102
+ initialize(_params: InitializeRequest): Promise<InitializeResponse>;
103
+ authenticate(_params: AuthenticateRequest): Promise<Record<string, never>>;
104
+ newSession(_params: NewSessionRequest): Promise<NewSessionResponse>;
105
+ prompt(params: PromptRequest): Promise<PromptResponse>;
106
+ cancel(params: CancelNotification): Promise<void>;
107
+ /**
108
+ * The host applies the `model` (and other operator options) as a `--model`
109
+ * spawn arg via the manifest `bin_args_template`, then ALSO calls this ACP
110
+ * config hook (the daemon's default "config" apply path). The model is
111
+ * already in effect, so this is a no-op that just reports our (empty) set of
112
+ * runtime-configurable options. Without it the spawn fails with
113
+ * "Method not found: session/set_config_option".
114
+ */
115
+ setSessionConfigOption(_params: SetSessionConfigOptionRequest): Promise<SetSessionConfigOptionResponse>;
116
+ /** No agent-specific modes; accept and ignore so a host that sets one
117
+ * doesn't error. */
118
+ setSessionMode(_params: SetSessionModeRequest): Promise<Record<string, never>>;
119
+ }
120
+
121
+ /**
122
+ * Builds a runnable Mastra agent from an AIP-42 AGENT.md — either a caller's
123
+ * file or a zero-config built-in default — wiring the model, the markdown body
124
+ * as instructions, the SQLite memory, and the workspace toolset.
125
+ */
126
+
127
+ /** Cheap OpenRouter coder by default — this is the budget first-party arm,
128
+ * same rationale as the hermes default. Override with --model / env. */
129
+ declare const DEFAULT_MODEL = "openrouter/z-ai/glm-5.2";
130
+ /** Tool ids the built-in default agent is granted (the workspace toolset). */
131
+ declare const DEFAULT_TOOL_IDS: readonly ["list_dir", "read_file", "write_file", "edit_file", "run_command"];
132
+ interface AgentSourceOptions {
133
+ /** Path to an AGENT.md. When omitted, the built-in default agent is used. */
134
+ agentFile?: string;
135
+ /** Model id for the default agent (ignored when agentFile carries a model). */
136
+ model?: string;
137
+ /** Workspace dir the tools are confined to. Defaults to `process.cwd()`. */
138
+ cwd?: string;
139
+ /** When false, the `run_command` tool is withheld. Default true. */
140
+ allowExec?: boolean;
141
+ }
142
+ /** The built-in AGENT.md used when no file is supplied. */
143
+ declare function defaultAgentManifest(model: string): string;
144
+ /**
145
+ * A lazy factory: parses the AGENT.md, builds the Mastra agent with the model
146
+ * resolver, SQLite memory, the markdown body as instructions, and the
147
+ * workspace toolset (matched by tool id), then returns it as the structural
148
+ * `MastraLike` the ACP host needs.
149
+ */
150
+ declare function makeAgentFactory(opts?: AgentSourceOptions): () => Promise<MastraLike>;
151
+
152
+ /**
153
+ * WP1 — AIP-42 `model:` ref -> a model Mastra's `Agent` can run.
154
+ *
155
+ * Mastra 1.x ships a model router (`models.dev` gateway) that accepts a bare
156
+ * `provider/model` string (e.g. `anthropic/claude-opus-4-8`,
157
+ * `openrouter/z-ai/glm-5.2`) and resolves it to a live ai-sdk model, reading
158
+ * the provider's key from the environment. So the resolver is a thin,
159
+ * *validated* pass-through: extract the ref string, sanity-check the shape,
160
+ * surface a friendly error when the obvious provider key is missing, and hand
161
+ * the string to Mastra. No bespoke provider wiring, no extra ai-sdk deps.
162
+ */
163
+
164
+ /** Pull the model id string out of an AIP-42 `ModelRef` (string | { ref }). */
165
+ declare function modelRefToString(ref: ModelRef): string;
166
+ /** The provider segment is everything before the first `/`. */
167
+ declare function providerOf(modelId: string): string;
168
+ /**
169
+ * Resolve an AIP-42 model ref to the value Mastra's `Agent` constructor takes.
170
+ * Returns the `provider/model` string — Mastra routes it. `env` defaults to
171
+ * `process.env`; injectable for tests.
172
+ */
173
+ declare function resolveMastraModel(ref: ModelRef, env?: Record<string, string | undefined>): string;
174
+
175
+ /**
176
+ * Workspace toolset — gives the Mastra agent the ability to inspect, edit, and
177
+ * run commands inside its session working directory, like a coding agent.
178
+ *
179
+ * SAFETY: every file path is resolved against the session `cwd` and rejected if
180
+ * it escapes (no `../` traversal, no absolute paths outside cwd). Command
181
+ * execution runs with `cwd` and a timeout, and is gated by `allowExec` (the CLI
182
+ * sets it from `AGENTPROTO_MASTRA_NO_EXEC`). The agent only ever touches the
183
+ * directory the daemon spawned it in.
184
+ */
185
+
186
+ interface WorkspaceToolsOptions {
187
+ /** Absolute path the agent is confined to (the spawn cwd). */
188
+ cwd: string;
189
+ /** When false, `run_command` is omitted from the toolset. Default true. */
190
+ allowExec?: boolean;
191
+ /** Per-command timeout (ms). Default 120_000. */
192
+ execTimeoutMs?: number;
193
+ }
194
+ /** Resolve `p` against `cwd`, throwing if the result escapes the workspace. */
195
+ declare function resolveInCwd(cwd: string, p: string): string;
196
+ /**
197
+ * Build the workspace toolset, all confined to `cwd`. Returns a record keyed by
198
+ * tool id (also the AGENT.md tool ref the resolver matches).
199
+ */
200
+ declare function makeWorkspaceTools(opts: WorkspaceToolsOptions): Record<string, ReturnType<typeof createTool>>;
201
+
202
+ /**
203
+ * SQLite-backed memory for the agent, via Mastra's LibSQL store.
204
+ *
205
+ * Each ACP session is a memory *thread* (see acp-host.ts), so the agent recalls
206
+ * earlier turns within a session. The db is a single SQLite file (LibSQL is a
207
+ * SQLite fork) under `~/.agentproto/mastra-agent/` by default — persistent
208
+ * across spawns — overridable with `AGENTPROTO_MASTRA_MEMORY_DB`.
209
+ */
210
+
211
+ /** A built Mastra memory — structural, matching `@agentproto/mastra`'s
212
+ * `MastraMemoryLike` expectation without importing the exact type. */
213
+ type MastraMemoryLike = Memory;
214
+ /** Resolve the SQLite file path, creating the parent dir. `AGENTPROTO_MASTRA_MEMORY_DB`
215
+ * wins; otherwise `~/.agentproto/mastra-agent/memory.db`. */
216
+ declare function resolveMemoryDbPath(env?: Record<string, string | undefined>): string;
217
+ /**
218
+ * Build a Mastra `Memory` from an AIP-42 `memory:` config. Returns `undefined`
219
+ * when memory is disabled (`scope: "none"`) so `buildMastraAgent` attaches none.
220
+ *
221
+ * - `retention_turns` → `options.lastMessages` (how many recent messages to
222
+ * replay into context). Defaults to 20.
223
+ * - Semantic recall is left off (it needs an embedder + vector index) — this is
224
+ * conversation-history memory, the SQLite ask.
225
+ */
226
+ declare function buildSqliteMemory(config?: MemoryConfig, env?: Record<string, string | undefined>): MastraMemoryLike | undefined;
227
+
228
+ /**
229
+ * Boots the ACP server over stdio — the standard wiring for a spawned ACP
230
+ * agent (mirrors the @agentclientprotocol/sdk agent example): the agent writes
231
+ * JSON-RPC to stdout and reads from stdin.
232
+ */
233
+
234
+ declare function runAcpOverStdio(buildAgent: AgentFactory): AgentSideConnection;
235
+
236
+ /**
237
+ * @agentproto/adapter-mastra-agent — the first-party agentproto agent.
238
+ *
239
+ * Unlike the codex/hermes/claude-code adapters (which wrap an external agent
240
+ * CLI), this adapter IS the agent: an AIP-42 AGENT.md is run as a live Mastra
241
+ * agent behind an AIP-44 ACP server (see ./acp-host.ts), shipped as a CLI bin
242
+ * (`agentproto-mastra acp`). The daemon spawns it like any other arm; a user
243
+ * can launch it standalone. Our loop, our models — no external CLI.
244
+ *
245
+ * import { mastraAgent, mastraAgentRuntime } from "@agentproto/adapter-mastra-agent"
246
+ */
247
+
248
+ declare const mastraAgent: AgentCliHandle;
249
+ declare function mastraAgentRuntime(): AgentCliRuntime;
250
+
251
+ export { DEFAULT_MODEL, DEFAULT_TOOL_IDS, MastraAcpAgent, type MastraLike, type MastraStreamChunk, buildSqliteMemory, chunkToSessionUpdate, defaultAgentManifest, makeAgentFactory, makeWorkspaceTools, mastraAgent, mastraAgentRuntime, modelRefToString, promptText, providerOf, resolveInCwd, resolveMastraModel, resolveMemoryDbPath, runAcpOverStdio, toolCallTitle, toolKindFor };
package/dist/index.mjs ADDED
@@ -0,0 +1,95 @@
1
+ export { DEFAULT_MODEL, DEFAULT_TOOL_IDS, MastraAcpAgent, buildSqliteMemory, chunkToSessionUpdate, defaultAgentManifest, makeAgentFactory, makeWorkspaceTools, modelRefToString, promptText, providerOf, resolveInCwd, resolveMastraModel, resolveMemoryDbPath, runAcpOverStdio, toolCallTitle, toolKindFor } from './chunk-7SWAWNDG.mjs';
2
+ import { fileURLToPath } from 'url';
3
+ import { defineAgentCli, createAgentCliRuntime } from '@agentproto/driver-agent-cli';
4
+
5
+ /**
6
+ * @agentproto/adapter-mastra-agent v0.1.0-alpha
7
+ * First-party agentproto agent: AGENT.md -> Mastra agent -> ACP server.
8
+ */
9
+ var cliEntry = fileURLToPath(new URL("./cli.mjs", import.meta.url));
10
+ var mastraAgent = defineAgentCli({
11
+ name: "mastra-agent",
12
+ id: "mastra-agent",
13
+ description: "First-party agentproto agent \u2014 an AIP-42 AGENT.md run as a live Mastra agent behind an AIP-44 ACP server. No external agent CLI: our own loop, our own models (routed via Mastra's model gateway from the spawn env). Spawned as `node cli.mjs acp` over stdio JSON-RPC, or launched standalone via `agentproto-mastra acp`.",
14
+ version: "0.1.0",
15
+ bin: "node",
16
+ bin_args: [cliEntry, "acp"],
17
+ install: [
18
+ {
19
+ method: "npm",
20
+ package: "@agentproto/adapter-mastra-agent",
21
+ global: true
22
+ }
23
+ ],
24
+ // No external runtime to probe — the agent runs in the spawned node process.
25
+ // A node version gate is enough to mark the arm ready.
26
+ version_check: {
27
+ cmd: "node --version",
28
+ parse: "(\\d+\\.\\d+\\.\\d+)",
29
+ range: ">=20.9.0",
30
+ timeout_ms: 5e3
31
+ },
32
+ auth: {
33
+ ref: "./mastra-agent.ACP.md",
34
+ // The chosen model's provider key is read from the spawn env by Mastra's
35
+ // model gateway. Any one of these covers the common providers.
36
+ state: {
37
+ env: ["OPENROUTER_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY"]
38
+ }
39
+ },
40
+ sandbox: "In-process: the agent runs inside the spawned node process. Tool-level sandboxing is the responsibility of the tools the AGENT.md grants.",
41
+ protocol: "acp",
42
+ acp: "./mastra-agent.ACP.md",
43
+ session: {
44
+ mode: "persistent",
45
+ idle_timeout_ms: 18e5,
46
+ context_carryover: true
47
+ },
48
+ models: {
49
+ // Cheap OpenRouter coder by default — this is the budget first-party arm.
50
+ default: "openrouter/z-ai/glm-5.2",
51
+ allowed: [
52
+ "openrouter/z-ai/glm-5.2",
53
+ "openrouter/deepseek/deepseek-v4-pro",
54
+ "anthropic/claude-opus-4-8",
55
+ "anthropic/claude-sonnet-4-6",
56
+ "openai/gpt-5"
57
+ ],
58
+ env: {
59
+ anthropic: "ANTHROPIC_API_KEY",
60
+ openrouter: "OPENROUTER_API_KEY",
61
+ openai: "OPENAI_API_KEY"
62
+ }
63
+ },
64
+ capabilities: {
65
+ streaming: true,
66
+ // Tool calls run inside Mastra and are relayed to the host as ACP
67
+ // tool_call / tool_call_update session updates (see acp-host.ts +
68
+ // tool-call-map.ts). The built-in default agent ships the workspace
69
+ // toolset (read/edit/run), so this is on.
70
+ tool_calls: true,
71
+ sub_agents: false,
72
+ file_io: false,
73
+ multimodal: false,
74
+ resumable: false,
75
+ bidirectional: true
76
+ },
77
+ options: [
78
+ {
79
+ id: "model",
80
+ // string (not enum) so any Mastra-routable `provider/model` id works
81
+ // without a code change. Applied as a `--model <id>` spawn arg.
82
+ type: "string",
83
+ description: "Model id routed via Mastra's model gateway (e.g. 'anthropic/claude-opus-4-8', 'openrouter/z-ai/glm-5.2'). Applied as a `--model` arg at spawn. Omit for the default.",
84
+ bin_args_template: ["--model", "{value}"]
85
+ }
86
+ ],
87
+ tags: ["mastra", "agentproto", "acp", "agent-runtime", "first-party"]
88
+ });
89
+ function mastraAgentRuntime() {
90
+ return createAgentCliRuntime(mastraAgent);
91
+ }
92
+
93
+ export { mastraAgent, mastraAgentRuntime };
94
+ //# sourceMappingURL=index.mjs.map
95
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;AAwBA,IAAM,WAAW,aAAA,CAAc,IAAI,IAAI,WAAA,EAAa,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AAE7D,IAAM,cAA8B,cAAA,CAAe;AAAA,EACxD,IAAA,EAAM,cAAA;AAAA,EACN,EAAA,EAAI,cAAA;AAAA,EACJ,WAAA,EACE,mUAAA;AAAA,EACF,OAAA,EAAS,OAAA;AAAA,EACT,GAAA,EAAK,MAAA;AAAA,EACL,QAAA,EAAU,CAAC,QAAA,EAAU,KAAK,CAAA;AAAA,EAC1B,OAAA,EAAS;AAAA,IACP;AAAA,MACE,MAAA,EAAQ,KAAA;AAAA,MACR,OAAA,EAAS,kCAAA;AAAA,MACT,MAAA,EAAQ;AAAA;AACV,GACF;AAAA;AAAA;AAAA,EAGA,aAAA,EAAe;AAAA,IACb,GAAA,EAAK,gBAAA;AAAA,IACL,KAAA,EAAO,sBAAA;AAAA,IACP,KAAA,EAAO,UAAA;AAAA,IACP,UAAA,EAAY;AAAA,GACd;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,GAAA,EAAK,uBAAA;AAAA;AAAA;AAAA,IAGL,KAAA,EAAO;AAAA,MACL,GAAA,EAAK,CAAC,oBAAA,EAAsB,mBAAA,EAAqB,gBAAgB;AAAA;AACnE,GACF;AAAA,EACA,OAAA,EACE,2IAAA;AAAA,EACF,QAAA,EAAU,KAAA;AAAA,EACV,GAAA,EAAK,uBAAA;AAAA,EACL,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,YAAA;AAAA,IACN,eAAA,EAAiB,IAAA;AAAA,IACjB,iBAAA,EAAmB;AAAA,GACrB;AAAA,EACA,MAAA,EAAQ;AAAA;AAAA,IAEN,OAAA,EAAS,yBAAA;AAAA,IACT,OAAA,EAAS;AAAA,MACP,yBAAA;AAAA,MACA,qCAAA;AAAA,MACA,2BAAA;AAAA,MACA,6BAAA;AAAA,MACA;AAAA,KACF;AAAA,IACA,GAAA,EAAK;AAAA,MACH,SAAA,EAAW,mBAAA;AAAA,MACX,UAAA,EAAY,oBAAA;AAAA,MACZ,MAAA,EAAQ;AAAA;AACV,GACF;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,SAAA,EAAW,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKX,UAAA,EAAY,IAAA;AAAA,IACZ,UAAA,EAAY,KAAA;AAAA,IACZ,OAAA,EAAS,KAAA;AAAA,IACT,UAAA,EAAY,KAAA;AAAA,IACZ,SAAA,EAAW,KAAA;AAAA,IACX,aAAA,EAAe;AAAA,GACjB;AAAA,EACA,OAAA,EAAS;AAAA,IACP;AAAA,MACE,EAAA,EAAI,OAAA;AAAA;AAAA;AAAA,MAGJ,IAAA,EAAM,QAAA;AAAA,MACN,WAAA,EACE,sKAAA;AAAA,MAGF,iBAAA,EAAmB,CAAC,SAAA,EAAW,SAAS;AAAA;AAC1C,GACF;AAAA,EACA,MAAM,CAAC,QAAA,EAAU,YAAA,EAAc,KAAA,EAAO,iBAAiB,aAAa;AACtE,CAAC;AAEM,SAAS,kBAAA,GAAsC;AACpD,EAAA,OAAO,sBAAsB,WAAW,CAAA;AAC1C","file":"index.mjs","sourcesContent":["/**\n * @agentproto/adapter-mastra-agent — the first-party agentproto agent.\n *\n * Unlike the codex/hermes/claude-code adapters (which wrap an external agent\n * CLI), this adapter IS the agent: an AIP-42 AGENT.md is run as a live Mastra\n * agent behind an AIP-44 ACP server (see ./acp-host.ts), shipped as a CLI bin\n * (`agentproto-mastra acp`). The daemon spawns it like any other arm; a user\n * can launch it standalone. Our loop, our models — no external CLI.\n *\n * import { mastraAgent, mastraAgentRuntime } from \"@agentproto/adapter-mastra-agent\"\n */\n\nimport { fileURLToPath } from \"node:url\"\nimport {\n createAgentCliRuntime,\n defineAgentCli,\n type AgentCliHandle,\n type AgentCliRuntime,\n} from \"@agentproto/driver-agent-cli\"\n\n// Self-locating: the built handle spawns `node <this-dist>/cli.mjs acp`.\n// import.meta.url resolves into dist/ at runtime, where cli.mjs sits next to\n// index.mjs — so the same build works whether the daemon spawns it (resolved\n// from node_modules) or a user runs the published bin.\nconst cliEntry = fileURLToPath(new URL(\"./cli.mjs\", import.meta.url))\n\nexport const mastraAgent: AgentCliHandle = defineAgentCli({\n name: \"mastra-agent\",\n id: \"mastra-agent\",\n description:\n \"First-party agentproto agent — an AIP-42 AGENT.md run as a live Mastra agent behind an AIP-44 ACP server. No external agent CLI: our own loop, our own models (routed via Mastra's model gateway from the spawn env). Spawned as `node cli.mjs acp` over stdio JSON-RPC, or launched standalone via `agentproto-mastra acp`.\",\n version: \"0.1.0\",\n bin: \"node\",\n bin_args: [cliEntry, \"acp\"],\n install: [\n {\n method: \"npm\",\n package: \"@agentproto/adapter-mastra-agent\",\n global: true,\n },\n ],\n // No external runtime to probe — the agent runs in the spawned node process.\n // A node version gate is enough to mark the arm ready.\n version_check: {\n cmd: \"node --version\",\n parse: \"(\\\\d+\\\\.\\\\d+\\\\.\\\\d+)\",\n range: \">=20.9.0\",\n timeout_ms: 5000,\n },\n auth: {\n ref: \"./mastra-agent.ACP.md\",\n // The chosen model's provider key is read from the spawn env by Mastra's\n // model gateway. Any one of these covers the common providers.\n state: {\n env: [\"OPENROUTER_API_KEY\", \"ANTHROPIC_API_KEY\", \"OPENAI_API_KEY\"],\n },\n },\n sandbox:\n \"In-process: the agent runs inside the spawned node process. Tool-level sandboxing is the responsibility of the tools the AGENT.md grants.\",\n protocol: \"acp\",\n acp: \"./mastra-agent.ACP.md\",\n session: {\n mode: \"persistent\",\n idle_timeout_ms: 1_800_000,\n context_carryover: true,\n },\n models: {\n // Cheap OpenRouter coder by default — this is the budget first-party arm.\n default: \"openrouter/z-ai/glm-5.2\",\n allowed: [\n \"openrouter/z-ai/glm-5.2\",\n \"openrouter/deepseek/deepseek-v4-pro\",\n \"anthropic/claude-opus-4-8\",\n \"anthropic/claude-sonnet-4-6\",\n \"openai/gpt-5\",\n ],\n env: {\n anthropic: \"ANTHROPIC_API_KEY\",\n openrouter: \"OPENROUTER_API_KEY\",\n openai: \"OPENAI_API_KEY\",\n },\n },\n capabilities: {\n streaming: true,\n // Tool calls run inside Mastra and are relayed to the host as ACP\n // tool_call / tool_call_update session updates (see acp-host.ts +\n // tool-call-map.ts). The built-in default agent ships the workspace\n // toolset (read/edit/run), so this is on.\n tool_calls: true,\n sub_agents: false,\n file_io: false,\n multimodal: false,\n resumable: false,\n bidirectional: true,\n },\n options: [\n {\n id: \"model\",\n // string (not enum) so any Mastra-routable `provider/model` id works\n // without a code change. Applied as a `--model <id>` spawn arg.\n type: \"string\" as const,\n description:\n \"Model id routed via Mastra's model gateway (e.g. \" +\n \"'anthropic/claude-opus-4-8', 'openrouter/z-ai/glm-5.2'). \" +\n \"Applied as a `--model` arg at spawn. Omit for the default.\",\n bin_args_template: [\"--model\", \"{value}\"],\n },\n ],\n tags: [\"mastra\", \"agentproto\", \"acp\", \"agent-runtime\", \"first-party\"],\n})\n\nexport function mastraAgentRuntime(): AgentCliRuntime {\n return createAgentCliRuntime(mastraAgent)\n}\n\nexport {\n makeAgentFactory,\n defaultAgentManifest,\n DEFAULT_MODEL,\n DEFAULT_TOOL_IDS,\n} from \"./default-agent.js\"\nexport { MastraAcpAgent, promptText, type MastraLike } from \"./acp-host.js\"\nexport { resolveMastraModel, modelRefToString, providerOf } from \"./model-resolver.js\"\nexport { makeWorkspaceTools, resolveInCwd } from \"./workspace-tools.js\"\nexport { buildSqliteMemory, resolveMemoryDbPath } from \"./memory.js\"\nexport {\n chunkToSessionUpdate,\n toolKindFor,\n toolCallTitle,\n type MastraStreamChunk,\n} from \"./tool-call-map.js\"\nexport { runAcpOverStdio } from \"./run.js\"\nexport type { AgentCliHandle, AgentCliRuntime }\n"]}
@@ -0,0 +1,29 @@
1
+ # mastra-agent — ACP wire profile
2
+
3
+ AIP-44 ACP profile for the first-party Mastra agent. The agent side is
4
+ implemented in `src/acp-host.ts` against `@agentclientprotocol/sdk`'s
5
+ `AgentSideConnection`, spawned over stdio (`agentproto-mastra acp`).
6
+
7
+ ## Lifecycle
8
+
9
+ | Method | Behaviour |
10
+ | ----------------- | --------------------------------------------------------------- |
11
+ | `initialize` | Returns `PROTOCOL_VERSION` + `agentCapabilities.loadSession=false`. |
12
+ | `authenticate` | No-op (`{}`). The model provider key is read from the spawn env. |
13
+ | `session/new` | Allocates a random session id; agent is built lazily on first prompt. |
14
+ | `session/prompt` | Extracts the user's text content blocks, drives the Mastra agent's `stream()`, and relays each text delta as an `agent_message_chunk` `session/update`. Resolves with `stopReason: end_turn` (or `cancelled` / `refusal`). |
15
+ | `session/cancel` | Aborts the in-flight turn for that session. |
16
+
17
+ ## Models
18
+
19
+ The `model` is a Mastra-routable `provider/model` id (e.g.
20
+ `anthropic/claude-opus-4-8`, `openrouter/z-ai/glm-5.2`), resolved by Mastra's
21
+ model gateway using the provider key in the environment. Override per spawn via
22
+ the `--model` arg (wired through the manifest `model` option's
23
+ `bin_args_template`).
24
+
25
+ ## Auth / secrets
26
+
27
+ One provider key for the chosen model, on the spawn env: `OPENROUTER_API_KEY`,
28
+ `ANTHROPIC_API_KEY`, or `OPENAI_API_KEY` (and the other gateway-supported
29
+ providers). No ACP-level auth handshake.
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@agentproto/adapter-mastra-agent",
3
+ "version": "0.1.0",
4
+ "description": "@agentproto/adapter-mastra-agent — first-party agentproto agent. An AIP-42 AGENT.md run as a live Mastra agent behind an AIP-44 ACP server, spawnable by the daemon like any other AGENT-CLI arm AND launchable standalone via `agentproto-mastra acp`. Our own loop, our own models — no external CLI.",
5
+ "keywords": [
6
+ "agentproto",
7
+ "aip-42",
8
+ "aip-44",
9
+ "aip-45",
10
+ "mastra",
11
+ "acp",
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/mastra-agent"
20
+ },
21
+ "license": "MIT",
22
+ "type": "module",
23
+ "main": "dist/index.mjs",
24
+ "module": "dist/index.mjs",
25
+ "types": "dist/index.d.ts",
26
+ "bin": {
27
+ "agentproto-mastra": "dist/cli.mjs"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.mjs",
33
+ "default": "./dist/index.mjs"
34
+ },
35
+ "./MASTRA-AGENT.md": "./MASTRA-AGENT.md",
36
+ "./mastra-agent.ACP.md": "./mastra-agent.ACP.md",
37
+ "./package.json": "./package.json"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "MASTRA-AGENT.md",
42
+ "mastra-agent.ACP.md",
43
+ "README.md",
44
+ "LICENSE"
45
+ ],
46
+ "publishConfig": {
47
+ "access": "public"
48
+ },
49
+ "dependencies": {
50
+ "@agentclientprotocol/sdk": "^0.21.0",
51
+ "@mastra/core": "^1.31.0",
52
+ "@mastra/memory": ">=1.15.0 <1.20.0",
53
+ "@mastra/libsql": "~1.10.0",
54
+ "zod": "^4.4.3",
55
+ "@agentproto/agent": "0.2.0",
56
+ "@agentproto/driver-agent-cli": "0.2.0",
57
+ "@agentproto/mastra": "0.2.0"
58
+ },
59
+ "devDependencies": {
60
+ "@types/node": "^25.6.2",
61
+ "tsup": "^8.5.1",
62
+ "typescript": "^5.9.3",
63
+ "vitest": "^3.2.4",
64
+ "@agentproto/tooling": "0.1.0-alpha.0"
65
+ },
66
+ "scripts": {
67
+ "dev": "tsup --watch",
68
+ "build": "tsup",
69
+ "clean": "rm -rf dist",
70
+ "check-types": "tsc --noEmit",
71
+ "test": "vitest run --passWithNoTests",
72
+ "test:watch": "vitest"
73
+ }
74
+ }