@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
package/src/index.ts ADDED
@@ -0,0 +1,296 @@
1
+ export {
2
+ createAgentFromDefinition,
3
+ createAgentFromProject,
4
+ createPlasmAgentConfig,
5
+ defineAgent,
6
+ resolveAgentDefinition,
7
+ } from "./define-agent.js";
8
+ export type {
9
+ AgentBuildConfig,
10
+ AgentCompactionConfig,
11
+ AgentDefinition,
12
+ AgentExperimentalConfig,
13
+ AgentModelOptions,
14
+ AgentWorkflowDefinition,
15
+ AgentWorkflowWorldDefinition,
16
+ CreateAgentFromDefinitionOptions,
17
+ ResolvedAgentDefinition,
18
+ } from "./define-agent.js";
19
+
20
+ export {
21
+ createAuthoringContext,
22
+ } from "./authoring/context.js";
23
+ export type { AuthoringContext, CreateAuthoringContextOptions } from "./authoring/context.js";
24
+ export { defineChannel, validateChannelRoute, isChannelDefinition } from "./authoring/define-channel.js";
25
+ export type {
26
+ ChannelDefinition,
27
+ ChannelHandler,
28
+ ChannelRoute,
29
+ DefineChannelInput,
30
+ HttpMethod,
31
+ } from "./authoring/define-channel.js";
32
+ export { defineHook, isHookDefinition } from "./authoring/define-hook.js";
33
+ export type { DefineHookInput, HookDefinition, HookEvent, HookHandler } from "./authoring/define-hook.js";
34
+ export { HookRunner } from "./authoring/hook-runner.js";
35
+ export { defineSchedule, cronIntervalMs, isScheduleDefinition } from "./authoring/define-schedule.js";
36
+ export type { DefineScheduleInput, ScheduleDefinition, ScheduleHandler } from "./authoring/define-schedule.js";
37
+ export { defineSkill, isSkillDefinition } from "./authoring/define-skill.js";
38
+ export type { DefineSkillInput, SkillDefinition } from "./authoring/define-skill.js";
39
+ export {
40
+ loadAuthoredSlots,
41
+ summarizeLoadedSlots,
42
+ } from "./authoring/slot-loader.js";
43
+ export type {
44
+ LoadedChannel,
45
+ LoadedHook,
46
+ LoadedProjectSlots,
47
+ LoadedSchedule,
48
+ LoadedSkill,
49
+ LoadedSlotsSummary,
50
+ LoadAuthoredSlotsOptions,
51
+ } from "./authoring/slot-loader.js";
52
+ export { tryHandleChannelRoute, listChannelRoutes } from "./authoring/channel-dispatch.js";
53
+ export {
54
+ exportScheduleCronManifest,
55
+ startScheduleTimers,
56
+ tryHandleScheduleCronRoute,
57
+ } from "./authoring/schedule-manager.js";
58
+ export type { ScheduleCronManifest, ScheduleHandle } from "./authoring/schedule-manager.js";
59
+ export {
60
+ createSubagentRegistry,
61
+ loadSubagents,
62
+ summarizeSubagents,
63
+ } from "./authoring/subagent-loader.js";
64
+ export type { LoadedSubagent, SubagentRegistry } from "./authoring/subagent-loader.js";
65
+ export { createHarnessTools, renderSkillIndex } from "./tools/harness-tools.js";
66
+ export { maybeCompactMessages } from "./runtime/compaction.js";
67
+
68
+ export { defineEval, isEvalDefinition } from "./evals/define-eval.js";
69
+ export type { DefineEvalInput, EvalAssert, EvalDefinition, EvalToolName } from "./evals/define-eval.js";
70
+ export {
71
+ discoverEvalFiles,
72
+ runAllEvals,
73
+ runEval,
74
+ runEvalFile,
75
+ requireLiveEvalGateway,
76
+ } from "./evals/run-eval.js";
77
+ export type { EvalRunResult } from "./evals/run-eval.js";
78
+
79
+ export {
80
+ authoredSlots,
81
+ walkAgentProject,
82
+ } from "./discovery/project-walker.js";
83
+ export type {
84
+ AuthoredSlot,
85
+ DiscoveredCatalog,
86
+ DiscoveredInstructions,
87
+ DiscoveredNamedFile,
88
+ DiscoveredSubagent,
89
+ DiscoveryDiagnostic,
90
+ ProjectDiscovery,
91
+ } from "./discovery/project-walker.js";
92
+
93
+ export { createDevServer, startDevServer } from "./dev/server.js";
94
+ export type { DevServerHandle, DevServerOptions } from "./dev/server.js";
95
+
96
+ export {
97
+ createPlasmApp,
98
+ handlePlasmRequest,
99
+ handlePlasmOperatorRequest,
100
+ normalizePlasmPathname,
101
+ rewriteRequestPath,
102
+ vercelPlasmHandler,
103
+ } from "./server/plasm-handler.js";
104
+ export type { PlasmApp, PlasmAppMode, PlasmAppOptions, VercelHandler } from "./server/plasm-handler.js";
105
+
106
+ export { SymbolRegistry } from "./symbol-registry.js";
107
+ export type { SymbolBinding, SymbolKind, SymbolRegistrySnapshot } from "./symbol-registry.js";
108
+
109
+ export {
110
+ LocalSessionStore,
111
+ SessionManager,
112
+ } from "./session-state.js";
113
+ export type {
114
+ AgentSessionState,
115
+ ExecuteSessionRef,
116
+ SessionStore,
117
+ TeachingWave,
118
+ } from "./session-state.js";
119
+
120
+ export {
121
+ CatalogManifestSchema,
122
+ FilesystemCatalogLoader,
123
+ } from "./catalog/loader.js";
124
+ export type {
125
+ CatalogLoader,
126
+ CatalogManifest,
127
+ LoadedCatalog,
128
+ } from "./catalog/loader.js";
129
+
130
+ export { StubPlasmEngine, NapiPlasmEngine, createEngine, isNativeEngineAvailable } from "./engine/napi-binding.js";
131
+ export { createDefaultHostTransport } from "./engine/host-transport.js";
132
+ export type { HostTransportOptions } from "./engine/host-transport.js";
133
+ export { createProductionHostTransport } from "./engine/create-host-transport.js";
134
+ export { loadAgentEnv } from "./load-env.js";
135
+ export {
136
+ connectorUidForEntry,
137
+ connectAuthOptionsForEntry,
138
+ resolveConnectBearer,
139
+ ConnectAuthorizationRequiredError,
140
+ } from "./engine/connect-auth.js";
141
+ export type { ConnectAuthOptions, ConnectTokenSubject } from "./engine/connect-auth.js";
142
+ export type {
143
+ DryRunResult,
144
+ HostTransportFn,
145
+ HostTransportRequest,
146
+ HostTransportResponse,
147
+ PlasmEngine,
148
+ ResolvedPlanPayload,
149
+ TeachingExposureResult,
150
+ } from "./engine/napi-binding.js";
151
+
152
+ export {
153
+ PlasmSpanAttributes,
154
+ createAgentTelemetry,
155
+ registerAgentInstrumentation,
156
+ } from "./instrumentation.js";
157
+ export type { AgentInstrumentationOptions } from "./instrumentation.js";
158
+
159
+ export { AgentRuntime } from "./runtime/agent-runtime.js";
160
+ export type {
161
+ AgentArchiveStore,
162
+ AgentRuntimeConfig,
163
+ DiscoverInput,
164
+ PlasmContextInput,
165
+ PlasmPlanInput,
166
+ PlasmRunInput,
167
+ } from "./runtime/agent-runtime.js";
168
+
169
+ export { PlasmAgent } from "./runtime/plasm-agent.js";
170
+ export type { AgentTurnResult, PlasmAgentConfig } from "./runtime/plasm-agent.js";
171
+
172
+ export {
173
+ formatLogicalSessionWireRef,
174
+ mintLogicalSessionId,
175
+ parseLogicalSessionWireRef,
176
+ } from "./runtime/logical-session.js";
177
+
178
+ export { createPlasmTools } from "./tools/plasm-tools.js";
179
+ export type { PlasmTools } from "./tools/plasm-tools.js";
180
+
181
+ export {
182
+ DISCOVER_TOOL_DESCRIPTION,
183
+ PLASM_CONTEXT_TOOL_DESCRIPTION,
184
+ PLASM_RUN_TOOL_DESCRIPTION,
185
+ PLASM_TOOL_DESCRIPTION,
186
+ } from "./tools/descriptions.js";
187
+
188
+ export { createOperatorRoutes, nitroOperatorHandler } from "./operator/routes.js";
189
+ export { renderOperatorShell } from "./operator/ui-shell.js";
190
+ export type { NitroHandler, OperatorHandler, OperatorRouteContext } from "./operator/routes.js";
191
+
192
+ export {
193
+ createAgentStateStore,
194
+ resolveStateBackend,
195
+ } from "./state/define-state.js";
196
+ export type { AgentStateStore, StateBackend } from "./state/define-state.js";
197
+
198
+ export {
199
+ bootstrapWorkflowWorld,
200
+ resolveWorkflowWorldType,
201
+ } from "./workflow/world-bootstrap.js";
202
+ export type { WorkflowWorldType } from "./workflow/world-bootstrap.js";
203
+
204
+ export {
205
+ LocalArchiveStore,
206
+ computeRunId,
207
+ resolveArchivePaths,
208
+ UnimplementedBlobArchiveAdapter,
209
+ UnimplementedKvArchiveIndexAdapter,
210
+ } from "./archive/index.js";
211
+ export { createArchiveStore, resolveArchiveBackend } from "./archive/resolve-backend.js";
212
+ export { ProdArchiveStore } from "./archive/prod-archive-store.js";
213
+ export { VercelBlobArchiveAdapter } from "./archive/vercel-blob-adapter.js";
214
+ export { VercelKvArchiveIndexAdapter } from "./archive/vercel-kv-adapter.js";
215
+ export { PostgresArchiveIndexAdapter } from "./archive/postgres-kv-adapter.js";
216
+ export type {
217
+ ArchivePaths,
218
+ BlobArchiveAdapter,
219
+ KvArchiveIndexAdapter,
220
+ PlanArchiveSnapshot,
221
+ RunSnapshot,
222
+ RunIdBundle,
223
+ TraceDetail,
224
+ TraceRecord,
225
+ TraceSummary,
226
+ } from "./archive/index.js";
227
+
228
+ export { plasmSpans, withPlasmSpan, activeTraceId } from "./telemetry/plasm-spans.js";
229
+ export type { PlasmSpanContext } from "./telemetry/plasm-spans.js";
230
+ export type {
231
+ OperatorCatalogEntry,
232
+ OperatorCatalogsResponse,
233
+ OperatorHealthResponse,
234
+ OperatorOpsResponse,
235
+ OperatorPlanCommit,
236
+ OperatorPlansResponse,
237
+ OperatorSessionEntry,
238
+ OperatorSessionsResponse,
239
+ OperatorStubFreshness,
240
+ } from "./operator/types.js";
241
+
242
+ export {
243
+ computeCatalogCgsHash,
244
+ generateAllStubs,
245
+ generateStubForCatalog,
246
+ parseCgsDomain,
247
+ parseDomainYaml,
248
+ readStubProvenance,
249
+ renderStubModule,
250
+ stubFreshness,
251
+ } from "./stubs/generator.js";
252
+ export type {
253
+ ParsedCapability,
254
+ ParsedCgsDomain,
255
+ ParsedDomain,
256
+ ParsedEntity,
257
+ StubGenerationResult,
258
+ StubProvenance,
259
+ } from "./stubs/generator.js";
260
+
261
+ export {
262
+ assignCapabilityBindings,
263
+ assignEntitySymbols,
264
+ stubEntityNames,
265
+ } from "./stubs/stub-symbols.js";
266
+ export type { CapabilityBinding, EntitySymbolBinding } from "./stubs/stub-symbols.js";
267
+
268
+ export {
269
+ valueDomainToTsType,
270
+ renderEntityType,
271
+ renderCapabilityInputType,
272
+ } from "./stubs/cgs-ts-types.js";
273
+
274
+ export {
275
+ createCatalogClient,
276
+ dryRunProgram,
277
+ ensureCatalogLoaded,
278
+ ensureStubSession,
279
+ executeRows,
280
+ buildDottedArgs,
281
+ plasmLiteral,
282
+ plasmNumber,
283
+ plasmBoolean,
284
+ } from "./stubs/catalog-client.js";
285
+ export type {
286
+ CatalogClientOptions,
287
+ ExecuteRowsResult,
288
+ StubInvokeOptions,
289
+ } from "./stubs/catalog-client.js";
290
+
291
+ export { createProgramBuilder } from "./stubs/program-builder.js";
292
+ export type {
293
+ ProgramBuilder,
294
+ ProgramBuilderOptions,
295
+ ProgramBuilderProvenance,
296
+ } from "./stubs/program-builder.js";
@@ -0,0 +1,56 @@
1
+ import { OpenTelemetryIntegration } from "@ai-sdk/otel";
2
+ import type { AttributeValue } from "@opentelemetry/api";
3
+ import {
4
+ registerTelemetryIntegration,
5
+ type TelemetryIntegration,
6
+ type TelemetrySettings,
7
+ } from "ai";
8
+
9
+ /** OpenTelemetry attribute keys for Plasm agent spans (Section 9). */
10
+ export const PlasmSpanAttributes = {
11
+ SESSION_ID: "plasm.session_id",
12
+ INTENT: "plasm.intent",
13
+ LOGICAL_SESSION_REF: "plasm.logical_session_ref",
14
+ CATALOG_CGS_HASH: "plasm.catalog_cgs_hash",
15
+ PLAN_COMMIT_REF: "plasm.plan_commit_ref",
16
+ RUN_ID: "plasm.run_id",
17
+ ENTRY_ID: "plasm.entry_id",
18
+ TOOL_NAME: "plasm.tool.name",
19
+ TRANSPORT_HOST: "plasm.transport.host",
20
+ TRANSPORT_STATUS: "plasm.transport.status",
21
+ } as const;
22
+
23
+ export interface AgentInstrumentationOptions {
24
+ serviceName?: string;
25
+ tracer?: TelemetrySettings["tracer"];
26
+ }
27
+
28
+ let registered = false;
29
+
30
+ function otelIntegration(options: AgentInstrumentationOptions): TelemetryIntegration {
31
+ return new OpenTelemetryIntegration({ tracer: options.tracer }) as unknown as TelemetryIntegration;
32
+ }
33
+
34
+ /** Register global AI SDK OTEL integration (eve-style auto-discovered instrumentation). */
35
+ export function registerAgentInstrumentation(
36
+ options: AgentInstrumentationOptions = {},
37
+ ): void {
38
+ if (registered) return;
39
+ registerTelemetryIntegration(otelIntegration(options));
40
+ registered = true;
41
+ void options.serviceName;
42
+ }
43
+
44
+ /** Per-call AI SDK telemetry settings with OpenTelemetry integration. */
45
+ export function createAgentTelemetry(
46
+ options: AgentInstrumentationOptions = {},
47
+ ): TelemetrySettings {
48
+ registerAgentInstrumentation(options);
49
+ return {
50
+ isEnabled: true,
51
+ metadata: {
52
+ "service.name": options.serviceName ?? "plasm-agent",
53
+ } satisfies Record<string, AttributeValue>,
54
+ integrations: otelIntegration(options),
55
+ };
56
+ }
@@ -0,0 +1,63 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
6
+
7
+ /** Repo root (`plasm/`) from `packages/plasm-agent`. */
8
+ export function defaultEnvFileCandidates(): string[] {
9
+ return [
10
+ path.join(packageRoot, ".env"),
11
+ path.join(packageRoot, "../../.env"),
12
+ path.join(packageRoot, "../../../.env"),
13
+ path.join(packageRoot, "agent", ".env"),
14
+ ];
15
+ }
16
+
17
+ function parseEnvLine(line: string): { key: string; value: string } | null {
18
+ const trimmed = line.trim();
19
+ if (!trimmed || trimmed.startsWith("#")) return null;
20
+ const withoutExport = trimmed.startsWith("export ")
21
+ ? trimmed.slice("export ".length).trim()
22
+ : trimmed;
23
+ const eq = withoutExport.indexOf("=");
24
+ if (eq <= 0) return null;
25
+ const key = withoutExport.slice(0, eq).trim();
26
+ let value = withoutExport.slice(eq + 1).trim();
27
+ if (
28
+ (value.startsWith('"') && value.endsWith('"')) ||
29
+ (value.startsWith("'") && value.endsWith("'"))
30
+ ) {
31
+ value = value.slice(1, -1);
32
+ }
33
+ return { key, value };
34
+ }
35
+
36
+ /** Load `.env` into `process.env` without overriding existing variables. */
37
+ export function loadAgentEnv(files: string[] = defaultEnvFileCandidates()): string | undefined {
38
+ let loaded: string | undefined;
39
+ for (const file of files) {
40
+ if (!existsSync(file)) continue;
41
+ const text = readFileSync(file, "utf8");
42
+ for (const line of text.split(/\r?\n/)) {
43
+ const parsed = parseEnvLine(line);
44
+ if (!parsed) continue;
45
+ if (process.env[parsed.key] === undefined) {
46
+ process.env[parsed.key] = parsed.value;
47
+ }
48
+ }
49
+ loaded = file;
50
+ }
51
+
52
+ // User alias → Vercel AI Gateway canonical name.
53
+ if (!process.env.AI_GATEWAY_API_KEY?.trim()) {
54
+ const alias =
55
+ process.env.AI_API_GATEWAY_KEY?.trim() ??
56
+ process.env.AI_GATEWAY_KEY?.trim();
57
+ if (alias) {
58
+ process.env.AI_GATEWAY_API_KEY = alias;
59
+ }
60
+ }
61
+
62
+ return loaded;
63
+ }
@@ -0,0 +1,287 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+
4
+ import { FilesystemCatalogLoader } from "../catalog/loader.js";
5
+ import type { LoadedCatalog } from "../catalog/loader.js";
6
+ import { isNativeEngineAvailable } from "../engine/napi-binding.js";
7
+ import { AgentRuntime } from "../runtime/agent-runtime.js";
8
+ import { createAgentStateStore } from "../state/define-state.js";
9
+ import {
10
+ parseDomainYaml,
11
+ stubFreshness,
12
+ } from "../stubs/generator.js";
13
+ import { resolveCatalogLiveHash } from "../stubs/catalog-hash.js";
14
+
15
+ import type {
16
+ OperatorCatalogsResponse,
17
+ OperatorHealthResponse,
18
+ OperatorOpsResponse,
19
+ OperatorPlansResponse,
20
+ OperatorSessionsResponse,
21
+ } from "./types.js";
22
+
23
+ export type NitroHandler = (event: {
24
+ req: { method?: string; url?: string };
25
+ res: { statusCode: number; end: (body: string) => void };
26
+ }) => void | Promise<void>;
27
+
28
+ export interface OperatorRouteContext {
29
+ agentRoot: string;
30
+ tenantScope?: string;
31
+ /** Pre-bootstrapped runtime; when omitted, catalog routes bootstrap on first request. */
32
+ runtime?: AgentRuntime;
33
+ }
34
+
35
+ export interface OperatorHandler {
36
+ health(): Promise<OperatorHealthResponse>;
37
+ listCatalogs(): Promise<OperatorCatalogsResponse>;
38
+ listSessions(): Promise<OperatorSessionsResponse>;
39
+ listPlans(): Promise<OperatorPlansResponse>;
40
+ ops(): Promise<OperatorOpsResponse>;
41
+ listTraces(): Promise<{ traces: unknown[] }>;
42
+ listArchives(): Promise<{ plans: unknown[]; runs: unknown[]; paths: unknown }>;
43
+ listRuns(): Promise<{ runs: unknown[] }>;
44
+ }
45
+
46
+ interface BootstrappedCatalog {
47
+ catalog: LoadedCatalog;
48
+ catalogCgsHash: string;
49
+ authScheme?: string;
50
+ entityCount: number;
51
+ capabilityCount: number;
52
+ }
53
+
54
+ async function resolveLiveHash(catalog: LoadedCatalog, runtime?: AgentRuntime): Promise<string> {
55
+ if (catalog.manifest.cgsHash) return catalog.manifest.cgsHash;
56
+ if (runtime) {
57
+ const loaded = runtime.listCatalogs().find((c) => c.manifest.entryId === catalog.manifest.entryId);
58
+ if (loaded?.manifest.cgsHash) return loaded.manifest.cgsHash;
59
+ }
60
+ return resolveCatalogLiveHash(catalog.rootDir);
61
+ }
62
+
63
+ async function bootstrapCatalogs(ctx: OperatorRouteContext): Promise<BootstrappedCatalog[]> {
64
+ let runtime = ctx.runtime;
65
+ if (!runtime) {
66
+ runtime = new AgentRuntime({ agentRoot: ctx.agentRoot });
67
+ await runtime.bootstrap();
68
+ ctx.runtime = runtime;
69
+ }
70
+
71
+ const loader = new FilesystemCatalogLoader();
72
+ const discovered = await loader.discover(ctx.agentRoot);
73
+ const loaded = runtime.listCatalogs();
74
+
75
+ const byEntry = new Map<string, LoadedCatalog>();
76
+ for (const catalog of [...discovered, ...loaded]) {
77
+ byEntry.set(catalog.manifest.entryId, catalog);
78
+ }
79
+
80
+ const bootstrapped: BootstrappedCatalog[] = [];
81
+ for (const catalog of byEntry.values()) {
82
+ const domainYaml = await readFile(path.join(catalog.rootDir, "domain.yaml"), "utf8");
83
+ const parsed = parseDomainYaml(domainYaml, path.basename(catalog.rootDir));
84
+ const catalogCgsHash = await resolveLiveHash(catalog, runtime);
85
+ bootstrapped.push({
86
+ catalog: {
87
+ ...catalog,
88
+ manifest: { ...catalog.manifest, cgsHash: catalogCgsHash },
89
+ },
90
+ catalogCgsHash,
91
+ authScheme: parsed.authScheme,
92
+ entityCount: parsed.entities.length,
93
+ capabilityCount: parsed.capabilities.length,
94
+ });
95
+ }
96
+
97
+ bootstrapped.sort((a, b) => a.catalog.manifest.entryId.localeCompare(b.catalog.manifest.entryId));
98
+ return bootstrapped;
99
+ }
100
+
101
+ export function createOperatorRoutes(ctx: OperatorRouteContext): OperatorHandler {
102
+ return {
103
+ async health() {
104
+ return { status: "ok" };
105
+ },
106
+
107
+ async listCatalogs() {
108
+ const bootstrapped = await bootstrapCatalogs(ctx);
109
+ const stubsDir = path.join(ctx.agentRoot, ".plasm", "stubs");
110
+ const catalogs = await Promise.all(
111
+ bootstrapped.map(async (item) => {
112
+ const stubPath = path.join(stubsDir, `${item.catalog.manifest.entryId}.ts`);
113
+ const stub = await stubFreshness(item.catalogCgsHash, stubPath);
114
+ return {
115
+ entryId: item.catalog.manifest.entryId,
116
+ label: item.catalog.manifest.label ?? item.catalog.manifest.entryId,
117
+ rootDir: item.catalog.rootDir,
118
+ catalogCgsHash: item.catalogCgsHash,
119
+ authScheme: item.authScheme,
120
+ entityCount: item.entityCount,
121
+ capabilityCount: item.capabilityCount,
122
+ stub,
123
+ };
124
+ }),
125
+ );
126
+ return { catalogs, generatedAt: new Date().toISOString() };
127
+ },
128
+
129
+ async listSessions() {
130
+ const store = createAgentStateStore({
131
+ agentRoot: ctx.agentRoot,
132
+ tenantScope: ctx.tenantScope ?? "local",
133
+ });
134
+ const intents = await store.listIntents();
135
+ const sessions = await Promise.all(
136
+ intents.map(async (intent) => {
137
+ const state = await store.get(intent);
138
+ if (!state) return null;
139
+ return {
140
+ intent: state.intent,
141
+ logicalSessionRef: state.logicalSessionRef,
142
+ logicalSessionId: state.logicalSessionId,
143
+ waveCount: state.waves.length,
144
+ seedCount: state.seeds.length,
145
+ planCommitCount: state.planCommits.length,
146
+ updatedAt: state.updatedAt,
147
+ };
148
+ }),
149
+ );
150
+ return {
151
+ sessions: sessions.filter((s): s is NonNullable<typeof s> => s !== null),
152
+ };
153
+ },
154
+
155
+ async listPlans() {
156
+ const store = createAgentStateStore({
157
+ agentRoot: ctx.agentRoot,
158
+ tenantScope: ctx.tenantScope ?? "local",
159
+ });
160
+ const intents = await store.listIntents();
161
+ const plans = [];
162
+ for (const intent of intents) {
163
+ const state = await store.get(intent);
164
+ if (!state) continue;
165
+ for (const commit of state.planCommits) {
166
+ plans.push({
167
+ intent: state.intent,
168
+ logicalSessionRef: state.logicalSessionRef,
169
+ ref: commit.ref,
170
+ program: commit.program,
171
+ at: commit.at,
172
+ });
173
+ }
174
+ }
175
+ plans.sort((a, b) => b.at.localeCompare(a.at));
176
+ return { plans };
177
+ },
178
+
179
+ async ops() {
180
+ const bootstrapped = await bootstrapCatalogs(ctx);
181
+ const store = createAgentStateStore({
182
+ agentRoot: ctx.agentRoot,
183
+ tenantScope: ctx.tenantScope ?? "local",
184
+ });
185
+ const intents = await store.listIntents();
186
+ let planCommitCount = 0;
187
+ for (const intent of intents) {
188
+ const state = await store.get(intent);
189
+ planCommitCount += state?.planCommits.length ?? 0;
190
+ }
191
+ const native = isNativeEngineAvailable();
192
+ return {
193
+ nativeEngineAvailable: native,
194
+ engineMode: native ? "napi" : "stub",
195
+ agentRoot: ctx.agentRoot,
196
+ catalogCount: bootstrapped.length,
197
+ sessionCount: intents.length,
198
+ planCommitCount,
199
+ };
200
+ },
201
+
202
+ async listTraces() {
203
+ const { LocalArchiveStore } = await import("../archive/index.js");
204
+ const archive = LocalArchiveStore.fromAgentRoot(ctx.agentRoot);
205
+ const tenantScope = ctx.tenantScope ?? "local";
206
+ const traces = await archive.listTraces(tenantScope);
207
+ return { traces };
208
+ },
209
+
210
+ async listArchives() {
211
+ const { LocalArchiveStore } = await import("../archive/index.js");
212
+ const archive = LocalArchiveStore.fromAgentRoot(ctx.agentRoot);
213
+ const { plans, runs, paths } = await archive.listArchives();
214
+ return { plans, runs, paths };
215
+ },
216
+
217
+ async listRuns() {
218
+ const { LocalArchiveStore } = await import("../archive/index.js");
219
+ const archive = LocalArchiveStore.fromAgentRoot(ctx.agentRoot);
220
+ const runs = await archive.listRuns();
221
+ return { runs };
222
+ },
223
+ };
224
+ }
225
+
226
+ function routePath(url: string): string {
227
+ const pathOnly = url.split("?")[0] ?? url;
228
+ if (pathOnly.startsWith("/operator/")) {
229
+ return pathOnly.slice("/operator".length);
230
+ }
231
+ return pathOnly;
232
+ }
233
+
234
+ function matchesRoute(pathOnly: string, route: string): boolean {
235
+ return pathOnly === route || pathOnly.startsWith(`${route}?`);
236
+ }
237
+
238
+ export function nitroOperatorHandler(ctx: OperatorRouteContext): NitroHandler {
239
+ const routes = createOperatorRoutes(ctx);
240
+ return async (event) => {
241
+ const pathOnly = routePath(event.req.url ?? "/");
242
+
243
+ if (matchesRoute(pathOnly, "/health") || matchesRoute(pathOnly, "/ops/health")) {
244
+ event.res.statusCode = 200;
245
+ event.res.end(JSON.stringify(await routes.health()));
246
+ return;
247
+ }
248
+ if (matchesRoute(pathOnly, "/catalogs")) {
249
+ event.res.statusCode = 200;
250
+ event.res.end(JSON.stringify(await routes.listCatalogs()));
251
+ return;
252
+ }
253
+ if (matchesRoute(pathOnly, "/sessions")) {
254
+ event.res.statusCode = 200;
255
+ event.res.end(JSON.stringify(await routes.listSessions()));
256
+ return;
257
+ }
258
+ if (matchesRoute(pathOnly, "/plans")) {
259
+ event.res.statusCode = 200;
260
+ event.res.end(JSON.stringify(await routes.listPlans()));
261
+ return;
262
+ }
263
+ if (matchesRoute(pathOnly, "/ops")) {
264
+ event.res.statusCode = 200;
265
+ event.res.end(JSON.stringify(await routes.ops()));
266
+ return;
267
+ }
268
+ if (matchesRoute(pathOnly, "/traces")) {
269
+ event.res.statusCode = 200;
270
+ event.res.end(JSON.stringify(await routes.listTraces()));
271
+ return;
272
+ }
273
+ if (matchesRoute(pathOnly, "/archives")) {
274
+ event.res.statusCode = 200;
275
+ event.res.end(JSON.stringify(await routes.listArchives()));
276
+ return;
277
+ }
278
+ if (matchesRoute(pathOnly, "/runs")) {
279
+ event.res.statusCode = 200;
280
+ event.res.end(JSON.stringify(await routes.listRuns()));
281
+ return;
282
+ }
283
+
284
+ event.res.statusCode = 404;
285
+ event.res.end(JSON.stringify({ error: "not_found", path: pathOnly }));
286
+ };
287
+ }