@noetaris/harness 0.7.0 → 0.8.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/README.md CHANGED
@@ -255,7 +255,7 @@ throws on an adapter event never fails a step. Every hook is optional.
255
255
  | Hook | Fires |
256
256
  |---|---|
257
257
  | `onRunStart(ctx)` | once when a run starts |
258
- | `onRunEnd(ctx, { signal, durationMs })` | once when a run settles — completed, stopped (`$stopped`), paused, or rejected by a thrown error (`$error`) |
258
+ | `onRunEnd(ctx, { signal, durationMs, error? })` | once when a run settles — completed, stopped (`$stopped`), paused, or rejected by a thrown error (`$error`). `error` is set when the run ends with `$error` because of an error no `onError` step handled (paused) or a rejection; a route that returns `'$error'` itself gives no `error`, so test for `error`, not the signal |
259
259
  | `onStepStart(ctx)` | before each step |
260
260
  | `onStepEnd(ctx, { durationMs })` | after a step's `run` succeeds, before its `route` |
261
261
  | `onStepError(ctx, { error, durationMs })` | when a step's `run` throws |
@@ -424,6 +424,7 @@ See [LIMITATIONS.md](LIMITATIONS.md) for known limitations.
424
424
  - [`@noetaris/harness-openai`](https://github.com/noetaris-lab/harness-openai) — OpenAI adapter
425
425
  - [`@noetaris/harness-google`](https://github.com/noetaris-lab/harness-google) — Google Gemini adapter
426
426
  - [`@noetaris/harness-otel`](https://github.com/noetaris-lab/harness-otel) — OpenTelemetry observer bridge
427
+ - [`@noetaris/harness-otel-genai`](https://github.com/noetaris-lab/harness-otel-genai) — GenAI semantic-conventions extension for the bridge
427
428
 
428
429
  ## License
429
430
 
package/dist/index.d.ts CHANGED
@@ -247,12 +247,19 @@ interface Observer {
247
247
  onRunStart?: (ctx: RunContext) => void;
248
248
  /**
249
249
  * Called once when a run settles — completed, stopped, paused, or rejected by a thrown error
250
- * (a graph-definition error or a throwing callback). On a rejection `signal` is `'$error'` and
251
- * the run rejects right after, with the original error.
250
+ * (a graph-definition error or a throwing callback).
251
+ *
252
+ * When the run ends because of an error, `signal` is `'$error'` and `error` holds it: the error
253
+ * a step's `run` or `route` threw, or a failed fork's branch error, when no `onError` step
254
+ * handled it (the run resolves paused); or the original thrown value when the run rejects (it
255
+ * rejects right after). `error` is absent on every other ending. A route may also return
256
+ * `'$error'` as an ordinary signal; that ending has no `error` key, so test for `error`, not
257
+ * for the signal.
252
258
  */
253
259
  onRunEnd?: (ctx: RunContext, event: {
254
260
  signal: string;
255
261
  durationMs: number;
262
+ error?: unknown;
256
263
  }) => void;
257
264
  /** Called immediately before each step's `run` function is invoked. */
258
265
  onStepStart?: (ctx: StepContext) => void;
package/dist/index.js CHANGED
@@ -725,9 +725,10 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
725
725
  };
726
726
  safeInvoke("onRunStart", () => obs?.onRunStart?.(runCtx), onObserverError);
727
727
  let runEnded = false;
728
- const endRun = (signal) => {
728
+ const endRun = (signal, failure) => {
729
729
  runEnded = true;
730
- safeInvoke("onRunEnd", () => obs?.onRunEnd?.(runCtx, { signal, durationMs: Date.now() - runStart }), onObserverError);
730
+ const event = { signal, durationMs: Date.now() - runStart, ...failure !== void 0 ? { error: failure.error } : {} };
731
+ safeInvoke("onRunEnd", () => obs?.onRunEnd?.(runCtx, event), onObserverError);
731
732
  };
732
733
  const callCountRef = { current: 0 };
733
734
  const stepCtxRef = { current: null };
@@ -854,7 +855,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
854
855
  } catch (e) {
855
856
  state.$error = e instanceof Error ? e : new Error(String(e));
856
857
  settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
857
- endRun("$error");
858
+ endRun("$error", { error: state.$error });
858
859
  return { state, signal: "$error", cursor, paused: true };
859
860
  }
860
861
  const routed = runSucceeded || step.run === void 0 ? { outcome: "ok", signal } : { outcome: "error", signal, error: state.$error };
@@ -878,7 +879,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
878
879
  cursor = graph.onError;
879
880
  } else {
880
881
  settle({ outcome: "error", error: state.$error, next: { kind: "pause" } });
881
- endRun("$error");
882
+ endRun("$error", { error: state.$error });
882
883
  return { state, signal: "$error", cursor, paused: true };
883
884
  }
884
885
  } else {
@@ -893,7 +894,7 @@ async function runLoop(graph, state, ctx, schema, shouldStop, startCursor, callb
893
894
  }
894
895
  }
895
896
  } catch (e) {
896
- if (!runEnded) endRun("$error");
897
+ if (!runEnded) endRun("$error", { error: e });
897
898
  throw e;
898
899
  }
899
900
  }
@@ -1036,7 +1037,7 @@ async function handleFork(forkNode, priorBranches, state, ctx, schema, shouldSto
1036
1037
  return { done: false, nextCursor: graph.onError };
1037
1038
  }
1038
1039
  fireSettled(obs, stepCtx, stepStart, { outcome: "error", error: state.$error, next: { kind: "pause" } }, onObserverError);
1039
- endRun("$error");
1040
+ endRun("$error", { error: state.$error });
1040
1041
  return { done: true, result: { state, signal: "$error", cursor: forkNode.name, paused: true } };
1041
1042
  }
1042
1043
  state.$error = null;