@plasm_lang/vercel-agent 0.3.82 → 0.3.84
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 +1 -1
- package/agent/hooks/trace-log.ts +1 -1
- package/agent/instructions.md +1 -1
- package/package.json +8 -4
- package/scripts/plasm-cli.ts +6 -0
- package/src/archive/paths.ts +4 -1
- package/src/authoring/slot-loader.ts +57 -4
- package/src/cli/build.ts +41 -2
- package/src/cli/compile-authored-slots.ts +79 -0
- package/src/cli/init-scaffold.ts +5 -6
- package/src/cli/init.ts +3 -1
- package/src/cli/nitro-dev.ts +23 -62
- package/src/gateway-model.ts +17 -6
- package/src/index.ts +5 -0
- package/src/instrumentation.ts +1 -1
- package/src/nitro/agent-summary.ts +133 -0
- package/src/nitro/build-application.ts +98 -0
- package/src/nitro/build-nitro-output.ts +14 -0
- package/src/nitro/copy-vercel-function-assets.ts +31 -0
- package/src/nitro/create-plasm-nitro.ts +111 -0
- package/src/nitro/patch-vercel-config.ts +37 -0
- package/src/nitro/paths.ts +30 -0
- package/src/nitro/prepare-host.ts +69 -0
- package/src/nitro/vercel-build-output-config.ts +18 -0
- package/src/nitro/workflow-directives.ts +43 -0
- package/src/nitro/write-nitro-entry.ts +87 -0
- package/src/package-version.ts +3 -11
- package/src/runtime/agent-runtime.ts +13 -12
- package/src/runtime/compaction.ts +1 -1
- package/src/server/plasm-handler.ts +22 -2
- package/src/telemetry/plasm-spans.ts +3 -3
- package/src/tools/descriptions.ts +29 -42
- package/src/tools/format.ts +2 -2
- package/src/tools/plasm-tools.ts +4 -4
- package/src/workflow/world-bootstrap.ts +12 -1
- package/templates/mcp-radar/agent/instrumentation.ts +18 -0
- package/templates/mcp-radar/lib/proof-store.ts +10 -3
- package/templates/mcp-radar/vercel.json +2 -13
- package/templates/scaffold/agent/instrumentation.ts +18 -0
- package/templates/scaffold/vercel.default.json +2 -2
- package/templates/scaffold/vercel.mcp-radar.json +2 -13
- package/templates/mcp-radar/api/[[...path]].ts +0 -23
- package/templates/mcp-radar/nitro.config.ts +0 -17
- package/templates/mcp-radar/routes/[...path].ts +0 -29
- package/templates/scaffold/api/[[...path]].ts +0 -23
- package/templates/scaffold/nitro.config.ts +0 -17
- package/templates/scaffold/routes/[...path].ts +0 -29
package/README.md
CHANGED
|
@@ -154,7 +154,7 @@ npm run agent -- "your prompt" # requires AI_GATEWAY_API_KEY (Vercel AI Gatewa
|
|
|
154
154
|
|
|
155
155
|
### Dev server (`plasm-agent dev`)
|
|
156
156
|
|
|
157
|
-
**Default:**
|
|
157
|
+
**Default:** `plasm-agent dev` runs programmatic **Nitro v3** (same host as Vercel). `plasm-agent build` emits **`.vercel/output`** on Vercel (Build Output API) or **`.plasm/nitro-output`** locally, plus **`.plasm/agent-summary.json`** for Agent Observability.
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
160
|
plasm-agent dev # Nitro (default)
|
package/agent/hooks/trace-log.ts
CHANGED
|
@@ -4,7 +4,7 @@ export default defineHook({
|
|
|
4
4
|
name: "trace-log",
|
|
5
5
|
on: ["run:complete", "plan:commit"],
|
|
6
6
|
handler: (_ctx, detail) => {
|
|
7
|
-
const event = detail?.
|
|
7
|
+
const event = detail?.runRef ? "plan:commit" : "run:complete";
|
|
8
8
|
console.log(`[plasm:hook:trace-log] ${event}`, detail ?? {});
|
|
9
9
|
},
|
|
10
10
|
});
|
package/agent/instructions.md
CHANGED
|
@@ -6,7 +6,7 @@ You operate external APIs through **Plasm catalogs**, not ad-hoc REST tools.
|
|
|
6
6
|
|
|
7
7
|
1. **`discover_capabilities`** — when you do not know which `api` / `entity` to use (optional).
|
|
8
8
|
2. **`plasm_context`** — open or extend a session with `{api, entity}` seeds. Returns **`logical_session_ref`** + teaching TSV (`e#`, `m#`, `p#`, `r#`).
|
|
9
|
-
3. **`plasm`** — dry-run a Plasm **program** using symbols from the teaching TSV. Returns **`
|
|
9
|
+
3. **`plasm`** — dry-run a Plasm **program** using symbols from the teaching TSV. Returns **`run_ref`** (`pcN`).
|
|
10
10
|
4. **`plasm_run`** — live execute the reviewed plan (`pcN` only — never resend the program).
|
|
11
11
|
|
|
12
12
|
## Session discipline
|
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.84",
|
|
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": {
|
|
@@ -63,17 +63,21 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@ai-sdk/otel": "^1.0.3",
|
|
65
65
|
"@opentelemetry/api": "^1.9.0",
|
|
66
|
-
"@plasm_lang/engine": "^0.3.
|
|
66
|
+
"@plasm_lang/engine": "^0.3.84",
|
|
67
67
|
"@vercel/blob": "^0.27.3",
|
|
68
68
|
"@vercel/connect": "^0.2.6",
|
|
69
69
|
"@vercel/kv": "^3.0.0",
|
|
70
|
+
"@vercel/otel": "^1.14.2",
|
|
71
|
+
"@workflow/nitro": "^4.1.1",
|
|
70
72
|
"@workflow/world-postgres": "^4.2.0",
|
|
71
73
|
"ai": "^6.0.0",
|
|
74
|
+
"esbuild": "^0.25.12",
|
|
72
75
|
"js-yaml": "^4.1.0",
|
|
76
|
+
"nitro": "^3.0.260610-beta",
|
|
73
77
|
"pg": "^8.22.0",
|
|
78
|
+
"tsx": "^4.19.4",
|
|
74
79
|
"workflow": "^4.5.0",
|
|
75
|
-
"zod": "^3.25.76"
|
|
76
|
-
"tsx": "^4.19.4"
|
|
80
|
+
"zod": "^3.25.76"
|
|
77
81
|
},
|
|
78
82
|
"devDependencies": {
|
|
79
83
|
"@types/js-yaml": "^4.0.9",
|
package/scripts/plasm-cli.ts
CHANGED
|
@@ -85,6 +85,12 @@ async function cmdBuild(): Promise<void> {
|
|
|
85
85
|
const result = await runPlasmBuild(project);
|
|
86
86
|
console.log(`Built ${result.stubs.length} stub(s)`);
|
|
87
87
|
console.log(`manifest: ${result.manifestPath}`);
|
|
88
|
+
if (result.outputDir) {
|
|
89
|
+
console.log(`nitro output: ${result.outputDir}${result.vercelOutput ? " (vercel build output)" : ""}`);
|
|
90
|
+
}
|
|
91
|
+
if (result.agentSummaryPath) {
|
|
92
|
+
console.log(`agent summary: ${result.agentSummaryPath}`);
|
|
93
|
+
}
|
|
88
94
|
for (const stub of result.stubs) {
|
|
89
95
|
console.log(` - ${stub.entryId} → ${path.relative(project.projectRoot, stub.outPath)}`);
|
|
90
96
|
}
|
package/src/archive/paths.ts
CHANGED
|
@@ -4,7 +4,10 @@ import type { ArchivePaths } from "./types.js";
|
|
|
4
4
|
|
|
5
5
|
/** Resolve local trace/run archive roots from env or agent defaults. */
|
|
6
6
|
export function resolveArchivePaths(agentRoot: string): ArchivePaths {
|
|
7
|
-
const archivesDir =
|
|
7
|
+
const archivesDir =
|
|
8
|
+
process.env.VERCEL === "1"
|
|
9
|
+
? path.join("/tmp", "plasm-archives", path.basename(path.resolve(agentRoot)))
|
|
10
|
+
: path.join(agentRoot, ".plasm", "archives");
|
|
8
11
|
const traceRoot =
|
|
9
12
|
process.env.PLASM_TRACE_ARCHIVE_DIR?.trim() ||
|
|
10
13
|
path.join(archivesDir, "traces");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
1
|
+
import { access, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
|
|
@@ -45,6 +45,10 @@ export interface LoadedProjectSlots {
|
|
|
45
45
|
export interface LoadAuthoredSlotsOptions {
|
|
46
46
|
discovery: ProjectDiscovery;
|
|
47
47
|
importCacheBust?: number;
|
|
48
|
+
agentRoot?: string;
|
|
49
|
+
projectRoot?: string;
|
|
50
|
+
/** agentRoot-relative source → projectRoot-relative compiled `.mjs` from build manifest. */
|
|
51
|
+
compiledSlots?: Record<string, string>;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
type SlotName = "channels" | "schedules" | "hooks" | "skills";
|
|
@@ -58,8 +62,52 @@ function slotDiagnostic(
|
|
|
58
62
|
return { level, slot, path: filePath, message };
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
async function
|
|
62
|
-
|
|
65
|
+
async function pathExists(p: string): Promise<boolean> {
|
|
66
|
+
try {
|
|
67
|
+
await access(p);
|
|
68
|
+
return true;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function resolveSlotImportPath(
|
|
75
|
+
filePath: string,
|
|
76
|
+
options: LoadAuthoredSlotsOptions,
|
|
77
|
+
): Promise<string> {
|
|
78
|
+
const agentRoot = path.resolve(options.agentRoot ?? options.discovery.agentRoot);
|
|
79
|
+
const relFromAgent = path.relative(agentRoot, filePath);
|
|
80
|
+
const fromManifest = options.compiledSlots?.[relFromAgent];
|
|
81
|
+
if (fromManifest && options.projectRoot) {
|
|
82
|
+
const compiled = path.join(options.projectRoot, fromManifest);
|
|
83
|
+
if (await pathExists(compiled)) return compiled;
|
|
84
|
+
}
|
|
85
|
+
const mirror = path.join(
|
|
86
|
+
agentRoot,
|
|
87
|
+
".plasm",
|
|
88
|
+
"compiled",
|
|
89
|
+
relFromAgent.replace(/\.ts$/, ".mjs"),
|
|
90
|
+
);
|
|
91
|
+
if (await pathExists(mirror)) return mirror;
|
|
92
|
+
return filePath;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function importSlotModule(
|
|
96
|
+
filePath: string,
|
|
97
|
+
cacheBust: number,
|
|
98
|
+
options: LoadAuthoredSlotsOptions,
|
|
99
|
+
): Promise<unknown> {
|
|
100
|
+
const agentRoot = path.resolve(options.agentRoot ?? options.discovery.agentRoot);
|
|
101
|
+
const relFromAgent = path.relative(agentRoot, filePath);
|
|
102
|
+
const preloaded = (
|
|
103
|
+
globalThis as typeof globalThis & { __PLASM_PRELOADED_SLOTS?: Record<string, unknown> }
|
|
104
|
+
).__PLASM_PRELOADED_SLOTS?.[relFromAgent];
|
|
105
|
+
if (preloaded !== undefined) {
|
|
106
|
+
return (preloaded as { default?: unknown }).default ?? preloaded;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const resolved = await resolveSlotImportPath(filePath, options);
|
|
110
|
+
const url = `${pathToFileURL(resolved).href}?t=${cacheBust}`;
|
|
63
111
|
const mod = await import(url);
|
|
64
112
|
return mod.default ?? mod;
|
|
65
113
|
}
|
|
@@ -85,9 +133,10 @@ async function loadTypescriptSlot<T extends ChannelDefinition | ScheduleDefiniti
|
|
|
85
133
|
slot: SlotName,
|
|
86
134
|
expected: "channel" | "schedule" | "hook" | "skill",
|
|
87
135
|
cacheBust: number,
|
|
136
|
+
options: LoadAuthoredSlotsOptions,
|
|
88
137
|
): Promise<{ definition: T } | { diagnostic: DiscoveryDiagnostic }> {
|
|
89
138
|
try {
|
|
90
|
-
const exported = await importSlotModule(filePath, cacheBust);
|
|
139
|
+
const exported = await importSlotModule(filePath, cacheBust, options);
|
|
91
140
|
const actual = classifySlotExport(exported);
|
|
92
141
|
if (actual !== expected) {
|
|
93
142
|
const helper =
|
|
@@ -147,6 +196,7 @@ export async function loadAuthoredSlots(
|
|
|
147
196
|
"channels",
|
|
148
197
|
EXPECTED_KIND.channels,
|
|
149
198
|
cacheBust,
|
|
199
|
+
options,
|
|
150
200
|
);
|
|
151
201
|
if ("diagnostic" in result) {
|
|
152
202
|
diagnostics.push(result.diagnostic);
|
|
@@ -163,6 +213,7 @@ export async function loadAuthoredSlots(
|
|
|
163
213
|
"schedules",
|
|
164
214
|
EXPECTED_KIND.schedules,
|
|
165
215
|
cacheBust,
|
|
216
|
+
options,
|
|
166
217
|
);
|
|
167
218
|
if ("diagnostic" in result) {
|
|
168
219
|
diagnostics.push(result.diagnostic);
|
|
@@ -179,6 +230,7 @@ export async function loadAuthoredSlots(
|
|
|
179
230
|
"hooks",
|
|
180
231
|
EXPECTED_KIND.hooks,
|
|
181
232
|
cacheBust,
|
|
233
|
+
options,
|
|
182
234
|
);
|
|
183
235
|
if ("diagnostic" in result) {
|
|
184
236
|
diagnostics.push(result.diagnostic);
|
|
@@ -198,6 +250,7 @@ export async function loadAuthoredSlots(
|
|
|
198
250
|
"skills",
|
|
199
251
|
"skill",
|
|
200
252
|
cacheBust,
|
|
253
|
+
options,
|
|
201
254
|
);
|
|
202
255
|
if ("diagnostic" in result) {
|
|
203
256
|
diagnostics.push(result.diagnostic);
|
package/src/cli/build.ts
CHANGED
|
@@ -1,21 +1,33 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, writeFile, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
+
import { compileAuthoredSlots } from "./compile-authored-slots.js";
|
|
5
|
+
import type { ResolvedAgentProject } from "./project-root.js";
|
|
4
6
|
import { isNativeEngineAvailable } from "../engine/napi-binding.js";
|
|
5
7
|
import { walkAgentProject } from "../discovery/project-walker.js";
|
|
6
8
|
import { generateAllStubs } from "../stubs/generator.js";
|
|
7
|
-
import
|
|
9
|
+
import { buildPlasmApplication } from "../nitro/build-application.js";
|
|
10
|
+
import { readPackageName } from "./project-root.js";
|
|
8
11
|
|
|
9
12
|
export interface PlasmBuildResult {
|
|
10
13
|
stubsDir: string;
|
|
11
14
|
discoveryDir: string;
|
|
12
15
|
manifestPath: string;
|
|
13
16
|
stubs: Array<{ entryId: string; catalogCgsHash: string; outPath: string }>;
|
|
17
|
+
outputDir?: string;
|
|
18
|
+
agentSummaryPath?: string;
|
|
19
|
+
vercelOutput?: boolean;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
export async function runPlasmBuild(project: ResolvedAgentProject): Promise<PlasmBuildResult> {
|
|
17
23
|
const stubs = await generateAllStubs(project.agentRoot);
|
|
18
24
|
const discovery = await walkAgentProject(project.agentRoot);
|
|
25
|
+
const { compiledSlots } = await compileAuthoredSlots(
|
|
26
|
+
project.projectRoot,
|
|
27
|
+
project.agentRoot,
|
|
28
|
+
discovery,
|
|
29
|
+
);
|
|
30
|
+
|
|
19
31
|
const discoveryDir = path.join(project.agentRoot, ".plasm", "discovery");
|
|
20
32
|
await mkdir(discoveryDir, { recursive: true });
|
|
21
33
|
const manifestPath = path.join(discoveryDir, "manifest.json");
|
|
@@ -24,6 +36,7 @@ export async function runPlasmBuild(project: ResolvedAgentProject): Promise<Plas
|
|
|
24
36
|
builtAt: new Date().toISOString(),
|
|
25
37
|
projectRoot: project.projectRoot,
|
|
26
38
|
agentRoot: project.agentRoot,
|
|
39
|
+
compiledSlots,
|
|
27
40
|
stubs: stubs.map((s) => ({
|
|
28
41
|
entryId: s.entryId,
|
|
29
42
|
catalogCgsHash: s.catalogCgsHash,
|
|
@@ -45,10 +58,36 @@ export async function runPlasmBuild(project: ResolvedAgentProject): Promise<Plas
|
|
|
45
58
|
},
|
|
46
59
|
};
|
|
47
60
|
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
61
|
+
|
|
62
|
+
const packageName = await readPackageName(project.projectRoot);
|
|
63
|
+
const appBuild = await buildPlasmApplication({
|
|
64
|
+
projectRoot: project.projectRoot,
|
|
65
|
+
agentRoot: project.agentRoot,
|
|
66
|
+
discovery,
|
|
67
|
+
compiledSlots,
|
|
68
|
+
packageName: packageName ?? undefined,
|
|
69
|
+
});
|
|
70
|
+
|
|
48
71
|
return {
|
|
49
72
|
stubsDir: path.join(project.agentRoot, ".plasm", "stubs"),
|
|
50
73
|
discoveryDir,
|
|
51
74
|
manifestPath,
|
|
52
75
|
stubs,
|
|
76
|
+
outputDir: appBuild.outputDir,
|
|
77
|
+
agentSummaryPath: appBuild.agentSummaryPath,
|
|
78
|
+
vercelOutput: appBuild.vercelOutput,
|
|
53
79
|
};
|
|
54
80
|
}
|
|
81
|
+
|
|
82
|
+
export async function readBuildManifest(agentRoot: string): Promise<{
|
|
83
|
+
compiledSlots?: Record<string, string>;
|
|
84
|
+
projectRoot?: string;
|
|
85
|
+
} | null> {
|
|
86
|
+
const manifestPath = path.join(agentRoot, ".plasm", "discovery", "manifest.json");
|
|
87
|
+
try {
|
|
88
|
+
const raw = await readFile(manifestPath, "utf8");
|
|
89
|
+
return JSON.parse(raw) as { compiledSlots?: Record<string, string>; projectRoot?: string };
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import * as esbuild from "esbuild";
|
|
5
|
+
|
|
6
|
+
import type { ProjectDiscovery } from "../discovery/project-walker.js";
|
|
7
|
+
|
|
8
|
+
const ESBUILD_BANNER =
|
|
9
|
+
'import { createRequire as __createRequire } from "module"; const require = __createRequire(import.meta.url);';
|
|
10
|
+
|
|
11
|
+
export type CompiledSlotMap = Record<string, string>;
|
|
12
|
+
|
|
13
|
+
export interface CompileAuthoredSlotsResult {
|
|
14
|
+
/** agentRoot-relative source path → projectRoot-relative compiled `.mjs`. */
|
|
15
|
+
compiledSlots: CompiledSlotMap;
|
|
16
|
+
compiledDir: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function slotFiles(
|
|
20
|
+
discovery: ProjectDiscovery,
|
|
21
|
+
): Array<{ filePath: string }> {
|
|
22
|
+
const out: Array<{ filePath: string }> = [];
|
|
23
|
+
for (const file of discovery.channels) {
|
|
24
|
+
if (file.kind === "typescript") out.push({ filePath: file.path });
|
|
25
|
+
}
|
|
26
|
+
for (const file of discovery.schedules) {
|
|
27
|
+
if (file.kind === "typescript") out.push({ filePath: file.path });
|
|
28
|
+
}
|
|
29
|
+
for (const file of discovery.hooks) {
|
|
30
|
+
if (file.kind === "typescript") out.push({ filePath: file.path });
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Compile authored TS slots to `.plasm/compiled/` for Vercel runtime (no dynamic `.ts` import). */
|
|
36
|
+
export async function compileAuthoredSlots(
|
|
37
|
+
projectRoot: string,
|
|
38
|
+
agentRoot: string,
|
|
39
|
+
discovery: ProjectDiscovery,
|
|
40
|
+
): Promise<CompileAuthoredSlotsResult> {
|
|
41
|
+
const compiledRoot = path.join(agentRoot, ".plasm", "compiled");
|
|
42
|
+
await mkdir(compiledRoot, { recursive: true });
|
|
43
|
+
|
|
44
|
+
const compiledSlots: CompiledSlotMap = {};
|
|
45
|
+
|
|
46
|
+
await Promise.all(
|
|
47
|
+
slotFiles(discovery).map(async ({ filePath }) => {
|
|
48
|
+
const relFromAgent = path.relative(agentRoot, filePath);
|
|
49
|
+
const outFile = path.join(
|
|
50
|
+
agentRoot,
|
|
51
|
+
".plasm",
|
|
52
|
+
"compiled",
|
|
53
|
+
relFromAgent.replace(/\.ts$/, ".mjs"),
|
|
54
|
+
);
|
|
55
|
+
await mkdir(path.dirname(outFile), { recursive: true });
|
|
56
|
+
|
|
57
|
+
await esbuild.build({
|
|
58
|
+
entryPoints: [filePath],
|
|
59
|
+
bundle: true,
|
|
60
|
+
platform: "node",
|
|
61
|
+
target: "node22",
|
|
62
|
+
format: "esm",
|
|
63
|
+
outfile: outFile,
|
|
64
|
+
banner: { js: ESBUILD_BANNER },
|
|
65
|
+
logLevel: "silent",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
compiledSlots[relFromAgent] = path.relative(projectRoot, outFile);
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
await writeFile(
|
|
73
|
+
path.join(compiledRoot, "package.json"),
|
|
74
|
+
`${JSON.stringify({ type: "module" }, null, 2)}\n`,
|
|
75
|
+
"utf8",
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return { compiledSlots, compiledDir: compiledRoot };
|
|
79
|
+
}
|
package/src/cli/init-scaffold.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface InitOptions {
|
|
|
11
11
|
npm?: boolean;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const SKIP_TEMPLATE_DIRS = new Set(["node_modules", ".plasm", ".nitro", ".output"
|
|
14
|
+
const SKIP_TEMPLATE_DIRS = new Set(["node_modules", ".plasm", ".nitro", ".output"]);
|
|
15
15
|
const SKIP_TEMPLATE_FILES = new Set(["package-lock.json", "vercel-build.ts"]);
|
|
16
16
|
|
|
17
17
|
function plasmAgentPackageRoot(): string {
|
|
@@ -65,9 +65,7 @@ export async function writeDeployScaffold(
|
|
|
65
65
|
): Promise<void> {
|
|
66
66
|
const vercelJsonName =
|
|
67
67
|
template === "mcp-radar" ? "vercel.mcp-radar.json" : "vercel.default.json";
|
|
68
|
-
await copyScaffoldFile("
|
|
69
|
-
await copyScaffoldFile("routes/[...path].ts", projectRoot);
|
|
70
|
-
await copyScaffoldFile("nitro.config.ts", projectRoot);
|
|
68
|
+
await copyScaffoldFile("agent/instrumentation.ts", projectRoot);
|
|
71
69
|
await copyScaffoldFile(".vercelignore", projectRoot);
|
|
72
70
|
const publicIndex =
|
|
73
71
|
template === "mcp-radar" ? "public/index.mcp-radar.html" : "public/index.html";
|
|
@@ -119,6 +117,7 @@ export async function patchProjectPackageJson(
|
|
|
119
117
|
"@vercel/blob": "^0.27.3",
|
|
120
118
|
"@vercel/functions": "^3.4.3",
|
|
121
119
|
"@vercel/kv": "^3.0.0",
|
|
120
|
+
"@vercel/otel": "^1.5.0",
|
|
122
121
|
};
|
|
123
122
|
} else {
|
|
124
123
|
pkg.dependencies = {
|
|
@@ -128,6 +127,7 @@ export async function patchProjectPackageJson(
|
|
|
128
127
|
"@vercel/blob": "^0.27.3",
|
|
129
128
|
"@vercel/functions": "^3.4.3",
|
|
130
129
|
"@vercel/kv": "^3.0.0",
|
|
130
|
+
"@vercel/otel": "^1.5.0",
|
|
131
131
|
};
|
|
132
132
|
}
|
|
133
133
|
|
|
@@ -143,9 +143,9 @@ export async function patchProjectPackageJson(
|
|
|
143
143
|
|
|
144
144
|
pkg.devDependencies = {
|
|
145
145
|
...pkg.devDependencies,
|
|
146
|
-
nitropack: "^2.13.4",
|
|
147
146
|
tsx: "^4.19.4",
|
|
148
147
|
};
|
|
148
|
+
delete pkg.devDependencies?.nitropack;
|
|
149
149
|
|
|
150
150
|
await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
151
151
|
}
|
|
@@ -168,7 +168,6 @@ export function blankPackageJsonScaffold(): Record<string, unknown> {
|
|
|
168
168
|
"@plasm_lang/engine": `^${version}`,
|
|
169
169
|
},
|
|
170
170
|
devDependencies: {
|
|
171
|
-
nitropack: "^2.13.4",
|
|
172
171
|
tsx: "^4.19.4",
|
|
173
172
|
},
|
|
174
173
|
};
|
package/src/cli/init.ts
CHANGED
|
@@ -89,7 +89,9 @@ capabilities:
|
|
|
89
89
|
path: /items
|
|
90
90
|
`;
|
|
91
91
|
|
|
92
|
-
const ENV_EXAMPLE = `# Vercel AI Gateway
|
|
92
|
+
const ENV_EXAMPLE = `# Vercel AI Gateway
|
|
93
|
+
# On Vercel: OIDC auth is automatic — no key required for gateway model slugs.
|
|
94
|
+
# Local / self-host: run \`plasm-agent link\` or set AI_GATEWAY_API_KEY.
|
|
93
95
|
AI_GATEWAY_API_KEY=
|
|
94
96
|
|
|
95
97
|
# Vercel Cron auth (production)
|
package/src/cli/nitro-dev.ts
CHANGED
|
@@ -1,69 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import { nitroDevNodeOptions } from "./node-dev-imports.js";
|
|
1
|
+
import { compileAuthoredSlots } from "./compile-authored-slots.js";
|
|
2
|
+
import { walkAgentProject } from "../discovery/project-walker.js";
|
|
3
|
+
import { generateAllStubs } from "../stubs/generator.js";
|
|
4
|
+
import { startPlasmNitroDev } from "../nitro/build-application.js";
|
|
7
5
|
import type { ResolvedAgentProject } from "./project-root.js";
|
|
8
6
|
|
|
9
|
-
function plasmAgentPackageRoot(): string {
|
|
10
|
-
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function assertNitroScaffold(projectRoot: string): Promise<void> {
|
|
14
|
-
const required = [
|
|
15
|
-
path.join(projectRoot, "nitro.config.ts"),
|
|
16
|
-
path.join(projectRoot, "routes", "[...path].ts"),
|
|
17
|
-
];
|
|
18
|
-
for (const filePath of required) {
|
|
19
|
-
try {
|
|
20
|
-
await access(filePath);
|
|
21
|
-
} catch {
|
|
22
|
-
throw new Error(
|
|
23
|
-
`Missing ${path.relative(projectRoot, filePath)} — run \`plasm-agent init\` to scaffold Nitro dev`,
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function nitroBin(projectRoot: string): string {
|
|
30
|
-
return path.join(projectRoot, "node_modules", ".bin", "nitro");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
7
|
export async function startNitroDevForProject(project: ResolvedAgentProject): Promise<void> {
|
|
34
|
-
await
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const child: ChildProcess = spawn(bin, ["dev"], {
|
|
48
|
-
cwd: project.projectRoot,
|
|
49
|
-
env: {
|
|
50
|
-
...process.env,
|
|
51
|
-
NODE_OPTIONS: nodeOptions,
|
|
52
|
-
},
|
|
53
|
-
stdio: "inherit",
|
|
8
|
+
await generateAllStubs(project.agentRoot);
|
|
9
|
+
const discovery = await walkAgentProject(project.agentRoot);
|
|
10
|
+
const { compiledSlots } = await compileAuthoredSlots(
|
|
11
|
+
project.projectRoot,
|
|
12
|
+
project.agentRoot,
|
|
13
|
+
discovery,
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const server = await startPlasmNitroDev({
|
|
17
|
+
projectRoot: project.projectRoot,
|
|
18
|
+
agentRoot: project.agentRoot,
|
|
19
|
+
discovery,
|
|
20
|
+
compiledSlots,
|
|
54
21
|
});
|
|
55
22
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
23
|
+
await new Promise<void>((resolve) => {
|
|
24
|
+
const shutdown = () => {
|
|
25
|
+
void server.close().finally(() => resolve());
|
|
26
|
+
};
|
|
27
|
+
process.on("SIGINT", shutdown);
|
|
28
|
+
process.on("SIGTERM", shutdown);
|
|
59
29
|
});
|
|
60
|
-
|
|
61
|
-
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
|
62
|
-
process.on(signal, () => {
|
|
63
|
-
child.kill(signal);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const exitCode = await wait;
|
|
68
|
-
process.exit(exitCode);
|
|
69
30
|
}
|
package/src/gateway-model.ts
CHANGED
|
@@ -11,10 +11,20 @@ function ensureGatewayApiKey(): void {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/**
|
|
14
|
+
/** Hosted Vercel Functions (OIDC gateway auth available without an API key). */
|
|
15
|
+
export function isVercelHosted(): boolean {
|
|
16
|
+
return (
|
|
17
|
+
process.env.VERCEL === "1" ||
|
|
18
|
+
Boolean(process.env.VERCEL_DEPLOYMENT_ID?.trim()) ||
|
|
19
|
+
Boolean(process.env.VERCEL_ENV?.trim())
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Whether `resolveGatewayModel` can run (API key, alias, or Vercel OIDC). */
|
|
15
24
|
export function isGatewayConfigured(): boolean {
|
|
16
25
|
ensureGatewayApiKey();
|
|
17
|
-
|
|
26
|
+
if (Boolean(process.env.AI_GATEWAY_API_KEY?.trim())) return true;
|
|
27
|
+
return isVercelHosted();
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
/** Resolve a Gateway model slug (`provider/model`) to an AI SDK `LanguageModel`. */
|
|
@@ -29,15 +39,16 @@ export function resolveGatewayModel(
|
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
ensureGatewayApiKey();
|
|
32
|
-
if (!
|
|
42
|
+
if (!isGatewayConfigured()) {
|
|
33
43
|
throw new Error(
|
|
34
44
|
[
|
|
35
|
-
"Vercel AI Gateway
|
|
36
|
-
"
|
|
37
|
-
"
|
|
45
|
+
"Vercel AI Gateway is not configured.",
|
|
46
|
+
"On Vercel: link the project — gateway model ids authenticate via OIDC (no API key).",
|
|
47
|
+
"Off Vercel: set AI_GATEWAY_API_KEY or run `plasm-agent link`.",
|
|
38
48
|
].join(" "),
|
|
39
49
|
);
|
|
40
50
|
}
|
|
41
51
|
|
|
52
|
+
// On Vercel without an explicit key, @ai-sdk/gateway uses @vercel/oidc automatically.
|
|
42
53
|
return gateway(slug);
|
|
43
54
|
}
|
package/src/index.ts
CHANGED
|
@@ -132,6 +132,11 @@ export { createDefaultHostTransport } from "./engine/host-transport.js";
|
|
|
132
132
|
export type { HostTransportOptions } from "./engine/host-transport.js";
|
|
133
133
|
export { createProductionHostTransport, createStubHostTransport } from "./engine/create-host-transport.js";
|
|
134
134
|
export { loadAgentEnv } from "./load-env.js";
|
|
135
|
+
export {
|
|
136
|
+
isGatewayConfigured,
|
|
137
|
+
isVercelHosted,
|
|
138
|
+
resolveGatewayModel,
|
|
139
|
+
} from "./gateway-model.js";
|
|
135
140
|
export {
|
|
136
141
|
connectorUidForEntry,
|
|
137
142
|
connectAuthOptionsForEntry,
|
package/src/instrumentation.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const PlasmSpanAttributes = {
|
|
|
12
12
|
INTENT: "plasm.intent",
|
|
13
13
|
LOGICAL_SESSION_REF: "plasm.logical_session_ref",
|
|
14
14
|
CATALOG_CGS_HASH: "plasm.catalog_cgs_hash",
|
|
15
|
-
|
|
15
|
+
RUN_REF: "plasm.run_ref",
|
|
16
16
|
RUN_ID: "plasm.run_id",
|
|
17
17
|
ENTRY_ID: "plasm.entry_id",
|
|
18
18
|
TOOL_NAME: "plasm.tool.name",
|