@oh-my-pi/pi-coding-agent 3.37.0 → 4.0.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 +89 -0
- package/README.md +44 -3
- package/docs/extensions.md +29 -4
- package/docs/sdk.md +3 -3
- package/package.json +5 -5
- package/src/cli/args.ts +8 -0
- package/src/config.ts +5 -15
- package/src/core/agent-session.ts +193 -47
- package/src/core/auth-storage.ts +16 -3
- package/src/core/bash-executor.ts +79 -14
- package/src/core/custom-commands/types.ts +1 -1
- package/src/core/custom-tools/types.ts +1 -1
- package/src/core/export-html/index.ts +33 -1
- package/src/core/export-html/template.css +99 -0
- package/src/core/export-html/template.generated.ts +1 -1
- package/src/core/export-html/template.js +133 -8
- package/src/core/extensions/index.ts +22 -4
- package/src/core/extensions/loader.ts +152 -214
- package/src/core/extensions/runner.ts +139 -79
- package/src/core/extensions/types.ts +143 -19
- package/src/core/extensions/wrapper.ts +5 -8
- package/src/core/hooks/types.ts +1 -1
- package/src/core/index.ts +2 -1
- package/src/core/keybindings.ts +4 -1
- package/src/core/model-registry.ts +1 -1
- package/src/core/model-resolver.ts +35 -26
- package/src/core/sdk.ts +96 -76
- package/src/core/settings-manager.ts +45 -14
- package/src/core/system-prompt.ts +5 -15
- package/src/core/tools/bash.ts +115 -54
- package/src/core/tools/find.ts +86 -7
- package/src/core/tools/grep.ts +27 -6
- package/src/core/tools/index.ts +15 -6
- package/src/core/tools/ls.ts +49 -18
- package/src/core/tools/render-utils.ts +2 -1
- package/src/core/tools/task/worker.ts +35 -12
- package/src/core/tools/web-search/auth.ts +37 -32
- package/src/core/tools/web-search/providers/anthropic.ts +35 -22
- package/src/index.ts +101 -9
- package/src/main.ts +60 -20
- package/src/migrations.ts +47 -2
- package/src/modes/index.ts +2 -2
- package/src/modes/interactive/components/assistant-message.ts +25 -7
- package/src/modes/interactive/components/bash-execution.ts +5 -0
- package/src/modes/interactive/components/branch-summary-message.ts +5 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +5 -0
- package/src/modes/interactive/components/countdown-timer.ts +38 -0
- package/src/modes/interactive/components/custom-editor.ts +8 -0
- package/src/modes/interactive/components/custom-message.ts +5 -0
- package/src/modes/interactive/components/footer.ts +2 -5
- package/src/modes/interactive/components/hook-input.ts +29 -20
- package/src/modes/interactive/components/hook-selector.ts +52 -38
- package/src/modes/interactive/components/index.ts +39 -0
- package/src/modes/interactive/components/login-dialog.ts +160 -0
- package/src/modes/interactive/components/model-selector.ts +10 -2
- package/src/modes/interactive/components/session-selector.ts +5 -1
- package/src/modes/interactive/components/settings-defs.ts +9 -0
- package/src/modes/interactive/components/status-line/segments.ts +3 -3
- package/src/modes/interactive/components/tool-execution.ts +9 -16
- package/src/modes/interactive/components/tree-selector.ts +1 -6
- package/src/modes/interactive/interactive-mode.ts +466 -215
- package/src/modes/interactive/theme/theme.ts +50 -2
- package/src/modes/print-mode.ts +78 -31
- package/src/modes/rpc/rpc-mode.ts +186 -78
- package/src/modes/rpc/rpc-types.ts +10 -3
- package/src/prompts/system-prompt.md +36 -28
- package/src/utils/clipboard.ts +90 -50
- package/src/utils/image-convert.ts +1 -1
- package/src/utils/image-resize.ts +1 -1
- package/src/utils/tools-manager.ts +2 -2
|
@@ -12,7 +12,21 @@
|
|
|
12
12
|
bytes[i] = binary.charCodeAt(i);
|
|
13
13
|
}
|
|
14
14
|
const data = JSON.parse(new TextDecoder('utf-8').decode(bytes));
|
|
15
|
-
const { header, entries, leafId, systemPrompt, tools } = data;
|
|
15
|
+
const { header, entries, leafId: defaultLeafId, systemPrompt, codexInjectionInfo, tools } = data;
|
|
16
|
+
|
|
17
|
+
// ============================================================
|
|
18
|
+
// URL PARAMETER HANDLING
|
|
19
|
+
// ============================================================
|
|
20
|
+
|
|
21
|
+
// Parse URL parameters for deep linking: leafId and targetId
|
|
22
|
+
// Check for injected params (when loaded in iframe via srcdoc) or use window.location
|
|
23
|
+
const injectedParams = document.querySelector('meta[name="pi-url-params"]');
|
|
24
|
+
const searchString = injectedParams ? injectedParams.content : window.location.search.substring(1);
|
|
25
|
+
const urlParams = new URLSearchParams(searchString);
|
|
26
|
+
const urlLeafId = urlParams.get('leafId');
|
|
27
|
+
const urlTargetId = urlParams.get('targetId');
|
|
28
|
+
// Use URL leafId if provided, otherwise fall back to session default
|
|
29
|
+
const leafId = urlLeafId || defaultLeafId;
|
|
16
30
|
|
|
17
31
|
// ============================================================
|
|
18
32
|
// DATA STRUCTURES
|
|
@@ -777,16 +791,98 @@
|
|
|
777
791
|
return html;
|
|
778
792
|
}
|
|
779
793
|
|
|
794
|
+
/**
|
|
795
|
+
* Build a shareable URL for a specific message.
|
|
796
|
+
* URL format: base?gistId&leafId=<leafId>&targetId=<entryId>
|
|
797
|
+
*/
|
|
798
|
+
function buildShareUrl(entryId) {
|
|
799
|
+
// Check for injected base URL (used when loaded in iframe via srcdoc)
|
|
800
|
+
const baseUrlMeta = document.querySelector('meta[name="pi-share-base-url"]');
|
|
801
|
+
const baseUrl = baseUrlMeta ? baseUrlMeta.content : window.location.href.split('?')[0];
|
|
802
|
+
|
|
803
|
+
const url = new URL(window.location.href);
|
|
804
|
+
// Find the gist ID (first query param without value, e.g., ?abc123)
|
|
805
|
+
const gistId = Array.from(url.searchParams.keys()).find(k => !url.searchParams.get(k));
|
|
806
|
+
|
|
807
|
+
// Build the share URL
|
|
808
|
+
const params = new URLSearchParams();
|
|
809
|
+
params.set('leafId', currentLeafId);
|
|
810
|
+
params.set('targetId', entryId);
|
|
811
|
+
|
|
812
|
+
// If we have an injected base URL (iframe context), use it directly
|
|
813
|
+
if (baseUrlMeta) {
|
|
814
|
+
return `${baseUrl}&${params.toString()}`;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
// Otherwise build from current location (direct file access)
|
|
818
|
+
url.search = gistId ? `?${gistId}&${params.toString()}` : `?${params.toString()}`;
|
|
819
|
+
return url.toString();
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Copy text to clipboard with visual feedback.
|
|
824
|
+
* Uses navigator.clipboard with fallback to execCommand for HTTP contexts.
|
|
825
|
+
*/
|
|
826
|
+
async function copyToClipboard(text, button) {
|
|
827
|
+
let success = false;
|
|
828
|
+
try {
|
|
829
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
830
|
+
await navigator.clipboard.writeText(text);
|
|
831
|
+
success = true;
|
|
832
|
+
}
|
|
833
|
+
} catch {
|
|
834
|
+
// Clipboard API failed, try fallback
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// Fallback for HTTP or when Clipboard API is unavailable
|
|
838
|
+
if (!success) {
|
|
839
|
+
try {
|
|
840
|
+
const textarea = document.createElement('textarea');
|
|
841
|
+
textarea.value = text;
|
|
842
|
+
textarea.style.position = 'fixed';
|
|
843
|
+
textarea.style.opacity = '0';
|
|
844
|
+
document.body.appendChild(textarea);
|
|
845
|
+
textarea.select();
|
|
846
|
+
success = document.execCommand('copy');
|
|
847
|
+
document.body.removeChild(textarea);
|
|
848
|
+
} catch {
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (success && button) {
|
|
853
|
+
const originalHtml = button.innerHTML;
|
|
854
|
+
button.innerHTML = '✓';
|
|
855
|
+
button.classList.add('copied');
|
|
856
|
+
setTimeout(() => {
|
|
857
|
+
button.innerHTML = originalHtml;
|
|
858
|
+
button.classList.remove('copied');
|
|
859
|
+
}, 1500);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Render the copy-link button HTML for a message.
|
|
865
|
+
*/
|
|
866
|
+
function renderCopyLinkButton(entryId) {
|
|
867
|
+
return `<button class="copy-link-btn" data-entry-id="${entryId}" title="Copy link to this message">
|
|
868
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
869
|
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/>
|
|
870
|
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
|
871
|
+
</svg>
|
|
872
|
+
</button>`;
|
|
873
|
+
}
|
|
874
|
+
|
|
780
875
|
function renderEntry(entry) {
|
|
781
876
|
const ts = formatTimestamp(entry.timestamp);
|
|
782
877
|
const tsHtml = ts ? `<div class="message-timestamp">${ts}</div>` : '';
|
|
783
878
|
const entryId = `entry-${entry.id}`;
|
|
879
|
+
const copyBtnHtml = renderCopyLinkButton(entry.id);
|
|
784
880
|
|
|
785
881
|
if (entry.type === 'message') {
|
|
786
882
|
const msg = entry.message;
|
|
787
883
|
|
|
788
884
|
if (msg.role === 'user') {
|
|
789
|
-
let html = `<div class="user-message" id="${entryId}">${tsHtml}`;
|
|
885
|
+
let html = `<div class="user-message" id="${entryId}">${copyBtnHtml}${tsHtml}`;
|
|
790
886
|
const content = msg.content;
|
|
791
887
|
|
|
792
888
|
if (Array.isArray(content)) {
|
|
@@ -810,7 +906,7 @@
|
|
|
810
906
|
}
|
|
811
907
|
|
|
812
908
|
if (msg.role === 'assistant') {
|
|
813
|
-
let html = `<div class="assistant-message" id="${entryId}">${tsHtml}`;
|
|
909
|
+
let html = `<div class="assistant-message" id="${entryId}">${copyBtnHtml}${tsHtml}`;
|
|
814
910
|
|
|
815
911
|
for (const block of msg.content) {
|
|
816
912
|
if (block.type === 'text' && block.text.trim()) {
|
|
@@ -857,7 +953,16 @@
|
|
|
857
953
|
}
|
|
858
954
|
|
|
859
955
|
if (entry.type === 'model_change') {
|
|
860
|
-
|
|
956
|
+
let html = `<div class="model-change" id="${entryId}">${tsHtml}Switched to model: <span class="model-name">${escapeHtml(entry.provider)}/${escapeHtml(entry.modelId)}</span>`;
|
|
957
|
+
|
|
958
|
+
if (entry.provider === 'openai-codex' && codexInjectionInfo) {
|
|
959
|
+
const fullContent = `# Codex Instructions\n${codexInjectionInfo.instructions}\n\n# Codex-Pi Bridge\n${codexInjectionInfo.bridge}`;
|
|
960
|
+
html += ` <span class="codex-bridge-toggle" onclick="event.stopPropagation(); this.parentElement.classList.toggle('show-bridge')">[bridge prompt]</span>`;
|
|
961
|
+
html += `<div class="codex-bridge-content"><pre>${escapeHtml(fullContent)}</pre></div>`;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
html += '</div>';
|
|
965
|
+
return html;
|
|
861
966
|
}
|
|
862
967
|
|
|
863
968
|
if (entry.type === 'compaction') {
|
|
@@ -1010,7 +1115,7 @@
|
|
|
1010
1115
|
return node;
|
|
1011
1116
|
}
|
|
1012
1117
|
|
|
1013
|
-
function navigateTo(targetId, scrollMode = 'target') {
|
|
1118
|
+
function navigateTo(targetId, scrollMode = 'target', scrollToEntryId = null) {
|
|
1014
1119
|
currentLeafId = targetId;
|
|
1015
1120
|
const path = getPath(targetId);
|
|
1016
1121
|
|
|
@@ -1032,15 +1137,30 @@
|
|
|
1032
1137
|
messagesEl.innerHTML = '';
|
|
1033
1138
|
messagesEl.appendChild(fragment);
|
|
1034
1139
|
|
|
1140
|
+
// Attach click handlers for copy-link buttons
|
|
1141
|
+
messagesEl.querySelectorAll('.copy-link-btn').forEach(btn => {
|
|
1142
|
+
btn.addEventListener('click', (e) => {
|
|
1143
|
+
e.stopPropagation();
|
|
1144
|
+
const entryId = btn.dataset.entryId;
|
|
1145
|
+
const shareUrl = buildShareUrl(entryId);
|
|
1146
|
+
copyToClipboard(shareUrl, btn);
|
|
1147
|
+
});
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1035
1150
|
// Use setTimeout(0) to ensure DOM is fully laid out before scrolling
|
|
1036
1151
|
setTimeout(() => {
|
|
1037
1152
|
const content = document.getElementById('content');
|
|
1038
1153
|
if (scrollMode === 'bottom') {
|
|
1039
1154
|
content.scrollTop = content.scrollHeight;
|
|
1040
1155
|
} else if (scrollMode === 'target') {
|
|
1041
|
-
const
|
|
1156
|
+
const scrollTargetId = scrollToEntryId || targetId;
|
|
1157
|
+
const targetEl = document.getElementById(`entry-${scrollTargetId}`);
|
|
1042
1158
|
if (targetEl) {
|
|
1043
1159
|
targetEl.scrollIntoView({ block: 'center' });
|
|
1160
|
+
if (scrollToEntryId) {
|
|
1161
|
+
targetEl.classList.add('highlight');
|
|
1162
|
+
setTimeout(() => targetEl.classList.remove('highlight'), 2000);
|
|
1163
|
+
}
|
|
1044
1164
|
}
|
|
1045
1165
|
}
|
|
1046
1166
|
}, 0);
|
|
@@ -1175,9 +1295,14 @@
|
|
|
1175
1295
|
}
|
|
1176
1296
|
});
|
|
1177
1297
|
|
|
1178
|
-
// Initial render
|
|
1298
|
+
// Initial render
|
|
1299
|
+
// If URL has targetId, scroll to that specific message; otherwise stay at top
|
|
1179
1300
|
if (leafId) {
|
|
1180
|
-
|
|
1301
|
+
if (urlTargetId && byId.has(urlTargetId)) {
|
|
1302
|
+
navigateTo(leafId, 'target', urlTargetId);
|
|
1303
|
+
} else {
|
|
1304
|
+
navigateTo(leafId, 'none');
|
|
1305
|
+
}
|
|
1181
1306
|
} else if (entries.length > 0) {
|
|
1182
1307
|
// Fallback: use last entry if no leafId
|
|
1183
1308
|
navigateTo(entries[entries.length - 1].id, 'none');
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
* Extension system for lifecycle events and custom tools.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export { discoverAndLoadExtensions, loadExtensionFromFactory, loadExtensions } from "./loader";
|
|
6
|
-
export type {
|
|
5
|
+
export { createExtensionRuntime, discoverAndLoadExtensions, loadExtensionFromFactory, loadExtensions } from "./loader";
|
|
6
|
+
export type {
|
|
7
|
+
BranchHandler,
|
|
8
|
+
ExtensionErrorListener,
|
|
9
|
+
NavigateTreeHandler,
|
|
10
|
+
NewSessionHandler,
|
|
11
|
+
ShutdownHandler,
|
|
12
|
+
} from "./runner";
|
|
7
13
|
export { ExtensionRunner } from "./runner";
|
|
8
14
|
export type {
|
|
9
15
|
AgentEndEvent,
|
|
@@ -11,6 +17,7 @@ export type {
|
|
|
11
17
|
// Re-exports
|
|
12
18
|
AgentToolResult,
|
|
13
19
|
AgentToolUpdateCallback,
|
|
20
|
+
AppAction,
|
|
14
21
|
AppendEntryHandler,
|
|
15
22
|
BashToolResultEvent,
|
|
16
23
|
BeforeAgentStartEvent,
|
|
@@ -23,26 +30,32 @@ export type {
|
|
|
23
30
|
EditToolResultEvent,
|
|
24
31
|
ExecOptions,
|
|
25
32
|
ExecResult,
|
|
33
|
+
Extension,
|
|
34
|
+
ExtensionActions,
|
|
26
35
|
// API
|
|
27
36
|
ExtensionAPI,
|
|
28
37
|
ExtensionCommandContext,
|
|
38
|
+
ExtensionCommandContextActions,
|
|
29
39
|
// Context
|
|
30
40
|
ExtensionContext,
|
|
41
|
+
ExtensionContextActions,
|
|
31
42
|
// Errors
|
|
32
43
|
ExtensionError,
|
|
33
44
|
ExtensionEvent,
|
|
34
45
|
ExtensionFactory,
|
|
35
46
|
ExtensionFlag,
|
|
36
47
|
ExtensionHandler,
|
|
48
|
+
ExtensionRuntime,
|
|
37
49
|
ExtensionShortcut,
|
|
38
50
|
ExtensionUIContext,
|
|
51
|
+
ExtensionUIDialogOptions,
|
|
39
52
|
FindToolResultEvent,
|
|
40
53
|
GetActiveToolsHandler,
|
|
41
54
|
GetAllToolsHandler,
|
|
55
|
+
GetThinkingLevelHandler,
|
|
42
56
|
GrepToolResultEvent,
|
|
57
|
+
KeybindingsManager,
|
|
43
58
|
LoadExtensionsResult,
|
|
44
|
-
// Loaded Extension
|
|
45
|
-
LoadedExtension,
|
|
46
59
|
LsToolResultEvent,
|
|
47
60
|
// Message Rendering
|
|
48
61
|
MessageRenderer,
|
|
@@ -52,6 +65,7 @@ export type {
|
|
|
52
65
|
RegisteredCommand,
|
|
53
66
|
RegisteredTool,
|
|
54
67
|
SendMessageHandler,
|
|
68
|
+
SendUserMessageHandler,
|
|
55
69
|
SessionBeforeBranchEvent,
|
|
56
70
|
SessionBeforeBranchResult,
|
|
57
71
|
SessionBeforeCompactEvent,
|
|
@@ -69,6 +83,8 @@ export type {
|
|
|
69
83
|
SessionSwitchEvent,
|
|
70
84
|
SessionTreeEvent,
|
|
71
85
|
SetActiveToolsHandler,
|
|
86
|
+
SetModelHandler,
|
|
87
|
+
SetThinkingLevelHandler,
|
|
72
88
|
// Events - Tool
|
|
73
89
|
ToolCallEvent,
|
|
74
90
|
ToolCallEventResult,
|
|
@@ -80,6 +96,8 @@ export type {
|
|
|
80
96
|
TreePreparation,
|
|
81
97
|
TurnEndEvent,
|
|
82
98
|
TurnStartEvent,
|
|
99
|
+
UserBashEvent,
|
|
100
|
+
UserBashEventResult,
|
|
83
101
|
WriteToolResultEvent,
|
|
84
102
|
} from "./types";
|
|
85
103
|
// Type guards
|