codemini-cli 0.6.3 → 0.6.5
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/codemini-web/dist/assets/{AboutDialog-jgqGjQgl.js → AboutDialog-BUp8EzDg.js} +2 -2
- package/codemini-web/dist/assets/CodeWikiPanel-Fp0VKdzo.js +1 -0
- package/codemini-web/dist/assets/ConfigDialog-DIpj779O.js +1 -0
- package/codemini-web/dist/assets/GitDiffDialog-ZLEuX8Qm.js +3 -0
- package/codemini-web/dist/assets/{MemoryDialog-BhxQgG0I.js → MemoryDialog-D2YbENVd.js} +3 -3
- package/codemini-web/dist/assets/MessageBubble-BIgpZsLn.js +12 -0
- package/codemini-web/dist/assets/PatchDiff-CvKNaHsw.js +230 -0
- package/codemini-web/dist/assets/ProjectSelector-DXIep3lE.js +1 -0
- package/codemini-web/dist/assets/{SkillDialog-DxS43NpR.js → SkillDialog-DjPF-XBx.js} +4 -4
- package/codemini-web/dist/assets/SoulDialog-BfIoKETs.js +1 -0
- package/codemini-web/dist/assets/chevron-right-CfNZHlyU.js +1 -0
- package/codemini-web/dist/assets/{chunk-BO2N2NFS-Budy_hfO.js → chunk-BO2N2NFS-DMUdjM9q.js} +6 -6
- package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-CQS1PAvD.js → highlighted-body-OFNGDK62-8ch0jz7Z.js} +1 -1
- package/codemini-web/dist/assets/index-BhMtCC8_.js +65 -0
- package/codemini-web/dist/assets/index-DRXwJ-n_.css +2 -0
- package/codemini-web/dist/assets/input-CYpdNDlR.js +1 -0
- package/codemini-web/dist/assets/lib-BXWizt13.js +1 -0
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-KBEtMEB9.js +1 -0
- package/codemini-web/dist/assets/{pencil-Ce_LFiEh.js → pencil-BdA2cEeE.js} +1 -1
- package/codemini-web/dist/assets/{refresh-cw-BKL-AZu5.js → refresh-cw-CJGgUGiS.js} +1 -1
- package/codemini-web/dist/assets/select-BLOccU1M.js +1 -0
- package/codemini-web/dist/assets/{trash-2-KmAlCwXd.js → trash-2-CQzNOch5.js} +1 -1
- package/codemini-web/dist/index.html +2 -2
- package/codemini-web/lib/runtime-bridge.js +332 -296
- package/codemini-web/server.js +319 -243
- package/package.json +1 -1
- package/src/core/agent-loop.js +188 -100
- package/src/core/chat-runtime.js +676 -571
- package/src/core/config-store.js +9 -3
- package/src/core/git-oplog-change-tracker.js +468 -0
- package/src/core/non-git-backup.js +116 -0
- package/src/core/paths.js +123 -123
- package/src/core/session-store.js +148 -99
- package/src/core/tools.js +555 -434
- package/src/tui/chat-app.js +196 -56
- package/codemini-web/dist/assets/CodeWikiPanel-EPuoerNv.js +0 -1
- package/codemini-web/dist/assets/ConfigDialog-B5IGZCc9.js +0 -1
- package/codemini-web/dist/assets/GitDiffDialog-Bb_Tw5ZK.js +0 -222
- package/codemini-web/dist/assets/MessageBubble-wUff4GP4.js +0 -6
- package/codemini-web/dist/assets/ProjectSelector-C0leTf6f.js +0 -1
- package/codemini-web/dist/assets/SoulDialog-XDTEGWvH.js +0 -1
- package/codemini-web/dist/assets/chevron-right-Dbzw7YzA.js +0 -1
- package/codemini-web/dist/assets/index-D0EGtNPr.js +0 -65
- package/codemini-web/dist/assets/index-wOUf3WkN.css +0 -2
- package/codemini-web/dist/assets/input-CNQgbKe6.js +0 -1
- package/codemini-web/dist/assets/lib-BOngVP_M.js +0 -11
- package/codemini-web/dist/assets/lib-DrOTTm_N.js +0 -1
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-DrBu5KyC.js +0 -1
- package/codemini-web/dist/assets/select-BZXfigic.js +0 -1
package/package.json
CHANGED
package/src/core/agent-loop.js
CHANGED
|
@@ -236,7 +236,7 @@ async function checkAutoDreamThreshold(config) {
|
|
|
236
236
|
|
|
237
237
|
// ─── Exported helpers ────────────────────────────────────────────────
|
|
238
238
|
|
|
239
|
-
function extractFileChange(toolName, result) {
|
|
239
|
+
function extractFileChange(toolName, result) {
|
|
240
240
|
if (!result || typeof result !== 'object') return null;
|
|
241
241
|
const FILE_TOOLS = new Set(['edit', 'write', 'delete']);
|
|
242
242
|
if (!FILE_TOOLS.has(toolName)) return null;
|
|
@@ -252,56 +252,90 @@ function extractFileChange(toolName, result) {
|
|
|
252
252
|
const isCreate = action === 'create';
|
|
253
253
|
const added = Number(result.lines_added || 0);
|
|
254
254
|
const removed = Number(result.lines_removed || 0);
|
|
255
|
-
return {
|
|
256
|
-
path: String(result.path || ''),
|
|
257
|
-
action: isCreate ? 'create' : 'edit',
|
|
258
|
-
linesAdded: added,
|
|
259
|
-
linesRemoved: removed,
|
|
260
|
-
changedLine: Number(result.changed_line || 0),
|
|
261
|
-
diffPreview: String(result.diff_preview || '')
|
|
262
|
-
};
|
|
263
|
-
}
|
|
255
|
+
return {
|
|
256
|
+
path: String(result.path || ''),
|
|
257
|
+
action: isCreate ? 'create' : 'edit',
|
|
258
|
+
linesAdded: added,
|
|
259
|
+
linesRemoved: removed,
|
|
260
|
+
changedLine: Number(result.changed_line || 0),
|
|
261
|
+
diffPreview: String(result.diff_preview || '')
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
264
267
|
|
|
265
|
-
return null;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
268
|
function normalizeFileChange(change) {
|
|
269
|
-
if (!change || typeof change !== 'object') return null;
|
|
270
|
-
const path = String(change.path || '').trim();
|
|
271
|
-
if (!path) return null;
|
|
272
|
-
const action = String(change.action || '').trim();
|
|
273
|
-
return {
|
|
274
|
-
path,
|
|
275
|
-
action: action === 'create' || action === 'delete' ? action : 'edit',
|
|
269
|
+
if (!change || typeof change !== 'object') return null;
|
|
270
|
+
const path = String(change.path || '').trim();
|
|
271
|
+
if (!path) return null;
|
|
272
|
+
const action = String(change.action || '').trim();
|
|
273
|
+
return {
|
|
274
|
+
path,
|
|
275
|
+
action: action === 'create' || action === 'delete' ? action : 'edit',
|
|
276
276
|
linesAdded: Number(change.linesAdded || 0),
|
|
277
277
|
linesRemoved: Number(change.linesRemoved || 0),
|
|
278
278
|
changedLine: Number(change.changedLine || 0),
|
|
279
|
-
diffPreview: String(change.diffPreview || '')
|
|
279
|
+
diffPreview: String(change.diffPreview || ''),
|
|
280
|
+
changeSetId: String(change.changeSetId || ''),
|
|
281
|
+
patchRef: String(change.patchRef || '')
|
|
280
282
|
};
|
|
281
283
|
}
|
|
282
|
-
|
|
283
|
-
function fileChangeFingerprint(change) {
|
|
284
|
-
return JSON.stringify({
|
|
285
|
-
path: change.path,
|
|
286
|
-
action: change.action,
|
|
287
|
-
linesAdded: Number(change.linesAdded || 0),
|
|
288
|
-
linesRemoved: Number(change.linesRemoved || 0),
|
|
289
|
-
changedLine: Number(change.changedLine || 0),
|
|
290
|
-
diffPreview: String(change.diffPreview || '')
|
|
284
|
+
|
|
285
|
+
function fileChangeFingerprint(change) {
|
|
286
|
+
return JSON.stringify({
|
|
287
|
+
path: change.path,
|
|
288
|
+
action: change.action,
|
|
289
|
+
linesAdded: Number(change.linesAdded || 0),
|
|
290
|
+
linesRemoved: Number(change.linesRemoved || 0),
|
|
291
|
+
changedLine: Number(change.changedLine || 0),
|
|
292
|
+
diffPreview: String(change.diffPreview || ''),
|
|
293
|
+
changeSetId: String(change.changeSetId || ''),
|
|
294
|
+
patchRef: String(change.patchRef || '')
|
|
291
295
|
});
|
|
292
296
|
}
|
|
293
|
-
|
|
297
|
+
|
|
294
298
|
function appendUniqueFileChange(message, fileChange) {
|
|
295
|
-
const existing = Array.isArray(message.file_changes) ? message.file_changes : [];
|
|
296
|
-
const nextKey = fileChangeFingerprint(fileChange);
|
|
297
|
-
if (existing.some((change) => fileChangeFingerprint(normalizeFileChange(change) || {}) === nextKey)) {
|
|
298
|
-
message.file_changes = existing;
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
299
|
+
const existing = Array.isArray(message.file_changes) ? message.file_changes : [];
|
|
300
|
+
const nextKey = fileChangeFingerprint(fileChange);
|
|
301
|
+
if (existing.some((change) => fileChangeFingerprint(normalizeFileChange(change) || {}) === nextKey)) {
|
|
302
|
+
message.file_changes = existing;
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
301
305
|
message.file_changes = [...existing, fileChange];
|
|
302
306
|
}
|
|
303
|
-
|
|
304
|
-
|
|
307
|
+
|
|
308
|
+
function normalizeFileChanges(changes) {
|
|
309
|
+
return (Array.isArray(changes) ? changes : [changes])
|
|
310
|
+
.map(normalizeFileChange)
|
|
311
|
+
.filter(Boolean);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function extractToolResultMeta(toolName, result) {
|
|
315
|
+
if (!result || typeof result !== 'object') return null;
|
|
316
|
+
if (!['edit', 'write', 'delete'].includes(String(toolName || ''))) return null;
|
|
317
|
+
const meta = {};
|
|
318
|
+
for (const key of [
|
|
319
|
+
'path',
|
|
320
|
+
'action',
|
|
321
|
+
'changed_line',
|
|
322
|
+
'lines_added',
|
|
323
|
+
'lines_removed',
|
|
324
|
+
'backupPath',
|
|
325
|
+
'backupRelativePath',
|
|
326
|
+
'backupCreated',
|
|
327
|
+
'backupReused',
|
|
328
|
+
'backupSkipped',
|
|
329
|
+
'backupError',
|
|
330
|
+
'backupReason',
|
|
331
|
+
'non_git_backup'
|
|
332
|
+
]) {
|
|
333
|
+
if (result[key] !== undefined && result[key] !== null && result[key] !== '') meta[key] = result[key];
|
|
334
|
+
}
|
|
335
|
+
return Object.keys(meta).length ? meta : null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export const trimInline = _trimInline;
|
|
305
339
|
|
|
306
340
|
function normalizeAssistantText(value) {
|
|
307
341
|
return String(value || '').trim();
|
|
@@ -537,7 +571,7 @@ function formatToolResult(toolResult, toolName, args, toolFormatters, toolResult
|
|
|
537
571
|
|
|
538
572
|
// ─── Main agent loop ────────────────────────────────────────────────
|
|
539
573
|
|
|
540
|
-
export async function runAgentLoop({
|
|
574
|
+
export async function runAgentLoop({
|
|
541
575
|
systemPrompt,
|
|
542
576
|
userPrompt,
|
|
543
577
|
model,
|
|
@@ -547,16 +581,19 @@ export async function runAgentLoop({
|
|
|
547
581
|
maxSteps = 8,
|
|
548
582
|
initialMessages = [],
|
|
549
583
|
onEvent,
|
|
550
|
-
executionMode = '
|
|
584
|
+
executionMode = 'normal',
|
|
585
|
+
approvalMode = 'review',
|
|
586
|
+
projectIsGit = false,
|
|
551
587
|
alwaysAllowTools = [],
|
|
552
588
|
requestToolApproval,
|
|
553
589
|
toolResultMaxChars = 12000,
|
|
554
590
|
toolFormatters = {},
|
|
555
591
|
deferredDefinitions = {},
|
|
556
|
-
signal,
|
|
557
|
-
skipAnalysisNudge = false,
|
|
558
|
-
config = {}
|
|
559
|
-
|
|
592
|
+
signal,
|
|
593
|
+
skipAnalysisNudge = false,
|
|
594
|
+
config = {},
|
|
595
|
+
changeTracker = null
|
|
596
|
+
}) {
|
|
560
597
|
const messages = [];
|
|
561
598
|
if (systemPrompt) {
|
|
562
599
|
messages.push({ role: 'system', content: systemPrompt });
|
|
@@ -576,10 +613,10 @@ export async function runAgentLoop({
|
|
|
576
613
|
let lastAutoDreamCheckStep = 0;
|
|
577
614
|
|
|
578
615
|
// Mutable tool list — grows as tool_search loads deferred tools
|
|
579
|
-
const activeTools = [...toolDefinitions];
|
|
580
|
-
|
|
581
|
-
async function maybeRunAutoDream(stepNumber = 0, { force = false } = {}) {
|
|
582
|
-
if (executionMode === 'plan') return;
|
|
616
|
+
const activeTools = [...toolDefinitions];
|
|
617
|
+
|
|
618
|
+
async function maybeRunAutoDream(stepNumber = 0, { force = false } = {}) {
|
|
619
|
+
if (executionMode === 'plan') return;
|
|
583
620
|
const interval = Math.max(1, Number(config?.memory?.auto_dream_check_interval_steps || 20));
|
|
584
621
|
const normalizedStep = Math.max(1, Number(stepNumber || 1));
|
|
585
622
|
if (!force && lastAutoDreamCheckStep > 0 && normalizedStep - lastAutoDreamCheckStep < interval) return;
|
|
@@ -635,13 +672,13 @@ export async function runAgentLoop({
|
|
|
635
672
|
const assistantText = completion.text || '';
|
|
636
673
|
lastAssistantText = assistantText || lastAssistantText;
|
|
637
674
|
|
|
638
|
-
const assistantMessage = completion?.assistantMessage
|
|
639
|
-
? {
|
|
640
|
-
...completion.assistantMessage,
|
|
641
|
-
role: 'assistant',
|
|
642
|
-
content: completion.assistantMessage.content ?? completion?.content ?? assistantText
|
|
643
|
-
}
|
|
644
|
-
: { role: 'assistant', content: completion?.content ?? assistantText };
|
|
675
|
+
const assistantMessage = completion?.assistantMessage
|
|
676
|
+
? {
|
|
677
|
+
...completion.assistantMessage,
|
|
678
|
+
role: 'assistant',
|
|
679
|
+
content: completion.assistantMessage.content ?? completion?.content ?? assistantText
|
|
680
|
+
}
|
|
681
|
+
: { role: 'assistant', content: completion?.content ?? assistantText };
|
|
645
682
|
if (!Array.isArray(assistantMessage.tool_calls) && toolCalls.length > 0) {
|
|
646
683
|
assistantMessage.tool_calls = toolCalls.map((tc) => ({
|
|
647
684
|
id: tc.id,
|
|
@@ -654,11 +691,11 @@ export async function runAgentLoop({
|
|
|
654
691
|
onEvent({
|
|
655
692
|
type: 'assistant:response',
|
|
656
693
|
step: step + 1,
|
|
657
|
-
text: assistantText,
|
|
658
|
-
toolCalls: toolCalls.map((tc) => tc.name),
|
|
659
|
-
usage: completion.usage || null,
|
|
660
|
-
assistantMessage
|
|
661
|
-
});
|
|
694
|
+
text: assistantText,
|
|
695
|
+
toolCalls: toolCalls.map((tc) => tc.name),
|
|
696
|
+
usage: completion.usage || null,
|
|
697
|
+
assistantMessage
|
|
698
|
+
});
|
|
662
699
|
}
|
|
663
700
|
|
|
664
701
|
if (toolCalls.length === 0) {
|
|
@@ -687,7 +724,11 @@ export async function runAgentLoop({
|
|
|
687
724
|
|
|
688
725
|
pendingSummaryNudges = 0;
|
|
689
726
|
|
|
690
|
-
|
|
727
|
+
const normalizedApprovalMode = ['review', 'auto', 'full_access'].includes(String(approvalMode || '').toLowerCase())
|
|
728
|
+
? String(approvalMode || '').toLowerCase()
|
|
729
|
+
: 'review';
|
|
730
|
+
|
|
731
|
+
if (executionMode === 'plan') {
|
|
691
732
|
const plannedLines = callsToPlanSummary(toolCalls);
|
|
692
733
|
finalText = [
|
|
693
734
|
assistantText || '',
|
|
@@ -713,17 +754,21 @@ export async function runAgentLoop({
|
|
|
713
754
|
});
|
|
714
755
|
|
|
715
756
|
// Approval checks first — must be done synchronously before any execution
|
|
716
|
-
const approvalResults = new Map();
|
|
717
|
-
for (const { call, toolName, displayName, args } of callsWithMeta) {
|
|
718
|
-
let approved = true;
|
|
719
|
-
let approvalArgs = args;
|
|
720
|
-
let preflightErrorContent = '';
|
|
721
|
-
const isSafeModeRun = toolName === 'run'
|
|
722
|
-
&& config?.policy?.safe_mode !== false
|
|
723
|
-
&& requiresApprovalEvaluation(args?.command || '', config?.shell?.default);
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
757
|
+
const approvalResults = new Map();
|
|
758
|
+
for (const { call, toolName, displayName, args } of callsWithMeta) {
|
|
759
|
+
let approved = true;
|
|
760
|
+
let approvalArgs = args;
|
|
761
|
+
let preflightErrorContent = '';
|
|
762
|
+
const isSafeModeRun = toolName === 'run'
|
|
763
|
+
&& config?.policy?.safe_mode !== false
|
|
764
|
+
&& requiresApprovalEvaluation(args?.command || '', config?.shell?.default);
|
|
765
|
+
const isFileWriteTool = toolName === 'edit' || toolName === 'write' || toolName === 'delete';
|
|
766
|
+
const needsApproval = normalizedApprovalMode === 'full_access'
|
|
767
|
+
? false
|
|
768
|
+
: normalizedApprovalMode === 'auto'
|
|
769
|
+
? ((!projectIsGit && isFileWriteTool) || isSafeModeRun)
|
|
770
|
+
: (toolName === 'delete' || isSafeModeRun || !alwaysAllowSet.has(toolName));
|
|
771
|
+
if (needsApproval) {
|
|
727
772
|
approved = false;
|
|
728
773
|
const handler = toolHandlers[toolName];
|
|
729
774
|
if (toolName === 'delete' && typeof handler?.prepareApproval === 'function') {
|
|
@@ -749,10 +794,10 @@ export async function runAgentLoop({
|
|
|
749
794
|
});
|
|
750
795
|
approvalArgs = { ...args, _risk: evaluation.risk, _evaluation: evaluation };
|
|
751
796
|
/* LLM says low-risk + allow → auto-approve, skip confirmation panel */
|
|
752
|
-
if (
|
|
753
|
-
approvalResults.set(call.id, { approved: true, args: approvalArgs });
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
797
|
+
if (normalizedApprovalMode !== 'review' && evaluation.risk === 'low' && evaluation.recommendation === 'allow') {
|
|
798
|
+
approvalResults.set(call.id, { approved: true, args: approvalArgs });
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
756
801
|
} catch (_) {
|
|
757
802
|
approvalArgs = { ...args, _risk: 'high', _evaluation: null };
|
|
758
803
|
}
|
|
@@ -862,9 +907,16 @@ export async function runAgentLoop({
|
|
|
862
907
|
};
|
|
863
908
|
}
|
|
864
909
|
|
|
865
|
-
let
|
|
866
|
-
|
|
867
|
-
|
|
910
|
+
let captureScope = null;
|
|
911
|
+
if (!isReadOnly && changeTracker && typeof changeTracker.begin === 'function') {
|
|
912
|
+
try {
|
|
913
|
+
captureScope = await changeTracker.begin({ toolName, args: effectiveArgs });
|
|
914
|
+
} catch {}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
let toolResult;
|
|
918
|
+
try {
|
|
919
|
+
toolResult = await handler(effectiveArgs);
|
|
868
920
|
} catch (error) {
|
|
869
921
|
const durationMs = Date.now() - startedAt;
|
|
870
922
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -885,13 +937,42 @@ export async function runAgentLoop({
|
|
|
885
937
|
};
|
|
886
938
|
}
|
|
887
939
|
|
|
888
|
-
const durationMs = Date.now() - startedAt;
|
|
889
|
-
const summary = summarizeToolResult(toolResult);
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
940
|
+
const durationMs = Date.now() - startedAt;
|
|
941
|
+
const summary = summarizeToolResult(toolResult);
|
|
942
|
+
const resultMeta = extractToolResultMeta(toolName, toolResult);
|
|
943
|
+
/* 提取文件改动统计 */
|
|
944
|
+
const declaredFileChange = extractFileChange(toolName, toolResult);
|
|
945
|
+
let fileChanges = [];
|
|
946
|
+
let fileChange = null;
|
|
947
|
+
if (!isReadOnly && changeTracker && typeof changeTracker.capture === 'function' && captureScope) {
|
|
948
|
+
try {
|
|
949
|
+
const captured = await changeTracker.capture(captureScope, {
|
|
950
|
+
toolName,
|
|
951
|
+
toolCallId: call.id,
|
|
952
|
+
summary,
|
|
953
|
+
args: effectiveArgs,
|
|
954
|
+
declaredFileChanges: normalizeFileChanges(declaredFileChange)
|
|
955
|
+
});
|
|
956
|
+
const capturedChanges = normalizeFileChanges(captured);
|
|
957
|
+
if (capturedChanges.length) {
|
|
958
|
+
fileChanges = capturedChanges;
|
|
959
|
+
fileChange = fileChanges[0] || null;
|
|
960
|
+
}
|
|
961
|
+
} catch (error) {
|
|
962
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
963
|
+
if (onEvent) {
|
|
964
|
+
onEvent({
|
|
965
|
+
type: 'system_tool:error',
|
|
966
|
+
id: `change-oplog-${call.id}`,
|
|
967
|
+
name: 'change_oplog',
|
|
968
|
+
summary: message
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
if (onEvent) {
|
|
974
|
+
onEvent({ type: 'tool:end', name: displayName, id: call.id, arguments: effectiveArgs, durationMs, summary, fileChange, fileChanges, resultMeta });
|
|
975
|
+
}
|
|
895
976
|
|
|
896
977
|
// Auto-capture non-throwing tool failures (e.g. shell non-zero exit)
|
|
897
978
|
if (toolResult && typeof toolResult === 'object') {
|
|
@@ -928,7 +1009,7 @@ export async function runAgentLoop({
|
|
|
928
1009
|
// P0: Persist to disk if still large
|
|
929
1010
|
formatted = await storeResultIfNeeded(call.id, formatted, toolResult);
|
|
930
1011
|
|
|
931
|
-
return { callId: call.id, content: formatted, durationMs, summary, status: 'done', fileChange };
|
|
1012
|
+
return { callId: call.id, content: formatted, durationMs, summary, status: 'done', fileChange, fileChanges, resultMeta };
|
|
932
1013
|
}
|
|
933
1014
|
|
|
934
1015
|
// Separate read-only and write calls, preserving order
|
|
@@ -972,15 +1053,17 @@ export async function runAgentLoop({
|
|
|
972
1053
|
continue;
|
|
973
1054
|
}
|
|
974
1055
|
|
|
975
|
-
attachToolCallSessionMeta(assistantMessage, call.id, { durationMs: entry.durationMs, summary: entry.summary || '', status: entry.status || 'done', fileChange: entry.fileChange });
|
|
1056
|
+
attachToolCallSessionMeta(assistantMessage, call.id, { durationMs: entry.durationMs, summary: entry.summary || '', status: entry.status || 'done', fileChange: entry.fileChange, fileChanges: entry.fileChanges, resultMeta: entry.resultMeta });
|
|
976
1057
|
messages.push({
|
|
977
|
-
role: 'tool',
|
|
978
|
-
tool_call_id: call.id,
|
|
979
|
-
content: entry.content,
|
|
980
|
-
tool_duration_ms: entry.durationMs,
|
|
1058
|
+
role: 'tool',
|
|
1059
|
+
tool_call_id: call.id,
|
|
1060
|
+
content: entry.content,
|
|
1061
|
+
tool_duration_ms: entry.durationMs,
|
|
981
1062
|
tool_summary: entry.summary || '',
|
|
982
1063
|
tool_status: entry.status || 'done',
|
|
983
|
-
...(entry.
|
|
1064
|
+
...(entry.resultMeta ? { tool_result_meta: entry.resultMeta } : {}),
|
|
1065
|
+
...(entry.fileChange ? { tool_file_change: entry.fileChange } : {}),
|
|
1066
|
+
...(Array.isArray(entry.fileChanges) && entry.fileChanges.length > 0 ? { tool_file_changes: entry.fileChanges } : {})
|
|
984
1067
|
});
|
|
985
1068
|
if (onEvent) {
|
|
986
1069
|
onEvent({ type: 'tool:result', name: displayName, id: call.id, arguments: args, content: entry.content });
|
|
@@ -1017,16 +1100,21 @@ function callsToPlanSummary(toolCalls = []) {
|
|
|
1017
1100
|
});
|
|
1018
1101
|
}
|
|
1019
1102
|
|
|
1020
|
-
function attachToolCallSessionMeta(assistantMessage, callId, meta = {}) {
|
|
1021
|
-
if (!assistantMessage || !Array.isArray(assistantMessage.tool_calls)) return;
|
|
1022
|
-
const call = assistantMessage.tool_calls.find((tc) => String(tc?.id || '') === String(callId || ''));
|
|
1023
|
-
if (!call) return;
|
|
1024
|
-
if (Number.isFinite(Number(meta.durationMs))) call.durationMs = Number(meta.durationMs);
|
|
1103
|
+
function attachToolCallSessionMeta(assistantMessage, callId, meta = {}) {
|
|
1104
|
+
if (!assistantMessage || !Array.isArray(assistantMessage.tool_calls)) return;
|
|
1105
|
+
const call = assistantMessage.tool_calls.find((tc) => String(tc?.id || '') === String(callId || ''));
|
|
1106
|
+
if (!call) return;
|
|
1107
|
+
if (Number.isFinite(Number(meta.durationMs))) call.durationMs = Number(meta.durationMs);
|
|
1025
1108
|
if (typeof meta.summary === 'string' && meta.summary.trim()) call.summary = meta.summary.trim();
|
|
1026
1109
|
if (typeof meta.status === 'string' && meta.status.trim()) call.status = meta.status.trim();
|
|
1110
|
+
if (meta.resultMeta && typeof meta.resultMeta === 'object') call.resultMeta = meta.resultMeta;
|
|
1027
1111
|
const fileChange = normalizeFileChange(meta.fileChange);
|
|
1028
1112
|
if (fileChange) {
|
|
1029
1113
|
call.fileChange = fileChange;
|
|
1030
|
-
|
|
1114
|
+
}
|
|
1115
|
+
const fileChanges = normalizeFileChanges(meta.fileChanges && meta.fileChanges.length ? meta.fileChanges : fileChange);
|
|
1116
|
+
if (fileChanges.length) {
|
|
1117
|
+
call.fileChanges = fileChanges;
|
|
1118
|
+
for (const change of fileChanges) appendUniqueFileChange(assistantMessage, change);
|
|
1031
1119
|
}
|
|
1032
1120
|
}
|