@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,63 @@
1
+ export interface OperatorStubFreshness {
2
+ stubPath: string;
3
+ liveCatalogCgsHash: string;
4
+ generatedCatalogCgsHash: string | null;
5
+ fresh: boolean;
6
+ lastBuiltAt: string | null;
7
+ validationErrors: string[];
8
+ }
9
+
10
+ export interface OperatorCatalogEntry {
11
+ entryId: string;
12
+ label: string;
13
+ rootDir: string;
14
+ catalogCgsHash: string;
15
+ authScheme?: string;
16
+ entityCount: number;
17
+ capabilityCount: number;
18
+ stub: OperatorStubFreshness;
19
+ }
20
+
21
+ export interface OperatorCatalogsResponse {
22
+ catalogs: OperatorCatalogEntry[];
23
+ generatedAt: string;
24
+ }
25
+
26
+ export interface OperatorSessionEntry {
27
+ intent: string;
28
+ logicalSessionRef: string;
29
+ logicalSessionId: string;
30
+ waveCount: number;
31
+ seedCount: number;
32
+ planCommitCount: number;
33
+ updatedAt: string;
34
+ }
35
+
36
+ export interface OperatorSessionsResponse {
37
+ sessions: OperatorSessionEntry[];
38
+ }
39
+
40
+ export interface OperatorPlanCommit {
41
+ intent: string;
42
+ logicalSessionRef: string;
43
+ ref: string;
44
+ program: string;
45
+ at: string;
46
+ }
47
+
48
+ export interface OperatorPlansResponse {
49
+ plans: OperatorPlanCommit[];
50
+ }
51
+
52
+ export interface OperatorOpsResponse {
53
+ nativeEngineAvailable: boolean;
54
+ engineMode: "napi" | "stub";
55
+ agentRoot: string;
56
+ catalogCount: number;
57
+ sessionCount: number;
58
+ planCommitCount: number;
59
+ }
60
+
61
+ export interface OperatorHealthResponse {
62
+ status: string;
63
+ }
@@ -0,0 +1,134 @@
1
+ /** Read-only operator shell — fetches `/operator/*` BFF JSON. */
2
+ export function renderOperatorShell(basePath = "/operator"): string {
3
+ const base = basePath.replace(/\/$/, "");
4
+ return `<!DOCTYPE html>
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
9
+ <title>Plasm Operator</title>
10
+ <style>
11
+ :root { color-scheme: dark; font-family: ui-sans-serif, system-ui, sans-serif; }
12
+ body { margin: 0; background: #0b0d10; color: #e8eaed; }
13
+ header { padding: 1rem 1.25rem; border-bottom: 1px solid #22262d; display: flex; gap: 1rem; align-items: center; }
14
+ header h1 { font-size: 1rem; font-weight: 600; margin: 0; letter-spacing: 0.02em; }
15
+ nav { display: flex; gap: 0.5rem; flex-wrap: wrap; }
16
+ nav button { background: #151922; border: 1px solid #2a3140; color: #c9d1d9; padding: 0.35rem 0.7rem; border-radius: 6px; cursor: pointer; }
17
+ nav button.active { background: #1f6feb33; border-color: #388bfd; color: #fff; }
18
+ main { padding: 1rem 1.25rem 2rem; }
19
+ .meta { color: #8b949e; font-size: 0.85rem; margin-bottom: 1rem; }
20
+ table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
21
+ th, td { text-align: left; padding: 0.5rem 0.65rem; border-bottom: 1px solid #22262d; vertical-align: top; }
22
+ th { color: #8b949e; font-weight: 500; }
23
+ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.82rem; }
24
+ .badge { display: inline-block; padding: 0.1rem 0.45rem; border-radius: 999px; font-size: 0.75rem; }
25
+ .ok { background: #23863633; color: #3fb950; }
26
+ .warn { background: #9e6a0333; color: #d29922; }
27
+ pre { background: #11151c; border: 1px solid #22262d; padding: 0.75rem; border-radius: 8px; overflow: auto; font-size: 0.8rem; }
28
+ .error { color: #f85149; }
29
+ </style>
30
+ </head>
31
+ <body>
32
+ <header>
33
+ <h1>Plasm Operator</h1>
34
+ <nav id="nav"></nav>
35
+ </header>
36
+ <main>
37
+ <div class="meta" id="meta"></div>
38
+ <div id="content"></div>
39
+ </main>
40
+ <script>
41
+ const BASE = ${JSON.stringify(base)};
42
+ const PANES = [
43
+ { id: "ops", label: "Ops", path: "/ops" },
44
+ { id: "catalogs", label: "Catalogs", path: "/catalogs" },
45
+ { id: "sessions", label: "Sessions", path: "/sessions" },
46
+ { id: "plans", label: "Plans", path: "/plans" },
47
+ { id: "runs", label: "Runs", path: "/runs" },
48
+ { id: "traces", label: "Traces", path: "/traces" },
49
+ { id: "archives", label: "Archives", path: "/archives" },
50
+ ];
51
+ let active = "catalogs";
52
+ const nav = document.getElementById("nav");
53
+ const meta = document.getElementById("meta");
54
+ const content = document.getElementById("content");
55
+ function setActive(id) {
56
+ active = id;
57
+ for (const btn of nav.querySelectorAll("button")) {
58
+ btn.classList.toggle("active", btn.dataset.id === id);
59
+ }
60
+ void loadPane(id);
61
+ }
62
+ for (const pane of PANES) {
63
+ const btn = document.createElement("button");
64
+ btn.textContent = pane.label;
65
+ btn.dataset.id = pane.id;
66
+ btn.onclick = () => setActive(pane.id);
67
+ nav.appendChild(btn);
68
+ }
69
+ async function fetchJson(path) {
70
+ const res = await fetch(BASE + path);
71
+ if (!res.ok) throw new Error(res.status + " " + path);
72
+ return res.json();
73
+ }
74
+ function esc(s) {
75
+ return String(s).replace(/[&<>"]/g, (c) => ({ "&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;" }[c]));
76
+ }
77
+ function renderTable(rows, columns) {
78
+ if (!rows.length) return "<p>No rows.</p>";
79
+ const head = columns.map((c) => "<th>" + esc(c.label) + "</th>").join("");
80
+ const body = rows.map((row) =>
81
+ "<tr>" + columns.map((c) => "<td>" + esc(c.render(row)) + "</td>").join("") + "</tr>"
82
+ ).join("");
83
+ return "<table><thead><tr>" + head + "</tr></thead><tbody>" + body + "</tbody></table>";
84
+ }
85
+ async function loadPane(id) {
86
+ meta.textContent = "Loading…";
87
+ content.innerHTML = "";
88
+ try {
89
+ const pane = PANES.find((p) => p.id === id);
90
+ const data = await fetchJson(pane.path);
91
+ meta.textContent = "Updated " + new Date().toLocaleString();
92
+ if (id === "ops") {
93
+ content.innerHTML = "<pre>" + esc(JSON.stringify(data, null, 2)) + "</pre>";
94
+ return;
95
+ }
96
+ if (id === "catalogs") {
97
+ content.innerHTML = renderTable(data.catalogs || [], [
98
+ { label: "entry", render: (r) => r.entryId },
99
+ { label: "hash", render: (r) => (r.catalogCgsHash || "").slice(0, 12) + "…" },
100
+ { label: "stub", render: (r) => r.stub?.fresh ? "fresh" : "stale" },
101
+ { label: "caps", render: (r) => String(r.capabilityCount ?? "") },
102
+ ]);
103
+ return;
104
+ }
105
+ if (id === "sessions") {
106
+ content.innerHTML = renderTable(data.sessions || [], [
107
+ { label: "intent", render: (r) => r.intent },
108
+ { label: "ref", render: (r) => r.logicalSessionRef },
109
+ { label: "plans", render: (r) => String(r.planCommitCount ?? 0) },
110
+ ]);
111
+ return;
112
+ }
113
+ if (id === "plans") {
114
+ content.innerHTML = renderTable(data.plans || [], [
115
+ { label: "pc", render: (r) => r.ref },
116
+ { label: "intent", render: (r) => r.intent },
117
+ { label: "program", render: (r) => (r.program || "").slice(0, 48) },
118
+ ]);
119
+ return;
120
+ }
121
+ if (id === "runs" || id === "traces" || id === "archives") {
122
+ content.innerHTML = "<pre>" + esc(JSON.stringify(data, null, 2)) + "</pre>";
123
+ return;
124
+ }
125
+ content.innerHTML = "<pre>" + esc(JSON.stringify(data, null, 2)) + "</pre>";
126
+ } catch (err) {
127
+ meta.innerHTML = '<span class="error">' + esc(String(err)) + "</span>";
128
+ }
129
+ }
130
+ setActive("catalogs");
131
+ </script>
132
+ </body>
133
+ </html>`;
134
+ }
@@ -0,0 +1,229 @@
1
+ import path from "node:path";
2
+
3
+ import { listChannelRoutes } from "./authoring/channel-dispatch.js";
4
+ import { loadAuthoredSlots, summarizeLoadedSlots } from "./authoring/slot-loader.js";
5
+ import { loadSubagents, summarizeSubagents } from "./authoring/subagent-loader.js";
6
+ import type { AgentDefinition } from "./define-agent.js";
7
+ import type { ProjectDiscovery } from "./discovery/project-walker.js";
8
+ import { walkAgentProject } from "./discovery/project-walker.js";
9
+ import { isNativeEngineAvailable } from "./engine/napi-binding.js";
10
+ import { exportScheduleCronManifest } from "./authoring/schedule-manager.js";
11
+ import { isGatewayConfigured } from "./gateway-model.js";
12
+ import type { LoadedProjectSlots } from "./authoring/slot-loader.js";
13
+ import { resolveCatalogLiveHash } from "./stubs/catalog-hash.js";
14
+ import { stubFreshness } from "./stubs/generator.js";
15
+
16
+ export const PLASM_LANGUAGE_TOOLS = [
17
+ "discover_capabilities",
18
+ "plasm_context",
19
+ "plasm",
20
+ "plasm_run",
21
+ ] as const;
22
+
23
+ export const FRAMEWORK_NAME = "@plasm_lang/vercel-agent";
24
+ export const FRAMEWORK_VERSION = "0.0.1";
25
+
26
+ export interface CatalogInfoEntry {
27
+ name: string;
28
+ entryId?: string;
29
+ stubPath: string;
30
+ stubFresh?: Awaited<ReturnType<typeof stubFreshness>>;
31
+ }
32
+
33
+ export interface ProjectInfoRoutes {
34
+ health: string;
35
+ info: string;
36
+ session: string;
37
+ sessionContinue: string;
38
+ sessionStream: string;
39
+ channels: Array<{ method: string; path: string }>;
40
+ scheduleCrons: string[];
41
+ operator: string;
42
+ }
43
+
44
+ export interface ProjectInfoPayload {
45
+ status: "ok" | "degraded";
46
+ framework: string;
47
+ version: string;
48
+ projectRoot: string;
49
+ agentRoot: string;
50
+ packageName?: string;
51
+ engine: {
52
+ native: boolean;
53
+ mode: "napi" | "stub";
54
+ };
55
+ gateway: {
56
+ configured: boolean;
57
+ };
58
+ discovery: ProjectDiscovery;
59
+ loadedSlots: ReturnType<typeof summarizeLoadedSlots> & {
60
+ subagents: ReturnType<typeof summarizeSubagents>;
61
+ };
62
+ catalogs: CatalogInfoEntry[];
63
+ diagnostics: Array<{
64
+ level: string;
65
+ slot: string;
66
+ path: string;
67
+ message: string;
68
+ }>;
69
+ languageTools: readonly string[];
70
+ /** Present on dev-server `/plasm/v1/info` only. */
71
+ dev?: {
72
+ model: AgentDefinition["model"];
73
+ compaction: AgentDefinition["compaction"] | null;
74
+ modelOptions: AgentDefinition["modelOptions"] | null;
75
+ build: AgentDefinition["build"] | null;
76
+ experimental: AgentDefinition["experimental"] | null;
77
+ scheduleCrons: ReturnType<typeof exportScheduleCronManifest> | null;
78
+ sessions: { active: number };
79
+ routes: ProjectInfoRoutes;
80
+ };
81
+ }
82
+
83
+ export interface CollectProjectInfoOptions {
84
+ projectRoot: string;
85
+ agentRoot: string;
86
+ packageName?: string;
87
+ cached?: {
88
+ discovery: ProjectDiscovery;
89
+ loadedSlots: LoadedProjectSlots;
90
+ subagents: ReturnType<typeof summarizeSubagents>;
91
+ };
92
+ dev?: {
93
+ definition: AgentDefinition;
94
+ sessionCount: number;
95
+ };
96
+ }
97
+
98
+ async function catalogEntries(
99
+ project: { projectRoot: string; agentRoot: string },
100
+ discovery: ProjectDiscovery,
101
+ ): Promise<CatalogInfoEntry[]> {
102
+ const catalogs: CatalogInfoEntry[] = [];
103
+ for (const catalog of discovery.catalogs) {
104
+ const entryId = catalog.entryId ?? catalog.name;
105
+ const stubPath = path.join(project.agentRoot, ".plasm", "stubs", `${entryId}.ts`);
106
+ let stubFresh: Awaited<ReturnType<typeof stubFreshness>> | undefined;
107
+ try {
108
+ const liveHash = await resolveCatalogLiveHash(catalog.path);
109
+ stubFresh = await stubFreshness(liveHash, stubPath);
110
+ } catch {
111
+ stubFresh = undefined;
112
+ }
113
+ catalogs.push({
114
+ name: catalog.name,
115
+ entryId: catalog.entryId,
116
+ stubPath: path.relative(project.projectRoot, stubPath),
117
+ stubFresh,
118
+ });
119
+ }
120
+ return catalogs;
121
+ }
122
+
123
+ export async function collectProjectInfo(
124
+ options: CollectProjectInfoOptions,
125
+ ): Promise<ProjectInfoPayload> {
126
+ const { projectRoot, agentRoot, packageName, cached, dev } = options;
127
+
128
+ let discovery: ProjectDiscovery;
129
+ let loadedSlots: LoadedProjectSlots;
130
+ let subagentSummary: ReturnType<typeof summarizeSubagents>;
131
+ let subDiagnostics: Array<{ level: string; slot: string; path: string; message: string }>;
132
+
133
+ if (cached) {
134
+ discovery = cached.discovery;
135
+ loadedSlots = cached.loadedSlots;
136
+ subagentSummary = cached.subagents;
137
+ subDiagnostics = [];
138
+ } else {
139
+ discovery = await walkAgentProject(agentRoot);
140
+ loadedSlots = await loadAuthoredSlots({ discovery });
141
+ const loaded = await loadSubagents({ discovery, parentSlots: loadedSlots });
142
+ subagentSummary = summarizeSubagents(loaded.subagents, agentRoot);
143
+ subDiagnostics = loaded.diagnostics;
144
+ }
145
+
146
+ const diagnostics = [
147
+ ...loadedSlots.diagnostics,
148
+ ...subDiagnostics,
149
+ ...discovery.diagnostics,
150
+ ];
151
+ const catalogs = await catalogEntries({ projectRoot, agentRoot }, discovery);
152
+ const hasErrors = diagnostics.some((d) => d.level === "error");
153
+ const native = isNativeEngineAvailable();
154
+
155
+ const payload: ProjectInfoPayload = {
156
+ status: hasErrors ? "degraded" : "ok",
157
+ framework: FRAMEWORK_NAME,
158
+ version: FRAMEWORK_VERSION,
159
+ projectRoot,
160
+ agentRoot,
161
+ packageName,
162
+ engine: {
163
+ native,
164
+ mode: native ? "napi" : "stub",
165
+ },
166
+ gateway: { configured: isGatewayConfigured() },
167
+ discovery,
168
+ loadedSlots: {
169
+ ...summarizeLoadedSlots(loadedSlots, agentRoot),
170
+ subagents: subagentSummary,
171
+ },
172
+ catalogs,
173
+ diagnostics,
174
+ languageTools: PLASM_LANGUAGE_TOOLS,
175
+ };
176
+
177
+ if (dev) {
178
+ const scheduleCrons = exportScheduleCronManifest(loadedSlots.schedules);
179
+ payload.dev = {
180
+ model: dev.definition.model,
181
+ compaction: dev.definition.compaction ?? null,
182
+ modelOptions: dev.definition.modelOptions ?? null,
183
+ build: dev.definition.build ?? null,
184
+ experimental: dev.definition.experimental ?? null,
185
+ scheduleCrons,
186
+ sessions: { active: dev.sessionCount },
187
+ routes: {
188
+ health: "GET /plasm/v1/health",
189
+ info: "GET /plasm/v1/info",
190
+ session: "POST /plasm/v1/session",
191
+ sessionContinue: "POST /plasm/v1/session/:sessionId",
192
+ sessionStream: "GET /plasm/v1/session/:id/stream",
193
+ channels: listChannelRoutes(loadedSlots.channels),
194
+ scheduleCrons: scheduleCrons.crons.map((c) => `GET ${c.path}`),
195
+ operator: "GET /operator",
196
+ },
197
+ };
198
+ }
199
+
200
+ return payload;
201
+ }
202
+
203
+ export function formatPlasmInfoHuman(info: ProjectInfoPayload): string {
204
+ const lines: string[] = [];
205
+ lines.push(`${info.framework} status=${info.status}`);
206
+ lines.push(`project: ${info.projectRoot}`);
207
+ lines.push(`agent: ${info.agentRoot}`);
208
+ lines.push(`engine: ${info.engine.mode}${info.engine.native ? " (native)" : " (fallback)"}`);
209
+ lines.push(
210
+ `gateway: ${info.gateway.configured ? "configured" : "missing — set AI_GATEWAY_API_KEY or run plasm-agent link"}`,
211
+ );
212
+ lines.push("");
213
+ lines.push(`catalogs (${info.discovery.catalogs.length})`);
214
+ for (const c of info.catalogs) {
215
+ const fresh = c.stubFresh?.fresh ? "fresh" : c.stubFresh ? "stale" : "missing";
216
+ lines.push(` - ${c.name} entry_id=${c.entryId ?? "?"} stub=${fresh}`);
217
+ }
218
+ lines.push(`channels: ${info.discovery.channels.length} schedules: ${info.discovery.schedules.length}`);
219
+ lines.push(`skills: ${info.discovery.skills.length} hooks: ${info.discovery.hooks.length}`);
220
+ lines.push(`subagents: ${info.loadedSlots.subagents.length}`);
221
+ if (info.diagnostics.length) {
222
+ lines.push("");
223
+ lines.push("diagnostics:");
224
+ for (const d of info.diagnostics) {
225
+ lines.push(` [${d.level}] ${d.slot}: ${d.message} (${d.path})`);
226
+ }
227
+ }
228
+ return lines.join("\n");
229
+ }