@poncho-ai/harness 0.32.0 → 0.33.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.
package/dist/index.js CHANGED
@@ -113,11 +113,13 @@ var parseCronJobs = (value) => {
113
113
  validateTimezone(timezone, path);
114
114
  }
115
115
  const channel = typeof jobValue.channel === "string" && jobValue.channel.trim() ? jobValue.channel.trim() : void 0;
116
+ const maxRuns = typeof jobValue.maxRuns === "number" && jobValue.maxRuns > 0 ? Math.max(1, Math.floor(jobValue.maxRuns)) : void 0;
116
117
  jobs[jobName] = {
117
118
  schedule: jobValue.schedule.trim(),
118
119
  task: jobValue.task,
119
120
  timezone,
120
- channel
121
+ channel,
122
+ maxRuns
121
123
  };
122
124
  }
123
125
  return jobs;
@@ -1467,8 +1469,6 @@ All credentials in \`poncho.config.js\` use **env var name** fields (\`*Env\` su
1467
1469
  | \`auth.tokenEnv\` | \`PONCHO_AUTH_TOKEN\` | Auth passphrase / bearer token |
1468
1470
  | \`storage.urlEnv\` | \`UPSTASH_REDIS_REST_URL\` / \`REDIS_URL\` | Storage connection URL |
1469
1471
  | \`storage.tokenEnv\` | \`UPSTASH_REDIS_REST_TOKEN\` | Upstash REST token |
1470
- | \`telemetry.latitude.apiKeyEnv\` | \`LATITUDE_API_KEY\` | Latitude API key |
1471
- | \`telemetry.latitude.projectIdEnv\` | \`LATITUDE_PROJECT_ID\` | Latitude project ID |
1472
1472
  | \`messaging[].botTokenEnv\` | \`SLACK_BOT_TOKEN\` | Slack bot token |
1473
1473
  | \`messaging[].signingSecretEnv\` | \`SLACK_SIGNING_SECRET\` | Slack signing secret |
1474
1474
  | \`messaging[].botTokenEnv\` | \`TELEGRAM_BOT_TOKEN\` | Telegram bot token |
@@ -1557,7 +1557,7 @@ export default {
1557
1557
  },
1558
1558
  },
1559
1559
 
1560
- // Telemetry destination \u2014 generic OTLP and/or Latitude
1560
+ // Telemetry \u2014 send OpenTelemetry traces to any OTLP-compatible collector
1561
1561
  telemetry: {
1562
1562
  enabled: true,
1563
1563
  // Generic OTLP: string shorthand or { url, headers? } object
@@ -1567,12 +1567,6 @@ export default {
1567
1567
  // url: 'https://api.honeycomb.io/v1/traces',
1568
1568
  // headers: { 'x-honeycomb-team': process.env.HONEYCOMB_API_KEY },
1569
1569
  // },
1570
- // Latitude (reads from LATITUDE_API_KEY and LATITUDE_PROJECT_ID env vars by default)
1571
- latitude: {
1572
- // apiKeyEnv: 'LATITUDE_API_KEY', // default
1573
- // projectIdEnv: 'LATITUDE_PROJECT_ID', // default
1574
- path: 'your/prompt-path', // optional, defaults to agent name
1575
- },
1576
1570
  },
1577
1571
 
1578
1572
  // Messaging platform integrations
@@ -1652,9 +1646,6 @@ Remote storage keys are namespaced and versioned, for example \`poncho:v1:<agent
1652
1646
  | \`PONCHO_INTERNAL_SECRET\` | No | Shared secret used by internal serverless callbacks (recommended for Vercel/Lambda) |
1653
1647
  | \`PONCHO_SELF_BASE_URL\` | No | Explicit base URL for internal self-callbacks when auto-detection is unavailable |
1654
1648
  | \`OTEL_EXPORTER_OTLP_ENDPOINT\` | No | OTLP trace endpoint (Jaeger, Tempo, Honeycomb, etc.) |
1655
- | \`LATITUDE_API_KEY\` | No | Latitude dashboard integration |
1656
- | \`LATITUDE_PROJECT_ID\` | No | Latitude project identifier for capture traces |
1657
- | \`LATITUDE_PATH\` | No | Latitude prompt path for grouping traces |
1658
1649
  | \`KV_REST_API_URL\` | No | Upstash REST URL (Vercel Marketplace naming) |
1659
1650
  | \`KV_REST_API_TOKEN\` | No | Upstash REST write token (Vercel Marketplace naming) |
1660
1651
  | \`UPSTASH_REDIS_REST_URL\` | No | Upstash REST URL (direct Upstash naming) |
@@ -1732,30 +1723,6 @@ export default {
1732
1723
  }
1733
1724
  \`\`\`
1734
1725
 
1735
- ### Latitude integration (optional)
1736
-
1737
- Send traces to [Latitude](https://latitude.so) for a dashboard with cost tracking and prompt management:
1738
-
1739
- \`\`\`bash
1740
- LATITUDE_API_KEY=lat_xxx
1741
- LATITUDE_PROJECT_ID=123
1742
- LATITUDE_PATH=agents/my-agent/run
1743
- \`\`\`
1744
-
1745
- Or configure via \`poncho.config.js\`:
1746
-
1747
- \`\`\`javascript
1748
- telemetry: {
1749
- latitude: {
1750
- // apiKeyEnv: 'LATITUDE_API_KEY', // default
1751
- // projectIdEnv: 'LATITUDE_PROJECT_ID', // default
1752
- path: 'your/prompt-path',
1753
- },
1754
- }
1755
- \`\`\`
1756
-
1757
- Both \`otlp\` and \`latitude\` can be configured simultaneously \u2014 all spans flow to both destinations.
1758
-
1759
1726
  ## Security
1760
1727
 
1761
1728
  ### Protect your endpoint
@@ -3220,13 +3187,14 @@ var parseReminderList = (raw) => {
3220
3187
  var pruneStale = (reminders) => {
3221
3188
  const cutoff = Date.now() - STALE_CANCELLED_MS;
3222
3189
  return reminders.filter(
3223
- (r) => r.status === "pending" || r.createdAt > cutoff
3190
+ (r) => r.status === "pending" || r.status === "cancelled" && r.createdAt > cutoff
3224
3191
  );
3225
3192
  };
3226
3193
  var generateId2 = () => (globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`).slice(0, 8);
3227
3194
  var InMemoryReminderStore = class {
3228
3195
  reminders = [];
3229
3196
  async list() {
3197
+ this.reminders = pruneStale(this.reminders);
3230
3198
  return [...this.reminders];
3231
3199
  }
3232
3200
  async create(input) {
@@ -3283,7 +3251,10 @@ var FileReminderStore = class {
3283
3251
  await writeJsonAtomic3(fp, reminders);
3284
3252
  }
3285
3253
  async list() {
3286
- return this.readAll();
3254
+ const all = await this.readAll();
3255
+ const pruned = pruneStale(all);
3256
+ if (pruned.length !== all.length) await this.writeAll(pruned);
3257
+ return pruned;
3287
3258
  }
3288
3259
  async create(input) {
3289
3260
  const reminder = {
@@ -3349,7 +3320,10 @@ var KVBackedReminderStore = class {
3349
3320
  }
3350
3321
  }
3351
3322
  async list() {
3352
- return this.readAll();
3323
+ const all = await this.readAll();
3324
+ const pruned = pruneStale(all);
3325
+ if (pruned.length !== all.length) await this.writeAll(pruned);
3326
+ return pruned;
3353
3327
  }
3354
3328
  async create(input) {
3355
3329
  let reminders;
@@ -5369,8 +5343,7 @@ var createSubagentTools = (manager) => [
5369
5343
  ];
5370
5344
 
5371
5345
  // src/harness.ts
5372
- import { LatitudeTelemetry } from "@latitude-data/telemetry";
5373
- import { trace, context as otelContext, SpanStatusCode } from "@opentelemetry/api";
5346
+ import { trace, context as otelContext, SpanStatusCode, SpanKind } from "@opentelemetry/api";
5374
5347
  import { NodeTracerProvider, BatchSpanProcessor } from "@opentelemetry/sdk-trace-node";
5375
5348
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
5376
5349
 
@@ -5903,25 +5876,17 @@ When modifying \`AGENT.md\`, follow these rules strictly:
5903
5876
 
5904
5877
  ## Telemetry Configuration (\`poncho.config.js\`)
5905
5878
 
5906
- When configuring Latitude telemetry, use **exactly** these field names:
5879
+ Send OpenTelemetry traces to any OTLP-compatible collector (Jaeger, Grafana Tempo, Honeycomb, Datadog, etc.):
5907
5880
 
5908
5881
  \`\`\`javascript
5909
5882
  telemetry: {
5910
5883
  enabled: true,
5911
- latitude: {
5912
- apiKeyEnv: "LATITUDE_API_KEY", // env var name (default)
5913
- projectIdEnv: "LATITUDE_PROJECT_ID", // env var name (default)
5914
- path: "your/prompt-path", // optional, defaults to agent name
5915
- },
5884
+ otlp: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
5885
+ // Or with auth headers:
5886
+ // otlp: { url: "https://api.honeycomb.io/v1/traces", headers: { "x-honeycomb-team": process.env.HONEYCOMB_API_KEY } },
5916
5887
  },
5917
5888
  \`\`\`
5918
5889
 
5919
- - \`apiKeyEnv\` specifies the environment variable name for the Latitude API key (defaults to \`"LATITUDE_API_KEY"\`).
5920
- - \`projectIdEnv\` specifies the environment variable name for the project ID (defaults to \`"LATITUDE_PROJECT_ID"\`).
5921
- - With defaults, you only need \`telemetry: { latitude: {} }\` if the env vars are already named \`LATITUDE_API_KEY\` and \`LATITUDE_PROJECT_ID\`.
5922
- - \`path\` must only contain letters, numbers, hyphens, underscores, dots, and slashes.
5923
- - For a generic OTLP endpoint instead: \`telemetry: { otlp: process.env.OTEL_EXPORTER_OTLP_ENDPOINT }\`.
5924
-
5925
5890
  ## Credential Configuration Pattern
5926
5891
 
5927
5892
  All credentials in \`poncho.config.js\` use the **env var name** pattern (\`*Env\` fields). Config specifies which environment variable to read \u2014 never the secret itself. Sensible defaults mean zero config when using conventional env var names.
@@ -5946,10 +5911,7 @@ export default {
5946
5911
  tokenEnv: "UPSTASH_REDIS_REST_TOKEN", // default (falls back to KV_REST_API_TOKEN)
5947
5912
  },
5948
5913
  telemetry: {
5949
- latitude: {
5950
- apiKeyEnv: "LATITUDE_API_KEY", // default
5951
- projectIdEnv: "LATITUDE_PROJECT_ID", // default
5952
- },
5914
+ otlp: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
5953
5915
  },
5954
5916
  messaging: [{ platform: "slack" }], // reads SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET by default
5955
5917
  }
@@ -6028,11 +5990,9 @@ var AgentHarness = class _AgentHarness {
6028
5990
  lastSkillRefreshAt = 0;
6029
5991
  activeSkillNames = /* @__PURE__ */ new Set();
6030
5992
  registeredMcpToolNames = /* @__PURE__ */ new Set();
6031
- latitudeTelemetry;
6032
5993
  otlpSpanProcessor;
6033
5994
  otlpTracerProvider;
6034
5995
  hasOtlpExporter = false;
6035
- insideTelemetryCapture = false;
6036
5996
  _browserSession;
6037
5997
  _browserMod;
6038
5998
  parsedAgent;
@@ -6641,22 +6601,6 @@ var AgentHarness = class _AgentHarness {
6641
6601
  await bridge.discoverTools();
6642
6602
  await this.refreshMcpTools("initialize");
6643
6603
  const telemetryEnabled = config?.telemetry?.enabled !== false;
6644
- const latitudeBlock = config?.telemetry?.latitude;
6645
- const latApiKeyEnv = latitudeBlock?.apiKeyEnv ?? "LATITUDE_API_KEY";
6646
- const latProjectIdEnv = latitudeBlock?.projectIdEnv ?? "LATITUDE_PROJECT_ID";
6647
- const latitudeApiKey = process.env[latApiKeyEnv];
6648
- const rawProjectId = process.env[latProjectIdEnv];
6649
- const latitudeProjectId = rawProjectId ? parseInt(rawProjectId, 10) : void 0;
6650
- if (telemetryEnabled && latitudeApiKey && latitudeProjectId) {
6651
- this.latitudeTelemetry = new LatitudeTelemetry(latitudeApiKey);
6652
- } else if (telemetryEnabled && latitudeBlock && (!latitudeApiKey || !latitudeProjectId)) {
6653
- const missing = [];
6654
- if (!latitudeApiKey) missing.push(`${latApiKeyEnv} env var`);
6655
- if (!latitudeProjectId) missing.push(`${latProjectIdEnv} env var`);
6656
- console.warn(
6657
- `[poncho][telemetry] Latitude telemetry is configured but missing: ${missing.join(", ")}. Traces will NOT be sent.`
6658
- );
6659
- }
6660
6604
  const otlpConfig = telemetryEnabled ? normalizeOtlp(config?.telemetry?.otlp) : void 0;
6661
6605
  if (otlpConfig) {
6662
6606
  const exporter = new OTLPTraceExporter({
@@ -6665,22 +6609,13 @@ var AgentHarness = class _AgentHarness {
6665
6609
  });
6666
6610
  const processor = new BatchSpanProcessor(exporter);
6667
6611
  this.otlpSpanProcessor = processor;
6668
- if (this.latitudeTelemetry) {
6669
- const globalProvider = trace.getTracerProvider();
6670
- const delegate = globalProvider.getDelegate?.() ?? globalProvider;
6671
- if (typeof delegate.addSpanProcessor === "function") {
6672
- delegate.addSpanProcessor(processor);
6673
- }
6674
- console.info(`[poncho][telemetry] OTLP exporter added (piggybacking on Latitude provider) \u2192 ${otlpConfig.url}`);
6675
- } else {
6676
- const provider2 = new NodeTracerProvider({
6677
- spanProcessors: [processor]
6678
- });
6679
- provider2.register();
6680
- this.otlpTracerProvider = provider2;
6681
- console.info(`[poncho][telemetry] OTLP exporter active (standalone provider) \u2192 ${otlpConfig.url}`);
6682
- }
6612
+ const provider2 = new NodeTracerProvider({
6613
+ spanProcessors: [processor]
6614
+ });
6615
+ provider2.register();
6616
+ this.otlpTracerProvider = provider2;
6683
6617
  this.hasOtlpExporter = true;
6618
+ console.info(`[poncho][telemetry] OTLP exporter active \u2192 ${otlpConfig.url}`);
6684
6619
  }
6685
6620
  }
6686
6621
  async buildBrowserStoragePersistence(config, sessionId) {
@@ -6824,14 +6759,6 @@ var AgentHarness = class _AgentHarness {
6824
6759
  this._browserSession = void 0;
6825
6760
  }
6826
6761
  await this.mcpBridge?.stopLocalServers();
6827
- if (this.latitudeTelemetry) {
6828
- await this.latitudeTelemetry.shutdown().catch((err) => {
6829
- console.warn(
6830
- `[poncho][telemetry] Latitude telemetry shutdown error: ${err instanceof Error ? err.message : String(err)}`
6831
- );
6832
- });
6833
- this.latitudeTelemetry = void 0;
6834
- }
6835
6762
  if (this.otlpSpanProcessor) {
6836
6763
  await this.otlpSpanProcessor.shutdown().catch((err) => {
6837
6764
  console.warn(
@@ -6854,85 +6781,21 @@ var AgentHarness = class _AgentHarness {
6854
6781
  return this.dispatcher.list();
6855
6782
  }
6856
6783
  /**
6857
- * Wraps the run() generator with telemetry capture for complete trace coverage.
6858
- * Supports Latitude, generic OTLP, or both simultaneously.
6859
- * Streams events in real-time using an event queue pattern.
6784
+ * Wraps the run() generator with an OTel root span (invoke_agent) so all
6785
+ * child spans (LLM calls via AI SDK, tool execution) group under one trace.
6860
6786
  */
6861
6787
  async *runWithTelemetry(input) {
6862
- const config = this.loadedConfig;
6863
- const telemetry = this.latitudeTelemetry;
6864
- if (telemetry) {
6865
- const latProjectIdEnv2 = config?.telemetry?.latitude?.projectIdEnv ?? "LATITUDE_PROJECT_ID";
6866
- const projectId = parseInt(process.env[latProjectIdEnv2] ?? "", 10);
6867
- const rawPath = config?.telemetry?.latitude?.path ?? this.parsedAgent?.frontmatter.name ?? "agent";
6868
- const path = rawPath.replace(/[^\w\-./]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "") || "agent";
6869
- const rawConversationId = input.conversationId ?? (typeof input.parameters?.__activeConversationId === "string" ? input.parameters.__activeConversationId : void 0);
6870
- const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6871
- const conversationUuid = rawConversationId && UUID_RE.test(rawConversationId) ? rawConversationId : void 0;
6872
- console.info(
6873
- `[poncho][telemetry] Latitude telemetry active \u2013 projectId=${projectId}, path="${path}"${conversationUuid ? `, conversation="${conversationUuid}"` : ""}`
6874
- );
6875
- const eventQueue = [];
6876
- let queueResolve = null;
6877
- let generatorDone = false;
6878
- let generatorError = null;
6879
- const capturePromise = telemetry.capture({ projectId, path, conversationUuid }, async () => {
6880
- this.insideTelemetryCapture = true;
6881
- try {
6882
- for await (const event of this.run(input)) {
6883
- eventQueue.push(event);
6884
- if (queueResolve) {
6885
- const resolve14 = queueResolve;
6886
- queueResolve = null;
6887
- resolve14();
6888
- }
6889
- }
6890
- } catch (error) {
6891
- generatorError = error;
6892
- } finally {
6893
- this.insideTelemetryCapture = false;
6894
- generatorDone = true;
6895
- if (queueResolve) {
6896
- queueResolve();
6897
- queueResolve = null;
6898
- }
6788
+ if (this.hasOtlpExporter) {
6789
+ const tracer = trace.getTracer("gen_ai");
6790
+ const agentName = this.parsedAgent?.frontmatter.name ?? "agent";
6791
+ const rootSpan = tracer.startSpan(`invoke_agent ${agentName}`, {
6792
+ kind: SpanKind.INTERNAL,
6793
+ attributes: {
6794
+ "gen_ai.operation.name": "invoke_agent",
6795
+ ...input.conversationId ? { "gen_ai.conversation.id": input.conversationId } : {}
6899
6796
  }
6900
6797
  });
6901
- try {
6902
- while (!generatorDone || eventQueue.length > 0) {
6903
- if (eventQueue.length > 0) {
6904
- yield eventQueue.shift();
6905
- } else if (!generatorDone) {
6906
- await new Promise((resolve14) => {
6907
- queueResolve = resolve14;
6908
- });
6909
- }
6910
- }
6911
- if (generatorError) {
6912
- throw generatorError;
6913
- }
6914
- } finally {
6915
- try {
6916
- await capturePromise;
6917
- } finally {
6918
- try {
6919
- await telemetry.flush();
6920
- console.info("[poncho][telemetry] flush completed");
6921
- } catch (flushErr) {
6922
- console.error("[poncho][telemetry] flush failed:", flushErr);
6923
- }
6924
- }
6925
- }
6926
- } else if (this.hasOtlpExporter) {
6927
- const tracer = trace.getTracer("poncho");
6928
- const agentName = this.parsedAgent?.frontmatter.name ?? "agent";
6929
- const rootSpan = tracer.startSpan(`agent.run ${agentName}`);
6930
- rootSpan.setAttribute("poncho.agent.name", agentName);
6931
- if (input.conversationId) {
6932
- rootSpan.setAttribute("poncho.conversation.id", input.conversationId);
6933
- }
6934
6798
  const spanContext = trace.setSpan(otelContext.active(), rootSpan);
6935
- this.insideTelemetryCapture = true;
6936
6799
  try {
6937
6800
  const gen = this.run(input);
6938
6801
  let next;
@@ -6949,7 +6812,6 @@ var AgentHarness = class _AgentHarness {
6949
6812
  rootSpan.recordException(error instanceof Error ? error : new Error(String(error)));
6950
6813
  throw error;
6951
6814
  } finally {
6952
- this.insideTelemetryCapture = false;
6953
6815
  rootSpan.end();
6954
6816
  try {
6955
6817
  await this.otlpSpanProcessor?.forceFlush();
@@ -6975,6 +6837,7 @@ var AgentHarness = class _AgentHarness {
6975
6837
  await this.initialize();
6976
6838
  }
6977
6839
  const memoryPromise = this.memoryStore ? this.memoryStore.getMainMemory() : void 0;
6840
+ const todosPromise = this.todoStore ? this.todoStore.get(input.conversationId ?? "__default__") : void 0;
6978
6841
  await this.refreshAgentIfChanged();
6979
6842
  await this.refreshSkillsIfChanged();
6980
6843
  let agent = this.parsedAgent;
@@ -7046,6 +6909,14 @@ Each conversation gets its own browser tab sharing a single browser instance. Ca
7046
6909
  ## Persistent Memory
7047
6910
 
7048
6911
  ${boundedMainMemory.trim()}` : "";
6912
+ const openTodos = (await todosPromise)?.filter(
6913
+ (t) => t.status === "pending" || t.status === "in_progress"
6914
+ ) ?? [];
6915
+ const todoContext = openTodos.length > 0 ? `
6916
+
6917
+ ## Open Tasks
6918
+
6919
+ ${openTodos.map((t) => `- [${t.status === "in_progress" ? "IN PROGRESS" : "PENDING"}] ${t.content} (id: ${t.id})`).join("\n")}` : "";
7049
6920
  const buildSystemPrompt = () => {
7050
6921
  const agentPrompt = renderCurrentAgentPrompt();
7051
6922
  const promptWithSkills = this.skillContextWindow ? `${agentPrompt}${developmentContext}
@@ -7054,16 +6925,9 @@ ${this.skillContextWindow}${browserContext}` : `${agentPrompt}${developmentConte
7054
6925
  const timeContext = this.reminderStore ? `
7055
6926
 
7056
6927
  Current UTC time: ${(/* @__PURE__ */ new Date()).toISOString()}` : "";
7057
- return `${promptWithSkills}${memoryContext}${timeContext}
7058
-
7059
- ## Execution Integrity
7060
-
7061
- - Do not claim that you executed a tool unless you actually emitted a tool call in this run.
7062
- - Do not fabricate "Tool Used" or "Tool Result" logs as plain text.
7063
- - Never output faux execution transcripts, markdown tool logs, or "Tool Used/Result" sections.
7064
- - If no suitable tool is available, explicitly say that and ask for guidance.`;
6928
+ return `${promptWithSkills}${memoryContext}${todoContext}${timeContext}`;
7065
6929
  };
7066
- let integrityPrompt = buildSystemPrompt();
6930
+ let systemPrompt = buildSystemPrompt();
7067
6931
  let lastPromptFingerprint = `${this.agentFileFingerprint}
7068
6932
  ${this.skillFingerprint}`;
7069
6933
  const pushEvent = (event) => {
@@ -7225,7 +7089,7 @@ ${this.skillFingerprint}`;
7225
7089
  inputSchema: t.inputSchema
7226
7090
  }))
7227
7091
  );
7228
- const requestTokenEstimate = estimateTotalTokens(integrityPrompt, messages, toolDefsJsonForEstimate);
7092
+ const requestTokenEstimate = estimateTotalTokens(systemPrompt, messages, toolDefsJsonForEstimate);
7229
7093
  yield pushEvent({ type: "model:request", tokens: requestTokenEstimate });
7230
7094
  const convertMessage = async (msg) => {
7231
7095
  if (msg.role === "tool") {
@@ -7417,7 +7281,7 @@ ${textContent}` };
7417
7281
  const modelInstance = this.modelProvider(modelName);
7418
7282
  const compactionConfig = resolveCompactionConfig(agent.frontmatter.compaction);
7419
7283
  if (compactionConfig.enabled && (step === 1 || step % COMPACTION_CHECK_INTERVAL_STEPS === 0)) {
7420
- const estimated = estimateTotalTokens(integrityPrompt, messages, toolDefsJsonForEstimate);
7284
+ const estimated = estimateTotalTokens(systemPrompt, messages, toolDefsJsonForEstimate);
7421
7285
  const lastReportedInput = totalInputTokens > 0 ? totalInputTokens : 0;
7422
7286
  const effectiveTokens = Math.max(estimated, lastReportedInput);
7423
7287
  if (effectiveTokens > compactionConfig.trigger * contextWindow) {
@@ -7437,7 +7301,7 @@ ${textContent}` };
7437
7301
  emittedMessages.pop();
7438
7302
  }
7439
7303
  }
7440
- const tokensAfterCompaction = estimateTotalTokens(integrityPrompt, messages, toolDefsJsonForEstimate);
7304
+ const tokensAfterCompaction = estimateTotalTokens(systemPrompt, messages, toolDefsJsonForEstimate);
7441
7305
  latestContextTokens = tokensAfterCompaction;
7442
7306
  toolOutputEstimateSinceModel = 0;
7443
7307
  yield pushEvent({
@@ -7468,14 +7332,14 @@ ${textContent}` };
7468
7332
  const telemetryEnabled = this.loadedConfig?.telemetry?.enabled !== false;
7469
7333
  const result = await streamText({
7470
7334
  model: modelInstance,
7471
- system: integrityPrompt,
7335
+ system: systemPrompt,
7472
7336
  messages: cachedMessages,
7473
7337
  tools,
7474
7338
  temperature,
7475
7339
  abortSignal: input.abortSignal,
7476
7340
  ...typeof maxTokens === "number" ? { maxTokens } : {},
7477
7341
  experimental_telemetry: {
7478
- isEnabled: telemetryEnabled && !!(this.latitudeTelemetry || this.hasOtlpExporter),
7342
+ isEnabled: telemetryEnabled && this.hasOtlpExporter,
7479
7343
  recordInputs: true,
7480
7344
  recordOutputs: true
7481
7345
  }
@@ -7820,36 +7684,21 @@ ${textContent}` };
7820
7684
  return;
7821
7685
  }
7822
7686
  const toolSpans = /* @__PURE__ */ new Map();
7823
- if (this.insideTelemetryCapture && this.latitudeTelemetry) {
7824
- for (const call of approvedCalls) {
7825
- toolSpans.set(
7826
- call.id,
7827
- this.latitudeTelemetry.span.tool({
7828
- name: call.name,
7829
- call: { id: call.id, arguments: call.input }
7830
- })
7831
- );
7832
- }
7833
- } else if (this.insideTelemetryCapture && this.hasOtlpExporter) {
7834
- const tracer = trace.getTracer("poncho");
7687
+ if (this.hasOtlpExporter) {
7688
+ const tracer = trace.getTracer("gen_ai");
7835
7689
  for (const call of approvedCalls) {
7836
- const span = tracer.startSpan(`tool ${call.name}`, {
7690
+ const toolDef = this.dispatcher.get(call.name);
7691
+ toolSpans.set(call.id, tracer.startSpan(`execute_tool ${call.name}`, {
7692
+ kind: SpanKind.INTERNAL,
7837
7693
  attributes: {
7838
- "poncho.tool.name": call.name,
7839
- "poncho.tool.call_id": call.id,
7840
- "poncho.tool.arguments": JSON.stringify(call.input)
7841
- }
7842
- });
7843
- toolSpans.set(call.id, {
7844
- end(opts) {
7845
- if (opts.result.isError) {
7846
- span.setStatus({ code: SpanStatusCode.ERROR, message: String(opts.result.value) });
7847
- } else {
7848
- span.setStatus({ code: SpanStatusCode.OK });
7849
- }
7850
- span.end();
7694
+ "gen_ai.operation.name": "execute_tool",
7695
+ "gen_ai.tool.name": call.name,
7696
+ "gen_ai.tool.call.id": call.id,
7697
+ "gen_ai.tool.type": "function",
7698
+ ...toolDef?.description ? { "gen_ai.tool.description": toolDef.description } : {},
7699
+ "gen_ai.tool.call.arguments": JSON.stringify(call.input)
7851
7700
  }
7852
- });
7701
+ }));
7853
7702
  }
7854
7703
  }
7855
7704
  const TOOL_DEADLINE_SENTINEL = /* @__PURE__ */ Symbol("tool_deadline");
@@ -7909,7 +7758,12 @@ ${textContent}` };
7909
7758
  for (const result2 of batchResults) {
7910
7759
  const span = toolSpans.get(result2.callId);
7911
7760
  if (result2.error) {
7912
- span?.end({ result: { value: result2.error, isError: true } });
7761
+ if (span) {
7762
+ span.setAttribute("error.type", "Error");
7763
+ span.setStatus({ code: SpanStatusCode.ERROR, message: result2.error });
7764
+ span.recordException(new Error(result2.error));
7765
+ span.end();
7766
+ }
7913
7767
  yield pushEvent({
7914
7768
  type: "tool:error",
7915
7769
  tool: result2.tool,
@@ -7943,7 +7797,11 @@ ${textContent}` };
7943
7797
  output: { type: "json", value: { error: result2.error } }
7944
7798
  });
7945
7799
  } else {
7946
- span?.end({ result: { value: result2.output ?? null, isError: false } });
7800
+ if (span) {
7801
+ span.setAttribute("gen_ai.tool.call.result", JSON.stringify(result2.output ?? null));
7802
+ span.setStatus({ code: SpanStatusCode.OK });
7803
+ span.end();
7804
+ }
7947
7805
  const serialized = JSON.stringify(result2.output ?? null);
7948
7806
  const outputTokenEstimate = Math.ceil(serialized.length / 4);
7949
7807
  toolOutputEstimateSinceModel += outputTokenEstimate;
@@ -8053,7 +7911,7 @@ ${textContent}` };
8053
7911
  const currentFingerprint = `${this.agentFileFingerprint}
8054
7912
  ${this.skillFingerprint}`;
8055
7913
  if (currentFingerprint !== lastPromptFingerprint) {
8056
- integrityPrompt = buildSystemPrompt();
7914
+ systemPrompt = buildSystemPrompt();
8057
7915
  lastPromptFingerprint = currentFingerprint;
8058
7916
  }
8059
7917
  }
@@ -8225,33 +8083,6 @@ ${this.skillFingerprint}`;
8225
8083
  }
8226
8084
  };
8227
8085
 
8228
- // src/latitude-capture.ts
8229
- var LatitudeCapture = class {
8230
- apiKey;
8231
- projectId;
8232
- path;
8233
- constructor(config) {
8234
- const apiKeyEnv = config?.apiKeyEnv ?? "LATITUDE_API_KEY";
8235
- this.apiKey = process.env[apiKeyEnv];
8236
- const projectIdEnv = config?.projectIdEnv ?? "LATITUDE_PROJECT_ID";
8237
- const rawProjectId = process.env[projectIdEnv];
8238
- const projectIdNumber = rawProjectId ? Number.parseInt(rawProjectId, 10) : Number.NaN;
8239
- this.projectId = Number.isFinite(projectIdNumber) ? projectIdNumber : void 0;
8240
- const rawPath = config?.path ?? process.env.LATITUDE_PATH ?? process.env.LATITUDE_DOCUMENT_PATH ?? config?.defaultPath;
8241
- this.path = rawPath;
8242
- }
8243
- isConfigured() {
8244
- return !!(this.apiKey && this.projectId && this.path);
8245
- }
8246
- getConfig() {
8247
- return {
8248
- apiKey: this.apiKey,
8249
- projectId: this.projectId,
8250
- path: this.path
8251
- };
8252
- }
8253
- };
8254
-
8255
8086
  // src/state.ts
8256
8087
  import { randomUUID as randomUUID4 } from "crypto";
8257
8088
  import { mkdir as mkdir7, readFile as readFile11, readdir as readdir4, rename as rename4, rm as rm4, writeFile as writeFile8 } from "fs/promises";
@@ -9425,7 +9256,6 @@ export {
9425
9256
  AgentHarness,
9426
9257
  InMemoryConversationStore,
9427
9258
  InMemoryStateStore,
9428
- LatitudeCapture,
9429
9259
  LocalMcpBridge,
9430
9260
  LocalUploadStore,
9431
9261
  OPENAI_CODEX_CLIENT_ID,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
@@ -23,7 +23,6 @@
23
23
  "@ai-sdk/anthropic": "^3.0.44",
24
24
  "@ai-sdk/openai": "^3.0.29",
25
25
  "@aws-sdk/client-dynamodb": "^3.988.0",
26
- "@latitude-data/telemetry": "^2.0.4",
27
26
  "@opentelemetry/api": "1.9.0",
28
27
  "@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
29
28
  "@opentelemetry/sdk-trace-node": "^2.6.0",
@@ -28,6 +28,7 @@ export interface CronJobConfig {
28
28
  task: string;
29
29
  timezone?: string;
30
30
  channel?: string;
31
+ maxRuns?: number;
31
32
  }
32
33
 
33
34
  export interface AgentFrontmatter {
@@ -144,11 +145,17 @@ const parseCronJobs = (
144
145
  ? jobValue.channel.trim()
145
146
  : undefined;
146
147
 
148
+ const maxRuns =
149
+ typeof jobValue.maxRuns === "number" && jobValue.maxRuns > 0
150
+ ? Math.max(1, Math.floor(jobValue.maxRuns))
151
+ : undefined;
152
+
147
153
  jobs[jobName] = {
148
154
  schedule: jobValue.schedule.trim(),
149
155
  task: jobValue.task,
150
156
  timezone,
151
157
  channel,
158
+ maxRuns,
152
159
  };
153
160
  }
154
161
  return jobs;
package/src/config.ts CHANGED
@@ -115,12 +115,6 @@ export interface PonchoConfig extends McpConfig {
115
115
  url: string;
116
116
  headers?: Record<string, string>;
117
117
  };
118
- latitude?: {
119
- apiKeyEnv?: string;
120
- projectIdEnv?: string;
121
- path?: string;
122
- documentPath?: string;
123
- };
124
118
  handler?: (event: unknown) => Promise<void> | void;
125
119
  };
126
120
  skills?: Record<string, Record<string, unknown>>;