@jx-grxf/patchpilot 0.3.0 → 0.4.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/README.md +17 -11
- package/SECURITY.md +3 -1
- package/dist/core/agent.d.ts +1 -0
- package/dist/core/agent.js +33 -6
- package/dist/core/agent.js.map +1 -1
- package/dist/core/gemini.js +27 -14
- package/dist/core/gemini.js.map +1 -1
- package/dist/core/nvidia.js +19 -1
- package/dist/core/nvidia.js.map +1 -1
- package/dist/core/openrouter.d.ts +2 -0
- package/dist/core/openrouter.js +51 -7
- package/dist/core/openrouter.js.map +1 -1
- package/dist/core/workspace.js +42 -21
- package/dist/core/workspace.js.map +1 -1
- package/dist/tui/App.js +111 -32
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/commands.js +8 -2
- package/dist/tui/commands.js.map +1 -1
- package/dist/tui/components/ApprovalPanel.d.ts +6 -0
- package/dist/tui/components/ApprovalPanel.js +16 -0
- package/dist/tui/components/ApprovalPanel.js.map +1 -0
- package/dist/tui/components/Composer.d.ts +1 -0
- package/dist/tui/components/Composer.js +2 -2
- package/dist/tui/components/Composer.js.map +1 -1
- package/dist/tui/components/Header.js +17 -1
- package/dist/tui/components/Header.js.map +1 -1
- package/dist/tui/components/Sidebar.js +39 -20
- package/dist/tui/components/Sidebar.js.map +1 -1
- package/dist/tui/components/Transcript.js +3 -2
- package/dist/tui/components/Transcript.js.map +1 -1
- package/dist/tui/modes.d.ts +10 -0
- package/dist/tui/modes.js +37 -0
- package/dist/tui/modes.js.map +1 -0
- package/dist/tui/types.d.ts +1 -1
- package/docs/releases/v0.3.1-beta.md +19 -0
- package/docs/releases/v0.4.0.md +27 -0
- package/package.json +1 -1
package/dist/tui/App.js
CHANGED
|
@@ -15,6 +15,7 @@ import { formatReasoningSupport } from "../core/reasoning.js";
|
|
|
15
15
|
import { listWorkspaceSessions, loadSessionSummary, SessionStore } from "../core/session.js";
|
|
16
16
|
import { addTelemetryToSession, emptySessionTelemetry, estimateTokens } from "../core/tokenAccounting.js";
|
|
17
17
|
import { WorkspaceTools } from "../core/workspace.js";
|
|
18
|
+
import { ApprovalPanel } from "./components/ApprovalPanel.js";
|
|
18
19
|
import { CommandSuggestions } from "./components/CommandSuggestions.js";
|
|
19
20
|
import { Composer, FooterHints } from "./components/Composer.js";
|
|
20
21
|
import { Header } from "./components/Header.js";
|
|
@@ -24,6 +25,7 @@ import { Transcript } from "./components/Transcript.js";
|
|
|
24
25
|
import { filterSlashCommands, formatCommandDetail, formatCommandHelp } from "./commands.js";
|
|
25
26
|
import { formatCost, formatSessionTokens, formatTokens, normalizeModelAlias, readToggle } from "./format.js";
|
|
26
27
|
import { checkOllamaHost, discoverOllamaHosts, normalizeOllamaUrl, readOllamaHostDetails, startLocalOllamaAppAndWait } from "./hosts.js";
|
|
28
|
+
import { initialAgentMode, modeDescription, modePermissionLabel, nextAgentMode, permissionsForMode } from "./modes.js";
|
|
27
29
|
import { selectableModels } from "./modelSelection.js";
|
|
28
30
|
import { readGpuStats, readSystemStats } from "./systemStats.js";
|
|
29
31
|
import { maxTranscriptLines } from "./types.js";
|
|
@@ -55,7 +57,8 @@ export function App(props) {
|
|
|
55
57
|
const [sessionTelemetry, setSessionTelemetry] = useState(() => emptySessionTelemetry());
|
|
56
58
|
const [systemStats, setSystemStats] = useState(() => readSystemStats().stats);
|
|
57
59
|
const [gpuStats, setGpuStats] = useState(null);
|
|
58
|
-
const [agentMode, setAgentMode] = useState(props.allowWrite
|
|
60
|
+
const [agentMode, setAgentMode] = useState(() => initialAgentMode({ allowWrite: props.allowWrite, allowShell: props.allowShell }));
|
|
61
|
+
const [bypassConfirmation, setBypassConfirmation] = useState(false);
|
|
59
62
|
const [hostOptions, setHostOptions] = useState([]);
|
|
60
63
|
const [activeHost, setActiveHost] = useState(null);
|
|
61
64
|
const [isLoadingHosts, setIsLoadingHosts] = useState(false);
|
|
@@ -101,7 +104,8 @@ export function App(props) {
|
|
|
101
104
|
const paletteReservedHeight = !onboarding && paletteItems.length > 0 ? Math.min(8, paletteItems.length) + 4 : 0;
|
|
102
105
|
const composerReservedHeight = onboarding ? 0 : 2;
|
|
103
106
|
const footerReservedHeight = onboarding ? 0 : 1;
|
|
104
|
-
const
|
|
107
|
+
const approvalReservedHeight = !onboarding && (pendingApproval || bypassConfirmation) ? 6 : 0;
|
|
108
|
+
const panelHeight = Math.max(8, rootHeight - headerReservedHeight - composerReservedHeight - paletteReservedHeight - footerReservedHeight - approvalReservedHeight);
|
|
105
109
|
const transcriptWidth = Math.max(42, terminalColumns - 38);
|
|
106
110
|
const scrollStep = Math.max(4, Math.floor(panelHeight * 0.8));
|
|
107
111
|
const appendLine = useCallback((line) => {
|
|
@@ -132,23 +136,58 @@ export function App(props) {
|
|
|
132
136
|
setPendingApproval(null);
|
|
133
137
|
}, [appendLine, pendingApproval]);
|
|
134
138
|
const applyMode = useCallback((nextMode, announce = true) => {
|
|
139
|
+
const permissions = permissionsForMode(nextMode);
|
|
135
140
|
setAgentMode(nextMode);
|
|
141
|
+
setBypassConfirmation(false);
|
|
136
142
|
setSettings((currentSettings) => ({
|
|
137
143
|
...currentSettings,
|
|
138
|
-
allowWrite:
|
|
139
|
-
allowShell:
|
|
144
|
+
allowWrite: permissions.allowWrite,
|
|
145
|
+
allowShell: permissions.allowShell
|
|
140
146
|
}));
|
|
141
147
|
if (announce) {
|
|
142
148
|
appendLine({
|
|
143
|
-
tone: "success",
|
|
149
|
+
tone: nextMode === "bypass" ? "warning" : "success",
|
|
144
150
|
label: "mode",
|
|
145
|
-
text:
|
|
151
|
+
text: modeDescription(nextMode),
|
|
152
|
+
detail: nextMode === "plan"
|
|
153
|
+
? "Read/search/status tools can still run. Writes, tests, scripts, and shell are denied."
|
|
154
|
+
: nextMode === "build"
|
|
155
|
+
? "Risky tools can run only after allow once/session approval."
|
|
156
|
+
: "Use only in a trusted workspace. Path guards and destructive shell guards still apply."
|
|
146
157
|
});
|
|
147
158
|
}
|
|
148
159
|
}, [appendLine]);
|
|
160
|
+
const requestBypassMode = useCallback(() => {
|
|
161
|
+
if (bypassConfirmation) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
setBypassConfirmation(true);
|
|
165
|
+
setStatus("bypass confirmation needed");
|
|
166
|
+
setWorkState("waiting_approval");
|
|
167
|
+
}, [bypassConfirmation]);
|
|
168
|
+
const confirmBypassMode = useCallback(() => {
|
|
169
|
+
applyMode("bypass");
|
|
170
|
+
}, [applyMode]);
|
|
171
|
+
const cancelBypassMode = useCallback(() => {
|
|
172
|
+
setBypassConfirmation(false);
|
|
173
|
+
setStatus("idle");
|
|
174
|
+
setWorkState("idle");
|
|
175
|
+
applyMode("build", false);
|
|
176
|
+
appendLine({
|
|
177
|
+
kind: "approval",
|
|
178
|
+
tone: "warning",
|
|
179
|
+
label: "bypass",
|
|
180
|
+
text: "Bypass cancelled. Build mode still uses approvals."
|
|
181
|
+
});
|
|
182
|
+
}, [appendLine, applyMode]);
|
|
149
183
|
const toggleMode = useCallback(() => {
|
|
150
|
-
|
|
151
|
-
|
|
184
|
+
const mode = nextAgentMode(agentMode);
|
|
185
|
+
if (mode === "bypass") {
|
|
186
|
+
requestBypassMode();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
applyMode(mode);
|
|
190
|
+
}, [agentMode, applyMode, requestBypassMode]);
|
|
152
191
|
const loadHostSuggestions = useCallback(async (refresh = false, announce = false) => {
|
|
153
192
|
if (isLoadingHosts) {
|
|
154
193
|
return hostOptions;
|
|
@@ -598,7 +637,7 @@ export function App(props) {
|
|
|
598
637
|
return;
|
|
599
638
|
}
|
|
600
639
|
const visibleModels = selectableModels(onboardingInput, onboarding.models);
|
|
601
|
-
const selectedModel = selectModelFromInput(value, visibleModels, onboardingIndex, {
|
|
640
|
+
const selectedModel = visibleModels[onboardingIndex] ?? selectModelFromInput(value, visibleModels, onboardingIndex, {
|
|
602
641
|
allowManual: onboarding.provider !== "ollama"
|
|
603
642
|
});
|
|
604
643
|
if (!selectedModel) {
|
|
@@ -657,6 +696,7 @@ export function App(props) {
|
|
|
657
696
|
abortControllerRef.current = abortController;
|
|
658
697
|
const taskRunner = new AgentRunner({
|
|
659
698
|
...runnableSettings,
|
|
699
|
+
mode: agentMode,
|
|
660
700
|
signal: abortController.signal,
|
|
661
701
|
sessionStore: sessionStoreRef.current,
|
|
662
702
|
approvalHandler: (request) => new Promise((resolve) => {
|
|
@@ -674,9 +714,14 @@ export function App(props) {
|
|
|
674
714
|
resolve("deny");
|
|
675
715
|
return;
|
|
676
716
|
}
|
|
717
|
+
if (agentMode === "bypass") {
|
|
718
|
+
resolve("allow_session");
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
677
721
|
setPendingApproval(request);
|
|
678
722
|
setWorkState("waiting_approval");
|
|
679
723
|
setStatus(`approval needed for ${request.tool}`);
|
|
724
|
+
setTranscriptScrollOffset(0);
|
|
680
725
|
appendLine({
|
|
681
726
|
kind: "approval",
|
|
682
727
|
tone: "warning",
|
|
@@ -747,16 +792,21 @@ export function App(props) {
|
|
|
747
792
|
return;
|
|
748
793
|
case "build":
|
|
749
794
|
case "plan":
|
|
795
|
+
case "bypass":
|
|
750
796
|
case "mode": {
|
|
751
797
|
const nextMode = command === "mode" ? args[0]?.toLowerCase() : command;
|
|
752
|
-
if (nextMode !== "plan" && nextMode !== "build") {
|
|
798
|
+
if (nextMode !== "plan" && nextMode !== "build" && nextMode !== "bypass") {
|
|
753
799
|
appendLine({
|
|
754
800
|
tone: "accent",
|
|
755
801
|
label: "mode",
|
|
756
|
-
text: `current ${agentMode}. Use /mode plan, /mode build, or press tab.`
|
|
802
|
+
text: `current ${agentMode}. Use /mode plan, /mode build, /mode bypass, or press tab.`
|
|
757
803
|
});
|
|
758
804
|
return;
|
|
759
805
|
}
|
|
806
|
+
if (nextMode === "bypass" && agentMode !== "bypass") {
|
|
807
|
+
requestBypassMode();
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
760
810
|
applyMode(nextMode);
|
|
761
811
|
return;
|
|
762
812
|
}
|
|
@@ -765,7 +815,8 @@ export function App(props) {
|
|
|
765
815
|
appendLine({
|
|
766
816
|
tone: "accent",
|
|
767
817
|
label: "permissions",
|
|
768
|
-
text: `
|
|
818
|
+
text: `mode ${agentMode} | write ${modePermissionLabel(agentMode, "write")} | shell ${modePermissionLabel(agentMode, "shell")} | subagents ${settings.subagents ? "on" : "off"}`,
|
|
819
|
+
detail: modeDescription(agentMode)
|
|
769
820
|
});
|
|
770
821
|
return;
|
|
771
822
|
case "provider": {
|
|
@@ -874,34 +925,30 @@ export function App(props) {
|
|
|
874
925
|
case "apply": {
|
|
875
926
|
const writeEnabled = readToggle(args[0], !settings.allowWrite);
|
|
876
927
|
if (writeEnabled) {
|
|
877
|
-
|
|
928
|
+
requestBypassMode();
|
|
929
|
+
return;
|
|
878
930
|
}
|
|
879
931
|
grantedPermissionsRef.current.allowWrite = writeEnabled;
|
|
880
|
-
|
|
881
|
-
...currentSettings,
|
|
882
|
-
allowWrite: writeEnabled
|
|
883
|
-
}));
|
|
932
|
+
applyMode("build", false);
|
|
884
933
|
appendLine({
|
|
885
934
|
tone: "success",
|
|
886
935
|
label: "write",
|
|
887
|
-
text:
|
|
936
|
+
text: "workspace writes require approval in build mode"
|
|
888
937
|
});
|
|
889
938
|
return;
|
|
890
939
|
}
|
|
891
940
|
case "shell": {
|
|
892
941
|
const shellEnabled = readToggle(args[0], !settings.allowShell);
|
|
893
942
|
if (shellEnabled) {
|
|
894
|
-
|
|
943
|
+
requestBypassMode();
|
|
944
|
+
return;
|
|
895
945
|
}
|
|
896
946
|
grantedPermissionsRef.current.allowShell = shellEnabled;
|
|
897
|
-
|
|
898
|
-
...currentSettings,
|
|
899
|
-
allowShell: shellEnabled
|
|
900
|
-
}));
|
|
947
|
+
applyMode("build", false);
|
|
901
948
|
appendLine({
|
|
902
949
|
tone: "success",
|
|
903
950
|
label: "shell",
|
|
904
|
-
text:
|
|
951
|
+
text: "shell commands require approval in build mode"
|
|
905
952
|
});
|
|
906
953
|
return;
|
|
907
954
|
}
|
|
@@ -1005,8 +1052,8 @@ export function App(props) {
|
|
|
1005
1052
|
tone: "accent",
|
|
1006
1053
|
label: "status",
|
|
1007
1054
|
text: settings.provider === "ollama"
|
|
1008
|
-
? `provider ollama | model ${settings.model} | host ${activeHost?.host.deviceName ?? settings.ollamaUrl} | route ${activeHost?.host.url ?? settings.ollamaUrl} | compute ${describeComputeTarget(settings.ollamaUrl).kind} | tools local | agents ${settings.subagents ? "on" : "off"} |
|
|
1009
|
-
: `provider ${settings.provider} | model ${settings.model} | host ${settings.provider} api | compute cloud | agents ${settings.subagents ? "on" : "off"} | think ${settings.thinkingMode} | reasoning ${formatReasoningSupport(settings.provider, settings.model, settings.reasoningEffort === "adaptive" ? undefined : settings.reasoningEffort)} |
|
|
1055
|
+
? `provider ollama | model ${settings.model} | host ${activeHost?.host.deviceName ?? settings.ollamaUrl} | route ${activeHost?.host.url ?? settings.ollamaUrl} | compute ${describeComputeTarget(settings.ollamaUrl).kind} | tools local | agents ${settings.subagents ? "on" : "off"} | mode ${agentMode} | write ${modePermissionLabel(agentMode, "write")} | shell ${modePermissionLabel(agentMode, "shell")} | draft ${draftTokens} tok | last ${formatTokens(telemetry)} | session ${formatSessionTokens(sessionTelemetry)} | cost ${formatCost(sessionTelemetry.estimatedCostUsd)}`
|
|
1056
|
+
: `provider ${settings.provider} | model ${settings.model} | host ${settings.provider} api | compute cloud | agents ${settings.subagents ? "on" : "off"} | think ${settings.thinkingMode} | reasoning ${formatReasoningSupport(settings.provider, settings.model, settings.reasoningEffort === "adaptive" ? undefined : settings.reasoningEffort)} | mode ${agentMode} | write ${modePermissionLabel(agentMode, "write")} | shell ${modePermissionLabel(agentMode, "shell")} | draft ${draftTokens} tok | last ${formatTokens(telemetry)} | session ${formatSessionTokens(sessionTelemetry)} | cost ${formatCost(sessionTelemetry.estimatedCostUsd)}`
|
|
1010
1057
|
});
|
|
1011
1058
|
return;
|
|
1012
1059
|
case "sessions": {
|
|
@@ -1191,6 +1238,7 @@ export function App(props) {
|
|
|
1191
1238
|
appendLine,
|
|
1192
1239
|
applyMode,
|
|
1193
1240
|
connectToHost,
|
|
1241
|
+
requestBypassMode,
|
|
1194
1242
|
draftTokens,
|
|
1195
1243
|
exit,
|
|
1196
1244
|
hostOptions,
|
|
@@ -1204,7 +1252,14 @@ export function App(props) {
|
|
|
1204
1252
|
]);
|
|
1205
1253
|
const handleSubmit = useCallback(async (value) => {
|
|
1206
1254
|
const nextValue = value.trim();
|
|
1207
|
-
if (!nextValue
|
|
1255
|
+
if (!nextValue) {
|
|
1256
|
+
return;
|
|
1257
|
+
}
|
|
1258
|
+
if (isRunning && nextValue.startsWith("/")) {
|
|
1259
|
+
await handleSlashCommand(nextValue);
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
if (isRunning) {
|
|
1208
1263
|
return;
|
|
1209
1264
|
}
|
|
1210
1265
|
if (onboarding) {
|
|
@@ -1319,6 +1374,21 @@ export function App(props) {
|
|
|
1319
1374
|
}
|
|
1320
1375
|
}, [hostOptions.length, input, isLoadingHosts, isLoadingModels, isRunning, loadHostSuggestions, loadProviderModels, modelOptions.length, onboarding, settings.provider]);
|
|
1321
1376
|
useInput((inputValue, key) => {
|
|
1377
|
+
if (bypassConfirmation) {
|
|
1378
|
+
const normalizedInput = inputValue.toLowerCase();
|
|
1379
|
+
if (key.tab) {
|
|
1380
|
+
cancelBypassMode();
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
if (normalizedInput === "y") {
|
|
1384
|
+
confirmBypassMode();
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
if (normalizedInput === "n" || key.escape) {
|
|
1388
|
+
cancelBypassMode();
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1322
1392
|
if (pendingApproval) {
|
|
1323
1393
|
const normalizedInput = inputValue.toLowerCase();
|
|
1324
1394
|
if (normalizedInput === "y") {
|
|
@@ -1461,7 +1531,7 @@ export function App(props) {
|
|
|
1461
1531
|
clearInterval(timer);
|
|
1462
1532
|
};
|
|
1463
1533
|
}, []);
|
|
1464
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rootHeight, overflowY: "hidden", children: [_jsx(Header, { model: settings.model, provider: settings.provider, workspace: settings.workspace, status: status, workState: workState, allowWrite: settings.allowWrite, allowShell: settings.allowShell, agentMode: agentMode, subagents: settings.subagents, thinkingMode: settings.thinkingMode, reasoningEffort: settings.reasoningEffort, ollamaUrl: settings.ollamaUrl, telemetry: telemetry, sessionTelemetry: sessionTelemetry, draftTokens: draftTokens, systemStats: systemStats, gpuStats: gpuStats, activeHost: activeHost }), onboarding ? (_jsx(OnboardingPanel, { state: onboarding, height: panelHeight, selectedIndex: onboardingIndex, input: onboardingInput, busyMessage: onboardingBusyMessage, notice: onboardingNotice, onInputChange: setOnboardingInput, onInputSubmit: (value) => void handleOnboardingSubmit(value) })) : (_jsxs(Box, { flexDirection: "row", height: panelHeight + composerReservedHeight + paletteReservedHeight + footerReservedHeight, overflowY: "hidden", children: [_jsx(Sidebar, { workspace: settings.workspace, model: settings.model, provider: settings.provider, ollamaUrl: settings.ollamaUrl, agentMode: agentMode, allowWrite: settings.allowWrite, allowShell: settings.allowShell, subagents: settings.subagents, workState: workState, sessionId: sessionStoreRef.current.sessionId, systemStats: systemStats, gpuStats: gpuStats, telemetry: telemetry, sessionTelemetry: sessionTelemetry, draftTokens: draftTokens, height: panelHeight, scrollOffset: sessionScrollOffset, advisors: advisorNotes, isActive: activeScrollPane === "session", activeHost: activeHost }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, height: panelHeight + composerReservedHeight + paletteReservedHeight + footerReservedHeight, overflowY: "hidden", children: [_jsx(Transcript, { lines: lines, isRunning: isRunning, isActive: activeScrollPane === "transcript", height: panelHeight, width: transcriptWidth, scrollOffset: transcriptScrollOffset }), _jsx(Composer, { input: input, isRunning: isRunning, status: status, draftTokens: draftTokens, onChange: setInput, onSubmit: (value) => void handleSubmit(value) }), paletteItems.length > 0 ? _jsx(CommandSuggestions, { items: paletteItems, selectedIndex: paletteIndex }) : null, _jsx(FooterHints, { activePane: activeScrollPane })] })] }))] }));
|
|
1534
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, height: rootHeight, overflowY: "hidden", children: [_jsx(Header, { model: settings.model, provider: settings.provider, workspace: settings.workspace, status: status, workState: workState, allowWrite: settings.allowWrite, allowShell: settings.allowShell, agentMode: agentMode, subagents: settings.subagents, thinkingMode: settings.thinkingMode, reasoningEffort: settings.reasoningEffort, ollamaUrl: settings.ollamaUrl, telemetry: telemetry, sessionTelemetry: sessionTelemetry, draftTokens: draftTokens, systemStats: systemStats, gpuStats: gpuStats, activeHost: activeHost }), onboarding ? (_jsx(OnboardingPanel, { state: onboarding, height: panelHeight, selectedIndex: onboardingIndex, input: onboardingInput, busyMessage: onboardingBusyMessage, notice: onboardingNotice, onInputChange: setOnboardingInput, onInputSubmit: (value) => void handleOnboardingSubmit(value) })) : (_jsxs(Box, { flexDirection: "row", height: panelHeight + approvalReservedHeight + composerReservedHeight + paletteReservedHeight + footerReservedHeight, overflowY: "hidden", children: [_jsx(Sidebar, { workspace: settings.workspace, model: settings.model, provider: settings.provider, ollamaUrl: settings.ollamaUrl, agentMode: agentMode, allowWrite: settings.allowWrite, allowShell: settings.allowShell, subagents: settings.subagents, workState: workState, sessionId: sessionStoreRef.current.sessionId, systemStats: systemStats, gpuStats: gpuStats, telemetry: telemetry, sessionTelemetry: sessionTelemetry, draftTokens: draftTokens, height: panelHeight, scrollOffset: sessionScrollOffset, advisors: advisorNotes, isActive: activeScrollPane === "session", activeHost: activeHost }), _jsxs(Box, { flexDirection: "column", flexGrow: 1, height: panelHeight + approvalReservedHeight + composerReservedHeight + paletteReservedHeight + footerReservedHeight, overflowY: "hidden", children: [_jsx(Transcript, { lines: lines, isRunning: isRunning, isActive: activeScrollPane === "transcript", height: panelHeight, width: transcriptWidth, scrollOffset: transcriptScrollOffset }), _jsx(ApprovalPanel, { request: pendingApproval, bypassConfirmation: bypassConfirmation }), _jsx(Composer, { input: input, isRunning: isRunning, status: status, draftTokens: draftTokens, isApprovalWaiting: Boolean(pendingApproval || bypassConfirmation), onChange: setInput, onSubmit: (value) => void handleSubmit(value) }), paletteItems.length > 0 ? _jsx(CommandSuggestions, { items: paletteItems, selectedIndex: paletteIndex }) : null, _jsx(FooterHints, { activePane: activeScrollPane })] })] }))] }));
|
|
1465
1535
|
}
|
|
1466
1536
|
async function loadAvailableModels(provider, ollamaUrl, setModelOptions, refresh = false) {
|
|
1467
1537
|
const cacheKey = `${provider}:${provider === "ollama" ? ollamaUrl : "default"}`;
|
|
@@ -1507,7 +1577,7 @@ async function switchModel(provider, nextModel, ollamaUrl, currentModel, appendL
|
|
|
1507
1577
|
if (!installedModels) {
|
|
1508
1578
|
return;
|
|
1509
1579
|
}
|
|
1510
|
-
if (!installedModels.includes(nextModel)) {
|
|
1580
|
+
if (!installedModels.includes(nextModel) && !(provider !== "ollama" && isPlausibleCloudModelId(nextModel))) {
|
|
1511
1581
|
appendLine({
|
|
1512
1582
|
tone: "warning",
|
|
1513
1583
|
label: "model",
|
|
@@ -1534,9 +1604,10 @@ async function switchModel(provider, nextModel, ollamaUrl, currentModel, appendL
|
|
|
1534
1604
|
PATCHPILOT_MODEL: nextModel
|
|
1535
1605
|
});
|
|
1536
1606
|
appendLine({
|
|
1537
|
-
tone: "success",
|
|
1607
|
+
tone: installedModels.includes(nextModel) ? "success" : "warning",
|
|
1538
1608
|
label: "model",
|
|
1539
|
-
text: `switched to ${nextModel}`
|
|
1609
|
+
text: installedModels.includes(nextModel) ? `switched to ${nextModel}` : `switched to unverified ${provider} model ${nextModel}`,
|
|
1610
|
+
detail: installedModels.includes(nextModel) ? undefined : "The provider did not list this model in discovery. PatchPilot will try it and surface the provider error if it is unavailable."
|
|
1540
1611
|
});
|
|
1541
1612
|
if (provider === "openrouter" && isOpenRouterFreeModel(nextModel)) {
|
|
1542
1613
|
appendLine({
|
|
@@ -1562,7 +1633,15 @@ async function resolveRunnableSettings(settings, modelOptions, appendLine, setMo
|
|
|
1562
1633
|
});
|
|
1563
1634
|
return null;
|
|
1564
1635
|
}
|
|
1565
|
-
if (installedModels.includes(settings.model)) {
|
|
1636
|
+
if (installedModels.includes(settings.model) || (settings.provider !== "ollama" && isPlausibleCloudModelId(settings.model))) {
|
|
1637
|
+
if (!installedModels.includes(settings.model)) {
|
|
1638
|
+
appendLine({
|
|
1639
|
+
tone: "warning",
|
|
1640
|
+
label: "model",
|
|
1641
|
+
text: `using unverified ${settings.provider} model ${settings.model}`,
|
|
1642
|
+
detail: "Model discovery did not list it; the next provider request will be the compatibility check."
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1566
1645
|
return settings;
|
|
1567
1646
|
}
|
|
1568
1647
|
appendLine({
|