@poncho-ai/harness 0.5.0 → 0.6.0

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/harness@0.5.0 build /Users/cesar/Dev/latitude/poncho-ai/packages/harness
2
+ > @poncho-ai/harness@0.6.0 build /Users/cesar/Dev/latitude/poncho-ai/packages/harness
3
3
  > tsup src/index.ts --format esm --dts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -7,8 +7,8 @@ CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.5.1
8
8
  CLI Target: es2022
9
9
  ESM Build start
10
- ESM dist/index.js 112.34 KB
11
- ESM ⚡️ Build success in 66ms
10
+ ESM dist/index.js 114.60 KB
11
+ ESM ⚡️ Build success in 69ms
12
12
  DTS Build start
13
- DTS ⚡️ Build success in 2273ms
14
- DTS dist/index.d.ts 15.87 KB
13
+ DTS ⚡️ Build success in 2819ms
14
+ DTS dist/index.d.ts 16.10 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @poncho-ai/harness
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add markdown table support and fix Latitude telemetry integration
8
+ - Add markdown table rendering with `marked` library in web UI
9
+ - Add table styling with horizontal scroll and hover effects
10
+ - Add margins to HR elements for better spacing
11
+ - Integrate Latitude telemetry with Vercel AI SDK using event queue pattern
12
+ - Enable real-time streaming while capturing complete traces
13
+ - Fix telemetry to show all messages and interactions in Latitude dashboard
14
+
3
15
  ## 0.5.0
4
16
 
5
17
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -323,6 +323,11 @@ declare class AgentHarness {
323
323
  initialize(): Promise<void>;
324
324
  shutdown(): Promise<void>;
325
325
  listTools(): ToolDefinition[];
326
+ /**
327
+ * Wraps the run() generator with Latitude telemetry capture for complete trace coverage
328
+ * Streams events in real-time using an event queue pattern
329
+ */
330
+ runWithTelemetry(input: RunInput): AsyncGenerator<AgentEvent>;
326
331
  run(input: RunInput): AsyncGenerator<AgentEvent>;
327
332
  runToCompletion(input: RunInput): Promise<HarnessRunOutput>;
328
333
  }
package/dist/index.js CHANGED
@@ -1863,6 +1863,9 @@ var extractRunnableFunction = (value) => {
1863
1863
  return void 0;
1864
1864
  };
1865
1865
 
1866
+ // src/harness.ts
1867
+ import { LatitudeTelemetry } from "@latitude-data/telemetry";
1868
+
1866
1869
  // src/tool-dispatcher.ts
1867
1870
  var ToolDispatcher = class {
1868
1871
  tools = /* @__PURE__ */ new Map();
@@ -2229,6 +2232,63 @@ var AgentHarness = class {
2229
2232
  listTools() {
2230
2233
  return this.dispatcher.list();
2231
2234
  }
2235
+ /**
2236
+ * Wraps the run() generator with Latitude telemetry capture for complete trace coverage
2237
+ * Streams events in real-time using an event queue pattern
2238
+ */
2239
+ async *runWithTelemetry(input) {
2240
+ const config = this.loadedConfig;
2241
+ const telemetryEnabled = config?.telemetry?.enabled !== false;
2242
+ const latitudeApiKey = config?.telemetry?.latitude?.apiKey;
2243
+ const rawProjectId = config?.telemetry?.latitude?.projectId;
2244
+ const projectId = typeof rawProjectId === "string" ? parseInt(rawProjectId, 10) : rawProjectId;
2245
+ const path = config?.telemetry?.latitude?.path ?? this.parsedAgent?.frontmatter.name ?? "agent";
2246
+ if (telemetryEnabled && latitudeApiKey && projectId) {
2247
+ const telemetry = new LatitudeTelemetry(latitudeApiKey);
2248
+ const eventQueue = [];
2249
+ let queueResolve = null;
2250
+ let generatorDone = false;
2251
+ let generatorError = null;
2252
+ const capturePromise = telemetry.capture({ projectId, path }, async () => {
2253
+ try {
2254
+ for await (const event of this.run(input)) {
2255
+ eventQueue.push(event);
2256
+ if (queueResolve) {
2257
+ const resolve8 = queueResolve;
2258
+ queueResolve = null;
2259
+ resolve8();
2260
+ }
2261
+ }
2262
+ } catch (error) {
2263
+ generatorError = error;
2264
+ } finally {
2265
+ generatorDone = true;
2266
+ if (queueResolve) {
2267
+ queueResolve();
2268
+ queueResolve = null;
2269
+ }
2270
+ }
2271
+ });
2272
+ try {
2273
+ while (!generatorDone || eventQueue.length > 0) {
2274
+ if (eventQueue.length > 0) {
2275
+ yield eventQueue.shift();
2276
+ } else if (!generatorDone) {
2277
+ await new Promise((resolve8) => {
2278
+ queueResolve = resolve8;
2279
+ });
2280
+ }
2281
+ }
2282
+ if (generatorError) {
2283
+ throw generatorError;
2284
+ }
2285
+ } finally {
2286
+ await capturePromise;
2287
+ }
2288
+ } else {
2289
+ yield* this.run(input);
2290
+ }
2291
+ }
2232
2292
  async *run(input) {
2233
2293
  if (!this.parsedAgent) {
2234
2294
  await this.initialize();
@@ -2374,13 +2434,18 @@ ${boundedMainMemory.trim()}` : "";
2374
2434
  const modelName = agent.frontmatter.model?.name ?? "claude-opus-4-5";
2375
2435
  const temperature = agent.frontmatter.model?.temperature ?? 0.2;
2376
2436
  const maxTokens = agent.frontmatter.model?.maxTokens ?? 1024;
2437
+ const telemetryEnabled = this.loadedConfig?.telemetry?.enabled !== false;
2438
+ const latitudeApiKey = this.loadedConfig?.telemetry?.latitude?.apiKey;
2377
2439
  const result = await streamText({
2378
2440
  model: this.modelProvider(modelName),
2379
2441
  system: integrityPrompt,
2380
2442
  messages: coreMessages,
2381
2443
  tools,
2382
2444
  temperature,
2383
- maxTokens
2445
+ maxTokens,
2446
+ experimental_telemetry: {
2447
+ isEnabled: telemetryEnabled && !!latitudeApiKey
2448
+ }
2384
2449
  });
2385
2450
  let streamedAnyChunk = false;
2386
2451
  let fullText = "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
package/src/harness.ts CHANGED
@@ -22,6 +22,7 @@ import { streamText, type CoreMessage } from "ai";
22
22
  import { jsonSchemaToZod } from "./schema-converter.js";
23
23
  import type { SkillMetadata } from "./skill-context.js";
24
24
  import { createSkillTools } from "./skill-tools.js";
25
+ import { LatitudeTelemetry } from "@latitude-data/telemetry";
25
26
  import {
26
27
  applyToolPolicy,
27
28
  matchesSlashPattern,
@@ -355,7 +356,6 @@ export class AgentHarness {
355
356
  this.registerConfiguredBuiltInTools(config);
356
357
  const provider = this.parsedAgent.frontmatter.model?.provider ?? "anthropic";
357
358
  const memoryConfig = resolveMemoryConfig(config);
358
- // TODO: Integrate Latitude telemetry with Vercel AI SDK
359
359
  // Only create modelProvider if one wasn't injected (for production use)
360
360
  // Tests can inject a mock modelProvider via constructor options
361
361
  if (!this.modelProviderInjected) {
@@ -409,6 +409,75 @@ export class AgentHarness {
409
409
  return this.dispatcher.list();
410
410
  }
411
411
 
412
+ /**
413
+ * Wraps the run() generator with Latitude telemetry capture for complete trace coverage
414
+ * Streams events in real-time using an event queue pattern
415
+ */
416
+ async *runWithTelemetry(input: RunInput): AsyncGenerator<AgentEvent> {
417
+ const config = this.loadedConfig;
418
+ const telemetryEnabled = config?.telemetry?.enabled !== false;
419
+ const latitudeApiKey = config?.telemetry?.latitude?.apiKey;
420
+ const rawProjectId = config?.telemetry?.latitude?.projectId;
421
+ const projectId = typeof rawProjectId === 'string' ? parseInt(rawProjectId, 10) : rawProjectId;
422
+ const path = config?.telemetry?.latitude?.path ?? this.parsedAgent?.frontmatter.name ?? 'agent';
423
+
424
+ // If Latitude telemetry is configured, wrap the entire run with capture
425
+ if (telemetryEnabled && latitudeApiKey && projectId) {
426
+ const telemetry = new LatitudeTelemetry(latitudeApiKey);
427
+
428
+ // Event queue for streaming events in real-time
429
+ const eventQueue: AgentEvent[] = [];
430
+ let queueResolve: ((value: void) => void) | null = null;
431
+ let generatorDone = false;
432
+ let generatorError: Error | null = null;
433
+
434
+ // Start the generator inside telemetry.capture() (runs in background)
435
+ const capturePromise = telemetry.capture({ projectId, path }, async () => {
436
+ try {
437
+ for await (const event of this.run(input)) {
438
+ eventQueue.push(event);
439
+ if (queueResolve) {
440
+ const resolve = queueResolve;
441
+ queueResolve = null;
442
+ resolve();
443
+ }
444
+ }
445
+ } catch (error) {
446
+ generatorError = error as Error;
447
+ } finally {
448
+ generatorDone = true;
449
+ if (queueResolve) {
450
+ queueResolve();
451
+ queueResolve = null;
452
+ }
453
+ }
454
+ });
455
+
456
+ // Yield events from the queue as they arrive
457
+ try {
458
+ while (!generatorDone || eventQueue.length > 0) {
459
+ if (eventQueue.length > 0) {
460
+ yield eventQueue.shift()!;
461
+ } else if (!generatorDone) {
462
+ // Wait for next event
463
+ await new Promise<void>((resolve) => {
464
+ queueResolve = resolve;
465
+ });
466
+ }
467
+ }
468
+
469
+ if (generatorError) {
470
+ throw generatorError;
471
+ }
472
+ } finally {
473
+ await capturePromise;
474
+ }
475
+ } else {
476
+ // No telemetry configured, just pass through
477
+ yield* this.run(input);
478
+ }
479
+ }
480
+
412
481
  async *run(input: RunInput): AsyncGenerator<AgentEvent> {
413
482
  if (!this.parsedAgent) {
414
483
  await this.initialize();
@@ -588,14 +657,20 @@ ${boundedMainMemory.trim()}`
588
657
  const temperature = agent.frontmatter.model?.temperature ?? 0.2;
589
658
  const maxTokens = agent.frontmatter.model?.maxTokens ?? 1024;
590
659
 
591
- // Stream response using Vercel AI SDK
660
+ // Stream response using Vercel AI SDK with telemetry enabled
661
+ const telemetryEnabled = this.loadedConfig?.telemetry?.enabled !== false;
662
+ const latitudeApiKey = this.loadedConfig?.telemetry?.latitude?.apiKey;
663
+
592
664
  const result = await streamText({
593
- model: this.modelProvider(modelName),
594
- system: integrityPrompt,
595
- messages: coreMessages,
596
- tools,
597
- temperature,
598
- maxTokens,
665
+ model: this.modelProvider(modelName),
666
+ system: integrityPrompt,
667
+ messages: coreMessages,
668
+ tools,
669
+ temperature,
670
+ maxTokens,
671
+ experimental_telemetry: {
672
+ isEnabled: telemetryEnabled && !!latitudeApiKey,
673
+ },
599
674
  });
600
675
 
601
676
  // Stream text chunks