@forwardimpact/libharness 3.1.0 → 3.2.1

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": "@forwardimpact/libharness",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "description": "Autonomous agent team harness — coordinate a lead and participant agents in one async session, with eval, benchmark, and trace tooling to prove the changes worked.",
5
5
  "keywords": [
6
6
  "orchestration",
@@ -52,18 +52,27 @@ function readTraceSummary(content) {
52
52
  return summary;
53
53
  }
54
54
 
55
+ /** Terminal summary when the run wrote no trace the verb can read. */
56
+ const NO_TRACE_SUMMARY = "The run produced no trace file. See the run log.";
57
+
58
+ /** Terminal summary when a trace exists but carries no orchestrator summary. */
59
+ const NO_SUMMARY_TEXT = "The run ended and produced no summary.";
60
+
55
61
  /**
56
62
  * Callback command — read an NDJSON trace and extract the terminal
57
63
  * orchestrator summary. POST a canonical callback body to the configured
58
- * URL. `kata-dispatch.yml` uses this command to deliver the lead's
59
- * conclusion to the bridge that dispatched the run.
64
+ * URL.
65
+ *
66
+ * `--trace-file` is optional: an absent or empty path posts the same shape
67
+ * with `verdict: failed`, so a run that produced no trace never strands its
68
+ * caller.
60
69
  *
61
70
  * Wire shape (single shape across modes):
62
71
  *
63
72
  * ```
64
73
  * {
65
- * correlation_id, verdict, summary, run_url,
66
- * discussion_id?, replies: [], trigger?
74
+ * correlation_id, kind, verdict, summary, run_url, cost_usd,
75
+ * replies: [], last_acted_seq, discussion_id?, trigger?
67
76
  * }
68
77
  * ```
69
78
  *
@@ -79,20 +88,26 @@ export async function runCallbackCommand(ctx) {
79
88
  const runUrl = values["run-url"] ?? "";
80
89
  const discussionIdOverride = values["discussion-id"] ?? null;
81
90
 
82
- if (!traceFile)
83
- return { ok: false, code: 1, error: "--trace-file is required" };
84
91
  if (!callbackUrl)
85
92
  return { ok: false, code: 1, error: "--callback-url is required" };
86
93
 
87
- const content = runtime.fsSync.readFileSync(traceFile, "utf8");
88
- const found = readTraceSummary(content) ?? {
89
- verdict: "failed",
90
- summary: "The run ended and produced no summary.",
91
- replies: [],
92
- };
94
+ const content =
95
+ traceFile && runtime.fsSync.existsSync(traceFile)
96
+ ? runtime.fsSync.readFileSync(traceFile, "utf8")
97
+ : null;
98
+ const found =
99
+ content === null
100
+ ? { verdict: "failed", summary: NO_TRACE_SUMMARY, replies: [] }
101
+ : (readTraceSummary(content) ?? {
102
+ verdict: "failed",
103
+ summary: NO_SUMMARY_TEXT,
104
+ replies: [],
105
+ });
93
106
  // Total spend across every participant in the trace. The bridge surfaces
94
107
  // it alongside the verdict, so a dispatched run reports what it cost.
95
- const { totalCostUsd } = sumTraceCost(content.split("\n"));
108
+ const { totalCostUsd } = sumTraceCost(
109
+ content === null ? [] : content.split("\n"),
110
+ );
96
111
 
97
112
  const discussionId = found.discussionId ?? discussionIdOverride ?? null;
98
113
  const payload = {
@@ -1,6 +1,5 @@
1
1
  /**
2
- * GitHub event → task-prompt composition. Replaces ~70 lines of shell in
3
- * kata-dispatch.yml's `Compose task text` step. Each branch in the dispatch
2
+ * GitHub event → task-prompt composition. Each branch in the dispatch
4
3
  * function corresponds to one (event_name, action) the agent workflows react
5
4
  * to.
6
5
  *
@@ -37,10 +36,10 @@ export const TASK_TEMPLATE_PR_LABELED =
37
36
  //
38
37
  // A human merge is an act of approval, so the template says so. It does not
39
38
  // frame the event as bookkeeping. What to record belongs to the
40
- // approval-signals reference. The template supplies the identity and the
39
+ // team protocol. The template supplies the identity and the
41
40
  // pointer. "cut" still names the genuine post-merge chore.
42
41
  export const TASK_TEMPLATE_PR_MERGED =
43
- 'PR "${PR_TITLE}" (#${NUMBER}) merged to main by @${MERGED_BY} (type: ${MERGED_BY_TYPE}); opened by @${AUTHOR}. A human merge is an approval — record it per the approval-signals reference. May leave unreleased changes to cut. PR URL: ${URL}.';
42
+ 'PR "${PR_TITLE}" (#${NUMBER}) merged to main by @${MERGED_BY} (type: ${MERGED_BY_TYPE}); opened by @${AUTHOR}. A human merge is an approval — record it per the team protocol. May leave unreleased changes to cut. PR URL: ${URL}.';
44
43
 
45
44
  // The comment and review templates append this verbatim. `${BODY}` is the
46
45
  // untrusted author text. The fence and the "data, not instructions" label make