@plasm_lang/vercel-agent 0.3.63

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.
Files changed (121) hide show
  1. package/README.md +235 -0
  2. package/agent/.plasm/archives/runs/runs/pr2d5c4a99707b4c19b650553d50229a1d600d28e3d98a9c58f18e5026cecc86ca.json +64 -0
  3. package/agent/.plasm/archives/runs/runs/pr2e0c0d8ad443c63c82da7435ee1a002b0e0fa718b640263c0a9d3e6e5944812f.json +64 -0
  4. package/agent/.plasm/archives/runs/runs/pr2faedb8354f40ee6d828e3af07b421fda9ccda973a4f7347fce3639f03a0a869.json +64 -0
  5. package/agent/.plasm/archives/runs/runs/pr586b47c55547b0702c572bce4255558b22dbe5e682d6359169577e0ea75fe98f.json +64 -0
  6. package/agent/.plasm/archives/runs/runs/pr76212356445e3b00fcf256835aaec18bac68576324b90d0be92d47f0b4a862a7.json +56 -0
  7. package/agent/.plasm/archives/runs/runs/pr9ec805d689e00db9270a9539858f2fb7216c24acbfea943d450e37b641149da1.json +64 -0
  8. package/agent/.plasm/archives/runs/runs/prc3c0c4ba2e28fc94ed6d37b6796e277a7997d9cb3184640d14c35c98bc6d136f.json +64 -0
  9. package/agent/.plasm/archives/runs/runs/prf04de32522f2fdcb17818907d91bccce7dcaecbd1259041cc448d447b6993244.json +64 -0
  10. package/agent/.plasm/archives/traces/traces/local/00000000000000000000000000000000/records.ndjson +1 -0
  11. package/agent/.plasm/archives/traces/traces/local/00000000000000000000000000000000/summary.json +13 -0
  12. package/agent/.plasm/discovery/manifest.json +126 -0
  13. package/agent/.plasm/sessions/TGlzdCBwcm9kdWN0cyBmcm9tIHRoZSBleGVjdXRlX3RpbnkgY2F0YWxvZw.json +44 -0
  14. package/agent/.plasm/sessions/TGlzdCBwcm9kdWN0cyBmcm9tIHRoZSBleGVjdXRlX3RpbnkgY2F0YWxvZw.teaching.tsv +23 -0
  15. package/agent/.plasm/sessions/bGlzdCBleGVjdXRlX3RpbnkgcHJvZHVjdHM.json +151 -0
  16. package/agent/.plasm/sessions/bGlzdCBleGVjdXRlX3RpbnkgcHJvZHVjdHM.teaching.tsv +131 -0
  17. package/agent/.plasm/sessions/bGlzdCBleGVjdXRlX3RpbnkgcHJvZHVjdHMgYXN5bmMgdHJhbnNwb3J0.json +44 -0
  18. package/agent/.plasm/sessions/bGlzdCBleGVjdXRlX3RpbnkgcHJvZHVjdHMgYXN5bmMgdHJhbnNwb3J0.teaching.tsv +23 -0
  19. package/agent/.plasm/stubs/.gitkeep +0 -0
  20. package/agent/.plasm/stubs/execute_tiny.ts +107 -0
  21. package/agent/agent.ts +52 -0
  22. package/agent/catalogs/README.md +15 -0
  23. package/agent/channels/.gitkeep +0 -0
  24. package/agent/channels/execute-tiny-webhook.ts +59 -0
  25. package/agent/channels/health.ts +16 -0
  26. package/agent/hooks/.gitkeep +0 -0
  27. package/agent/hooks/trace-log.ts +10 -0
  28. package/agent/instructions.md +25 -0
  29. package/agent/schedules/.gitkeep +0 -0
  30. package/agent/schedules/ping.ts +13 -0
  31. package/agent/skills/.gitkeep +0 -0
  32. package/agent/skills/plasm-authoring.md +8 -0
  33. package/agent/subagents/.gitkeep +0 -0
  34. package/agent/subagents/tiny/agent.ts +28 -0
  35. package/bin/plasm-agent.mjs +18 -0
  36. package/package.json +77 -0
  37. package/scripts/plasm-node.mjs +19 -0
  38. package/scripts/resolve-ts-extension.mjs +18 -0
  39. package/src/archive/adapters.ts +27 -0
  40. package/src/archive/index.ts +99 -0
  41. package/src/archive/local-run-archive.ts +90 -0
  42. package/src/archive/local-trace-archive.ts +91 -0
  43. package/src/archive/paths.ts +15 -0
  44. package/src/archive/postgres-kv-adapter.ts +72 -0
  45. package/src/archive/prod-archive-store.ts +143 -0
  46. package/src/archive/resolve-backend.ts +60 -0
  47. package/src/archive/run-id.ts +23 -0
  48. package/src/archive/types.ts +75 -0
  49. package/src/archive/vercel-blob-adapter.ts +21 -0
  50. package/src/archive/vercel-kv-adapter.ts +24 -0
  51. package/src/authoring/channel-dispatch.ts +44 -0
  52. package/src/authoring/context.ts +34 -0
  53. package/src/authoring/define-channel.ts +83 -0
  54. package/src/authoring/define-hook.ts +51 -0
  55. package/src/authoring/define-schedule.ts +64 -0
  56. package/src/authoring/define-skill.ts +38 -0
  57. package/src/authoring/hook-runner.ts +18 -0
  58. package/src/authoring/schedule-manager.ts +118 -0
  59. package/src/authoring/slot-loader.ts +253 -0
  60. package/src/authoring/subagent-loader.ts +121 -0
  61. package/src/catalog/loader.ts +71 -0
  62. package/src/cli/build.ts +54 -0
  63. package/src/cli/dev.ts +60 -0
  64. package/src/cli/info.ts +12 -0
  65. package/src/cli/init.ts +372 -0
  66. package/src/cli/link.ts +68 -0
  67. package/src/cli/project-root.ts +57 -0
  68. package/src/define-agent.ts +150 -0
  69. package/src/dev/client/ansi.ts +36 -0
  70. package/src/dev/client/http-session.ts +180 -0
  71. package/src/dev/client/repl.ts +92 -0
  72. package/src/dev/client/slash.ts +119 -0
  73. package/src/dev/dev-session.ts +153 -0
  74. package/src/dev/http.ts +29 -0
  75. package/src/dev/server.ts +147 -0
  76. package/src/dev/session-routes.ts +185 -0
  77. package/src/discovery/project-walker.ts +272 -0
  78. package/src/engine/connect-auth.ts +135 -0
  79. package/src/engine/create-host-transport.ts +7 -0
  80. package/src/engine/fixture-mock-transport.ts +54 -0
  81. package/src/engine/host-transport-bridge.ts +32 -0
  82. package/src/engine/host-transport.ts +84 -0
  83. package/src/engine/napi-binding.ts +265 -0
  84. package/src/evals/define-eval.ts +56 -0
  85. package/src/evals/run-eval.ts +136 -0
  86. package/src/gateway-model.ts +43 -0
  87. package/src/index.ts +296 -0
  88. package/src/instrumentation.ts +56 -0
  89. package/src/load-env.ts +63 -0
  90. package/src/operator/routes.ts +287 -0
  91. package/src/operator/types.ts +63 -0
  92. package/src/operator/ui-shell.ts +134 -0
  93. package/src/project-info.ts +229 -0
  94. package/src/runtime/agent-runtime.ts +469 -0
  95. package/src/runtime/compaction.ts +81 -0
  96. package/src/runtime/logical-session.ts +72 -0
  97. package/src/runtime/plasm-agent.ts +199 -0
  98. package/src/server/plasm-handler.ts +331 -0
  99. package/src/session-state.ts +135 -0
  100. package/src/state/define-state.ts +57 -0
  101. package/src/state/fs-state-adapter.ts +72 -0
  102. package/src/state/kv-state-adapter.ts +62 -0
  103. package/src/state/postgres-state-adapter.ts +116 -0
  104. package/src/stubs/capability-invoke-shape.ts +135 -0
  105. package/src/stubs/catalog-client.ts +170 -0
  106. package/src/stubs/catalog-hash.ts +11 -0
  107. package/src/stubs/catalog-introspection.ts +121 -0
  108. package/src/stubs/cgs-ts-types.ts +164 -0
  109. package/src/stubs/domain-parser.ts +203 -0
  110. package/src/stubs/generator.ts +390 -0
  111. package/src/stubs/input-type-to-ts.ts +233 -0
  112. package/src/stubs/plasm-value-emitter.ts +162 -0
  113. package/src/stubs/program-builder.ts +82 -0
  114. package/src/stubs/stub-symbols.ts +89 -0
  115. package/src/symbol-registry.ts +74 -0
  116. package/src/telemetry/plasm-spans.ts +83 -0
  117. package/src/tools/descriptions.ts +94 -0
  118. package/src/tools/format.ts +29 -0
  119. package/src/tools/harness-tools.ts +65 -0
  120. package/src/tools/plasm-tools.ts +104 -0
  121. package/src/workflow/world-bootstrap.ts +52 -0
@@ -0,0 +1,121 @@
1
+ import path from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+
4
+ import {
5
+ createAgentFromDefinition,
6
+ defineAgent,
7
+ type AgentDefinition,
8
+ } from "../define-agent.js";
9
+ import type { ProjectDiscovery, DiscoveryDiagnostic } from "../discovery/project-walker.js";
10
+ import type { PlasmAgent } from "../runtime/plasm-agent.js";
11
+ import type { LoadedProjectSlots } from "./slot-loader.js";
12
+
13
+ export interface LoadedSubagent {
14
+ name: string;
15
+ agentRoot: string;
16
+ sourcePath: string;
17
+ definition: AgentDefinition;
18
+ agent: PlasmAgent;
19
+ }
20
+
21
+ export interface SubagentRegistry {
22
+ list(): LoadedSubagent[];
23
+ get(name: string): LoadedSubagent | undefined;
24
+ delegate(name: string, message: string): Promise<{ text: string; steps: number }>;
25
+ }
26
+
27
+ export interface LoadSubagentsOptions {
28
+ discovery: ProjectDiscovery;
29
+ parentSlots?: LoadedProjectSlots;
30
+ tenantScope?: string;
31
+ maxSteps?: number;
32
+ telemetry?: boolean;
33
+ importCacheBust?: number;
34
+ }
35
+
36
+ function isAgentDefinition(value: unknown): value is AgentDefinition {
37
+ return (
38
+ typeof value === "object" &&
39
+ value !== null &&
40
+ typeof (value as AgentDefinition).model === "string"
41
+ );
42
+ }
43
+
44
+ export async function loadSubagents(
45
+ options: LoadSubagentsOptions,
46
+ ): Promise<{ subagents: LoadedSubagent[]; diagnostics: DiscoveryDiagnostic[] }> {
47
+ const cacheBust = options.importCacheBust ?? Date.now();
48
+ const diagnostics: DiscoveryDiagnostic[] = [];
49
+ const subagents: LoadedSubagent[] = [];
50
+
51
+ for (const entry of options.discovery.subagents) {
52
+ try {
53
+ const url = `${pathToFileURL(entry.agentPath).href}?t=${cacheBust}`;
54
+ const mod = await import(url);
55
+ const exported = mod.default ?? mod;
56
+ if (!isAgentDefinition(exported)) {
57
+ diagnostics.push({
58
+ level: "error",
59
+ slot: "subagents",
60
+ path: entry.agentPath,
61
+ message: "agent.ts default export must be defineAgent() result",
62
+ });
63
+ continue;
64
+ }
65
+
66
+ const agent = await createAgentFromDefinition(exported, {
67
+ agentRoot: entry.path,
68
+ tenantScope: options.tenantScope,
69
+ maxSteps: options.maxSteps,
70
+ telemetry: options.telemetry,
71
+ loadedSlots: options.parentSlots,
72
+ });
73
+
74
+ subagents.push({
75
+ name: entry.name,
76
+ agentRoot: entry.path,
77
+ sourcePath: entry.agentPath,
78
+ definition: exported,
79
+ agent,
80
+ });
81
+ } catch (err) {
82
+ diagnostics.push({
83
+ level: "error",
84
+ slot: "subagents",
85
+ path: entry.agentPath,
86
+ message: `failed to load subagent: ${String(err)}`,
87
+ });
88
+ }
89
+ }
90
+
91
+ subagents.sort((a, b) => a.name.localeCompare(b.name));
92
+ return { subagents, diagnostics };
93
+ }
94
+
95
+ export function createSubagentRegistry(subagents: LoadedSubagent[]): SubagentRegistry {
96
+ const byName = new Map(subagents.map((s) => [s.name, s]));
97
+ return {
98
+ list: () => [...subagents],
99
+ get: (name: string) => byName.get(name),
100
+ async delegate(name: string, message: string) {
101
+ const sub = byName.get(name);
102
+ if (!sub) {
103
+ throw new Error(`unknown subagent: ${name}`);
104
+ }
105
+ const result = await sub.agent.generate(message);
106
+ return { text: result.text, steps: result.steps.length };
107
+ },
108
+ };
109
+ }
110
+
111
+ export function summarizeSubagents(
112
+ subagents: LoadedSubagent[],
113
+ agentRoot: string,
114
+ ): Array<{ name: string; path: string; model: string }> {
115
+ const rel = (p: string) => path.relative(agentRoot, p);
116
+ return subagents.map((s) => ({
117
+ name: s.name,
118
+ path: rel(s.agentRoot),
119
+ model: s.definition.model,
120
+ }));
121
+ }
@@ -0,0 +1,71 @@
1
+ import { access, readFile, readdir, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { z } from "zod";
5
+
6
+ export const CatalogManifestSchema = z.object({
7
+ entryId: z.string(),
8
+ label: z.string().optional(),
9
+ cgsHash: z.string().optional(),
10
+ });
11
+
12
+ export type CatalogManifest = z.infer<typeof CatalogManifestSchema>;
13
+
14
+ export interface LoadedCatalog {
15
+ rootDir: string;
16
+ manifest: CatalogManifest;
17
+ }
18
+
19
+ export interface CatalogLoader {
20
+ discover(agentRoot: string): Promise<LoadedCatalog[]>;
21
+ }
22
+
23
+ async function pathExists(p: string): Promise<boolean> {
24
+ try {
25
+ await access(p);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ async function readEntryId(catalogDir: string): Promise<string> {
33
+ const domainPath = path.join(catalogDir, "domain.yaml");
34
+ const raw = await readFile(domainPath, "utf8");
35
+ const match = raw.match(/^entry_id:\s*["']?([^"'\n]+)["']?/m);
36
+ if (match?.[1]) return match[1].trim();
37
+ return path.basename(catalogDir);
38
+ }
39
+
40
+ export class FilesystemCatalogLoader implements CatalogLoader {
41
+ async discover(agentRoot: string): Promise<LoadedCatalog[]> {
42
+ const catalogsDir = path.join(agentRoot, "catalogs");
43
+ if (!(await pathExists(catalogsDir))) {
44
+ return [];
45
+ }
46
+ const entries = await readdir(catalogsDir, { withFileTypes: true });
47
+ const loaded: LoadedCatalog[] = [];
48
+ for (const entry of entries) {
49
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
50
+ const rootDir = path.join(catalogsDir, entry.name);
51
+ try {
52
+ const info = await stat(rootDir);
53
+ if (!info.isDirectory()) continue;
54
+ } catch {
55
+ continue;
56
+ }
57
+ const domainYaml = path.join(rootDir, "domain.yaml");
58
+ const mappingsYaml = path.join(rootDir, "mappings.yaml");
59
+ if (!(await pathExists(domainYaml)) || !(await pathExists(mappingsYaml))) {
60
+ continue;
61
+ }
62
+ const entryId = await readEntryId(rootDir);
63
+ loaded.push({
64
+ rootDir,
65
+ manifest: { entryId, label: entry.name },
66
+ });
67
+ }
68
+ loaded.sort((a, b) => a.manifest.entryId.localeCompare(b.manifest.entryId));
69
+ return loaded;
70
+ }
71
+ }
@@ -0,0 +1,54 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { isNativeEngineAvailable } from "../engine/napi-binding.js";
5
+ import { walkAgentProject } from "../discovery/project-walker.js";
6
+ import { generateAllStubs } from "../stubs/generator.js";
7
+ import type { ResolvedAgentProject } from "./project-root.js";
8
+
9
+ export interface PlasmBuildResult {
10
+ stubsDir: string;
11
+ discoveryDir: string;
12
+ manifestPath: string;
13
+ stubs: Array<{ entryId: string; catalogCgsHash: string; outPath: string }>;
14
+ }
15
+
16
+ export async function runPlasmBuild(project: ResolvedAgentProject): Promise<PlasmBuildResult> {
17
+ const stubs = await generateAllStubs(project.agentRoot);
18
+ const discovery = await walkAgentProject(project.agentRoot);
19
+ const discoveryDir = path.join(project.agentRoot, ".plasm", "discovery");
20
+ await mkdir(discoveryDir, { recursive: true });
21
+ const manifestPath = path.join(discoveryDir, "manifest.json");
22
+ const native = isNativeEngineAvailable();
23
+ const manifest = {
24
+ builtAt: new Date().toISOString(),
25
+ projectRoot: project.projectRoot,
26
+ agentRoot: project.agentRoot,
27
+ stubs: stubs.map((s) => ({
28
+ entryId: s.entryId,
29
+ catalogCgsHash: s.catalogCgsHash,
30
+ outPath: path.relative(project.projectRoot, s.outPath),
31
+ })),
32
+ discovery: {
33
+ agentRoot: discovery.agentRoot,
34
+ catalogs: discovery.catalogs,
35
+ skills: discovery.skills,
36
+ channels: discovery.channels,
37
+ schedules: discovery.schedules,
38
+ hooks: discovery.hooks,
39
+ subagents: discovery.subagents,
40
+ diagnostics: discovery.diagnostics,
41
+ },
42
+ engine: {
43
+ native,
44
+ mode: native ? "napi" : "stub",
45
+ },
46
+ };
47
+ await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
48
+ return {
49
+ stubsDir: path.join(project.agentRoot, ".plasm", "stubs"),
50
+ discoveryDir,
51
+ manifestPath,
52
+ stubs,
53
+ };
54
+ }
package/src/cli/dev.ts ADDED
@@ -0,0 +1,60 @@
1
+ import path from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+
4
+ import type { ResolvedAgentProject } from "./project-root.js";
5
+ import { startDevServer, type DevServerHandle } from "../dev/server.js";
6
+
7
+ export interface StartDevServerForProjectOptions {
8
+ project: ResolvedAgentProject;
9
+ port?: number;
10
+ host?: string;
11
+ tenantScope?: string;
12
+ maxSteps?: number;
13
+ telemetry?: boolean;
14
+ /** `auto` attaches TUI when stdin is a TTY. Pass `false` for headless server only. */
15
+ tui?: boolean | "auto";
16
+ }
17
+
18
+ function shouldAttachTui(tui?: boolean | "auto"): boolean {
19
+ if (tui === false) return false;
20
+ if (tui === true) return true;
21
+ return Boolean(process.stdin.isTTY && !process.env.PLASM_DEV_NO_TUI);
22
+ }
23
+
24
+ export async function startDevServerForProject(
25
+ options: StartDevServerForProjectOptions,
26
+ ): Promise<DevServerHandle> {
27
+ const agentModule = path.join(options.project.agentRoot, "agent.ts");
28
+ const mod = await import(pathToFileURL(agentModule).href);
29
+ const definition = mod.default;
30
+ if (!definition) {
31
+ throw new Error(`No default export in ${agentModule}`);
32
+ }
33
+
34
+ const handle = await startDevServer({
35
+ agentRoot: options.project.agentRoot,
36
+ definition,
37
+ port: options.port ?? Number(process.env.PORT ?? 3000),
38
+ host: options.host ?? process.env.PLASM_DEV_HOST ?? "127.0.0.1",
39
+ tenantScope: options.tenantScope ?? process.env.PLASM_TENANT_SCOPE ?? "local",
40
+ maxSteps: options.maxSteps ?? 20,
41
+ telemetry: options.telemetry ?? process.env.PLASM_AGENT_TELEMETRY !== "0",
42
+ });
43
+
44
+ if (shouldAttachTui(options.tui)) {
45
+ const { runDevTui } = await import("../dev/client/repl.js");
46
+ await runDevTui({ baseUrl: handle.url });
47
+ await handle.close();
48
+ process.exit(0);
49
+ }
50
+
51
+ return handle;
52
+ }
53
+
54
+ export function installDevServerShutdown(handle: DevServerHandle): void {
55
+ for (const signal of ["SIGINT", "SIGTERM"] as const) {
56
+ process.on(signal, () => {
57
+ void handle.close().finally(() => process.exit(0));
58
+ });
59
+ }
60
+ }
@@ -0,0 +1,12 @@
1
+ export {
2
+ collectProjectInfo,
3
+ collectProjectInfo as collectPlasmInfo,
4
+ formatPlasmInfoHuman,
5
+ FRAMEWORK_NAME,
6
+ FRAMEWORK_VERSION,
7
+ PLASM_LANGUAGE_TOOLS,
8
+ type CatalogInfoEntry,
9
+ type CollectProjectInfoOptions,
10
+ type ProjectInfoPayload,
11
+ type ProjectInfoRoutes,
12
+ } from "../project-info.js";
@@ -0,0 +1,372 @@
1
+ import { access, cp, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ import { FRAMEWORK_VERSION } from "../project-info.js";
6
+ import type { ResolvedAgentProject } from "./project-root.js";
7
+
8
+ export interface InitOptions {
9
+ template?: string;
10
+ }
11
+
12
+ const SKIP_TEMPLATE_DIRS = new Set(["node_modules", ".plasm"]);
13
+ const SKIP_TEMPLATE_FILES = new Set(["package-lock.json"]);
14
+
15
+ function plasmAgentPackageRoot(): string {
16
+ return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
17
+ }
18
+
19
+ function resolveTemplateDir(template: string): string {
20
+ const templates: Record<string, string> = {
21
+ "mcp-radar": path.join(plasmAgentPackageRoot(), "../../../examples/mcp-radar-agent"),
22
+ };
23
+ const dir = templates[template];
24
+ if (!dir) {
25
+ throw new Error(
26
+ `Unknown template "${template}". Available: ${Object.keys(templates).join(", ")}`,
27
+ );
28
+ }
29
+ return dir;
30
+ }
31
+
32
+ function shouldCopyTemplateEntry(src: string, templateRoot: string): boolean {
33
+ const rel = path.relative(templateRoot, src);
34
+ if (!rel || rel === "") return true;
35
+ const parts = rel.split(path.sep);
36
+ if (parts.some((part) => SKIP_TEMPLATE_DIRS.has(part))) return false;
37
+ if (SKIP_TEMPLATE_FILES.has(path.basename(src))) return false;
38
+ return true;
39
+ }
40
+
41
+ async function copyTemplate(templateRoot: string, projectRoot: string): Promise<void> {
42
+ await cp(templateRoot, projectRoot, {
43
+ recursive: true,
44
+ filter: (src) => src === templateRoot || shouldCopyTemplateEntry(src, templateRoot),
45
+ });
46
+ }
47
+
48
+ async function patchBootstrapPackageJson(
49
+ projectRoot: string,
50
+ packageRoot: string,
51
+ ): Promise<void> {
52
+ const pkgPath = path.join(projectRoot, "package.json");
53
+ const raw = await readFile(pkgPath, "utf8");
54
+ const pkg = JSON.parse(raw) as {
55
+ name?: string;
56
+ scripts?: Record<string, string>;
57
+ dependencies?: Record<string, string>;
58
+ };
59
+ pkg.name = path.basename(projectRoot);
60
+ const engineRoot = path.resolve(packageRoot, "../plasm-engine");
61
+ pkg.dependencies = {
62
+ ...pkg.dependencies,
63
+ "@plasm_lang/vercel-agent": `file:${path.resolve(packageRoot)}`,
64
+ "@plasm_lang/engine": `file:${engineRoot}`,
65
+ "@vercel/blob": "^0.27.3",
66
+ "@vercel/functions": "^3.4.3",
67
+ "@vercel/kv": "^3.0.0",
68
+ };
69
+ const nodeRunner =
70
+ "node --experimental-strip-types --experimental-transform-types ./node_modules/@plasm_lang/vercel-agent/scripts/plasm-node.mjs";
71
+ pkg.scripts = {
72
+ build: "plasm-agent build",
73
+ "vercel-build": "plasm-agent build",
74
+ dev: "plasm-agent dev",
75
+ info: "plasm-agent info",
76
+ deploy: "vercel deploy",
77
+ ...pkg.scripts,
78
+ eval: `${nodeRunner} scripts/run-evals.ts`,
79
+ "smoke:channel": `${nodeRunner} scripts/smoke-channel.ts`,
80
+ };
81
+ await writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
82
+ }
83
+
84
+ async function runTemplateInit(
85
+ targetDir: string,
86
+ template: string,
87
+ ): Promise<ResolvedAgentProject> {
88
+ const projectRoot = path.resolve(targetDir);
89
+ const agentRoot = path.join(projectRoot, "agent");
90
+ if (await exists(path.join(agentRoot, "agent.ts"))) {
91
+ throw new Error(`agent/agent.ts already exists in ${projectRoot}`);
92
+ }
93
+ const templateRoot = resolveTemplateDir(template);
94
+ if (!(await exists(templateRoot))) {
95
+ throw new Error(`Template source missing: ${templateRoot}`);
96
+ }
97
+ await mkdir(projectRoot, { recursive: true });
98
+ await copyTemplate(templateRoot, projectRoot);
99
+ await patchBootstrapPackageJson(projectRoot, plasmAgentPackageRoot());
100
+ await writeVercelScaffold(projectRoot, template);
101
+ return { projectRoot, agentRoot };
102
+ }
103
+
104
+ const AGENT_TS = `import path from "node:path";
105
+ import { fileURLToPath } from "node:url";
106
+
107
+ import { createAgentFromProject, defineAgent } from "@plasm_lang/vercel-agent";
108
+ import { loadAgentEnv } from "@plasm_lang/vercel-agent";
109
+
110
+ loadAgentEnv();
111
+
112
+ const agentRoot = path.dirname(fileURLToPath(import.meta.url));
113
+
114
+ const agentDefinition = defineAgent({
115
+ model: process.env.PLASM_AGENT_MODEL ?? "anthropic/claude-sonnet-4.6",
116
+ compaction: { thresholdPercent: 0.75 },
117
+ experimental: { skills: true },
118
+ });
119
+
120
+ export default agentDefinition;
121
+
122
+ export async function createPlasmAgent() {
123
+ return createAgentFromProject(agentDefinition, {
124
+ agentRoot,
125
+ tenantScope: process.env.PLASM_TENANT_SCOPE ?? "local",
126
+ maxSteps: 20,
127
+ telemetry: process.env.PLASM_AGENT_TELEMETRY !== "0",
128
+ });
129
+ }
130
+ `;
131
+
132
+ const INSTRUCTIONS_MD = `# Catalog-native Plasm agent
133
+
134
+ Use **discover_capabilities → plasm_context → plasm → plasm_run** for catalog-backed work.
135
+ Keep one stable \`intent\` per user goal.
136
+ `;
137
+
138
+ const DOMAIN_YAML = `version: 1
139
+ auth:
140
+ scheme: none
141
+ http_backend: http://example.test
142
+ entities:
143
+ Item:
144
+ id_field: id
145
+ description: Starter entity
146
+ fields:
147
+ id:
148
+ required: true
149
+ value_ref: nv_item_id
150
+ name:
151
+ required: false
152
+ value_ref: nv_item_name
153
+ relations: {}
154
+ capabilities:
155
+ item_list:
156
+ description: List items
157
+ kind: query
158
+ entity: Item
159
+ provides:
160
+ - id
161
+ - name
162
+ values:
163
+ nv_item_id:
164
+ type: string
165
+ string_semantics: short
166
+ nv_item_name:
167
+ type: string
168
+ string_semantics: short
169
+ `;
170
+
171
+ const MAPPINGS_YAML = `version: 1
172
+ capabilities:
173
+ item_list:
174
+ transport:
175
+ kind: http
176
+ method: GET
177
+ path: /items
178
+ `;
179
+
180
+ const ENV_EXAMPLE = `# Vercel AI Gateway (run \`plasm-agent link\` to pull from a linked project)
181
+ AI_GATEWAY_API_KEY=
182
+
183
+ # Vercel Cron auth (production)
184
+ CRON_SECRET=
185
+
186
+ # Durable state on Vercel (optional locally)
187
+ KV_REST_API_URL=
188
+ KV_REST_API_TOKEN=
189
+ BLOB_READ_WRITE_TOKEN=
190
+ PLASM_STATE_BACKEND=kv
191
+ PLASM_ARCHIVE_BACKEND=vercel
192
+
193
+ # Optional overrides
194
+ PLASM_AGENT_MODEL=anthropic/claude-sonnet-4.6
195
+ PLASM_TENANT_SCOPE=local
196
+ PORT=3000
197
+ `;
198
+
199
+ const VERCEL_JSON_DEFAULT = `{
200
+ "buildCommand": "plasm-agent build",
201
+ "rewrites": [{ "source": "/(.*)", "destination": "/api/$1" }]
202
+ }
203
+ `;
204
+
205
+ const VERCEL_JSON_MCP_RADAR = `{
206
+ "buildCommand": "plasm-agent build",
207
+ "rewrites": [{ "source": "/(.*)", "destination": "/api/$1" }],
208
+ "crons": [
209
+ {
210
+ "path": "/internal/cron/mcp-radar-scan",
211
+ "schedule": "0 */6 * * *"
212
+ }
213
+ ],
214
+ "functions": {
215
+ "api/**": {
216
+ "maxDuration": 300
217
+ }
218
+ }
219
+ }
220
+ `;
221
+
222
+ const VERCELIGNORE = `node_modules
223
+ .env*
224
+ agent/.plasm/research
225
+ `;
226
+
227
+ const API_HANDLER_TS = `import path from "node:path";
228
+ import { fileURLToPath } from "node:url";
229
+
230
+ import agentDefinition from "../agent/agent.js";
231
+ import { createPlasmApp, vercelPlasmHandler } from "@plasm_lang/vercel-agent";
232
+
233
+ const packageRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
234
+ const agentRoot = path.join(packageRoot, "agent");
235
+
236
+ let app: Awaited<ReturnType<typeof createPlasmApp>> | undefined;
237
+
238
+ export default async function handler(
239
+ req: import("node:http").IncomingMessage,
240
+ res: import("node:http").ServerResponse,
241
+ ): Promise<void> {
242
+ app ??= await createPlasmApp({
243
+ agentRoot,
244
+ definition: agentDefinition,
245
+ mode: "prod",
246
+ sessions: false,
247
+ });
248
+ await vercelPlasmHandler(app)(req, res);
249
+ }
250
+ `;
251
+
252
+ const PACKAGE_JSON_TEMPLATE = {
253
+ name: "my-plasm-agent",
254
+ private: true,
255
+ type: "module",
256
+ scripts: {
257
+ dev: "plasm-agent dev",
258
+ build: "plasm-agent build",
259
+ "vercel-build": "plasm-agent build",
260
+ info: "plasm-agent info",
261
+ deploy: "vercel deploy",
262
+ },
263
+ dependencies: {} as Record<string, string>,
264
+ };
265
+
266
+ async function writeVercelScaffold(
267
+ projectRoot: string,
268
+ template?: string,
269
+ ): Promise<void> {
270
+ const vercelJson =
271
+ template === "mcp-radar" ? VERCEL_JSON_MCP_RADAR : VERCEL_JSON_DEFAULT;
272
+ await mkdir(path.join(projectRoot, "api"), { recursive: true });
273
+ await writeFile(path.join(projectRoot, "vercel.json"), vercelJson, "utf8");
274
+ await writeFile(path.join(projectRoot, ".vercelignore"), VERCELIGNORE, "utf8");
275
+ await writeFile(
276
+ path.join(projectRoot, "api", "[[...path]].ts"),
277
+ API_HANDLER_TS,
278
+ "utf8",
279
+ );
280
+ }
281
+
282
+ function packageJsonScaffold(): Record<string, unknown> {
283
+ return {
284
+ ...PACKAGE_JSON_TEMPLATE,
285
+ dependencies: {
286
+ "@plasm_lang/vercel-agent": `^${FRAMEWORK_VERSION}`,
287
+ },
288
+ };
289
+ }
290
+
291
+ async function exists(p: string): Promise<boolean> {
292
+ try {
293
+ await access(p);
294
+ return true;
295
+ } catch {
296
+ return false;
297
+ }
298
+ }
299
+
300
+ export async function runPlasmInit(
301
+ targetDir: string,
302
+ options?: InitOptions,
303
+ ): Promise<ResolvedAgentProject> {
304
+ if (options?.template) {
305
+ return runTemplateInit(targetDir, options.template);
306
+ }
307
+
308
+ const projectRoot = path.resolve(targetDir);
309
+ const agentRoot = path.join(projectRoot, "agent");
310
+
311
+ if (await exists(path.join(agentRoot, "agent.ts"))) {
312
+ throw new Error(`agent/agent.ts already exists in ${projectRoot}`);
313
+ }
314
+
315
+ await mkdir(path.join(agentRoot, "catalogs", "starter"), { recursive: true });
316
+ await mkdir(path.join(agentRoot, "skills"), { recursive: true });
317
+ await mkdir(path.join(agentRoot, "channels"), { recursive: true });
318
+ await mkdir(path.join(agentRoot, "schedules"), { recursive: true });
319
+ await mkdir(path.join(agentRoot, "hooks"), { recursive: true });
320
+ await mkdir(path.join(projectRoot, "evals"), { recursive: true });
321
+
322
+ await writeFile(path.join(agentRoot, "agent.ts"), AGENT_TS, "utf8");
323
+ await writeFile(path.join(agentRoot, "instructions.md"), INSTRUCTIONS_MD, "utf8");
324
+ await writeFile(path.join(agentRoot, "catalogs", "starter", "domain.yaml"), DOMAIN_YAML, "utf8");
325
+ await writeFile(path.join(agentRoot, "catalogs", "starter", "mappings.yaml"), MAPPINGS_YAML, "utf8");
326
+ await writeFile(path.join(projectRoot, ".env.example"), ENV_EXAMPLE, "utf8");
327
+
328
+ if (!(await exists(path.join(projectRoot, "package.json")))) {
329
+ await writeFile(
330
+ path.join(projectRoot, "package.json"),
331
+ `${JSON.stringify(packageJsonScaffold(), null, 2)}\n`,
332
+ "utf8",
333
+ );
334
+ await patchBootstrapPackageJson(projectRoot, plasmAgentPackageRoot());
335
+ }
336
+
337
+ await writeVercelScaffold(projectRoot);
338
+
339
+ return { projectRoot, agentRoot };
340
+ }
341
+
342
+ export function formatInitSuccess(
343
+ project: ResolvedAgentProject,
344
+ options?: InitOptions,
345
+ ): string {
346
+ if (options?.template === "mcp-radar") {
347
+ return [
348
+ `Initialized MCP Radar agent from template at ${project.projectRoot}`,
349
+ "",
350
+ "Next:",
351
+ ` cd ${project.projectRoot}`,
352
+ " npm install",
353
+ " plasm-agent link # pull AI_GATEWAY_API_KEY from Vercel",
354
+ " plasm-agent build",
355
+ " npm run smoke:channel",
356
+ " plasm-agent dev # local dev server + TUI",
357
+ " vercel deploy # production (channels + cron)",
358
+ ].join("\n");
359
+ }
360
+
361
+ return [
362
+ `Initialized Plasm agent project at ${project.projectRoot}`,
363
+ "",
364
+ "Next:",
365
+ ` cd ${project.projectRoot}`,
366
+ " npm install",
367
+ " plasm-agent link # pull AI_GATEWAY_API_KEY from Vercel",
368
+ " plasm-agent build # generate CGS stubs + discovery manifest",
369
+ " plasm-agent dev # HTTP dev server (sessions + SSE)",
370
+ " vercel deploy # production on Vercel",
371
+ ].join("\n");
372
+ }