@poncho-ai/harness 0.59.4 → 0.59.6

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.59.4 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
2
+ > @poncho-ai/harness@0.59.6 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
3
3
  > node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
4
4
 
5
5
  [embed-docs] Generated poncho-docs.ts with 4 topics
@@ -8,9 +8,9 @@
8
8
  CLI tsup v8.5.1
9
9
  CLI Target: es2022
10
10
  ESM Build start
11
- ESM dist/index.js 556.98 KB
12
11
  ESM dist/isolate-F2PPSUL6.js 53.82 KB
13
- ESM ⚡️ Build success in 270ms
12
+ ESM dist/index.js 557.73 KB
13
+ ESM ⚡️ Build success in 256ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 8326ms
16
- DTS dist/index.d.ts 101.34 KB
15
+ DTS ⚡️ Build success in 8276ms
16
+ DTS dist/index.d.ts 101.66 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @poncho-ai/harness
2
2
 
3
+ ## 0.59.6
4
+
5
+ ### Patch Changes
6
+
7
+ - [`e573f72`](https://github.com/cesr/poncho-ai/commit/e573f72ca31627e48dbdbf296946a72c59a488db) Thanks [@cesr](https://github.com/cesr)! - Preserve the LLM transcript when a turn dies. The errored branch of
8
+ runConversationTurn persisted only the display draft — `_harnessMessages`
9
+ was never updated, so the model's next turn had no memory of the entire
10
+ failed interaction (its user message included), even though the user could
11
+ see it on screen. Both the errored branch and the cancelled-without-
12
+ `run:cancelled.messages` fallback now append a faithful plain-text
13
+ reconstruction (user message + assistant text-so-far + tool activity + an
14
+ interruption note) to the transcript. Plain text on purpose: replaying real
15
+ tool_use blocks would need paired results or the next API call rejects the
16
+ dangling pair.
17
+
18
+ ## 0.59.5
19
+
20
+ ### Patch Changes
21
+
22
+ - [`d14c390`](https://github.com/cesr/poncho-ai/commit/d14c390ce6830f7446ea7a4e934d2cb76833c455) Thanks [@cesr](https://github.com/cesr)! - `continueFromToolResult` accepts and forwards the per-run `model` override, so approval-checkpoint continuations run on the same model as the checkpointed run instead of re-reading the (possibly concurrently-mutated) agent frontmatter.
23
+
3
24
  ## 0.59.4
4
25
 
5
26
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1582,6 +1582,11 @@ declare class AgentHarness {
1582
1582
  /** Emit no telemetry for the continuation run (e.g. resuming an
1583
1583
  * incognito turn after an approval). */
1584
1584
  suppressTelemetry?: boolean;
1585
+ /** Per-run model override for the continuation run — same semantics as
1586
+ * `RunInput.model`. Forward the model the checkpointed run was using,
1587
+ * otherwise the continuation falls back to the agent definition's
1588
+ * (possibly concurrently-mutated) frontmatter model. */
1589
+ model?: string;
1585
1590
  }): AsyncGenerator<AgentEvent>;
1586
1591
  runToCompletion(input: RunInput): Promise<HarnessRunOutput>;
1587
1592
  }
package/dist/index.js CHANGED
@@ -11921,7 +11921,8 @@ ${this.skillFingerprint}`;
11921
11921
  tenantId: input.tenantId,
11922
11922
  parameters: input.parameters,
11923
11923
  abortSignal: input.abortSignal,
11924
- suppressTelemetry: input.suppressTelemetry
11924
+ suppressTelemetry: input.suppressTelemetry,
11925
+ model: input.model
11925
11926
  });
11926
11927
  }
11927
11928
  async runToCompletion(input) {
@@ -14497,6 +14498,20 @@ var runConversationTurn = async (opts) => {
14497
14498
  };
14498
14499
  } catch (error) {
14499
14500
  flushTurnDraft(draft);
14501
+ const reconstructTranscriptTail = (reason) => {
14502
+ const parts = [];
14503
+ if (draft.assistantResponse.length > 0) parts.push(draft.assistantResponse);
14504
+ if (draft.toolTimeline.length > 0) {
14505
+ parts.push(`Tool activity before interruption:
14506
+ ${draft.toolTimeline.join("\n")}`);
14507
+ }
14508
+ parts.push(`[This turn was interrupted: ${reason}. The work above may be incomplete.]`);
14509
+ return [
14510
+ ...conversation._harnessMessages ?? [],
14511
+ userMessage,
14512
+ { role: "assistant", content: parts.join("\n\n") }
14513
+ ];
14514
+ };
14500
14515
  const aborted = opts.abortSignal?.aborted === true;
14501
14516
  if (aborted || runCancelled) {
14502
14517
  if (draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0) {
@@ -14507,7 +14522,7 @@ var runConversationTurn = async (opts) => {
14507
14522
  latestRunId,
14508
14523
  contextTokens: 0,
14509
14524
  contextWindow: 0,
14510
- harnessMessages: cancelHarnessMessages,
14525
+ harnessMessages: cancelHarnessMessages ?? reconstructTranscriptTail("cancelled"),
14511
14526
  toolResultArchive: opts.harness.getToolResultArchive(opts.conversationId)
14512
14527
  },
14513
14528
  { shouldRebuildCanonical: true }
@@ -14550,6 +14565,9 @@ var runConversationTurn = async (opts) => {
14550
14565
  }
14551
14566
  if (draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0) {
14552
14567
  conversation.messages = buildMessages(false);
14568
+ conversation._harnessMessages = reconstructTranscriptTail(
14569
+ error instanceof Error ? `error \u2014 ${error.message}` : "error"
14570
+ );
14553
14571
  conversation.updatedAt = Date.now();
14554
14572
  await opts.conversationStore.update(conversation);
14555
14573
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.59.4",
3
+ "version": "0.59.6",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
package/src/harness.ts CHANGED
@@ -3841,6 +3841,11 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
3841
3841
  /** Emit no telemetry for the continuation run (e.g. resuming an
3842
3842
  * incognito turn after an approval). */
3843
3843
  suppressTelemetry?: boolean;
3844
+ /** Per-run model override for the continuation run — same semantics as
3845
+ * `RunInput.model`. Forward the model the checkpointed run was using,
3846
+ * otherwise the continuation falls back to the agent definition's
3847
+ * (possibly concurrently-mutated) frontmatter model. */
3848
+ model?: string;
3844
3849
  }): AsyncGenerator<AgentEvent> {
3845
3850
  const messages = [...input.messages];
3846
3851
  const lastMsg = messages[messages.length - 1];
@@ -3900,6 +3905,7 @@ Code is wrapped in an async IIFE — use \`return\` to return a value to the too
3900
3905
  parameters: input.parameters,
3901
3906
  abortSignal: input.abortSignal,
3902
3907
  suppressTelemetry: input.suppressTelemetry,
3908
+ model: input.model,
3903
3909
  });
3904
3910
  }
3905
3911
 
@@ -420,6 +420,31 @@ export const runConversationTurn = async (
420
420
  };
421
421
  } catch (error) {
422
422
  flushTurnDraft(draft);
423
+
424
+ // The LLM transcript (`_harnessMessages`) is normally only written at a
425
+ // clean finalize / a cancel that delivered `run:cancelled.messages`. A
426
+ // turn that dies any other way (in-process error, abort that never
427
+ // surfaced the cancel event) would leave the transcript WITHOUT this
428
+ // turn at all — the display shows the partial work but the model has
429
+ // amnesia about the whole interaction on the next turn. Reconstruct a
430
+ // faithful plain-text record from the draft instead: the user message
431
+ // plus an assistant message carrying the text-so-far + tool activity.
432
+ // Plain text on purpose — replaying real tool_use blocks would need
433
+ // paired results or the next API call 400s on the dangling pair.
434
+ const reconstructTranscriptTail = (reason: string): Message[] => {
435
+ const parts: string[] = [];
436
+ if (draft.assistantResponse.length > 0) parts.push(draft.assistantResponse);
437
+ if (draft.toolTimeline.length > 0) {
438
+ parts.push(`Tool activity before interruption:\n${draft.toolTimeline.join("\n")}`);
439
+ }
440
+ parts.push(`[This turn was interrupted: ${reason}. The work above may be incomplete.]`);
441
+ return [
442
+ ...(conversation._harnessMessages ?? []),
443
+ userMessage,
444
+ { role: "assistant" as const, content: parts.join("\n\n") },
445
+ ];
446
+ };
447
+
423
448
  const aborted = opts.abortSignal?.aborted === true;
424
449
  if (aborted || runCancelled) {
425
450
  if (
@@ -434,7 +459,8 @@ export const runConversationTurn = async (
434
459
  latestRunId,
435
460
  contextTokens: 0,
436
461
  contextWindow: 0,
437
- harnessMessages: cancelHarnessMessages,
462
+ harnessMessages:
463
+ cancelHarnessMessages ?? reconstructTranscriptTail("cancelled"),
438
464
  toolResultArchive: opts.harness.getToolResultArchive(opts.conversationId),
439
465
  },
440
466
  { shouldRebuildCanonical: true },
@@ -484,6 +510,12 @@ export const runConversationTurn = async (
484
510
  draft.sections.length > 0
485
511
  ) {
486
512
  conversation.messages = buildMessages(false); // terminal: errored
513
+ // Keep the LLM transcript faithful too (see reconstructTranscriptTail
514
+ // above) — without this, the next turn's model context skipped the
515
+ // whole errored interaction.
516
+ conversation._harnessMessages = reconstructTranscriptTail(
517
+ error instanceof Error ? `error — ${error.message}` : "error",
518
+ );
487
519
  conversation.updatedAt = Date.now();
488
520
  await opts.conversationStore.update(conversation);
489
521
  }