@rulvar/cli 1.47.0 → 1.49.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/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as runCli, t as processIo } from "./io-BK1hP3Od.js";
2
+ import { r as runCli, t as processIo } from "./io-DHbOOiYv.js";
3
3
  import { sanitizeTerminalText } from "@rulvar/core";
4
4
  import { inspect } from "node:util";
5
5
  //#region src/cli.ts
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as looksLikeFile, a as resumeCommand, c as driveRun, d as attachProgress, f as renderEventLine, g as loadWorkflowModule, h as loadCliConfig, i as inspectCommand, l as reportOutcome, m as assembleEngine, n as HELP, o as runCommand, p as DEFAULT_STORE_DIR, r as runCli, s as runsLsCommand, t as processIo, u as strictExitCode } from "./io-BK1hP3Od.js";
1
+ import { _ as looksLikeFile, a as resumeCommand, c as driveRun, d as attachProgress, f as renderEventLine, g as loadWorkflowModule, h as loadCliConfig, i as inspectCommand, l as reportOutcome, m as assembleEngine, n as HELP, o as runCommand, p as DEFAULT_STORE_DIR, r as runCli, s as runsLsCommand, t as processIo, u as strictExitCode } from "./io-DHbOOiYv.js";
2
2
  import { ConfigError, InvalidResolutionError, JournalCompatibilityError, LeaseHeldError, Replayer, RulvarError, buildDeriverRegistry, costReportFromJournal, maskSecrets, normalizeEntry, readRunMeta, scanJournalCompatibility, validateSchemaSpec } from "@rulvar/core";
3
3
  //#region src/server.ts
4
4
  /**
@@ -775,8 +775,13 @@ function createWorker(engine, options) {
775
775
  * tracer)` maps the spanId tree of a run 1:1 onto OTel spans: one span
776
776
  * per rulvar span, parented per the span hierarchy (run > phase >
777
777
  * agent > tool > child), with start/end timestamps from the lifecycle
778
- * events. Events without an own span (log, budget:update) attach as span
779
- * events on their enclosing span.
778
+ * events; each agent:phase pair additionally becomes a child span of
779
+ * its agent span keyed (spanId, invocation), carrying the phase's role,
780
+ * model, usage, and cost. Events without an own span (log,
781
+ * budget:update) attach as span events on their enclosing span. An
782
+ * opener for an already-open span never duplicates it (replayed
783
+ * re-emissions mark the original; a pre-RV-207 stream's extra per-phase
784
+ * agent:start cannot leak the agent span unended).
780
785
  *
781
786
  * `@opentelemetry/api` ^1.9 is an OPTIONAL peer: the CLI has no OTel
782
787
  * dependency, and the exporter is typed against a minimal structural
@@ -799,11 +804,24 @@ function msOf(ts) {
799
804
  /** The OTel status codes (UNSET 0, OK 1, ERROR 2); inlined to avoid the peer. */
800
805
  const STATUS_OK = 1;
801
806
  const STATUS_ERROR = 2;
807
+ /**
808
+ * Usage, cost, and retry attributes on a closing span, defensively: the
809
+ * exporter tolerates foreign or truncated streams, so absent fields
810
+ * simply do not become attributes.
811
+ */
812
+ function setUsageAttributes(open, event, retryKey, retries) {
813
+ if (open === void 0) return;
814
+ if (typeof event.usage?.inputTokens === "number") open.span.setAttribute("gen_ai.usage.input_tokens", event.usage.inputTokens);
815
+ if (typeof event.usage?.outputTokens === "number") open.span.setAttribute("gen_ai.usage.output_tokens", event.usage.outputTokens);
816
+ if (typeof event.costUsd === "number") open.span.setAttribute("rulvar.cost_usd", event.costUsd);
817
+ if (typeof retries === "number") open.span.setAttribute(retryKey, retries);
818
+ }
802
819
  function spanName(event) {
803
820
  switch (event.type) {
804
821
  case "run:start": return `run ${event.workflow}`;
805
822
  case "phase:start": return `phase ${event.phase}`;
806
823
  case "agent:start": return `agent ${event.agentType || "(anon)"} ${event.role}`;
824
+ case "agent:phase:start": return `invocation ${event.role}`;
807
825
  case "tool:start": return `tool ${event.toolName}`;
808
826
  case "child:start": return `workflow ${event.workflow}`;
809
827
  default: return event.type;
@@ -822,6 +840,12 @@ function openAttributes(event, runId) {
822
840
  attrs["gen_ai.request.model"] = event.model;
823
841
  attrs["gen_ai.operation.name"] = event.role;
824
842
  }
843
+ if (event.type === "agent:phase:start") {
844
+ attrs["rulvar.agent_type"] = event.agentType;
845
+ attrs["gen_ai.request.model"] = event.model;
846
+ attrs["gen_ai.operation.name"] = event.role;
847
+ attrs["rulvar.invocation"] = event.invocation;
848
+ }
825
849
  if (event.type === "tool:start") attrs["rulvar.tool_name"] = event.toolName;
826
850
  for (const [key, value] of Object.entries(attrs)) if (typeof value === "string") attrs[key] = maskSecrets(value);
827
851
  return attrs;
@@ -838,12 +862,13 @@ async function toOtel(run, tracer, options = {}) {
838
862
  const stack = [];
839
863
  let created = 0;
840
864
  const { contextApi, setSpan } = options;
841
- const startSpan = (event) => {
842
- if (event.replayed === true && openBySpanId.has(event.spanId)) {
843
- openBySpanId.get(event.spanId)?.span.setAttribute("rulvar.replayed", true);
865
+ const startSpan = (event, key = event.spanId, parentKey) => {
866
+ const parentId = parentKey ?? event.parentSpanId;
867
+ if (openBySpanId.has(key)) {
868
+ if (event.replayed === true) openBySpanId.get(key)?.span.setAttribute("rulvar.replayed", true);
844
869
  return;
845
870
  }
846
- const parent = event.parentSpanId === void 0 ? void 0 : openBySpanId.get(event.parentSpanId);
871
+ const parent = parentId === void 0 ? void 0 : openBySpanId.get(parentId);
847
872
  const parentContext = parent !== void 0 && contextApi !== void 0 && setSpan !== void 0 ? setSpan(contextApi.active(), parent.span) : void 0;
848
873
  const span = tracer.startSpan(spanName(event), {
849
874
  startTime: msOf(event.ts),
@@ -852,10 +877,10 @@ async function toOtel(run, tracer, options = {}) {
852
877
  created += 1;
853
878
  const open = {
854
879
  span,
855
- spanId: event.spanId,
856
- ...event.parentSpanId === void 0 ? {} : { parentSpanId: event.parentSpanId }
880
+ spanId: key,
881
+ ...parentId === void 0 ? {} : { parentSpanId: parentId }
857
882
  };
858
- openBySpanId.set(event.spanId, open);
883
+ openBySpanId.set(key, open);
859
884
  stack.push(open);
860
885
  };
861
886
  const endSpan = (spanId, ts, status, message) => {
@@ -880,7 +905,17 @@ async function toOtel(run, tracer, options = {}) {
880
905
  case "run:end":
881
906
  endSpan(event.spanId, event.ts, event.status);
882
907
  break;
908
+ case "agent:phase:start":
909
+ startSpan(event, `${event.spanId}#${event.invocation}`, event.spanId);
910
+ break;
911
+ case "agent:phase:end": {
912
+ const key = `${event.spanId}#${event.invocation}`;
913
+ setUsageAttributes(openBySpanId.get(key), event, "rulvar.retries", event.retries);
914
+ endSpan(key, event.ts, event.outcome);
915
+ break;
916
+ }
883
917
  case "agent:end":
918
+ setUsageAttributes(openBySpanId.get(event.spanId), event, "rulvar.retry_count", event.retryCount);
884
919
  endSpan(event.spanId, event.ts, event.status);
885
920
  break;
886
921
  case "tool:end":
@@ -197,7 +197,9 @@ function rawEventLine(event) {
197
197
  case "run:start": return `run ${str(event.runId)} ${event.resumed === true ? "resumed" : "started"} (workflow ${str(event.workflow)})`;
198
198
  case "phase:start": return `phase ${str(event.phase)}`;
199
199
  case "agent:start": return `${replayMark}agent ${who}${str(event.label) === "" ? "" : ` [${str(event.label)}]`} ${str(event.role)} on ${str(event.model)}`;
200
- case "agent:end": return `${replayMark}agent ${who} ${str(event.status)} (${money(num(event.costUsd))}, ${String(num(event.usage?.inputTokens) + num(event.usage?.outputTokens))} tok)${event.usageApprox === true ? " approx" : ""}`;
200
+ case "agent:phase:start": return `${replayMark}agent ${who} ${str(event.role)} phase on ${str(event.model)}`;
201
+ case "agent:phase:end": return `${replayMark}agent ${who} ${str(event.role)} phase ${str(event.outcome)} (${money(num(event.costUsd))}, ${String(num(event.usage?.inputTokens) + num(event.usage?.outputTokens))} tok, ${String(num(event.durationMs))}ms${num(event.retries) > 0 ? `, ${String(num(event.retries))} retries` : ""})`;
202
+ case "agent:end": return `${replayMark}agent ${who} ${str(event.status)} (${money(num(event.costUsd))}, ${String(num(event.usage?.inputTokens) + num(event.usage?.outputTokens))} tok${num(event.retryCount) > 0 ? `, ${String(num(event.retryCount))} retries` : ""})${event.usageApprox === true ? " approx" : ""}`;
201
203
  case "agent:error": return `agent ${who} error: ${str(event.error?.message)}${event.willRetry === true ? " (will retry)" : ""}`;
202
204
  case "agent:queued": return `agent ${who} queued`;
203
205
  case "tool:start": return `${replayMark}tool ${str(event.toolName)}`;
@@ -219,6 +221,8 @@ function attachProgress(handle, io) {
219
221
  "run:start",
220
222
  "phase:start",
221
223
  "agent:start",
224
+ "agent:phase:start",
225
+ "agent:phase:end",
222
226
  "agent:end",
223
227
  "agent:error",
224
228
  "agent:queued",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/cli",
3
- "version": "1.47.0",
3
+ "version": "1.49.0",
4
4
  "description": "Rulvar shell: run/resume/runs/inspect/plan/kb commands, TUI progress, createServer, createWorker, OTel exporter.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -22,17 +22,17 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@rulvar/core": "1.47.0"
25
+ "@rulvar/core": "1.49.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.20.0",
29
29
  "tsdown": "^0.22.3",
30
30
  "typescript": "~6.0.3",
31
- "@rulvar/testing": "1.47.0",
32
- "@rulvar/plan": "1.47.0",
33
- "@rulvar/store-sqlite": "1.47.0",
34
- "@rulvar/evals": "1.47.0",
35
- "@rulvar/planner": "1.47.0"
31
+ "@rulvar/store-sqlite": "1.49.0",
32
+ "@rulvar/planner": "1.49.0",
33
+ "@rulvar/plan": "1.49.0",
34
+ "@rulvar/evals": "1.49.0",
35
+ "@rulvar/testing": "1.49.0"
36
36
  },
37
37
  "bin": {
38
38
  "rulvar": "./dist/cli.js"