agent-relay-runner 0.10.22 → 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.22",
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.22",
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";
@@ -317,6 +317,7 @@ export class AgentRunner {
317
317
 
318
318
  private enqueueMessage(message: Message): void {
319
319
  if (!this.matchesMessage(message)) return;
320
+ if (message.kind === "session") return;
320
321
  this.pendingMessages.set(message.id, message);
321
322
  this.scheduleDrain();
322
323
  }
@@ -689,6 +690,12 @@ export class AgentRunner {
689
690
  } catch {
690
691
  return;
691
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
+ }
692
699
  const body = extractLastAssistantTurn(jsonl);
693
700
  if (!body) return;
694
701