@plasm_lang/vercel-agent 0.3.76 → 0.3.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -2
- package/package.json +6 -3
- package/scripts/build-stubs.ts +27 -0
- package/scripts/plasm-cli.ts +161 -0
- package/scripts/register-plasm-loader.mjs +6 -0
- package/src/cli/init.ts +77 -3
- package/src/cli/nitro-dev.ts +73 -0
- package/src/engine/create-host-transport.ts +7 -0
- package/src/index.ts +1 -1
- package/src/stubs/catalog-client.ts +2 -2
- package/src/tools/harness-tools.ts +42 -41
- package/src/tools/plasm-tools.ts +64 -55
- package/src/tools/tool-input.ts +9 -0
package/README.md
CHANGED
|
@@ -152,9 +152,16 @@ npm run smoke:stubs:matrix
|
|
|
152
152
|
npm run agent -- "your prompt" # requires AI_GATEWAY_API_KEY (Vercel AI Gateway)
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
### Dev server (`
|
|
155
|
+
### Dev server (`plasm-agent dev`)
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
**Default:** Nitro dev server with **Vercel routing parity** (channels, cron, `/plasm/v1/info`) — same `vercelPlasmHandler` as deploy. Init scaffolds `nitro.config.ts` + `routes/[...path].ts` and adds `nitropack` as a devDependency.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
plasm-agent dev # Nitro (default)
|
|
161
|
+
plasm-agent dev --interactive # in-process server + TUI when TTY
|
|
162
|
+
plasm-agent dev --interactive --no-tui
|
|
163
|
+
plasm-agent chat --url http://127.0.0.1:3000
|
|
164
|
+
```
|
|
158
165
|
|
|
159
166
|
Slash commands in the TUI: `/help`, `/info`, `/model`, `/channels`, `/catalogs`, `/new`, `/quit`.
|
|
160
167
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasm_lang/vercel-agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.78",
|
|
4
4
|
"description": "Catalog-native TypeScript agent framework (Plasm CGS/CML, Vercel AI SDK, Nitro-oriented)",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"src",
|
|
27
27
|
"bin",
|
|
28
28
|
"scripts/plasm-node.mjs",
|
|
29
|
+
"scripts/plasm-cli.ts",
|
|
30
|
+
"scripts/build-stubs.ts",
|
|
31
|
+
"scripts/register-plasm-loader.mjs",
|
|
29
32
|
"scripts/resolve-ts-extension.mjs",
|
|
30
33
|
"agent",
|
|
31
34
|
"README.md"
|
|
@@ -54,7 +57,7 @@
|
|
|
54
57
|
"dependencies": {
|
|
55
58
|
"@ai-sdk/otel": "^1.0.0-beta.6",
|
|
56
59
|
"@opentelemetry/api": "^1.9.0",
|
|
57
|
-
"@plasm_lang/engine": "^0.3.
|
|
60
|
+
"@plasm_lang/engine": "^0.3.78",
|
|
58
61
|
"@vercel/blob": "^0.27.3",
|
|
59
62
|
"@vercel/connect": "^0.2.6",
|
|
60
63
|
"@vercel/kv": "^3.0.0",
|
|
@@ -63,7 +66,7 @@
|
|
|
63
66
|
"js-yaml": "^4.1.0",
|
|
64
67
|
"pg": "^8.22.0",
|
|
65
68
|
"workflow": "^4.5.0",
|
|
66
|
-
"zod": "^3.
|
|
69
|
+
"zod": "^3.25.76"
|
|
67
70
|
},
|
|
68
71
|
"devDependencies": {
|
|
69
72
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Scan agent/catalogs/ and emit TypeScript stubs to agent/.plasm/stubs/<entry_id>.ts
|
|
4
|
+
*/
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import { generateAllStubs } from "../src/stubs/generator.js";
|
|
9
|
+
|
|
10
|
+
const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const agentRoot = path.join(packageRoot, "agent");
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
const results = await generateAllStubs(agentRoot);
|
|
15
|
+
if (!results.length) {
|
|
16
|
+
console.error("no catalogs found under agent/catalogs/");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
for (const result of results) {
|
|
20
|
+
console.log(`${result.entryId}\t${result.catalogCgsHash}\t${result.outPath}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
main().catch((err) => {
|
|
25
|
+
console.error(err);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* TypeScript CLI for @plasm_lang/vercel-agent (not the Rust plasm-server TUI).
|
|
4
|
+
*/
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
import { runPlasmBuild } from "../src/cli/build.js";
|
|
8
|
+
import {
|
|
9
|
+
installDevServerShutdown,
|
|
10
|
+
startDevServerForProject,
|
|
11
|
+
} from "../src/cli/dev.js";
|
|
12
|
+
import { startNitroDevForProject } from "../src/cli/nitro-dev.js";
|
|
13
|
+
import { runDevTui } from "../src/dev/client/repl.js";
|
|
14
|
+
import { collectProjectInfo, formatPlasmInfoHuman } from "../src/cli/info.js";
|
|
15
|
+
import { runPlasmInit, formatInitSuccess } from "../src/cli/init.js";
|
|
16
|
+
import { runPlasmLink } from "../src/cli/link.js";
|
|
17
|
+
import {
|
|
18
|
+
requireAgentProject,
|
|
19
|
+
readPackageName,
|
|
20
|
+
resolveAgentProject,
|
|
21
|
+
} from "../src/cli/project-root.js";
|
|
22
|
+
import { loadAgentEnv } from "../src/load-env.js";
|
|
23
|
+
|
|
24
|
+
loadAgentEnv();
|
|
25
|
+
|
|
26
|
+
const args = process.argv.slice(2);
|
|
27
|
+
const command = args[0];
|
|
28
|
+
|
|
29
|
+
function flag(name: string): boolean {
|
|
30
|
+
return args.includes(name);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function optionValue(flagName: string): string | undefined {
|
|
34
|
+
const index = args.indexOf(flagName);
|
|
35
|
+
if (index < 0) return undefined;
|
|
36
|
+
const value = args[index + 1];
|
|
37
|
+
if (!value || value.startsWith("-")) return undefined;
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function initTargetDir(): string {
|
|
42
|
+
for (let i = 1; i < args.length; i++) {
|
|
43
|
+
const arg = args[i];
|
|
44
|
+
if (arg.startsWith("-")) {
|
|
45
|
+
if (arg === "--template") i++;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
return arg;
|
|
49
|
+
}
|
|
50
|
+
return process.cwd();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function cmdInit(): Promise<void> {
|
|
54
|
+
const template = optionValue("--template");
|
|
55
|
+
const target = initTargetDir();
|
|
56
|
+
const project = await runPlasmInit(target, { template });
|
|
57
|
+
console.log(formatInitSuccess(project, { template }));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function cmdInfo(): Promise<void> {
|
|
61
|
+
const project = await requireAgentProject();
|
|
62
|
+
const packageName = await readPackageName(project.projectRoot);
|
|
63
|
+
const info = await collectProjectInfo({
|
|
64
|
+
projectRoot: project.projectRoot,
|
|
65
|
+
agentRoot: project.agentRoot,
|
|
66
|
+
packageName,
|
|
67
|
+
});
|
|
68
|
+
if (flag("--json")) {
|
|
69
|
+
console.log(JSON.stringify(info, null, 2));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
console.log(formatPlasmInfoHuman(info));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function cmdLink(): Promise<void> {
|
|
76
|
+
const project = await requireAgentProject();
|
|
77
|
+
const result = await runPlasmLink(project.projectRoot);
|
|
78
|
+
for (const line of result.messages) console.log(line);
|
|
79
|
+
if (!result.linked && !result.envPulled) process.exitCode = 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function cmdBuild(): Promise<void> {
|
|
83
|
+
const project = await requireAgentProject();
|
|
84
|
+
const result = await runPlasmBuild(project);
|
|
85
|
+
console.log(`Built ${result.stubs.length} stub(s)`);
|
|
86
|
+
console.log(`manifest: ${result.manifestPath}`);
|
|
87
|
+
for (const stub of result.stubs) {
|
|
88
|
+
console.log(` - ${stub.entryId} → ${path.relative(project.projectRoot, stub.outPath)}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function cmdDev(): Promise<void> {
|
|
93
|
+
const project = await requireAgentProject();
|
|
94
|
+
if (flag("--interactive")) {
|
|
95
|
+
const handle = await startDevServerForProject({
|
|
96
|
+
project,
|
|
97
|
+
tui: flag("--no-tui") ? false : "auto",
|
|
98
|
+
});
|
|
99
|
+
installDevServerShutdown(handle);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
await startNitroDevForProject(project);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function cmdChat(): Promise<void> {
|
|
106
|
+
const urlFlag = args.find((a, i) => args[i - 1] === "--url");
|
|
107
|
+
const baseUrl = urlFlag ?? process.env.PLASM_DEV_URL ?? "http://127.0.0.1:3000";
|
|
108
|
+
await runDevTui({ baseUrl });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function main(): Promise<void> {
|
|
112
|
+
if (!command || command === "--help" || command === "-h") {
|
|
113
|
+
console.log(`plasm-agent — @plasm_lang/vercel-agent CLI
|
|
114
|
+
|
|
115
|
+
Commands:
|
|
116
|
+
init [dir] [--template NAME] Scaffold agent project (templates: mcp-radar)
|
|
117
|
+
info [--json] Project + catalog diagnostics
|
|
118
|
+
link vercel link + env pull (AI Gateway key)
|
|
119
|
+
build CGS stubs + .plasm/discovery/manifest.json
|
|
120
|
+
dev [--interactive] [--no-tui] Nitro dev (Vercel routing parity; default). --interactive for TUI/sessions.
|
|
121
|
+
chat [--url URL] Terminal client for a running dev server
|
|
122
|
+
`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
switch (command) {
|
|
127
|
+
case "init":
|
|
128
|
+
await cmdInit();
|
|
129
|
+
break;
|
|
130
|
+
case "info": {
|
|
131
|
+
const resolved = await resolveAgentProject();
|
|
132
|
+
if (!resolved) {
|
|
133
|
+
console.error("No agent project found. Run `plasm-agent init` first.");
|
|
134
|
+
process.exitCode = 1;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
await cmdInfo();
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
case "link":
|
|
141
|
+
await cmdLink();
|
|
142
|
+
break;
|
|
143
|
+
case "build":
|
|
144
|
+
await cmdBuild();
|
|
145
|
+
break;
|
|
146
|
+
case "dev":
|
|
147
|
+
await cmdDev();
|
|
148
|
+
break;
|
|
149
|
+
case "chat":
|
|
150
|
+
await cmdChat();
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
console.error(`Unknown command: ${command}`);
|
|
154
|
+
process.exitCode = 1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await main().catch((err) => {
|
|
159
|
+
console.error(err);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
});
|
package/src/cli/init.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface InitOptions {
|
|
|
9
9
|
template?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const SKIP_TEMPLATE_DIRS = new Set(["node_modules", ".plasm"]);
|
|
12
|
+
const SKIP_TEMPLATE_DIRS = new Set(["node_modules", ".plasm", ".nitro", ".output", "server"]);
|
|
13
13
|
const SKIP_TEMPLATE_FILES = new Set(["package-lock.json"]);
|
|
14
14
|
|
|
15
15
|
function plasmAgentPackageRoot(): string {
|
|
@@ -55,6 +55,7 @@ async function patchBootstrapPackageJson(
|
|
|
55
55
|
name?: string;
|
|
56
56
|
scripts?: Record<string, string>;
|
|
57
57
|
dependencies?: Record<string, string>;
|
|
58
|
+
devDependencies?: Record<string, string>;
|
|
58
59
|
};
|
|
59
60
|
pkg.name = path.basename(projectRoot);
|
|
60
61
|
const engineRoot = path.resolve(packageRoot, "../plasm-engine");
|
|
@@ -72,12 +73,17 @@ async function patchBootstrapPackageJson(
|
|
|
72
73
|
build: "plasm-agent build",
|
|
73
74
|
"vercel-build": "plasm-agent build",
|
|
74
75
|
dev: "plasm-agent dev",
|
|
76
|
+
"dev:interactive": "plasm-agent dev --interactive",
|
|
75
77
|
info: "plasm-agent info",
|
|
76
78
|
deploy: "vercel deploy",
|
|
77
79
|
...pkg.scripts,
|
|
78
80
|
eval: `${nodeRunner} scripts/run-evals.ts`,
|
|
79
81
|
"smoke:channel": `${nodeRunner} scripts/smoke-channel.ts`,
|
|
80
82
|
};
|
|
83
|
+
pkg.devDependencies = {
|
|
84
|
+
...pkg.devDependencies,
|
|
85
|
+
nitropack: "^2.13.4",
|
|
86
|
+
};
|
|
81
87
|
await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
82
88
|
}
|
|
83
89
|
|
|
@@ -98,6 +104,7 @@ async function runTemplateInit(
|
|
|
98
104
|
await copyTemplate(templateRoot, projectRoot);
|
|
99
105
|
await patchBootstrapPackageJson(projectRoot, plasmAgentPackageRoot());
|
|
100
106
|
await writeVercelScaffold(projectRoot, template);
|
|
107
|
+
await writeNitroScaffold(projectRoot);
|
|
101
108
|
return { projectRoot, agentRoot };
|
|
102
109
|
}
|
|
103
110
|
|
|
@@ -249,18 +256,72 @@ export default async function handler(
|
|
|
249
256
|
}
|
|
250
257
|
`;
|
|
251
258
|
|
|
259
|
+
const NITRO_CONFIG_TS = `import { defineNitroConfig } from "nitropack/config";
|
|
260
|
+
|
|
261
|
+
export default defineNitroConfig({
|
|
262
|
+
compatibilityDate: "2026-06-26",
|
|
263
|
+
srcDir: ".",
|
|
264
|
+
ignore: ["api/**"],
|
|
265
|
+
devServer: {
|
|
266
|
+
port: Number(process.env.PORT ?? 3000),
|
|
267
|
+
host: process.env.HOST ?? "127.0.0.1",
|
|
268
|
+
},
|
|
269
|
+
typescript: {
|
|
270
|
+
strict: false,
|
|
271
|
+
},
|
|
272
|
+
externals: {
|
|
273
|
+
inline: ["@plasm_lang/engine"],
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
`;
|
|
277
|
+
|
|
278
|
+
const NITRO_CATCHALL_ROUTE_TS = `import path from "node:path";
|
|
279
|
+
|
|
280
|
+
import { fromNodeMiddleware } from "h3";
|
|
281
|
+
|
|
282
|
+
import agentDefinition from "../agent/agent.js";
|
|
283
|
+
import { createPlasmApp, vercelPlasmHandler } from "@plasm_lang/vercel-agent";
|
|
284
|
+
|
|
285
|
+
const agentRoot = path.join(process.cwd(), "agent");
|
|
286
|
+
|
|
287
|
+
let appPromise: ReturnType<typeof createPlasmApp> | undefined;
|
|
288
|
+
|
|
289
|
+
async function plasmApp() {
|
|
290
|
+
appPromise ??= createPlasmApp({
|
|
291
|
+
agentRoot,
|
|
292
|
+
definition: agentDefinition,
|
|
293
|
+
mode: "prod",
|
|
294
|
+
sessions: false,
|
|
295
|
+
});
|
|
296
|
+
return appPromise;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export default fromNodeMiddleware(async (req, res) => {
|
|
300
|
+
const app = await plasmApp();
|
|
301
|
+
await new Promise<void>((resolve, reject) => {
|
|
302
|
+
res.once("finish", () => resolve());
|
|
303
|
+
res.once("error", reject);
|
|
304
|
+
void vercelPlasmHandler(app)(req, res).catch(reject);
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
`;
|
|
308
|
+
|
|
252
309
|
const PACKAGE_JSON_TEMPLATE = {
|
|
253
310
|
name: "my-plasm-agent",
|
|
254
311
|
private: true,
|
|
255
312
|
type: "module",
|
|
256
313
|
scripts: {
|
|
257
314
|
dev: "plasm-agent dev",
|
|
315
|
+
"dev:interactive": "plasm-agent dev --interactive",
|
|
258
316
|
build: "plasm-agent build",
|
|
259
317
|
"vercel-build": "plasm-agent build",
|
|
260
318
|
info: "plasm-agent info",
|
|
261
319
|
deploy: "vercel deploy",
|
|
262
320
|
},
|
|
263
321
|
dependencies: {} as Record<string, string>,
|
|
322
|
+
devDependencies: {
|
|
323
|
+
nitropack: "^2.13.4",
|
|
324
|
+
},
|
|
264
325
|
};
|
|
265
326
|
|
|
266
327
|
async function writeVercelScaffold(
|
|
@@ -279,6 +340,16 @@ async function writeVercelScaffold(
|
|
|
279
340
|
);
|
|
280
341
|
}
|
|
281
342
|
|
|
343
|
+
async function writeNitroScaffold(projectRoot: string): Promise<void> {
|
|
344
|
+
await mkdir(path.join(projectRoot, "routes"), { recursive: true });
|
|
345
|
+
await writeFile(path.join(projectRoot, "nitro.config.ts"), NITRO_CONFIG_TS, "utf8");
|
|
346
|
+
await writeFile(
|
|
347
|
+
path.join(projectRoot, "routes", "[...path].ts"),
|
|
348
|
+
NITRO_CATCHALL_ROUTE_TS,
|
|
349
|
+
"utf8",
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
|
|
282
353
|
function packageJsonScaffold(): Record<string, unknown> {
|
|
283
354
|
return {
|
|
284
355
|
...PACKAGE_JSON_TEMPLATE,
|
|
@@ -335,6 +406,7 @@ export async function runPlasmInit(
|
|
|
335
406
|
}
|
|
336
407
|
|
|
337
408
|
await writeVercelScaffold(projectRoot);
|
|
409
|
+
await writeNitroScaffold(projectRoot);
|
|
338
410
|
|
|
339
411
|
return { projectRoot, agentRoot };
|
|
340
412
|
}
|
|
@@ -353,7 +425,8 @@ export function formatInitSuccess(
|
|
|
353
425
|
" plasm-agent link # pull AI_GATEWAY_API_KEY from Vercel",
|
|
354
426
|
" plasm-agent build",
|
|
355
427
|
" npm run smoke:channel",
|
|
356
|
-
" plasm-agent dev #
|
|
428
|
+
" plasm-agent dev # Nitro dev server (Vercel routing parity)",
|
|
429
|
+
" plasm-agent dev --interactive # optional TUI + sessions + hot reload",
|
|
357
430
|
" vercel deploy # production (channels + cron)",
|
|
358
431
|
].join("\n");
|
|
359
432
|
}
|
|
@@ -366,7 +439,8 @@ export function formatInitSuccess(
|
|
|
366
439
|
" npm install",
|
|
367
440
|
" plasm-agent link # pull AI_GATEWAY_API_KEY from Vercel",
|
|
368
441
|
" plasm-agent build # generate CGS stubs + discovery manifest",
|
|
369
|
-
" plasm-agent dev #
|
|
442
|
+
" plasm-agent dev # Nitro dev server (Vercel routing parity)",
|
|
443
|
+
" plasm-agent dev --interactive # optional TUI + sessions",
|
|
370
444
|
" vercel deploy # production on Vercel",
|
|
371
445
|
].join("\n");
|
|
372
446
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import type { ResolvedAgentProject } from "./project-root.js";
|
|
7
|
+
|
|
8
|
+
function plasmAgentPackageRoot(): string {
|
|
9
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function assertNitroScaffold(projectRoot: string): Promise<void> {
|
|
13
|
+
const required = [
|
|
14
|
+
path.join(projectRoot, "nitro.config.ts"),
|
|
15
|
+
path.join(projectRoot, "routes", "[...path].ts"),
|
|
16
|
+
];
|
|
17
|
+
for (const filePath of required) {
|
|
18
|
+
try {
|
|
19
|
+
await access(filePath);
|
|
20
|
+
} catch {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Missing ${path.relative(projectRoot, filePath)} — run \`plasm-agent init\` to scaffold Nitro dev`,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function nitroBin(projectRoot: string): string {
|
|
29
|
+
return path.join(projectRoot, "node_modules", ".bin", "nitro");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function startNitroDevForProject(project: ResolvedAgentProject): Promise<void> {
|
|
33
|
+
await assertNitroScaffold(project.projectRoot);
|
|
34
|
+
|
|
35
|
+
const loader = path.join(plasmAgentPackageRoot(), "scripts", "register-plasm-loader.mjs");
|
|
36
|
+
const nodeOptions = [
|
|
37
|
+
"--experimental-strip-types",
|
|
38
|
+
"--experimental-transform-types",
|
|
39
|
+
`--import=${loader}`,
|
|
40
|
+
].join(" ");
|
|
41
|
+
|
|
42
|
+
const bin = nitroBin(project.projectRoot);
|
|
43
|
+
try {
|
|
44
|
+
await access(bin);
|
|
45
|
+
} catch {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"nitropack is not installed — run `npm install` in the project root (init adds nitropack as a devDependency)",
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const child: ChildProcess = spawn(bin, ["dev"], {
|
|
52
|
+
cwd: project.projectRoot,
|
|
53
|
+
env: {
|
|
54
|
+
...process.env,
|
|
55
|
+
NODE_OPTIONS: nodeOptions,
|
|
56
|
+
},
|
|
57
|
+
stdio: "inherit",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const wait = new Promise<number>((resolve, reject) => {
|
|
61
|
+
child.on("error", reject);
|
|
62
|
+
child.on("close", (code) => resolve(code ?? 0));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
|
66
|
+
process.on(signal, () => {
|
|
67
|
+
child.kill(signal);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const exitCode = await wait;
|
|
72
|
+
process.exit(exitCode);
|
|
73
|
+
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { createDefaultHostTransport } from "./host-transport.js";
|
|
2
|
+
import { connectAuthOptionsForEntry } from "./connect-auth.js";
|
|
2
3
|
import type { HostTransportFn } from "./napi-binding.js";
|
|
3
4
|
|
|
5
|
+
/** Outbound HTTP for CGS stub live execute: Connect only when configured for the catalog. */
|
|
6
|
+
export function createStubHostTransport(entryId?: string): HostTransportFn {
|
|
7
|
+
const useConnect = connectAuthOptionsForEntry(entryId)?.connector != null;
|
|
8
|
+
return createDefaultHostTransport({ useConnect });
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
/** Production host transport: env bearer → Vercel Connect `getToken()` → fetch. */
|
|
5
12
|
export function createProductionHostTransport(): HostTransportFn {
|
|
6
13
|
return createDefaultHostTransport({ useConnect: true });
|
package/src/index.ts
CHANGED
|
@@ -130,7 +130,7 @@ export type {
|
|
|
130
130
|
export { StubPlasmEngine, NapiPlasmEngine, createEngine, isNativeEngineAvailable } from "./engine/napi-binding.js";
|
|
131
131
|
export { createDefaultHostTransport } from "./engine/host-transport.js";
|
|
132
132
|
export type { HostTransportOptions } from "./engine/host-transport.js";
|
|
133
|
-
export { createProductionHostTransport } from "./engine/create-host-transport.js";
|
|
133
|
+
export { createProductionHostTransport, createStubHostTransport } from "./engine/create-host-transport.js";
|
|
134
134
|
export { loadAgentEnv } from "./load-env.js";
|
|
135
135
|
export {
|
|
136
136
|
connectorUidForEntry,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HostTransportFn } from "../engine/napi-binding.js";
|
|
2
|
-
import {
|
|
2
|
+
import { createStubHostTransport } from "../engine/create-host-transport.js";
|
|
3
3
|
import {
|
|
4
4
|
createEngine,
|
|
5
5
|
type DryRunResult,
|
|
@@ -143,7 +143,7 @@ export async function executeRows<TRow>(
|
|
|
143
143
|
options?.transport ??
|
|
144
144
|
(process.env.PLASM_STUB_USE_MOCK_TRANSPORT === "1"
|
|
145
145
|
? createFixtureMockTransport()
|
|
146
|
-
:
|
|
146
|
+
: createStubHostTransport(builder.entryId));
|
|
147
147
|
|
|
148
148
|
if (typeof bound.run !== "function") {
|
|
149
149
|
throw new Error("executeRows: program builder missing run()");
|
|
@@ -1,58 +1,59 @@
|
|
|
1
|
-
import { tool } from "ai";
|
|
1
|
+
import { tool, type ToolSet } from "ai";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import type { SkillDefinition } from "../authoring/define-skill.js";
|
|
5
5
|
import type { SubagentRegistry } from "../authoring/subagent-loader.js";
|
|
6
|
+
import { toolInput } from "./tool-input.js";
|
|
7
|
+
|
|
8
|
+
const readSkillInputSchema = z.object({
|
|
9
|
+
name: z.string().min(1).describe("Skill name from the index"),
|
|
10
|
+
});
|
|
6
11
|
|
|
7
12
|
export function createHarnessTools(options: {
|
|
8
13
|
skills?: SkillDefinition[];
|
|
9
14
|
subagents?: SubagentRegistry;
|
|
10
|
-
}) {
|
|
15
|
+
}): ToolSet {
|
|
16
|
+
const tools: ToolSet = {};
|
|
11
17
|
const skillByName = new Map((options.skills ?? []).map((s) => [s.name, s]));
|
|
12
18
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
execute: async ({ name }: { name: string }) => {
|
|
23
|
-
const skill = skillByName.get(name);
|
|
24
|
-
if (!skill) {
|
|
25
|
-
return `Unknown skill "${name}". Available: ${[...skillByName.keys()].join(", ")}`;
|
|
26
|
-
}
|
|
27
|
-
return skill.body.trim();
|
|
28
|
-
},
|
|
29
|
-
}),
|
|
19
|
+
if (skillByName.size > 0) {
|
|
20
|
+
tools.read_skill = tool({
|
|
21
|
+
description:
|
|
22
|
+
"Load full text for a filesystem skill by name. Use when the skill index in the system prompt is not enough.",
|
|
23
|
+
inputSchema: toolInput(readSkillInputSchema),
|
|
24
|
+
execute: async ({ name }: { name: string }) => {
|
|
25
|
+
const skill = skillByName.get(name);
|
|
26
|
+
if (!skill) {
|
|
27
|
+
return `Unknown skill "${name}". Available: ${[...skillByName.keys()].join(", ")}`;
|
|
30
28
|
}
|
|
31
|
-
|
|
29
|
+
return skill.body.trim();
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
32
33
|
|
|
33
34
|
const subagentNames = options.subagents?.list().map((s) => s.name) ?? [];
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
35
|
+
if (subagentNames.length > 0 && options.subagents) {
|
|
36
|
+
const subagents = options.subagents;
|
|
37
|
+
tools.delegate_subagent = tool({
|
|
38
|
+
description:
|
|
39
|
+
"Delegate a sub-task to a filesystem-isolated child agent. Each subagent has its own catalogs and session scope.",
|
|
40
|
+
inputSchema: toolInput(
|
|
41
|
+
z.object({
|
|
42
|
+
name: z
|
|
43
|
+
.string()
|
|
44
|
+
.min(1)
|
|
45
|
+
.describe(`Subagent name. One of: ${subagentNames.join(", ")}`),
|
|
46
|
+
message: z.string().min(1).describe("User message for the child agent turn"),
|
|
47
|
+
}),
|
|
48
|
+
),
|
|
49
|
+
execute: async ({ name, message }: { name: string; message: string }) => {
|
|
50
|
+
const result = await subagents.delegate(name, message);
|
|
51
|
+
return `${result.text}\n\n(steps: ${result.steps})`;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
54
55
|
|
|
55
|
-
return
|
|
56
|
+
return tools;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
export function renderSkillIndex(skills: SkillDefinition[]): string {
|
package/src/tools/plasm-tools.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { tool } from "ai";
|
|
1
|
+
import { tool, type ToolSet } from "ai";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
|
|
4
4
|
import type { AgentRuntime } from "../runtime/agent-runtime.js";
|
|
@@ -8,89 +8,98 @@ import {
|
|
|
8
8
|
PLASM_RUN_TOOL_DESCRIPTION,
|
|
9
9
|
PLASM_TOOL_DESCRIPTION,
|
|
10
10
|
} from "./descriptions.js";
|
|
11
|
+
import { toolInput } from "./tool-input.js";
|
|
11
12
|
|
|
12
13
|
const seedSchema = z.object({
|
|
13
14
|
api: z.string().describe("Registry entry_id / catalog api"),
|
|
14
15
|
entity: z.string().describe("CGS entity name from discovery or prior knowledge"),
|
|
15
16
|
});
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
const discoverInputSchema = z.object({
|
|
19
|
+
intent: z
|
|
20
|
+
.string()
|
|
21
|
+
.min(1)
|
|
22
|
+
.describe(
|
|
23
|
+
"One plain-language task description for the whole user goal. Returns catalog api/entity picks — not program symbols.",
|
|
24
|
+
),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const plasmContextInputSchema = z.object({
|
|
28
|
+
intent: z
|
|
29
|
+
.string()
|
|
30
|
+
.min(1)
|
|
31
|
+
.describe(
|
|
32
|
+
"Stable string for one user goal (same value every turn for that goal — do not rotate per message).",
|
|
33
|
+
),
|
|
34
|
+
seeds: z
|
|
35
|
+
.array(seedSchema)
|
|
36
|
+
.min(1)
|
|
37
|
+
.describe("Non-empty array of {api, entity} capability picks"),
|
|
38
|
+
ranked_capabilities: z
|
|
39
|
+
.array(z.string())
|
|
40
|
+
.nullable()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe(
|
|
43
|
+
"Optional capability wire names from discover_capabilities. Omit on expand; null or [] clears.",
|
|
44
|
+
),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const plasmInputSchema = z.object({
|
|
48
|
+
logical_session_ref: z
|
|
49
|
+
.string()
|
|
50
|
+
.describe("Same logical_session_ref returned by plasm_context"),
|
|
51
|
+
program: z
|
|
52
|
+
.string()
|
|
53
|
+
.min(1)
|
|
54
|
+
.describe("Plasm source program using e#/m#/p#/r# from the session teaching TSV"),
|
|
55
|
+
reasoning: z
|
|
56
|
+
.string()
|
|
57
|
+
.optional()
|
|
58
|
+
.describe("Optional short note explaining the intent of this call"),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const plasmRunInputSchema = z.object({
|
|
62
|
+
logical_session_ref: z
|
|
63
|
+
.string()
|
|
64
|
+
.describe("Same logical_session_ref returned by plasm_context"),
|
|
65
|
+
plan_commit_ref: z
|
|
66
|
+
.string()
|
|
67
|
+
.describe("Executable plan token (pcN) from a prior plasm dry-run"),
|
|
68
|
+
reasoning: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe("Optional short note explaining the intent of this call"),
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
export function createPlasmTools(runtime: AgentRuntime): ToolSet {
|
|
18
75
|
return {
|
|
19
76
|
discover_capabilities: tool({
|
|
20
77
|
description: DISCOVER_TOOL_DESCRIPTION,
|
|
21
|
-
inputSchema:
|
|
22
|
-
intent: z
|
|
23
|
-
.string()
|
|
24
|
-
.min(1)
|
|
25
|
-
.describe(
|
|
26
|
-
"One plain-language task description for the whole user goal. Returns catalog api/entity picks — not program symbols.",
|
|
27
|
-
),
|
|
28
|
-
}),
|
|
78
|
+
inputSchema: toolInput(discoverInputSchema),
|
|
29
79
|
execute: async ({ intent }) => runtime.discoverCapabilities({ intent }),
|
|
30
80
|
}),
|
|
31
81
|
|
|
32
82
|
plasm_context: tool({
|
|
33
83
|
description: PLASM_CONTEXT_TOOL_DESCRIPTION,
|
|
34
|
-
inputSchema:
|
|
35
|
-
intent: z
|
|
36
|
-
.string()
|
|
37
|
-
.min(1)
|
|
38
|
-
.describe(
|
|
39
|
-
"Stable string for one user goal (same value every turn for that goal — do not rotate per message).",
|
|
40
|
-
),
|
|
41
|
-
seeds: z
|
|
42
|
-
.array(seedSchema)
|
|
43
|
-
.min(1)
|
|
44
|
-
.describe("Non-empty array of {api, entity} capability picks"),
|
|
45
|
-
ranked_capabilities: z
|
|
46
|
-
.array(z.string())
|
|
47
|
-
.nullable()
|
|
48
|
-
.optional()
|
|
49
|
-
.describe(
|
|
50
|
-
"Optional capability wire names from discover_capabilities. Omit on expand; null or [] clears.",
|
|
51
|
-
),
|
|
52
|
-
}),
|
|
84
|
+
inputSchema: toolInput(plasmContextInputSchema),
|
|
53
85
|
execute: async ({ intent, seeds, ranked_capabilities }) =>
|
|
54
86
|
runtime.plasmContext({
|
|
55
87
|
intent,
|
|
56
|
-
seeds
|
|
88
|
+
seeds: seeds as Array<{ api: string; entity: string }>,
|
|
57
89
|
rankedCapabilities: ranked_capabilities,
|
|
58
90
|
}),
|
|
59
91
|
}),
|
|
60
92
|
|
|
61
93
|
plasm: tool({
|
|
62
94
|
description: PLASM_TOOL_DESCRIPTION,
|
|
63
|
-
inputSchema:
|
|
64
|
-
logical_session_ref: z
|
|
65
|
-
.string()
|
|
66
|
-
.describe("Same logical_session_ref returned by plasm_context"),
|
|
67
|
-
program: z
|
|
68
|
-
.string()
|
|
69
|
-
.min(1)
|
|
70
|
-
.describe("Plasm source program using e#/m#/p#/r# from the session teaching TSV"),
|
|
71
|
-
reasoning: z
|
|
72
|
-
.string()
|
|
73
|
-
.optional()
|
|
74
|
-
.describe("Optional short note explaining the intent of this call"),
|
|
75
|
-
}),
|
|
95
|
+
inputSchema: toolInput(plasmInputSchema),
|
|
76
96
|
execute: async ({ logical_session_ref, program, reasoning }) =>
|
|
77
97
|
runtime.plasm({ logicalSessionRef: logical_session_ref, program, reasoning }),
|
|
78
98
|
}),
|
|
79
99
|
|
|
80
100
|
plasm_run: tool({
|
|
81
101
|
description: PLASM_RUN_TOOL_DESCRIPTION,
|
|
82
|
-
inputSchema:
|
|
83
|
-
logical_session_ref: z
|
|
84
|
-
.string()
|
|
85
|
-
.describe("Same logical_session_ref returned by plasm_context"),
|
|
86
|
-
plan_commit_ref: z
|
|
87
|
-
.string()
|
|
88
|
-
.describe("Executable plan token (pcN) from a prior plasm dry-run"),
|
|
89
|
-
reasoning: z
|
|
90
|
-
.string()
|
|
91
|
-
.optional()
|
|
92
|
-
.describe("Optional short note explaining the intent of this call"),
|
|
93
|
-
}),
|
|
102
|
+
inputSchema: toolInput(plasmRunInputSchema),
|
|
94
103
|
execute: async ({ logical_session_ref, plan_commit_ref, reasoning }) =>
|
|
95
104
|
runtime.plasmRun({
|
|
96
105
|
logicalSessionRef: logical_session_ref,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { zodSchema, type FlexibleSchema } from "ai";
|
|
2
|
+
import type { z } from "zod";
|
|
3
|
+
|
|
4
|
+
/** Wrap Zod for AI SDK `tool()` — avoids TS2589 deep instantiation during Vercel typecheck. */
|
|
5
|
+
export function toolInput<T extends z.ZodTypeAny>(
|
|
6
|
+
schema: T,
|
|
7
|
+
): FlexibleSchema<z.infer<T>> {
|
|
8
|
+
return zodSchema(schema);
|
|
9
|
+
}
|