agent-relay-runner 0.119.10 → 0.119.11

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.119.10",
3
+ "version": "0.119.11",
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.119.10",
4
+ "version": "0.119.11",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
@@ -2,6 +2,10 @@ type EmitAgentMessage = (body: string) => void;
2
2
  type ResponseEntry = { itemId?: string; body: string };
3
3
  type CompleteItemOptions = { includeInResponse?: boolean; phase?: unknown };
4
4
 
5
+ const MAX_CAPTURED_RESPONSE_CHARS = 16_000;
6
+ const TRUNCATION_MARKER_PREFIX = "[truncated:";
7
+ const TERSE_FINAL_MAX_CHARS = 280;
8
+
5
9
  export class CodexAgentMessageCapture {
6
10
  private segments: ResponseEntry[][] = [];
7
11
  private currentSegment: ResponseEntry[] = [];
@@ -44,9 +48,10 @@ export class CodexAgentMessageCapture {
44
48
 
45
49
  finish(emit: EmitAgentMessage): string | undefined {
46
50
  this.closeSegment(emit);
47
- const finalSegment = this.segments.at(-1);
51
+ const selectedSegments = selectResponseSegments(this.segments);
48
52
  this.segments = [];
49
- return finalSegment?.length ? finalSegment.map((entry) => entry.body).join("\n\n").trim() : undefined;
53
+ const text = selectedSegments.length ? selectedSegments.map(segmentBody).join("\n\n").trim() : undefined;
54
+ return truncateCapturedResponse(text);
50
55
  }
51
56
 
52
57
  private flushPending(emit: EmitAgentMessage): void {
@@ -90,3 +95,45 @@ export class CodexAgentMessageCapture {
90
95
  .filter((segment) => segment.length > 0);
91
96
  }
92
97
  }
98
+
99
+ function selectResponseSegments(segments: ResponseEntry[][]): ResponseEntry[][] {
100
+ const nonEmptySegments = segments.filter((segment) => segment.length > 0);
101
+ const finalSegment = nonEmptySegments.at(-1);
102
+ if (!finalSegment) return [];
103
+ const finalBody = segmentBody(finalSegment);
104
+ if (!isTerseTerminalStatus(finalBody)) return [finalSegment];
105
+
106
+ // Codex often posts the real wrap-up across multiple assistant segments before a
107
+ // final workspace-ready/status line. Keep contiguous substantive segments after
108
+ // the last procedural assistant segment, then append the terminal status.
109
+ const selectedSegments: ResponseEntry[][] = [];
110
+ for (let index = nonEmptySegments.length - 2; index >= 0; index -= 1) {
111
+ const candidate = nonEmptySegments[index];
112
+ if (!candidate || !isSubstantiveResponseSegment(segmentBody(candidate))) break;
113
+ selectedSegments.unshift(candidate);
114
+ }
115
+ return [...selectedSegments, finalSegment];
116
+ }
117
+
118
+ function segmentBody(segment: ResponseEntry[]): string {
119
+ return segment.map((entry) => entry.body).join("\n\n").trim();
120
+ }
121
+
122
+ function isTerseTerminalStatus(body: string): boolean {
123
+ if (body.length > TERSE_FINAL_MAX_CHARS) return false;
124
+ if (/\n\s*(?:[-*]|\d+\.)\s+/.test(body)) return false;
125
+ return /\b(?:landed|ready for review|handed off|no further action|done|complete|completed|committed|merged)\b/i.test(body);
126
+ }
127
+
128
+ function isSubstantiveResponseSegment(body: string): boolean {
129
+ if (body.length >= 120) return true;
130
+ if (/\n\s*(?:[-*]|\d+\.)\s+/.test(body)) return true;
131
+ if (/\*\*(?:what changed|changes?|tests?|gates?|verification|risks?|residual risk|summary)\b/i.test(body)) return true;
132
+ if (/^#{1,3}\s+\S/m.test(body)) return true;
133
+ return body.split(/\n{2,}/).length >= 2;
134
+ }
135
+
136
+ function truncateCapturedResponse(text: string | undefined): string | undefined {
137
+ if (!text || text.length <= MAX_CAPTURED_RESPONSE_CHARS) return text;
138
+ return `${text.slice(0, MAX_CAPTURED_RESPONSE_CHARS).trimEnd()}\n\n${TRUNCATION_MARKER_PREFIX} showing first ${MAX_CAPTURED_RESPONSE_CHARS} of ${text.length} chars]`;
139
+ }
@@ -533,9 +533,9 @@ export class CodexAdapter implements ProviderAdapter {
533
533
  }
534
534
 
535
535
  // Turn one completed Codex thread item into a session-mirror event. agentMessage text is
536
- // mirrored as narration as it lands; only the final contiguous assistant segment is repeated
537
- // as the turn-final response. The rest (user prompt echo, reasoning, tool steps) is surfaced
538
- // as it lands.
536
+ // mirrored as narration as it lands; the captured turn-final response repeats the substantive
537
+ // assistant answer, including a substantive wrap-up segment before a terse terminal status.
538
+ // The rest (user prompt echo, reasoning, tool steps) is surfaced as it lands.
539
539
  private handleCodexItem(item: Record<string, unknown> | undefined): void {
540
540
  if (!item) return;
541
541
  const type = stringValue(item.type);