@mono-agent/agent-runtime 0.6.0 → 0.6.2
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 +1 -1
- package/src/agent/tools/pi-bridge.js +13 -42
- package/src/ai/file-change-stats.js +0 -21
- package/src/ai/providers/claude-sdk.js +2 -128
- package/src/ai/providers/codex-app.js +1 -2
- package/src/ai/providers/pi-native/stream-subscriber.js +9 -0
- package/src/ai/streaming/codex-events.js +7 -15
- package/types/ai/file-change-stats.d.ts +0 -24
- package/types/ai/streaming/codex-events.d.ts +15 -6
package/package.json
CHANGED
|
@@ -18,8 +18,6 @@ import {
|
|
|
18
18
|
writeToolImpl,
|
|
19
19
|
} from "./index.js";
|
|
20
20
|
import {
|
|
21
|
-
createFileEditToolResultEvent,
|
|
22
|
-
createFileEditToolUseEvent,
|
|
23
21
|
fileChangeSummary,
|
|
24
22
|
readFileChangeSnapshot,
|
|
25
23
|
statsForCompletedChange,
|
|
@@ -165,19 +163,18 @@ function compactRawMcpResult(out) {
|
|
|
165
163
|
};
|
|
166
164
|
}
|
|
167
165
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
166
|
+
function writeFileChangeDetails(path, before, after) {
|
|
167
|
+
const change = {
|
|
168
|
+
path,
|
|
169
|
+
kind: before && before.exists ? "update" : "add",
|
|
170
|
+
};
|
|
173
171
|
const lineStats = statsForCompletedChange(change, before, after);
|
|
174
172
|
const completedChange = lineStats ? { ...change, line_stats: lineStats } : change;
|
|
175
173
|
const summary = fileChangeSummary([completedChange]);
|
|
176
174
|
return {
|
|
175
|
+
status: "completed",
|
|
177
176
|
changes: [completedChange],
|
|
178
|
-
status,
|
|
179
177
|
...(summary ? { summary } : {}),
|
|
180
|
-
...(error ? { error } : {}),
|
|
181
178
|
};
|
|
182
179
|
}
|
|
183
180
|
|
|
@@ -262,24 +259,8 @@ function createBuiltinTool(name, label, description, parameters, execute, { cwd,
|
|
|
262
259
|
if (name === "Bash" && toolPolicy?.bashReadOnly && !isReadOnlyShellCommand(normalized.command)) {
|
|
263
260
|
throw new Error("Error: Planning shell policy allows only read-only inspection commands.");
|
|
264
261
|
}
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
if (isFileEdit && normalized.file_path) {
|
|
268
|
-
const before = readFileChangeSnapshot(normalized.file_path);
|
|
269
|
-
editState = {
|
|
270
|
-
path: normalized.file_path,
|
|
271
|
-
before,
|
|
272
|
-
change: {
|
|
273
|
-
path: normalized.file_path,
|
|
274
|
-
kind: name === "Write" && before && !before.exists ? "add" : "update",
|
|
275
|
-
},
|
|
276
|
-
};
|
|
277
|
-
onEvent?.(createFileEditToolUseEvent(`file_edit:${toolCallId}`, {
|
|
278
|
-
changes: [editState.change],
|
|
279
|
-
status: "in_progress",
|
|
280
|
-
}));
|
|
281
|
-
}
|
|
282
|
-
|
|
262
|
+
const shouldTrackWrite = name === "Write" && typeof normalized.file_path === "string" && normalized.file_path.length > 0;
|
|
263
|
+
const beforeWrite = shouldTrackWrite ? readFileChangeSnapshot(normalized.file_path) : null;
|
|
283
264
|
const raw = await execute(normalized, { signal, sandboxPolicy, sandboxEngine, ctx });
|
|
284
265
|
// Image reads (e.g. Read on a .png) come back as a structured image
|
|
285
266
|
// result so vision models see pixels; emit an image content block and let
|
|
@@ -288,22 +269,12 @@ function createBuiltinTool(name, label, description, parameters, execute, { cwd,
|
|
|
288
269
|
return imageResult(raw.data, raw.mimeType, { tool: name, params: normalized });
|
|
289
270
|
}
|
|
290
271
|
const text = toolText(raw);
|
|
291
|
-
if (isFileEdit && editState) {
|
|
292
|
-
const failed = isErrorText(text);
|
|
293
|
-
const after = readFileChangeSnapshot(editState.path);
|
|
294
|
-
onEvent?.(createFileEditToolResultEvent(
|
|
295
|
-
`file_edit:${toolCallId}`,
|
|
296
|
-
fileEditPayload(editState.change, {
|
|
297
|
-
status: failed ? "failed" : "completed",
|
|
298
|
-
before: editState.before,
|
|
299
|
-
after,
|
|
300
|
-
error: failed ? text : null,
|
|
301
|
-
}),
|
|
302
|
-
{ isError: failed },
|
|
303
|
-
));
|
|
304
|
-
}
|
|
305
272
|
if (isErrorText(text)) throw new Error(text);
|
|
306
|
-
|
|
273
|
+
const details = { tool: name, params: normalized };
|
|
274
|
+
if (shouldTrackWrite) {
|
|
275
|
+
details.file_change = writeFileChangeDetails(normalized.file_path, beforeWrite, readFileChangeSnapshot(normalized.file_path));
|
|
276
|
+
}
|
|
277
|
+
return textResult(text, details);
|
|
307
278
|
},
|
|
308
279
|
};
|
|
309
280
|
}
|
|
@@ -211,24 +211,3 @@ export function createFileChangePayload(raw, { cwd = process.cwd(), snapshots =
|
|
|
211
211
|
...(summary ? { summary } : {}),
|
|
212
212
|
};
|
|
213
213
|
}
|
|
214
|
-
|
|
215
|
-
export function createFileEditToolUseEvent(id, payload) {
|
|
216
|
-
return {
|
|
217
|
-
type: "assistant",
|
|
218
|
-
message: { content: [{ type: "tool_use", id, name: "file_edit", input: payload }] },
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export function createFileEditToolResultEvent(id, payload, { isError = false } = {}) {
|
|
223
|
-
return {
|
|
224
|
-
type: "user",
|
|
225
|
-
message: {
|
|
226
|
-
content: [{
|
|
227
|
-
type: "tool_result",
|
|
228
|
-
tool_use_id: id,
|
|
229
|
-
content: payload,
|
|
230
|
-
is_error: isError,
|
|
231
|
-
}],
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
createFileEditToolResultEvent,
|
|
6
|
-
createFileEditToolUseEvent,
|
|
7
|
-
fileChangeSummary,
|
|
8
|
-
readFileChangeSnapshot,
|
|
9
|
-
statsForCompletedChange,
|
|
10
|
-
} from "../file-change-stats.js";
|
|
11
3
|
import { formatLiveInputGuidance } from "../live-input-prompt.js";
|
|
12
4
|
import { estimateCost } from "../cost.js";
|
|
13
5
|
import { modelWithContextWindow } from "../runtime/context-windows.js";
|
|
@@ -209,8 +201,6 @@ function structuredOutputRejectionFromEvent(event) {
|
|
|
209
201
|
return /structured output|required schema|did not match schema|schema violation/i.test(result) ? result : null;
|
|
210
202
|
}
|
|
211
203
|
|
|
212
|
-
const CLAUDE_FILE_EDIT_MATCHER = "Edit|Write|NotebookEdit";
|
|
213
|
-
|
|
214
204
|
function mergeHookMatchers(existing = {}, additions = {}) {
|
|
215
205
|
const merged = {};
|
|
216
206
|
for (const [name, groups] of Object.entries(existing || {})) {
|
|
@@ -291,122 +281,7 @@ export function toolPayloadLimit(options) {
|
|
|
291
281
|
return { bytes: MAX_TOOL_RESULT_BYTES, usedSettings: false };
|
|
292
282
|
}
|
|
293
283
|
|
|
294
|
-
function claudeEditPath(toolName, toolInput) {
|
|
295
|
-
const input = objectInput(toolInput);
|
|
296
|
-
if (toolName === "NotebookEdit") return input.notebook_path || input.file_path || "";
|
|
297
|
-
return input.file_path || "";
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
function claudeEditKind(toolName, before) {
|
|
301
|
-
if (toolName === "Write" && before && !before.exists) return "add";
|
|
302
|
-
return "update";
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function fileEditStateKey(input, toolUseID, path) {
|
|
306
|
-
return toolUseID || input?.tool_use_id || input?.toolUseID || `${input?.tool_name || "file_edit"}:${path}`;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* @param {any} change
|
|
311
|
-
* @param {{status?: any, before?: any, after?: any, error?: any}} [options]
|
|
312
|
-
*/
|
|
313
|
-
function fileEditPayload(change, { status, before, after, error } = {}) {
|
|
314
|
-
const lineStats = statsForCompletedChange(change, before, after);
|
|
315
|
-
const completedChange = lineStats ? { ...change, line_stats: lineStats } : change;
|
|
316
|
-
const summary = fileChangeSummary([completedChange]);
|
|
317
|
-
return {
|
|
318
|
-
changes: [completedChange],
|
|
319
|
-
status,
|
|
320
|
-
...(summary ? { summary } : {}),
|
|
321
|
-
...(error ? { error } : {}),
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function createClaudeFileEditHooks({ cwd, emitEvent }) {
|
|
326
|
-
const edits = new Map();
|
|
327
|
-
const runCwd = cwd || process.cwd();
|
|
328
|
-
|
|
329
|
-
function createState(input, toolUseID, { readBefore = true } = {}) {
|
|
330
|
-
const toolName = input?.tool_name;
|
|
331
|
-
const path = claudeEditPath(toolName, input?.tool_input);
|
|
332
|
-
if (!path) return null;
|
|
333
|
-
const resolvedPath = resolve(runCwd, path);
|
|
334
|
-
const key = fileEditStateKey(input, toolUseID, resolvedPath);
|
|
335
|
-
const before = readBefore ? readFileChangeSnapshot(resolvedPath) : null;
|
|
336
|
-
return {
|
|
337
|
-
key,
|
|
338
|
-
id: `file_edit:${key}`,
|
|
339
|
-
path: resolvedPath,
|
|
340
|
-
change: { path: resolvedPath, kind: claudeEditKind(toolName, before) },
|
|
341
|
-
before,
|
|
342
|
-
started: false,
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function emitStart(state) {
|
|
347
|
-
if (!state || state.started) return;
|
|
348
|
-
emitEvent(createFileEditToolUseEvent(state.id, {
|
|
349
|
-
changes: [state.change],
|
|
350
|
-
status: "in_progress",
|
|
351
|
-
}));
|
|
352
|
-
state.started = true;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* @param {any} input
|
|
357
|
-
* @param {any} toolUseID
|
|
358
|
-
* @param {{status?: any, error?: any}} [options]
|
|
359
|
-
*/
|
|
360
|
-
function complete(input, toolUseID, { status, error } = {}) {
|
|
361
|
-
const directKey = toolUseID || input?.tool_use_id || input?.toolUseID;
|
|
362
|
-
const fallback = createState(input, toolUseID, { readBefore: false });
|
|
363
|
-
const state = (directKey && edits.get(directKey)) || (fallback?.key && edits.get(fallback.key)) || fallback;
|
|
364
|
-
if (!state) return;
|
|
365
|
-
emitStart(state);
|
|
366
|
-
const after = readFileChangeSnapshot(state.path);
|
|
367
|
-
const payload = fileEditPayload(state.change, {
|
|
368
|
-
status,
|
|
369
|
-
before: state.before,
|
|
370
|
-
after,
|
|
371
|
-
error,
|
|
372
|
-
});
|
|
373
|
-
emitEvent(createFileEditToolResultEvent(state.id, payload, { isError: status === "failed" }));
|
|
374
|
-
edits.delete(state.key);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
return {
|
|
378
|
-
PreToolUse: [{
|
|
379
|
-
matcher: CLAUDE_FILE_EDIT_MATCHER,
|
|
380
|
-
hooks: [async (input, toolUseID) => {
|
|
381
|
-
const state = createState(input, toolUseID);
|
|
382
|
-
if (!state) return {};
|
|
383
|
-
edits.set(state.key, state);
|
|
384
|
-
emitStart(state);
|
|
385
|
-
return {};
|
|
386
|
-
}],
|
|
387
|
-
}],
|
|
388
|
-
PostToolUse: [{
|
|
389
|
-
matcher: CLAUDE_FILE_EDIT_MATCHER,
|
|
390
|
-
hooks: [async (input, toolUseID) => {
|
|
391
|
-
complete(input, toolUseID, { status: "completed" });
|
|
392
|
-
return {};
|
|
393
|
-
}],
|
|
394
|
-
}],
|
|
395
|
-
PostToolUseFailure: [{
|
|
396
|
-
matcher: CLAUDE_FILE_EDIT_MATCHER,
|
|
397
|
-
hooks: [async (input, toolUseID) => {
|
|
398
|
-
complete(input, toolUseID, {
|
|
399
|
-
status: "failed",
|
|
400
|
-
error: stringifyError(input?.error) || "tool failed",
|
|
401
|
-
});
|
|
402
|
-
return {};
|
|
403
|
-
}],
|
|
404
|
-
}],
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
|
|
408
284
|
function createClaudeRuntimeHooks({
|
|
409
|
-
cwd,
|
|
410
285
|
emitEvent,
|
|
411
286
|
persistArtifact,
|
|
412
287
|
qaOutputDir,
|
|
@@ -414,7 +289,7 @@ function createClaudeRuntimeHooks({
|
|
|
414
289
|
onToolUse,
|
|
415
290
|
onToolResult,
|
|
416
291
|
}) {
|
|
417
|
-
return
|
|
292
|
+
return {
|
|
418
293
|
PreToolUse: [{
|
|
419
294
|
matcher: "*",
|
|
420
295
|
hooks: [async (input) => {
|
|
@@ -469,7 +344,7 @@ function createClaudeRuntimeHooks({
|
|
|
469
344
|
return {};
|
|
470
345
|
}],
|
|
471
346
|
}],
|
|
472
|
-
}
|
|
347
|
+
};
|
|
473
348
|
}
|
|
474
349
|
|
|
475
350
|
function promptStringFromMessages(messages) {
|
|
@@ -607,7 +482,6 @@ export async function generateClaudeResponse(systemPrompt, options) {
|
|
|
607
482
|
...(approvalManager ? { canUseTool: createClaudeCanUseTool(approvalManager, model.model) } : {}),
|
|
608
483
|
...(nativeAgents ? { agents: nativeAgents } : {}),
|
|
609
484
|
hooks: mergeHookMatchers(hooks, createClaudeRuntimeHooks({
|
|
610
|
-
cwd,
|
|
611
485
|
emitEvent,
|
|
612
486
|
persistArtifact,
|
|
613
487
|
qaOutputDir,
|
|
@@ -116,8 +116,7 @@ function sandboxForPermissionMode(permissionMode) {
|
|
|
116
116
|
|
|
117
117
|
function approvalPolicyForPermissionMode(permissionMode) {
|
|
118
118
|
if (permissionMode === "bypassPermissions") return "never";
|
|
119
|
-
|
|
120
|
-
return "on-failure";
|
|
119
|
+
return "on-request";
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
function codexMcpConfig(mcpServers = {}) {
|
|
@@ -25,6 +25,13 @@ export function toolStartProgressText(toolName) {
|
|
|
25
25
|
return `Running ${toolName}...`;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function toolResultFileChange(result) {
|
|
29
|
+
const fileChange = result?.details?.file_change;
|
|
30
|
+
return fileChange && typeof fileChange === "object" && !Array.isArray(fileChange)
|
|
31
|
+
? jsonSerializable(fileChange, null)
|
|
32
|
+
: null;
|
|
33
|
+
}
|
|
34
|
+
|
|
28
35
|
/**
|
|
29
36
|
* The slice of run state the stream subscriber reads and mutates. A structural
|
|
30
37
|
* subset of the orchestrator's runState.
|
|
@@ -96,6 +103,7 @@ export function createStreamSubscriber(runState, { onEvent, options, toolLimits,
|
|
|
96
103
|
});
|
|
97
104
|
} else if (event.type === "tool_execution_end") {
|
|
98
105
|
const resultContent = toolResultContent(event.result);
|
|
106
|
+
const fileChange = toolResultFileChange(event.result);
|
|
99
107
|
if (!event.isError) runState.toolResultsSeen += 1;
|
|
100
108
|
const startedAt = runState.toolStartTimes.get(event.toolCallId);
|
|
101
109
|
if (startedAt !== undefined) {
|
|
@@ -116,6 +124,7 @@ export function createStreamSubscriber(runState, { onEvent, options, toolLimits,
|
|
|
116
124
|
tool_use_id: event.toolCallId,
|
|
117
125
|
content: resultContent,
|
|
118
126
|
raw_result: compactToolRawResult(jsonSerializable(event.result, resultContent), resultContent),
|
|
127
|
+
...(fileChange === null ? {} : { file_change: fileChange }),
|
|
119
128
|
is_error: !!event.isError,
|
|
120
129
|
}],
|
|
121
130
|
},
|
|
@@ -70,22 +70,14 @@ export function normalizeCodexItemEvent(raw, context = {}) {
|
|
|
70
70
|
if (type === "file_change") {
|
|
71
71
|
const id = itemId(item, "file_change");
|
|
72
72
|
const payload = fileChangePayload(raw, item, context);
|
|
73
|
-
if (!isCompleted(raw)) {
|
|
74
|
-
return {
|
|
75
|
-
type: "assistant",
|
|
76
|
-
message: { content: [{ type: "tool_use", id, name: "file_edit", input: payload }] },
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
73
|
return {
|
|
80
|
-
type: "
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}],
|
|
88
|
-
},
|
|
74
|
+
type: "file_change",
|
|
75
|
+
id,
|
|
76
|
+
status: payload.status,
|
|
77
|
+
changes: payload.changes,
|
|
78
|
+
...(payload.summary ? { summary: payload.summary } : {}),
|
|
79
|
+
...(item.error ? { error: item.error } : {}),
|
|
80
|
+
is_error: itemFailed(item),
|
|
89
81
|
};
|
|
90
82
|
}
|
|
91
83
|
|
|
@@ -49,27 +49,3 @@ export function createFileChangePayload(raw: any, { cwd, snapshots }?: {
|
|
|
49
49
|
changes: any;
|
|
50
50
|
status: any;
|
|
51
51
|
};
|
|
52
|
-
export function createFileEditToolUseEvent(id: any, payload: any): {
|
|
53
|
-
type: string;
|
|
54
|
-
message: {
|
|
55
|
-
content: {
|
|
56
|
-
type: string;
|
|
57
|
-
id: any;
|
|
58
|
-
name: string;
|
|
59
|
-
input: any;
|
|
60
|
-
}[];
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
export function createFileEditToolResultEvent(id: any, payload: any, { isError }?: {
|
|
64
|
-
isError?: boolean;
|
|
65
|
-
}): {
|
|
66
|
-
type: string;
|
|
67
|
-
message: {
|
|
68
|
-
content: {
|
|
69
|
-
type: string;
|
|
70
|
-
tool_use_id: any;
|
|
71
|
-
content: any;
|
|
72
|
-
is_error: boolean;
|
|
73
|
-
}[];
|
|
74
|
-
};
|
|
75
|
-
};
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
export function normalizeCodexItemType(type: any): any;
|
|
2
2
|
export function normalizeCodexItemEvent(raw: any, context?: {}): {
|
|
3
|
+
is_error: boolean;
|
|
4
|
+
error?: any;
|
|
5
|
+
summary?: any;
|
|
6
|
+
type: string;
|
|
7
|
+
id: any;
|
|
8
|
+
status: any;
|
|
9
|
+
changes: any;
|
|
10
|
+
message?: undefined;
|
|
11
|
+
} | {
|
|
3
12
|
type: string;
|
|
4
13
|
message: {
|
|
5
14
|
content: {
|
|
6
15
|
type: string;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
16
|
+
id: any;
|
|
17
|
+
name: any;
|
|
18
|
+
input: any;
|
|
10
19
|
}[];
|
|
11
20
|
};
|
|
12
21
|
} | {
|
|
@@ -14,9 +23,9 @@ export function normalizeCodexItemEvent(raw: any, context?: {}): {
|
|
|
14
23
|
message: {
|
|
15
24
|
content: {
|
|
16
25
|
type: string;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
tool_use_id: any;
|
|
27
|
+
content: any;
|
|
28
|
+
is_error: boolean;
|
|
20
29
|
}[];
|
|
21
30
|
};
|
|
22
31
|
} | {
|