agent-relay-runner 0.10.23 → 0.10.24

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": "agent-relay-runner",
3
- "version": "0.10.23",
3
+ "version": "0.10.24",
4
4
  "description": "Unified provider lifecycle runner for Agent Relay",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
3
  "description": "Thin Agent Relay runner bridge for Claude Code",
4
- "version": "0.10.23",
4
+ "version": "0.10.24",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
@@ -49,6 +49,28 @@ function assistantText(entry: TranscriptEntry): string {
49
49
  .join("\n\n");
50
50
  }
51
51
 
52
+ /**
53
+ * Returns true when the transcript's final parseable entry is an assistant
54
+ * message with stop_reason "end_turn", meaning the full turn has been flushed.
55
+ * Used to detect a race where the Stop hook reads the file before the last
56
+ * assistant block is written to disk.
57
+ */
58
+ export function transcriptLooksComplete(jsonl: string): boolean {
59
+ const lines = jsonl.split("\n");
60
+ let lastAssistantStopReason: string | undefined;
61
+ for (const line of lines) {
62
+ const trimmed = line.trim();
63
+ if (!trimmed) continue;
64
+ try {
65
+ const entry = JSON.parse(trimmed) as TranscriptEntry;
66
+ if (entry.type === "assistant") lastAssistantStopReason = entry.message?.stop_reason;
67
+ } catch {
68
+ continue;
69
+ }
70
+ }
71
+ return lastAssistantStopReason === "end_turn";
72
+ }
73
+
52
74
  /**
53
75
  * Returns the concatenated assistant `text` for the most recent turn (since the
54
76
  * last real user prompt), or "" if there is nothing user-visible to surface.
package/src/runner.ts CHANGED
@@ -9,7 +9,7 @@ import type { ManagedProcess, ProviderAdapter, ProviderConfig, ProviderPermissio
9
9
  import { messagesWithCachedAttachments } from "./attachment-cache";
10
10
  import { ClaimTracker } from "./claim-tracker";
11
11
  import { startControlServer, type ControlServer } from "./control-server";
12
- import { extractLastAssistantTurn } from "./adapters/claude-transcript";
12
+ import { extractLastAssistantTurn, transcriptLooksComplete } from "./adapters/claude-transcript";
13
13
  import { agentProfileProjectionReport } from "./profile-projection";
14
14
  import { profileUsesHostProviderGlobals } from "./profile-home";
15
15
  import { runtimeMetadata } from "./version";
@@ -690,6 +690,12 @@ export class AgentRunner {
690
690
  } catch {
691
691
  return;
692
692
  }
693
+ // The Stop hook can fire before the final assistant entry is flushed to
694
+ // disk. Retry a few times with a short delay to catch the race.
695
+ for (let attempt = 0; attempt < 3 && !transcriptLooksComplete(jsonl); attempt++) {
696
+ await new Promise((r) => setTimeout(r, 100));
697
+ try { jsonl = await readFile(input.transcriptPath, "utf8"); } catch { return; }
698
+ }
693
699
  const body = extractLastAssistantTurn(jsonl);
694
700
  if (!body) return;
695
701