Skip to content

Commit fa7e001

Browse files
{"message":"Resource not accessible by integration","documentation_url":"https://docs.github.com/rest/users/users#get-the-authenticated-user","status":"403"}oz-agent
authored andcommitted
fix: prevent raw JSON dump in respond-to-comment PR replies
When the agent completes work via tool calls without producing a final text message (type='agent'), the Reply to Comment step was falling back to posting the entire raw NDJSON stream as the PR comment body. Fix the JSONL parsing to: - Extract agent_reasoning text as fallback when no agent text exists - Extract run URL from system events to include a link to the full run - Never fall back to dumping raw JSONL output - Still handle non-JSONL (text format) output correctly Fixes REMOTE-1224 Co-Authored-By: Oz <oz-agent@warp.dev>
1 parent ce1621a commit fa7e001

2 files changed

Lines changed: 56 additions & 8 deletions

File tree

.github/workflows/respond-to-comment.yml

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,27 +201,51 @@ jobs:
201201
script: |
202202
const raw = process.env.AGENT_OUTPUT || '';
203203
204-
// Walk JSONL lines and capture the last agent text message, if any.
205-
let lastText = '';
204+
// Walk JSONL lines and extract structured content.
205+
// Priority: last `agent` text > last `agent_reasoning` text.
206+
// Never fall back to dumping raw JSONL.
207+
let lastAgentText = '';
208+
let lastReasoning = '';
209+
let runUrl = '';
210+
let isJsonl = false;
206211
for (const line of raw.split('\n')) {
207212
const trimmed = line.trim();
208213
if (!trimmed) continue;
209214
try {
210215
const obj = JSON.parse(trimmed);
216+
isJsonl = true;
211217
if (obj.type === 'agent' && typeof obj.text === 'string') {
212-
lastText = obj.text;
218+
lastAgentText = obj.text;
219+
} else if (obj.type === 'agent_reasoning' && typeof obj.text === 'string') {
220+
lastReasoning = obj.text;
221+
} else if (obj.type === 'system' && obj.run_url) {
222+
runUrl = obj.run_url;
213223
}
214224
} catch (e) {
215225
// Ignore non-JSON lines
216226
}
217227
}
218228
219-
const content = (lastText || raw || '').trim();
229+
// Use agent text if available, otherwise fall back to reasoning summary.
230+
// For non-JSONL output (e.g. text format), use raw output as-is.
231+
let content = '';
232+
if (lastAgentText) {
233+
content = lastAgentText.trim();
234+
} else if (!isJsonl && raw.trim()) {
235+
content = raw.trim();
236+
} else if (lastReasoning) {
237+
content = lastReasoning.trim();
238+
}
239+
220240
if (!content) {
221241
core.info('No agent output to reply with.');
222242
return;
223243
}
224244
245+
if (runUrl) {
246+
content += `\n\n[View full agent run](${runUrl})`;
247+
}
248+
225249
const body = `@${context.payload.comment.user.login}: ${content}`;
226250
const { owner, repo } = context.repo;
227251

examples/respond-to-comment.yml

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,27 +189,51 @@ jobs:
189189
script: |
190190
const raw = process.env.AGENT_OUTPUT || '';
191191
192-
// Walk JSONL lines and capture the last agent text message, if any.
193-
let lastText = '';
192+
// Walk JSONL lines and extract structured content.
193+
// Priority: last `agent` text > last `agent_reasoning` text.
194+
// Never fall back to dumping raw JSONL.
195+
let lastAgentText = '';
196+
let lastReasoning = '';
197+
let runUrl = '';
198+
let isJsonl = false;
194199
for (const line of raw.split('\n')) {
195200
const trimmed = line.trim();
196201
if (!trimmed) continue;
197202
try {
198203
const obj = JSON.parse(trimmed);
204+
isJsonl = true;
199205
if (obj.type === 'agent' && typeof obj.text === 'string') {
200-
lastText = obj.text;
206+
lastAgentText = obj.text;
207+
} else if (obj.type === 'agent_reasoning' && typeof obj.text === 'string') {
208+
lastReasoning = obj.text;
209+
} else if (obj.type === 'system' && obj.run_url) {
210+
runUrl = obj.run_url;
201211
}
202212
} catch (e) {
203213
// Ignore non-JSON lines
204214
}
205215
}
206216
207-
const content = (lastText || raw || '').trim();
217+
// Use agent text if available, otherwise fall back to reasoning summary.
218+
// For non-JSONL output (e.g. text format), use raw output as-is.
219+
let content = '';
220+
if (lastAgentText) {
221+
content = lastAgentText.trim();
222+
} else if (!isJsonl && raw.trim()) {
223+
content = raw.trim();
224+
} else if (lastReasoning) {
225+
content = lastReasoning.trim();
226+
}
227+
208228
if (!content) {
209229
core.info('No agent output to reply with.');
210230
return;
211231
}
212232
233+
if (runUrl) {
234+
content += `\n\n[View full agent run](${runUrl})`;
235+
}
236+
213237
const body = `@${context.payload.comment.user.login}: ${content}`;
214238
const { owner, repo } = context.repo;
215239

0 commit comments

Comments
 (0)