@plasm_lang/vercel-agent 0.3.123 → 0.3.125

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasm_lang/vercel-agent",
3
- "version": "0.3.123",
3
+ "version": "0.3.125",
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": {
@@ -65,7 +65,7 @@
65
65
  "dependencies": {
66
66
  "@ai-sdk/otel": "1.0.14",
67
67
  "@opentelemetry/api": "^1.9.0",
68
- "@plasm_lang/engine": "^0.3.123",
68
+ "@plasm_lang/engine": "^0.3.125",
69
69
  "@vercel/blob": "^0.27.3",
70
70
  "@vercel/connect": "^0.2.6",
71
71
  "@vercel/kv": "^3.0.0",
package/src/index.ts CHANGED
@@ -158,6 +158,7 @@ export type {
158
158
  export {
159
159
  PlasmSpanAttributes,
160
160
  createAgentTelemetry,
161
+ ensureOtelIntegration,
161
162
  registerAgentInstrumentation,
162
163
  } from "./instrumentation.js";
163
164
  export type { AgentInstrumentationOptions } from "./instrumentation.js";
@@ -1,9 +1,5 @@
1
- import { LegacyOpenTelemetry } from "@ai-sdk/otel";
2
- import {
3
- registerTelemetry,
4
- type Telemetry,
5
- type TelemetryOptions,
6
- } from "ai";
1
+ import { OpenTelemetry } from "@ai-sdk/otel";
2
+ import { registerTelemetry, type TelemetryOptions } from "ai";
7
3
 
8
4
  /** OpenTelemetry attribute keys for Plasm agent spans (Section 9). */
9
5
  export const PlasmSpanAttributes = {
@@ -25,27 +21,29 @@ export interface AgentInstrumentationOptions {
25
21
 
26
22
  let registered = false;
27
23
 
28
- function otelIntegration(_options: AgentInstrumentationOptions): Telemetry {
29
- return new LegacyOpenTelemetry();
24
+ /** Register global AI SDK OTEL integration (Eve uses OpenTelemetry + runtimeContext). */
25
+ export function ensureOtelIntegration(): void {
26
+ if (registered) return;
27
+ registerTelemetry(new OpenTelemetry({ runtimeContext: true }));
28
+ registered = true;
30
29
  }
31
30
 
32
31
  /** Register global AI SDK OTEL integration (eve-style auto-discovered instrumentation). */
33
32
  export function registerAgentInstrumentation(
34
33
  options: AgentInstrumentationOptions = {},
35
34
  ): void {
36
- if (registered) return;
37
- registerTelemetry(otelIntegration(options));
38
- registered = true;
35
+ ensureOtelIntegration();
39
36
  void options.serviceName;
40
37
  }
41
38
 
42
- /** Per-call AI SDK telemetry settings with OpenTelemetry integration. */
39
+ /** Per-call AI SDK telemetry settings (OTEL registered in agent/instrumentation.ts). */
43
40
  export function createAgentTelemetry(
44
41
  options: AgentInstrumentationOptions = {},
45
42
  ): TelemetryOptions {
46
- registerAgentInstrumentation(options);
47
43
  return {
48
44
  isEnabled: true,
49
45
  functionId: options.serviceName ?? "plasm-agent",
46
+ recordInputs: true,
47
+ recordOutputs: true,
50
48
  };
51
49
  }
@@ -7,10 +7,11 @@ import { exportScheduleTaskManifest } from "../authoring/schedule-manager.js";
7
7
  import type { AgentDefinition } from "../define-agent.js";
8
8
  import type { ProjectDiscovery } from "../discovery/project-walker.js";
9
9
  import { frameworkPackageVersion } from "../package-version.js";
10
- import { plasmAgentSummaryPath } from "./paths.js";
10
+ import { plasmAgentSummaryPath, eveAgentSummaryPath } from "./paths.js";
11
11
 
12
12
  export const VERCEL_PLASM_AGENT_SUMMARY_KIND = "vercel-plasm-agent-summary";
13
13
  export const VERCEL_PLASM_AGENT_SUMMARY_VERSION = 3;
14
+ export const VERCEL_EVE_AGENT_SUMMARY_KIND = "vercel-eve-agent-summary";
14
15
 
15
16
  export interface PlasmAgentSummaryPayload {
16
17
  kind: typeof VERCEL_PLASM_AGENT_SUMMARY_KIND;
@@ -131,3 +132,61 @@ export async function emitPlasmAgentSummary(options: {
131
132
  await writeFile(outPath, `${JSON.stringify(options.summary, null, 2)}\n`, "utf8");
132
133
  return outPath;
133
134
  }
135
+
136
+ function mapSkillSourceKind(sourceKind: string): "markdown" | "module" | "skill-package" {
137
+ if (sourceKind === "markdown") return "markdown";
138
+ if (sourceKind === "skill-package") return "skill-package";
139
+ return "module";
140
+ }
141
+
142
+ /** Eve-compatible summary for Vercel Agent Runs build ingestion (`.eve/agent-summary.json`). */
143
+ export function toEveAgentSummary(
144
+ summary: PlasmAgentSummaryPayload,
145
+ instructionsMarkdown?: string | null,
146
+ ): Record<string, unknown> {
147
+ return {
148
+ kind: VERCEL_EVE_AGENT_SUMMARY_KIND,
149
+ schemaVersion: VERCEL_PLASM_AGENT_SUMMARY_VERSION,
150
+ generatorVersion: summary.generatorVersion,
151
+ agent: {
152
+ name: summary.agent.name,
153
+ ...(summary.agent.description ? { description: summary.agent.description } : {}),
154
+ modelId: summary.agent.modelId,
155
+ },
156
+ instructions: summary.instructions
157
+ ? {
158
+ logicalPath: summary.instructions.logicalPath,
159
+ sourceKind: summary.instructions.sourceKind === "markdown" ? "markdown" : "module",
160
+ markdown: instructionsMarkdown ?? summary.instructions.markdown ?? "",
161
+ }
162
+ : null,
163
+ schedules: summary.schedules,
164
+ tools: summary.tools,
165
+ skills: summary.skills.map((skill) => ({
166
+ name: skill.name,
167
+ description: skill.description,
168
+ logicalPath: skill.logicalPath,
169
+ sourceKind: mapSkillSourceKind(skill.sourceKind),
170
+ })),
171
+ connections: summary.connections,
172
+ channels: summary.channels,
173
+ sandbox: summary.sandbox,
174
+ subagents: summary.subagents,
175
+ diagnostics: summary.diagnostics,
176
+ };
177
+ }
178
+
179
+ export async function emitEveCompatibleAgentSummary(options: {
180
+ projectRoot: string;
181
+ summary: PlasmAgentSummaryPayload;
182
+ instructionsMarkdown?: string | null;
183
+ }): Promise<string> {
184
+ const outPath = eveAgentSummaryPath(options.projectRoot);
185
+ await mkdir(path.dirname(outPath), { recursive: true });
186
+ await writeFile(
187
+ outPath,
188
+ `${JSON.stringify(toEveAgentSummary(options.summary, options.instructionsMarkdown), null, 2)}\n`,
189
+ "utf8",
190
+ );
191
+ return outPath;
192
+ }
@@ -1,9 +1,14 @@
1
+ import { readFile } from "node:fs/promises";
1
2
  import path from "node:path";
2
3
 
3
4
  import type { CompiledSlotMap } from "../cli/compile-authored-slots.js";
4
5
  import { loadAuthoredSlots } from "../authoring/slot-loader.js";
5
6
  import type { ProjectDiscovery } from "../discovery/project-walker.js";
6
- import { buildPlasmAgentSummary, emitPlasmAgentSummary } from "./agent-summary.js";
7
+ import {
8
+ buildPlasmAgentSummary,
9
+ emitEveCompatibleAgentSummary,
10
+ emitPlasmAgentSummary,
11
+ } from "./agent-summary.js";
7
12
  import { buildNitroOutput } from "./build-nitro-output.js";
8
13
  import { createPlasmNitro } from "./create-plasm-nitro.js";
9
14
  import { patchVercelOutputConfig } from "./patch-vercel-config.js";
@@ -18,6 +23,7 @@ import { writeWorkflowDispatchRoute } from "./write-workflow-dispatch-route.js";
18
23
  export interface PlasmApplicationBuildResult {
19
24
  outputDir: string;
20
25
  agentSummaryPath: string;
26
+ eveAgentSummaryPath: string;
21
27
  vercelOutput: boolean;
22
28
  }
23
29
 
@@ -85,12 +91,27 @@ export async function buildPlasmApplication(options: {
85
91
  });
86
92
  const agentSummaryPath = await emitPlasmAgentSummary({ projectRoot, summary });
87
93
 
94
+ let instructionsMarkdown: string | null = null;
95
+ if (discovery.instructions?.path) {
96
+ try {
97
+ instructionsMarkdown = await readFile(discovery.instructions.path, "utf8");
98
+ } catch {
99
+ instructionsMarkdown = null;
100
+ }
101
+ }
102
+ const eveAgentSummaryPath = await emitEveCompatibleAgentSummary({
103
+ projectRoot,
104
+ summary,
105
+ instructionsMarkdown,
106
+ });
107
+
88
108
  const vercelOutput = isVercelBuildEnvironment();
89
109
  const resolvedOutput = vercelOutput ? vercelOutputDir(projectRoot) : outputDir;
90
110
 
91
111
  return {
92
112
  outputDir: resolvedOutput,
93
113
  agentSummaryPath,
114
+ eveAgentSummaryPath,
94
115
  vercelOutput,
95
116
  };
96
117
  } finally {
@@ -4,6 +4,7 @@ export const PLASM_NITRO_BUILD_DIR = ".plasm/nitro";
4
4
  export const PLASM_NITRO_ROUTES_DIR = ".plasm/nitro/routes";
5
5
  export const PLASM_NITRO_OUTPUT_DIR = ".plasm/nitro-output";
6
6
  export const PLASM_AGENT_SUMMARY_PATH = ".plasm/agent-summary.json";
7
+ export const EVE_AGENT_SUMMARY_PATH = ".eve/agent-summary.json";
7
8
 
8
9
  export function plasmNitroBuildDir(projectRoot: string): string {
9
10
  return path.join(projectRoot, PLASM_NITRO_BUILD_DIR);
@@ -21,6 +22,10 @@ export function plasmAgentSummaryPath(projectRoot: string): string {
21
22
  return path.join(projectRoot, PLASM_AGENT_SUMMARY_PATH);
22
23
  }
23
24
 
25
+ export function eveAgentSummaryPath(projectRoot: string): string {
26
+ return path.join(projectRoot, EVE_AGENT_SUMMARY_PATH);
27
+ }
28
+
24
29
  export function vercelOutputDir(projectRoot: string): string {
25
30
  return path.join(projectRoot, ".vercel", "output");
26
31
  }
@@ -1,7 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
- import { generateText, stepCountIs, type LanguageModel, type ModelMessage, type ToolSet } from "ai";
4
+ import { type LanguageModel, type ModelMessage, type ToolSet } from "ai";
5
5
 
6
6
  import type {
7
7
  AgentBuildConfig,
@@ -19,10 +19,16 @@ import { maybeCompactMessages } from "../runtime/compaction.js";
19
19
  import { AgentRuntime, type AgentRuntimeConfig } from "../runtime/agent-runtime.js";
20
20
  import { createHarnessTools, renderSkillIndex } from "../tools/harness-tools.js";
21
21
  import { createPlasmTools } from "../tools/plasm-tools.js";
22
+ import { runEveToolLoop, type AgentStepEvent } from "../telemetry/eve-tool-loop.js";
23
+ import type { EveChannelKind } from "../telemetry/eve-agent-runs.js";
24
+
25
+ export type { AgentStepEvent };
22
26
 
23
27
  export interface PlasmAgentConfig extends AgentRuntimeConfig {
24
28
  /** AI Gateway model slug, e.g. `anthropic/claude-sonnet-4.6`. */
25
29
  model: string | LanguageModel;
30
+ /** Agent Runs / OTEL function id (defaults from agent root directory name). */
31
+ agentName?: string;
26
32
  instructionsPath?: string;
27
33
  maxSteps?: number;
28
34
  telemetry?: boolean;
@@ -36,22 +42,18 @@ export interface PlasmAgentConfig extends AgentRuntimeConfig {
36
42
  getAuthoringContext?: () => AuthoringContext;
37
43
  }
38
44
 
39
- export interface AgentStepEvent {
40
- toolCalls?: Array<{ toolName: string }>;
41
- text?: string;
42
- finishReason?: string;
43
- }
44
-
45
45
  export interface AgentGenerateOptions {
46
46
  messages?: ModelMessage[];
47
47
  resetConversation?: boolean;
48
48
  onStepFinish?: (step: AgentStepEvent) => void | Promise<void>;
49
+ /** Eve Agent Runs channel kind (`schedule`, `http`, `channel:<name>`, …). */
50
+ channelKind?: EveChannelKind;
49
51
  }
50
52
 
51
53
  export interface AgentTurnResult {
52
54
  text: string;
53
55
  steps: unknown[];
54
- usage: Awaited<ReturnType<typeof generateText>>["usage"];
56
+ usage: Awaited<ReturnType<typeof runEveToolLoop>>["usage"];
55
57
  }
56
58
 
57
59
  export class PlasmAgent {
@@ -67,6 +69,7 @@ export class PlasmAgent {
67
69
  private readonly hookRunner?: HookRunner;
68
70
  private readonly subagentRegistry?: SubagentRegistry;
69
71
  private readonly getAuthoringContext?: () => AuthoringContext;
72
+ private readonly agentName: string;
70
73
  private conversation: ModelMessage[] = [];
71
74
 
72
75
  constructor(config: PlasmAgentConfig) {
@@ -77,6 +80,10 @@ export class PlasmAgent {
77
80
  config.instructionsPath ?? path.join(config.agentRoot, "instructions.md");
78
81
  this.maxSteps = config.maxSteps ?? 20;
79
82
  this.telemetryEnabled = config.telemetry ?? true;
83
+ this.agentName =
84
+ config.agentName?.trim() ||
85
+ process.env.PLASM_AGENT_NAME?.trim() ||
86
+ path.basename(path.dirname(config.agentRoot));
80
87
  this.loadedSkills = config.loadedSkills ?? [];
81
88
  this.compaction = config.compaction;
82
89
  const skillsFlag = config.experimental?.skills;
@@ -142,7 +149,7 @@ export class PlasmAgent {
142
149
  } as ToolSet;
143
150
 
144
151
  const telemetry = this.telemetryEnabled
145
- ? createAgentTelemetry({ serviceName: "plasm-agent" })
152
+ ? createAgentTelemetry({ serviceName: this.agentName })
146
153
  : { isEnabled: false };
147
154
  const model = resolveGatewayModel(this.model, this.modelOptions);
148
155
 
@@ -160,31 +167,24 @@ export class PlasmAgent {
160
167
 
161
168
  messages = await maybeCompactMessages(messages, this.compaction, this.model);
162
169
 
163
- const generation = {
170
+ const onStepFinish = async (step: AgentStepEvent) => {
171
+ await options.onStepFinish?.(step);
172
+ if (!this.hookRunner || !this.getAuthoringContext) return;
173
+ const toolsUsed = (step.toolCalls ?? []).map((call) => call.toolName);
174
+ await this.hookRunner.emit("agent:step", this.getAuthoringContext(), { toolsUsed });
175
+ };
176
+
177
+ const result = await runEveToolLoop({
164
178
  model,
165
179
  system,
166
180
  tools,
167
- stopWhen: stepCountIs(this.maxSteps),
168
- experimental_telemetry: telemetry,
169
- onStepFinish: async (step: AgentStepEvent) => {
170
- await options.onStepFinish?.(step);
171
- if (!this.hookRunner || !this.getAuthoringContext) return;
172
- const toolsUsed = (step.toolCalls ?? []).map((call) => call.toolName);
173
- await this.hookRunner.emit("agent:step", this.getAuthoringContext(), { toolsUsed });
174
- },
175
- ...(this.modelOptions?.temperature !== undefined
176
- ? { temperature: this.modelOptions.temperature }
177
- : {}),
178
- ...(this.modelOptions?.maxOutputTokens !== undefined
179
- ? { maxOutputTokens: this.modelOptions.maxOutputTokens }
180
- : {}),
181
- ...(this.modelOptions?.topP !== undefined ? { topP: this.modelOptions.topP } : {}),
182
- ...(this.modelOptions?.topK !== undefined ? { topK: this.modelOptions.topK } : {}),
183
- };
184
-
185
- const result = await generateText({
186
- ...generation,
187
181
  messages,
182
+ maxSteps: this.maxSteps,
183
+ agentName: this.agentName,
184
+ channelKind: options.channelKind,
185
+ telemetry,
186
+ onStepFinish,
187
+ modelOptions: this.modelOptions,
188
188
  });
189
189
 
190
190
  if (!externalMessages) {
@@ -0,0 +1,103 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ import { SpanStatusCode, trace } from "@opentelemetry/api";
4
+ import type { TelemetryOptions } from "ai";
5
+
6
+ import { frameworkPackageVersion } from "../package-version.js";
7
+
8
+ const EVE_TRACER_NAME = "eve";
9
+
10
+ export type EveChannelKind = string;
11
+
12
+ export interface EveEmissionState {
13
+ sessionId: string;
14
+ turnId: string;
15
+ sequence: number;
16
+ stepIndex: number;
17
+ channelKind: EveChannelKind;
18
+ }
19
+
20
+ function eveEnvironment(): string {
21
+ return process.env.VERCEL_ENV?.trim() || process.env.NODE_ENV?.trim() || "development";
22
+ }
23
+
24
+ /** Session id for one agent turn — matches Eve `eve.session.id` on Agent Runs spans. */
25
+ export function createEveSessionId(): string {
26
+ return randomUUID();
27
+ }
28
+
29
+ /** Turn id inside a session (Eve default: `turn_0`, `turn_1`, …). */
30
+ export function createEveTurnId(sequence: number): string {
31
+ return `turn_${sequence}`;
32
+ }
33
+
34
+ /** Runtime context keys Vercel Agent Runs reads from AI SDK spans. */
35
+ export function buildEveRuntimeContext(state: EveEmissionState): Record<string, string> {
36
+ return {
37
+ "eve.version": frameworkPackageVersion(),
38
+ "eve.environment": eveEnvironment(),
39
+ "eve.session.id": state.sessionId,
40
+ "eve.turn.id": state.turnId,
41
+ "eve.turn.sequence": String(state.sequence),
42
+ "eve.step.index": String(state.stepIndex),
43
+ "eve.channel.kind": state.channelKind,
44
+ };
45
+ }
46
+
47
+ /** Eve-style telemetry options: record I/O + propagate runtime context onto spans. */
48
+ export function enrichEveTelemetry(
49
+ base: TelemetryOptions,
50
+ runtimeContext: Record<string, string>,
51
+ ): TelemetryOptions {
52
+ const includeRuntimeContext: Record<string, boolean> = {};
53
+ for (const key of Object.keys(runtimeContext)) {
54
+ includeRuntimeContext[key] = true;
55
+ }
56
+ return {
57
+ ...base,
58
+ isEnabled: base.isEnabled ?? true,
59
+ recordInputs: base.recordInputs ?? true,
60
+ recordOutputs: base.recordOutputs ?? true,
61
+ includeRuntimeContext,
62
+ };
63
+ }
64
+
65
+ export interface EveTurnSpanOptions extends EveEmissionState {
66
+ functionId?: string;
67
+ attributes?: Record<string, string | number | boolean>;
68
+ }
69
+
70
+ function eveTurnSpanAttributes(options: EveTurnSpanOptions): Record<string, string | number | boolean> {
71
+ return {
72
+ "eve.version": frameworkPackageVersion(),
73
+ "eve.environment": eveEnvironment(),
74
+ "eve.session.id": options.sessionId,
75
+ "eve.turn.id": options.turnId,
76
+ "eve.turn.sequence": options.sequence,
77
+ "eve.step.index": options.stepIndex,
78
+ "eve.channel.kind": options.channelKind,
79
+ ...(options.functionId ? { "ai.telemetry.functionId": options.functionId } : {}),
80
+ ...options.attributes,
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Parent span Vercel Agent Runs ingests (`ai.eve.turn` + `eve.session.id`).
86
+ * Eve emits one per tool-loop step; Plasm matches that shape.
87
+ */
88
+ export async function withEveTurnSpan<T>(
89
+ options: EveTurnSpanOptions,
90
+ fn: () => Promise<T>,
91
+ ): Promise<T> {
92
+ const tracer = trace.getTracer(EVE_TRACER_NAME);
93
+ const attributes = eveTurnSpanAttributes(options);
94
+
95
+ return tracer.startActiveSpan("ai.eve.turn", { attributes }, async (span) => {
96
+ try {
97
+ return await fn();
98
+ } catch (err) {
99
+ span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
100
+ throw err;
101
+ }
102
+ });
103
+ }
@@ -0,0 +1,145 @@
1
+ import {
2
+ streamText,
3
+ stepCountIs,
4
+ type LanguageModel,
5
+ type LanguageModelUsage,
6
+ type ModelMessage,
7
+ type TelemetryOptions,
8
+ type ToolSet,
9
+ } from "ai";
10
+ import type { Context } from "@ai-sdk/provider-utils";
11
+
12
+ import { ensureOtelIntegration } from "../instrumentation.js";
13
+ import {
14
+ buildEveRuntimeContext,
15
+ createEveSessionId,
16
+ createEveTurnId,
17
+ enrichEveTelemetry,
18
+ withEveTurnSpan,
19
+ type EveChannelKind,
20
+ } from "./eve-agent-runs.js";
21
+
22
+ export interface AgentStepEvent {
23
+ toolCalls?: Array<{ toolName: string }>;
24
+ text?: string;
25
+ finishReason?: string;
26
+ }
27
+
28
+ export interface EveToolLoopModelOptions {
29
+ temperature?: number;
30
+ maxOutputTokens?: number;
31
+ topP?: number;
32
+ topK?: number;
33
+ }
34
+
35
+ export interface EveToolLoopOptions {
36
+ model: LanguageModel;
37
+ system: string;
38
+ tools: ToolSet;
39
+ messages: ModelMessage[];
40
+ maxSteps: number;
41
+ agentName: string;
42
+ channelKind?: EveChannelKind;
43
+ telemetry?: TelemetryOptions;
44
+ onStepFinish?: (step: AgentStepEvent) => void | Promise<void>;
45
+ modelOptions?: EveToolLoopModelOptions;
46
+ }
47
+
48
+ export interface EveToolLoopResult {
49
+ text: string;
50
+ steps: unknown[];
51
+ usage: LanguageModelUsage;
52
+ }
53
+
54
+ /**
55
+ * Eve-compatible tool loop: one `ai.eve.turn` parent span per step, `streamText`
56
+ * child spans via AI SDK OTEL (`OpenTelemetry` + runtime context).
57
+ */
58
+ export async function runEveToolLoop(options: EveToolLoopOptions): Promise<EveToolLoopResult> {
59
+ ensureOtelIntegration();
60
+
61
+ const sessionId = createEveSessionId();
62
+ const turnId = createEveTurnId(0);
63
+ const channelKind = options.channelKind ?? "unknown";
64
+
65
+ let messages = options.messages;
66
+ let stepIndex = 0;
67
+ let finalText = "";
68
+ let lastUsage: LanguageModelUsage | undefined;
69
+ const aggregatedSteps: unknown[] = [];
70
+
71
+ while (stepIndex < options.maxSteps) {
72
+ const runtimeContext = buildEveRuntimeContext({
73
+ sessionId,
74
+ turnId,
75
+ sequence: 0,
76
+ stepIndex,
77
+ channelKind,
78
+ });
79
+ const telemetry = enrichEveTelemetry(
80
+ options.telemetry ?? { isEnabled: true, functionId: options.agentName },
81
+ runtimeContext,
82
+ );
83
+
84
+ const stepResult = await withEveTurnSpan(
85
+ {
86
+ sessionId,
87
+ turnId,
88
+ sequence: 0,
89
+ stepIndex,
90
+ channelKind,
91
+ functionId: options.agentName,
92
+ },
93
+ async () => {
94
+ const streamResult = streamText({
95
+ model: options.model,
96
+ system: options.system,
97
+ tools: options.tools,
98
+ messages,
99
+ stopWhen: stepCountIs(1),
100
+ runtimeContext: runtimeContext as Context,
101
+ experimental_telemetry: telemetry,
102
+ onStepFinish: options.onStepFinish,
103
+ ...(options.modelOptions?.temperature !== undefined
104
+ ? { temperature: options.modelOptions.temperature }
105
+ : {}),
106
+ ...(options.modelOptions?.maxOutputTokens !== undefined
107
+ ? { maxOutputTokens: options.modelOptions.maxOutputTokens }
108
+ : {}),
109
+ ...(options.modelOptions?.topP !== undefined ? { topP: options.modelOptions.topP } : {}),
110
+ ...(options.modelOptions?.topK !== undefined ? { topK: options.modelOptions.topK } : {}),
111
+ });
112
+
113
+ const [text, finishReason, steps, usage, response] = await Promise.all([
114
+ streamResult.text,
115
+ streamResult.finishReason,
116
+ streamResult.steps,
117
+ streamResult.usage,
118
+ streamResult.response,
119
+ ]);
120
+
121
+ return { text, finishReason, steps, usage, response };
122
+ },
123
+ );
124
+
125
+ finalText = stepResult.text;
126
+ lastUsage = stepResult.usage;
127
+ aggregatedSteps.push(...stepResult.steps);
128
+ messages = stepResult.response.messages as ModelMessage[];
129
+
130
+ stepIndex += 1;
131
+ if (stepResult.finishReason !== "tool-calls") {
132
+ break;
133
+ }
134
+ }
135
+
136
+ if (!lastUsage) {
137
+ throw new Error("eve tool loop produced no model steps");
138
+ }
139
+
140
+ return {
141
+ text: finalText,
142
+ steps: aggregatedSteps,
143
+ usage: lastUsage,
144
+ };
145
+ }
@@ -11,7 +11,7 @@ const serviceName =
11
11
 
12
12
  export function register(): void {
13
13
  registerOTel({ serviceName });
14
- registerTelemetry(new OpenTelemetry());
14
+ registerTelemetry(new OpenTelemetry({ runtimeContext: true }));
15
15
  }
16
16
 
17
17
  register();
@@ -11,6 +11,8 @@ export const MCP_RADAR_INTENT =
11
11
  export interface RadarRunOptions {
12
12
  force?: boolean;
13
13
  reset?: boolean;
14
+ /** Eve Agent Runs `eve.channel.kind` (default `schedule`). */
15
+ channelKind?: string;
14
16
  }
15
17
 
16
18
  export interface RadarRunResult {
@@ -54,7 +56,10 @@ export async function runRadar(
54
56
  try {
55
57
  resetRunAudit();
56
58
  const agent = await ctx.getAgent();
57
- const turn = await agent.generate(buildRadarGoal(options), { resetConversation: false });
59
+ const turn = await agent.generate(buildRadarGoal(options), {
60
+ resetConversation: false,
61
+ channelKind: options.channelKind ?? "schedule",
62
+ });
58
63
  void drainRunAudit();
59
64
 
60
65
  return {
@@ -1,10 +1,17 @@
1
- import { registerOTel } from "@vercel/otel";
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+
2
4
  import { OpenTelemetry } from "@ai-sdk/otel";
5
+ import { registerOTel } from "@vercel/otel";
3
6
  import { registerTelemetry } from "ai";
4
7
 
8
+ const agentRoot = path.dirname(fileURLToPath(import.meta.url));
9
+ const serviceName =
10
+ process.env.PLASM_AGENT_NAME?.trim() || path.basename(path.dirname(agentRoot));
11
+
5
12
  export function register(): void {
6
- registerOTel({ serviceName: "plasm-agent" });
7
- registerTelemetry(new OpenTelemetry());
13
+ registerOTel({ serviceName });
14
+ registerTelemetry(new OpenTelemetry({ runtimeContext: true }));
8
15
  }
9
16
 
10
17
  register();