agent-relay-runner 0.119.10 → 0.120.0
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.
|
|
3
|
+
"version": "0.120.0",
|
|
4
4
|
"description": "Unified provider lifecycle runner for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"agent-relay-providers": "0.104.3",
|
|
24
|
-
"agent-relay-sdk": "0.2.
|
|
24
|
+
"agent-relay-sdk": "0.2.107",
|
|
25
25
|
"callmux": "0.23.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
@@ -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
|
|
51
|
+
const selectedSegments = selectResponseSegments(this.segments);
|
|
48
52
|
this.segments = [];
|
|
49
|
-
|
|
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
|
+
}
|
package/src/adapters/codex.ts
CHANGED
|
@@ -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;
|
|
537
|
-
//
|
|
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);
|
package/src/relay-mcp.ts
CHANGED
|
@@ -67,13 +67,16 @@ export function tomlString(value: string): string {
|
|
|
67
67
|
// project-cwd `.mcp.json` is never discovered (its enable modal can't wedge a headless
|
|
68
68
|
// agent). Whatever the assembler decides host mode should keep is injected here explicitly;
|
|
69
69
|
// strict then preserves exactly that set and nothing else.
|
|
70
|
-
import type
|
|
70
|
+
import { SHARED_CALLMUX_TOOL_CALL_TIMEOUT_MS, type ProvisioningMcpServer } from "agent-relay-sdk";
|
|
71
71
|
|
|
72
72
|
// The shared-listener bridge as a provider-neutral stdio server descriptor: a `callmux bridge`
|
|
73
73
|
// child fronting the host's shared listener, with per-agent `--cwd "$WORKTREE"` for session-cwd
|
|
74
74
|
// isolation (#672). It flows through the existing claude/codex stdio injection unchanged.
|
|
75
75
|
export function sharedMcpBridgeServer(url: string, cwd: string): ProvisioningMcpServer {
|
|
76
|
-
return {
|
|
76
|
+
return {
|
|
77
|
+
command: "callmux",
|
|
78
|
+
args: ["bridge", "--url", url, "--cwd", cwd, "--call-timeout", String(SHARED_CALLMUX_TOOL_CALL_TIMEOUT_MS)],
|
|
79
|
+
};
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
function claudeMcpServerEntry(server: ProvisioningMcpServer): Record<string, unknown> {
|