@oh-my-pi/pi-coding-agent 14.0.4 → 14.1.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/CHANGELOG.md +83 -0
- package/package.json +11 -8
- package/src/async/index.ts +1 -0
- package/src/async/support.ts +5 -0
- package/src/cli/list-models.ts +96 -57
- package/src/commit/model-selection.ts +16 -13
- package/src/config/model-equivalence.ts +674 -0
- package/src/config/model-registry.ts +182 -13
- package/src/config/model-resolver.ts +203 -74
- package/src/config/settings-schema.ts +23 -0
- package/src/config/settings.ts +9 -2
- package/src/dap/session.ts +31 -39
- package/src/debug/log-formatting.ts +2 -2
- package/src/edit/modes/chunk.ts +8 -3
- package/src/export/html/template.css +82 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +612 -97
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/internal-urls/jobs-protocol.ts +2 -1
- package/src/lsp/client.ts +5 -3
- package/src/lsp/index.ts +4 -9
- package/src/lsp/utils.ts +26 -0
- package/src/main.ts +6 -1
- package/src/memories/index.ts +7 -6
- package/src/modes/components/diff.ts +1 -1
- package/src/modes/components/model-selector.ts +221 -64
- package/src/modes/controllers/command-controller.ts +18 -0
- package/src/modes/controllers/event-controller.ts +438 -426
- package/src/modes/controllers/selector-controller.ts +13 -5
- package/src/modes/theme/mermaid-cache.ts +5 -7
- package/src/priority.json +8 -0
- package/src/prompts/agents/designer.md +1 -2
- package/src/prompts/system/system-prompt.md +5 -1
- package/src/prompts/tools/bash.md +15 -0
- package/src/prompts/tools/cancel-job.md +1 -1
- package/src/prompts/tools/chunk-edit.md +39 -40
- package/src/prompts/tools/read-chunk.md +13 -1
- package/src/prompts/tools/read.md +9 -0
- package/src/prompts/tools/write.md +1 -0
- package/src/sdk.ts +7 -4
- package/src/session/agent-session.ts +33 -6
- package/src/session/compaction/compaction.ts +1 -1
- package/src/task/executor.ts +5 -1
- package/src/tools/await-tool.ts +2 -1
- package/src/tools/bash.ts +221 -56
- package/src/tools/browser.ts +84 -21
- package/src/tools/cancel-job.ts +2 -1
- package/src/tools/fetch.ts +1 -1
- package/src/tools/find.ts +40 -94
- package/src/tools/gemini-image.ts +1 -0
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/read.ts +218 -1
- package/src/tools/render-utils.ts +1 -1
- package/src/tools/sqlite-reader.ts +623 -0
- package/src/tools/write.ts +187 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/git.ts +24 -1
- package/src/utils/image-resize.ts +73 -37
- package/src/utils/title-generator.ts +1 -1
- package/src/web/scrapers/types.ts +50 -32
- package/src/web/search/providers/codex.ts +21 -2
|
@@ -292,7 +292,7 @@
|
|
|
292
292
|
parts.push(msg.role);
|
|
293
293
|
if (msg.content) parts.push(extractContent(msg.content));
|
|
294
294
|
if (msg.role === 'bashExecution' && msg.command) parts.push(msg.command);
|
|
295
|
-
if (msg.role === '
|
|
295
|
+
if (msg.role === 'jsExecution' && msg.code) parts.push(msg.code);
|
|
296
296
|
break;
|
|
297
297
|
}
|
|
298
298
|
case 'custom_message':
|
|
@@ -480,9 +480,9 @@
|
|
|
480
480
|
const cmd = truncate(normalize(msg.command || ''));
|
|
481
481
|
return labelHtml + `<span class="tree-role-tool">[bash]:</span> ${escapeHtml(cmd)}`;
|
|
482
482
|
}
|
|
483
|
-
if (msg.role === '
|
|
483
|
+
if (msg.role === 'jsExecution') {
|
|
484
484
|
const code = truncate(normalize(msg.code || ''));
|
|
485
|
-
return labelHtml + `<span class="tree-role-tool">[
|
|
485
|
+
return labelHtml + `<span class="tree-role-tool">[js]:</span> ${escapeHtml(code)}`;
|
|
486
486
|
}
|
|
487
487
|
return labelHtml + `<span class="tree-muted">[${msg.role}]</span>`;
|
|
488
488
|
}
|
|
@@ -702,123 +702,638 @@
|
|
|
702
702
|
return out;
|
|
703
703
|
}
|
|
704
704
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
const statusClass = result ? (isError ? 'error' : 'success') : 'pending';
|
|
705
|
+
// ============================================================
|
|
706
|
+
// TOOL CALL RENDERING
|
|
707
|
+
// ============================================================
|
|
709
708
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
709
|
+
// Shared helpers for per-tool renderers.
|
|
710
|
+
function toolHead(label, pathHtml, badges) {
|
|
711
|
+
let html = '<div class="tool-header"><span class="tool-name">' + escapeHtml(label) + '</span>';
|
|
712
|
+
if (pathHtml) html += ' <span class="tool-path">' + pathHtml + '</span>';
|
|
713
|
+
if (badges) {
|
|
714
|
+
for (const badge of badges) {
|
|
715
|
+
if (badge != null && badge !== '') {
|
|
716
|
+
html += ' <span class="tool-badge">' + escapeHtml(String(badge)) + '</span>';
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
html += '</div>';
|
|
721
|
+
return html;
|
|
722
|
+
}
|
|
715
723
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
};
|
|
724
|
+
function invalidArgHtml() {
|
|
725
|
+
return '<span class="tool-error">[invalid arg]</span>';
|
|
726
|
+
}
|
|
720
727
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
+
function pathDisplay(filePath, offset, limit) {
|
|
729
|
+
if (filePath == null) return invalidArgHtml();
|
|
730
|
+
let html = escapeHtml(shortenPath(filePath || ''));
|
|
731
|
+
if (offset !== undefined || limit !== undefined) {
|
|
732
|
+
const start = offset == null ? 1 : offset;
|
|
733
|
+
const end = limit !== undefined ? start + limit - 1 : '';
|
|
734
|
+
html += '<span class="line-numbers">:' + start + (end ? '-' + end : '') + '</span>';
|
|
735
|
+
}
|
|
736
|
+
return html;
|
|
737
|
+
}
|
|
728
738
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const
|
|
739
|
+
function codeBlock(code, lang) {
|
|
740
|
+
if (code == null || code === '') return '';
|
|
741
|
+
const text = String(code);
|
|
742
|
+
let highlighted;
|
|
743
|
+
try {
|
|
744
|
+
highlighted = lang ? hljs.highlight(text, { language: lang }).value : escapeHtml(text);
|
|
745
|
+
} catch {
|
|
746
|
+
highlighted = escapeHtml(text);
|
|
747
|
+
}
|
|
748
|
+
return '<div class="tool-output"><pre><code class="hljs">' + highlighted + '</code></pre></div>';
|
|
749
|
+
}
|
|
732
750
|
|
|
733
|
-
|
|
751
|
+
// Per-tool renderers. Each accepts (name, args, result, ctx) and returns the inner HTML.
|
|
752
|
+
function renderBash(name, args, result, ctx) {
|
|
753
|
+
const command = str(args.command);
|
|
754
|
+
const cwd = str(args.cwd);
|
|
755
|
+
const env = args.env && typeof args.env === 'object' ? args.env : null;
|
|
756
|
+
const cmdDisplay = command === null ? invalidArgHtml() : escapeHtml(command || '...');
|
|
757
|
+
let prefix = '';
|
|
758
|
+
if (env) {
|
|
759
|
+
for (const [k, v] of Object.entries(env)) {
|
|
760
|
+
prefix += escapeHtml(k) + '=' + escapeHtml(String(v)) + ' ';
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
let html = '<div class="tool-command">$ ' + prefix + cmdDisplay + '</div>';
|
|
764
|
+
const badges = [];
|
|
765
|
+
if (cwd) badges.push('cwd=' + shortenPath(cwd));
|
|
766
|
+
if (args.timeout) badges.push('timeout=' + args.timeout + 's');
|
|
767
|
+
if (args.pty) badges.push('pty');
|
|
768
|
+
if (args.head) badges.push('head=' + args.head);
|
|
769
|
+
if (args.tail) badges.push('tail=' + args.tail);
|
|
770
|
+
if (badges.length) {
|
|
771
|
+
html += '<div class="tool-meta">' + badges.map(b => '<span class="tool-badge">' + escapeHtml(b) + '</span>').join(' ') + '</div>';
|
|
772
|
+
}
|
|
773
|
+
if (result) {
|
|
774
|
+
html += ctx.renderResultImages();
|
|
775
|
+
const output = ctx.getResultText().trim();
|
|
776
|
+
if (output) html += formatExpandableOutput(output, 5);
|
|
777
|
+
}
|
|
778
|
+
return html;
|
|
779
|
+
}
|
|
734
780
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
781
|
+
function renderJsLike(name, args, result, ctx) {
|
|
782
|
+
const lang = name === 'python' ? 'python' : 'javascript';
|
|
783
|
+
const badges = [];
|
|
784
|
+
if (args.cwd) badges.push('cwd=' + shortenPath(String(args.cwd)));
|
|
785
|
+
if (args.timeout) badges.push('timeout=' + args.timeout + 's');
|
|
786
|
+
if (args.reset) badges.push('reset');
|
|
787
|
+
let html = toolHead(name, '', badges);
|
|
788
|
+
const cells = Array.isArray(args.cells) ? args.cells : null;
|
|
789
|
+
if (!cells) {
|
|
790
|
+
html += '<div class="tool-error">[missing cells]</div>';
|
|
791
|
+
} else {
|
|
792
|
+
for (const cell of cells) {
|
|
793
|
+
html += '<div class="tool-cell">';
|
|
794
|
+
if (cell && cell.title) html += '<div class="tool-cell-title">' + escapeHtml(String(cell.title)) + '</div>';
|
|
795
|
+
const code = cell && typeof cell.code === 'string' ? cell.code : '';
|
|
796
|
+
html += codeBlock(code, lang);
|
|
797
|
+
html += '</div>';
|
|
745
798
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
799
|
+
}
|
|
800
|
+
if (result) {
|
|
801
|
+
html += ctx.renderResultImages();
|
|
802
|
+
const output = ctx.getResultText();
|
|
803
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
804
|
+
}
|
|
805
|
+
return html;
|
|
806
|
+
}
|
|
750
807
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
808
|
+
function renderRead(name, args, result, ctx) {
|
|
809
|
+
const filePath = str(args.file_path == null ? args.path : args.file_path);
|
|
810
|
+
let pathHtml = pathDisplay(filePath, args.offset, args.limit);
|
|
811
|
+
if (args.sel) pathHtml += '<span class="line-numbers">:' + escapeHtml(String(args.sel)) + '</span>';
|
|
812
|
+
let html = toolHead('read', pathHtml);
|
|
813
|
+
if (result) {
|
|
814
|
+
html += ctx.renderResultImages();
|
|
815
|
+
const output = ctx.getResultText();
|
|
816
|
+
const lang = filePath ? getLanguageFromPath(filePath) : null;
|
|
817
|
+
if (output) html += formatExpandableOutput(output, 10, lang);
|
|
818
|
+
}
|
|
819
|
+
return html;
|
|
820
|
+
}
|
|
757
821
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
822
|
+
function renderWrite(name, args, result, ctx) {
|
|
823
|
+
const filePath = str(args.file_path == null ? args.path : args.file_path);
|
|
824
|
+
const content = str(args.content);
|
|
825
|
+
const pathHtml = filePath === null ? invalidArgHtml() : escapeHtml(shortenPath(filePath || ''));
|
|
826
|
+
const lineCount = (content != null && content !== '') ? content.split('\n').length : 0;
|
|
827
|
+
const badges = lineCount > 10 ? ['(' + lineCount + ' lines)'] : null;
|
|
828
|
+
let html = toolHead('write', pathHtml, badges);
|
|
829
|
+
if (content === null) {
|
|
830
|
+
html += '<div class="tool-error">[invalid content arg - expected string]</div>';
|
|
831
|
+
} else if (content) {
|
|
832
|
+
const lang = filePath ? getLanguageFromPath(filePath) : null;
|
|
833
|
+
html += formatExpandableOutput(content, 10, lang);
|
|
834
|
+
}
|
|
835
|
+
if (result) {
|
|
836
|
+
const output = ctx.getResultText().trim();
|
|
837
|
+
if (output) html += '<div class="tool-output"><div>' + escapeHtml(output) + '</div></div>';
|
|
838
|
+
}
|
|
839
|
+
return html;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function renderEdit(name, args, result, ctx) {
|
|
843
|
+
const filePath = str(args.file_path == null ? args.path : args.file_path);
|
|
844
|
+
const pathHtml = filePath === null ? invalidArgHtml() : escapeHtml(shortenPath(filePath || ''));
|
|
845
|
+
let html = toolHead('edit', pathHtml);
|
|
846
|
+
if (Array.isArray(args.edits)) {
|
|
847
|
+
html += '<div class="tool-args">';
|
|
848
|
+
for (const e of args.edits) {
|
|
849
|
+
const op = e && typeof e.op === 'string' ? e.op : '?';
|
|
850
|
+
const sel = e && typeof e.sel === 'string' ? e.sel : '?';
|
|
851
|
+
html += '<div class="tool-arg"><span class="tool-arg-key">' + escapeHtml(op) + '</span> <span class="tool-arg-val">' + escapeHtml(sel) + '</span></div>';
|
|
766
852
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
853
|
+
html += '</div>';
|
|
854
|
+
}
|
|
855
|
+
if (result?.details?.diff) {
|
|
856
|
+
const diffLines = String(result.details.diff).split('\n');
|
|
857
|
+
html += '<div class="tool-diff">';
|
|
858
|
+
for (const line of diffLines) {
|
|
859
|
+
const cls = line.match(/^\+/) ? 'diff-added' : line.match(/^-/) ? 'diff-removed' : 'diff-context';
|
|
860
|
+
html += '<div class="' + cls + '">' + escapeHtml(replaceTabs(line)) + '</div>';
|
|
861
|
+
}
|
|
862
|
+
html += '</div>';
|
|
863
|
+
} else if (result) {
|
|
864
|
+
const output = ctx.getResultText().trim();
|
|
865
|
+
if (output) html += '<div class="tool-output"><pre>' + escapeHtml(output) + '</pre></div>';
|
|
866
|
+
}
|
|
867
|
+
return html;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
function renderAstEdit(name, args, result, ctx) {
|
|
871
|
+
const lang = args.lang || null;
|
|
872
|
+
const pathHtml = args.path ? escapeHtml(shortenPath(String(args.path))) : '';
|
|
873
|
+
const badges = [];
|
|
874
|
+
if (lang) badges.push(lang);
|
|
875
|
+
if (args.glob) badges.push('glob=' + args.glob);
|
|
876
|
+
if (args.sel) badges.push('sel=' + args.sel);
|
|
877
|
+
let html = toolHead('ast_edit', pathHtml, badges);
|
|
878
|
+
if (Array.isArray(args.ops)) {
|
|
879
|
+
for (const op of args.ops) {
|
|
880
|
+
html += '<div class="tool-cell">';
|
|
881
|
+
html += '<div class="tool-cell-title">pattern</div>';
|
|
882
|
+
html += codeBlock(String(op?.pat == null ? '' : op.pat), lang);
|
|
883
|
+
html += '<div class="tool-cell-title">replacement</div>';
|
|
884
|
+
html += codeBlock(String(op?.out == null ? '' : op.out), lang);
|
|
776
885
|
html += '</div>';
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if (result) {
|
|
889
|
+
const output = ctx.getResultText();
|
|
890
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
891
|
+
}
|
|
892
|
+
return html;
|
|
893
|
+
}
|
|
777
894
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
895
|
+
function renderAstGrep(name, args, result, ctx) {
|
|
896
|
+
const lang = args.lang || null;
|
|
897
|
+
const pathHtml = args.path ? escapeHtml(shortenPath(String(args.path))) : '';
|
|
898
|
+
const badges = [];
|
|
899
|
+
if (lang) badges.push(lang);
|
|
900
|
+
if (args.glob) badges.push('glob=' + args.glob);
|
|
901
|
+
if (args.sel) badges.push('sel=' + args.sel);
|
|
902
|
+
let html = toolHead('ast_grep', pathHtml, badges);
|
|
903
|
+
if (Array.isArray(args.pat)) {
|
|
904
|
+
for (const pat of args.pat) {
|
|
905
|
+
html += '<div class="tool-cell">' + codeBlock(String(pat == null ? '' : pat), lang) + '</div>';
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
if (result) {
|
|
909
|
+
const output = ctx.getResultText();
|
|
910
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
911
|
+
}
|
|
912
|
+
return html;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
function renderGrep(name, args, result, ctx) {
|
|
916
|
+
const pattern = str(args.pattern);
|
|
917
|
+
const pathHtml = args.path ? escapeHtml(shortenPath(String(args.path))) : escapeHtml('.');
|
|
918
|
+
const patHtml = pattern === null ? invalidArgHtml() : escapeHtml(pattern);
|
|
919
|
+
let head = '<span class="tool-name">grep</span> <span class="tool-pattern">/' + patHtml + '/</span>';
|
|
920
|
+
head += ' <span class="tool-arg-key">in</span> <span class="tool-path">' + pathHtml + '</span>';
|
|
921
|
+
const badges = [];
|
|
922
|
+
if (args.glob) badges.push('glob=' + args.glob);
|
|
923
|
+
if (args.type) badges.push('type=' + args.type);
|
|
924
|
+
if (args.i) badges.push('i');
|
|
925
|
+
if (args.multiline) badges.push('multiline');
|
|
926
|
+
for (const b of badges) head += ' <span class="tool-badge">' + escapeHtml(b) + '</span>';
|
|
927
|
+
let html = '<div class="tool-header">' + head + '</div>';
|
|
928
|
+
if (result) {
|
|
929
|
+
const output = ctx.getResultText();
|
|
930
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
931
|
+
}
|
|
932
|
+
return html;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function renderFind(name, args, result, ctx) {
|
|
936
|
+
const pattern = str(args.pattern);
|
|
937
|
+
const patHtml = pattern === null ? invalidArgHtml() : escapeHtml(pattern);
|
|
938
|
+
const badges = args.limit ? ['limit=' + args.limit] : null;
|
|
939
|
+
let html = toolHead('find', '<span class="tool-pattern">' + patHtml + '</span>', badges);
|
|
940
|
+
if (result) {
|
|
941
|
+
const output = ctx.getResultText();
|
|
942
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
943
|
+
}
|
|
944
|
+
return html;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
function renderLsp(name, args, result, ctx) {
|
|
948
|
+
const action = str(args.action) || '?';
|
|
949
|
+
let head = '<span class="tool-name">lsp</span> <span class="tool-badge">' + escapeHtml(action) + '</span>';
|
|
950
|
+
if (args.file && args.file !== '*') {
|
|
951
|
+
head += ' <span class="tool-path">' + escapeHtml(shortenPath(String(args.file))) + '</span>';
|
|
952
|
+
} else if (args.file === '*') {
|
|
953
|
+
head += ' <span class="tool-badge">workspace</span>';
|
|
954
|
+
}
|
|
955
|
+
if (args.line) head += '<span class="line-numbers">:' + args.line + '</span>';
|
|
956
|
+
if (args.symbol) head += ' <span class="tool-arg-val">' + escapeHtml(String(args.symbol)) + '</span>';
|
|
957
|
+
if (args.query) head += ' <span class="tool-arg-key">query=</span><span class="tool-arg-val">' + escapeHtml(String(args.query)) + '</span>';
|
|
958
|
+
if (args.new_name) head += ' <span class="tool-arg-key">→</span> <span class="tool-arg-val">' + escapeHtml(String(args.new_name)) + '</span>';
|
|
959
|
+
let html = '<div class="tool-header">' + head + '</div>';
|
|
960
|
+
if (result) {
|
|
961
|
+
const output = ctx.getResultText();
|
|
962
|
+
if (output) html += formatExpandableOutput(output, 12);
|
|
963
|
+
}
|
|
964
|
+
return html;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
function renderTodoWrite(name, args, result, ctx) {
|
|
968
|
+
let html = toolHead('todo_write');
|
|
969
|
+
const ops = Array.isArray(args.ops) ? args.ops : null;
|
|
970
|
+
if (ops) {
|
|
971
|
+
html += '<div class="tool-args">';
|
|
972
|
+
for (const op of ops) {
|
|
973
|
+
const t = op && op.op ? op.op : '?';
|
|
974
|
+
let line = '<span class="tool-arg-key">' + escapeHtml(t) + '</span>';
|
|
975
|
+
if (op?.id) line += ' <span class="tool-arg-val">' + escapeHtml(String(op.id)) + '</span>';
|
|
976
|
+
if (op?.status) line += ' <span class="tool-badge">' + escapeHtml(String(op.status)) + '</span>';
|
|
977
|
+
if (op?.content) line += ' ' + escapeHtml(truncate(String(op.content), 80));
|
|
978
|
+
if (op?.task && typeof op.task === 'object' && op.task.content) line += ' ' + escapeHtml(truncate(String(op.task.content), 80));
|
|
979
|
+
html += '<div class="tool-arg">' + line + '</div>';
|
|
789
980
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
981
|
+
html += '</div>';
|
|
982
|
+
}
|
|
983
|
+
const phases = result?.details?.phases;
|
|
984
|
+
if (Array.isArray(phases)) {
|
|
985
|
+
html += '<div class="todo-tree">';
|
|
986
|
+
for (const phase of phases) {
|
|
987
|
+
html += '<div class="todo-phase">' + escapeHtml(String(phase.name || '')) + '</div>';
|
|
988
|
+
if (Array.isArray(phase.tasks)) {
|
|
989
|
+
for (const task of phase.tasks) {
|
|
990
|
+
const status = task.status || 'pending';
|
|
991
|
+
const icon = status === 'completed' ? '✓' : status === 'in_progress' ? '→' : status === 'abandoned' ? '✕' : '○';
|
|
992
|
+
html += '<div class="todo-task todo-' + status + '"><span class="todo-icon">' + icon + '</span> ' + escapeHtml(String(task.content || '')) + '</div>';
|
|
800
993
|
}
|
|
801
|
-
html += '</div>';
|
|
802
|
-
} else if (result) {
|
|
803
|
-
const output = getResultText().trim();
|
|
804
|
-
if (output) html += `<div class="tool-output"><pre>${escapeHtml(output)}</pre></div>`;
|
|
805
994
|
}
|
|
806
|
-
break;
|
|
807
995
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
996
|
+
html += '</div>';
|
|
997
|
+
} else if (result) {
|
|
998
|
+
const output = ctx.getResultText();
|
|
999
|
+
if (output) html += formatExpandableOutput(output, 8);
|
|
1000
|
+
}
|
|
1001
|
+
return html;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
function renderTask(name, args, result, ctx) {
|
|
1005
|
+
const agent = str(args.agent) || '?';
|
|
1006
|
+
const tasks = Array.isArray(args.tasks) ? args.tasks : [];
|
|
1007
|
+
const badges = ['agent=' + agent, tasks.length + ' subtask' + (tasks.length === 1 ? '' : 's')];
|
|
1008
|
+
if (args.isolated) badges.push('isolated');
|
|
1009
|
+
let html = toolHead('task', '', badges);
|
|
1010
|
+
if (tasks.length) {
|
|
1011
|
+
html += '<div class="tool-args">';
|
|
1012
|
+
for (const t of tasks) {
|
|
1013
|
+
const id = t?.id ? escapeHtml(String(t.id)) : '?';
|
|
1014
|
+
const desc = t?.description ? escapeHtml(String(t.description)) : '';
|
|
1015
|
+
html += '<div class="tool-arg"><span class="tool-arg-key">' + id + '</span> ' + desc + '</div>';
|
|
1016
|
+
}
|
|
1017
|
+
html += '</div>';
|
|
1018
|
+
}
|
|
1019
|
+
if (result) {
|
|
1020
|
+
const output = ctx.getResultText();
|
|
1021
|
+
if (output) html += formatExpandableOutput(output, 12);
|
|
1022
|
+
}
|
|
1023
|
+
return html;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
function renderWebSearch(name, args, result, ctx) {
|
|
1027
|
+
const query = str(args.query);
|
|
1028
|
+
const queryHtml = query === null ? invalidArgHtml() : escapeHtml(query);
|
|
1029
|
+
const badges = [];
|
|
1030
|
+
if (args.recency) badges.push('recency=' + args.recency);
|
|
1031
|
+
if (args.limit) badges.push('limit=' + args.limit);
|
|
1032
|
+
let html = toolHead('web_search', '<span class="tool-pattern">' + queryHtml + '</span>', badges);
|
|
1033
|
+
if (result) {
|
|
1034
|
+
const output = ctx.getResultText();
|
|
1035
|
+
if (output) html += formatExpandableOutput(output, 12, 'markdown');
|
|
1036
|
+
}
|
|
1037
|
+
return html;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
function renderFetch(name, args, result, ctx) {
|
|
1041
|
+
const url = str(args.url) || '';
|
|
1042
|
+
const badges = args.method ? [String(args.method)] : null;
|
|
1043
|
+
let html = toolHead('fetch', '<span class="tool-path">' + escapeHtml(url) + '</span>', badges);
|
|
1044
|
+
if (result) {
|
|
1045
|
+
const output = ctx.getResultText();
|
|
1046
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
1047
|
+
}
|
|
1048
|
+
return html;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function renderDebug(name, args, result, ctx) {
|
|
1052
|
+
const action = str(args.action) || '?';
|
|
1053
|
+
const badges = [];
|
|
1054
|
+
if (args.adapter) badges.push(args.adapter);
|
|
1055
|
+
if (args.program) badges.push('program=' + shortenPath(String(args.program)));
|
|
1056
|
+
if (args.file) badges.push('file=' + shortenPath(String(args.file)));
|
|
1057
|
+
if (args.line) badges.push('line=' + args.line);
|
|
1058
|
+
let head = '<span class="tool-name">debug</span> <span class="tool-badge">' + escapeHtml(action) + '</span>';
|
|
1059
|
+
for (const b of badges) head += ' <span class="tool-badge">' + escapeHtml(String(b)) + '</span>';
|
|
1060
|
+
let html = '<div class="tool-header">' + head + '</div>';
|
|
1061
|
+
if (args.expression) html += codeBlock(String(args.expression));
|
|
1062
|
+
if (result) {
|
|
1063
|
+
const output = ctx.getResultText();
|
|
1064
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
1065
|
+
}
|
|
1066
|
+
return html;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
function renderPuppeteer(name, args, result, ctx) {
|
|
1070
|
+
const action = str(args.action) || '?';
|
|
1071
|
+
const badges = [];
|
|
1072
|
+
if (args.url) badges.push(String(args.url));
|
|
1073
|
+
if (args.selector) badges.push('selector=' + args.selector);
|
|
1074
|
+
if (args.element_id != null) badges.push('id=' + args.element_id);
|
|
1075
|
+
let head = '<span class="tool-name">puppeteer</span> <span class="tool-badge">' + escapeHtml(action) + '</span>';
|
|
1076
|
+
for (const b of badges) head += ' <span class="tool-badge">' + escapeHtml(String(b)) + '</span>';
|
|
1077
|
+
let html = '<div class="tool-header">' + head + '</div>';
|
|
1078
|
+
if (args.script) html += codeBlock(String(args.script), 'javascript');
|
|
1079
|
+
if (args.text) html += '<div class="tool-output"><div>' + escapeHtml(String(args.text)) + '</div></div>';
|
|
1080
|
+
if (result) {
|
|
1081
|
+
html += ctx.renderResultImages();
|
|
1082
|
+
const output = ctx.getResultText();
|
|
1083
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
1084
|
+
}
|
|
1085
|
+
return html;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
function renderInspectImage(name, args, result, ctx) {
|
|
1089
|
+
const p = str(args.path == null ? args.url : args.path) || '';
|
|
1090
|
+
let html = toolHead('inspect_image', escapeHtml(shortenPath(p)));
|
|
1091
|
+
if (result) {
|
|
1092
|
+
html += ctx.renderResultImages();
|
|
1093
|
+
const output = ctx.getResultText();
|
|
1094
|
+
if (output) html += formatExpandableOutput(output, 8);
|
|
1095
|
+
}
|
|
1096
|
+
return html;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
function renderGenerateImage(name, args, result, ctx) {
|
|
1100
|
+
const subject = str(args.subject) || '';
|
|
1101
|
+
const badges = args.aspect_ratio ? [String(args.aspect_ratio)] : null;
|
|
1102
|
+
let html = toolHead('generate_image', '', badges);
|
|
1103
|
+
if (subject) html += '<div class="tool-output"><div>' + escapeHtml(subject) + '</div></div>';
|
|
1104
|
+
if (result) {
|
|
1105
|
+
html += ctx.renderResultImages();
|
|
1106
|
+
}
|
|
1107
|
+
return html;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
function renderAsk(name, args, result, ctx) {
|
|
1111
|
+
let html = toolHead('ask');
|
|
1112
|
+
const questions = Array.isArray(args.questions) ? args.questions : null;
|
|
1113
|
+
if (questions) {
|
|
1114
|
+
html += '<div class="tool-args">';
|
|
1115
|
+
for (const q of questions) {
|
|
1116
|
+
html += '<div class="tool-arg"><span class="tool-arg-key">Q:</span> ' + escapeHtml(String(q?.question || '')) + '</div>';
|
|
1117
|
+
if (Array.isArray(q?.options)) {
|
|
1118
|
+
for (const opt of q.options) {
|
|
1119
|
+
html += '<div class="tool-arg"><span class="tool-arg-key"> -</span> ' + escapeHtml(String(opt?.label || '')) + '</div>';
|
|
1120
|
+
}
|
|
814
1121
|
}
|
|
815
1122
|
}
|
|
1123
|
+
html += '</div>';
|
|
1124
|
+
}
|
|
1125
|
+
if (result) {
|
|
1126
|
+
const output = ctx.getResultText();
|
|
1127
|
+
if (output) html += formatExpandableOutput(output, 8);
|
|
1128
|
+
}
|
|
1129
|
+
return html;
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
function renderExitPlanMode(name, args, result, ctx) {
|
|
1133
|
+
const badges = args.title ? [String(args.title)] : null;
|
|
1134
|
+
let html = toolHead('exit_plan_mode', '', badges);
|
|
1135
|
+
if (result) {
|
|
1136
|
+
const output = ctx.getResultText();
|
|
1137
|
+
if (output) html += formatExpandableOutput(output, 8);
|
|
816
1138
|
}
|
|
1139
|
+
return html;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
function renderResolve(name, args, result, ctx) {
|
|
1143
|
+
const action = str(args.action) || '?';
|
|
1144
|
+
let html = toolHead('resolve', '', [action]);
|
|
1145
|
+
if (args.reason) html += '<div class="tool-output"><div>' + escapeHtml(String(args.reason)) + '</div></div>';
|
|
1146
|
+
if (result) {
|
|
1147
|
+
const output = ctx.getResultText();
|
|
1148
|
+
if (output) html += formatExpandableOutput(output, 6);
|
|
1149
|
+
}
|
|
1150
|
+
return html;
|
|
1151
|
+
}
|
|
817
1152
|
|
|
1153
|
+
function renderGh(name, args, result, ctx) {
|
|
1154
|
+
const badges = [];
|
|
1155
|
+
if (args.repo) badges.push(String(args.repo));
|
|
1156
|
+
if (args.issue) badges.push('#' + args.issue);
|
|
1157
|
+
if (args.pr) badges.push('PR ' + args.pr);
|
|
1158
|
+
if (args.branch) badges.push('branch=' + args.branch);
|
|
1159
|
+
if (args.query) badges.push('query=' + args.query);
|
|
1160
|
+
if (args.run) badges.push('run=' + args.run);
|
|
1161
|
+
let html = toolHead(name, '', badges);
|
|
1162
|
+
if (result) {
|
|
1163
|
+
const output = ctx.getResultText();
|
|
1164
|
+
if (output) html += formatExpandableOutput(output, 12, 'markdown');
|
|
1165
|
+
}
|
|
1166
|
+
return html;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
function renderMermaid(name, args, result, ctx) {
|
|
1170
|
+
let html = toolHead('render_mermaid');
|
|
1171
|
+
const code = args.code || args.source;
|
|
1172
|
+
if (code) html += codeBlock(String(code), 'mermaid');
|
|
1173
|
+
if (result) {
|
|
1174
|
+
html += ctx.renderResultImages();
|
|
1175
|
+
const output = ctx.getResultText();
|
|
1176
|
+
if (output) html += formatExpandableOutput(output, 6);
|
|
1177
|
+
}
|
|
1178
|
+
return html;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
function renderSubmitResult(name, args, result, ctx) {
|
|
1182
|
+
let html = toolHead('submit_result');
|
|
1183
|
+
if (args.data !== undefined) {
|
|
1184
|
+
html += '<div class="tool-output"><pre>' + escapeHtml(JSON.stringify(args.data, null, 2)) + '</pre></div>';
|
|
1185
|
+
}
|
|
1186
|
+
if (result) {
|
|
1187
|
+
const output = ctx.getResultText();
|
|
1188
|
+
if (output) html += formatExpandableOutput(output, 6);
|
|
1189
|
+
}
|
|
1190
|
+
return html;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function renderReportFinding(name, args, result, ctx) {
|
|
1194
|
+
const badges = [];
|
|
1195
|
+
if (args.priority) badges.push('priority=' + args.priority);
|
|
1196
|
+
if (args.confidence != null) badges.push('confidence=' + args.confidence);
|
|
1197
|
+
if (args.file_path) badges.push(shortenPath(String(args.file_path)));
|
|
1198
|
+
let html = toolHead('report_finding', args.title ? escapeHtml(String(args.title)) : '', badges);
|
|
1199
|
+
if (args.body) html += '<div class="tool-output"><div>' + escapeHtml(String(args.body)) + '</div></div>';
|
|
1200
|
+
return html;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
function renderReportToolIssue(name, args, result, ctx) {
|
|
1204
|
+
const pathHtml = args.tool ? '<span class="tool-badge">' + escapeHtml(String(args.tool)) + '</span>' : '';
|
|
1205
|
+
let html = toolHead('report_tool_issue', pathHtml);
|
|
1206
|
+
if (args.report) html += '<div class="tool-output"><div>' + escapeHtml(String(args.report)) + '</div></div>';
|
|
1207
|
+
return html;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function renderCalc(name, args, result, ctx) {
|
|
1211
|
+
let html = toolHead('calc');
|
|
1212
|
+
const exprs = args.expressions || (args.expression ? [args.expression] : []);
|
|
1213
|
+
for (const e of exprs) html += codeBlock(String(e), 'plaintext');
|
|
1214
|
+
if (result) {
|
|
1215
|
+
const output = ctx.getResultText();
|
|
1216
|
+
if (output) html += formatExpandableOutput(output, 6);
|
|
1217
|
+
}
|
|
1218
|
+
return html;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
function renderAwait(name, args, result, ctx) {
|
|
1222
|
+
const badges = [];
|
|
1223
|
+
if (Array.isArray(args.jobIds)) badges.push(args.jobIds.length + ' job' + (args.jobIds.length === 1 ? '' : 's'));
|
|
1224
|
+
let html = toolHead('await', '', badges);
|
|
1225
|
+
if (result) {
|
|
1226
|
+
const output = ctx.getResultText();
|
|
1227
|
+
if (output) html += formatExpandableOutput(output, 8);
|
|
1228
|
+
}
|
|
1229
|
+
return html;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function renderCancelJob(name, args, result, ctx) {
|
|
1233
|
+
let html = toolHead('cancel_job', args.jobId ? escapeHtml(String(args.jobId)) : '');
|
|
1234
|
+
if (result) {
|
|
1235
|
+
const output = ctx.getResultText();
|
|
1236
|
+
if (output) html += formatExpandableOutput(output, 4);
|
|
1237
|
+
}
|
|
1238
|
+
return html;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
function renderGenericTool(name, args, result, ctx) {
|
|
1242
|
+
let html = toolHead(name);
|
|
1243
|
+
const argText = JSON.stringify(args, null, 2);
|
|
1244
|
+
if (argText && argText !== '{}') {
|
|
1245
|
+
html += '<div class="tool-output"><pre>' + escapeHtml(argText) + '</pre></div>';
|
|
1246
|
+
}
|
|
1247
|
+
if (result) {
|
|
1248
|
+
html += ctx.renderResultImages();
|
|
1249
|
+
const output = ctx.getResultText();
|
|
1250
|
+
if (output) html += formatExpandableOutput(output, 10);
|
|
1251
|
+
}
|
|
1252
|
+
return html;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
const TOOL_RENDERERS = {
|
|
1256
|
+
bash: renderBash,
|
|
1257
|
+
js: renderJsLike,
|
|
1258
|
+
python: renderJsLike,
|
|
1259
|
+
notebook: renderJsLike,
|
|
1260
|
+
read: renderRead,
|
|
1261
|
+
write: renderWrite,
|
|
1262
|
+
edit: renderEdit,
|
|
1263
|
+
ast_edit: renderAstEdit,
|
|
1264
|
+
ast_grep: renderAstGrep,
|
|
1265
|
+
grep: renderGrep,
|
|
1266
|
+
find: renderFind,
|
|
1267
|
+
lsp: renderLsp,
|
|
1268
|
+
todo_write: renderTodoWrite,
|
|
1269
|
+
task: renderTask,
|
|
1270
|
+
web_search: renderWebSearch,
|
|
1271
|
+
fetch: renderFetch,
|
|
1272
|
+
debug: renderDebug,
|
|
1273
|
+
puppeteer: renderPuppeteer,
|
|
1274
|
+
inspect_image: renderInspectImage,
|
|
1275
|
+
generate_image: renderGenerateImage,
|
|
1276
|
+
ask: renderAsk,
|
|
1277
|
+
exit_plan_mode: renderExitPlanMode,
|
|
1278
|
+
resolve: renderResolve,
|
|
1279
|
+
gh_repo_view: renderGh,
|
|
1280
|
+
gh_issue_view: renderGh,
|
|
1281
|
+
gh_pr_view: renderGh,
|
|
1282
|
+
gh_pr_diff: renderGh,
|
|
1283
|
+
gh_pr_checkout: renderGh,
|
|
1284
|
+
gh_pr_push: renderGh,
|
|
1285
|
+
gh_run_watch: renderGh,
|
|
1286
|
+
gh_search_issues: renderGh,
|
|
1287
|
+
gh_search_prs: renderGh,
|
|
1288
|
+
render_mermaid: renderMermaid,
|
|
1289
|
+
submit_result: renderSubmitResult,
|
|
1290
|
+
report_finding: renderReportFinding,
|
|
1291
|
+
report_tool_issue: renderReportToolIssue,
|
|
1292
|
+
calc: renderCalc,
|
|
1293
|
+
calculator: renderCalc,
|
|
1294
|
+
await: renderAwait,
|
|
1295
|
+
cancel_job: renderCancelJob,
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
function renderToolCall(call) {
|
|
1299
|
+
const result = findToolResult(call.id);
|
|
1300
|
+
const isError = result?.isError || false;
|
|
1301
|
+
const statusClass = result ? (isError ? 'error' : 'success') : 'pending';
|
|
1302
|
+
const args = call.arguments || {};
|
|
1303
|
+
const name = call.name;
|
|
1304
|
+
|
|
1305
|
+
const ctx = {
|
|
1306
|
+
getResultText: () => {
|
|
1307
|
+
if (!result) return '';
|
|
1308
|
+
const textBlocks = result.content.filter(c => c.type === 'text');
|
|
1309
|
+
return textBlocks.map(c => c.text).join('\n');
|
|
1310
|
+
},
|
|
1311
|
+
getResultImages: () => {
|
|
1312
|
+
if (!result) return [];
|
|
1313
|
+
return result.content.filter(c => c.type === 'image');
|
|
1314
|
+
},
|
|
1315
|
+
renderResultImages: () => {
|
|
1316
|
+
if (!result) return '';
|
|
1317
|
+
const images = result.content.filter(c => c.type === 'image');
|
|
1318
|
+
if (images.length === 0) return '';
|
|
1319
|
+
return '<div class="tool-images">' +
|
|
1320
|
+
images.map(img => '<img src="data:' + img.mimeType + ';base64,' + img.data + '" class="tool-image" />').join('') +
|
|
1321
|
+
'</div>';
|
|
1322
|
+
},
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
const renderer = TOOL_RENDERERS[name] || renderGenericTool;
|
|
1326
|
+
let html = '<div class="tool-execution ' + statusClass + '">';
|
|
1327
|
+
try {
|
|
1328
|
+
html += renderer(name, args, result, ctx);
|
|
1329
|
+
} catch (err) {
|
|
1330
|
+
html += renderGenericTool(name, args, result, ctx);
|
|
1331
|
+
}
|
|
818
1332
|
html += '</div>';
|
|
819
1333
|
return html;
|
|
820
1334
|
}
|
|
821
1335
|
|
|
1336
|
+
|
|
822
1337
|
/**
|
|
823
1338
|
* Build a shareable URL for a specific message.
|
|
824
1339
|
* URL format: base?gistId&leafId=<leafId>&targetId=<entryId>
|
|
@@ -977,7 +1492,7 @@
|
|
|
977
1492
|
return html;
|
|
978
1493
|
}
|
|
979
1494
|
|
|
980
|
-
if (msg.role === '
|
|
1495
|
+
if (msg.role === 'jsExecution') {
|
|
981
1496
|
const isError = msg.cancelled || (msg.exitCode !== 0 && msg.exitCode !== null);
|
|
982
1497
|
let html = `<div class="tool-execution ${isError ? 'error' : 'success'}" id="${entryId}">${tsHtml}`;
|
|
983
1498
|
html += `<div class="tool-command">$ ${escapeHtml(msg.code)}</div>`;
|