pum-agent 0.2.29-beta.1 → 0.2.30-beta.1
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 +4 -0
- package/package.json +4 -3
- package/src/app.tsx +29 -1
- package/src/headless.ts +2 -0
- package/src/main.tsx +2 -0
- package/src/model-catalog.ts +47 -0
- package/src/settings-popup.tsx +7 -1
package/README.md
CHANGED
|
@@ -23,6 +23,10 @@ pum
|
|
|
23
23
|
PUM opens the login panel automatically on the first start. Press `?` on an
|
|
24
24
|
empty prompt to see every control.
|
|
25
25
|
|
|
26
|
+
Select a model in `Ctrl+P`. GPT-6 Astra is listed for OpenAI and Codex;
|
|
27
|
+
using it requires access on your provider account. Press `r` in the model
|
|
28
|
+
list to refresh providers that support catalog discovery.
|
|
29
|
+
|
|
26
30
|
> [!WARNING]
|
|
27
31
|
> PUM can read, write, and delete files. Check mode adds deterministic policy
|
|
28
32
|
> checks, and supported hosts can enforce native Bash isolation, but the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pum-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.30-beta.1",
|
|
4
4
|
"description": "A compact terminal coding agent powered by pi and OpenTUI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -50,8 +50,9 @@
|
|
|
50
50
|
"pack:check": "bun run scripts/validate-pack.ts"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@earendil-works/pi-ai": "^0.
|
|
54
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
53
|
+
"@earendil-works/pi-ai": "^0.85.0",
|
|
54
|
+
"@earendil-works/pi-coding-agent": "^0.85.0",
|
|
55
|
+
"@earendil-works/pi-server": "^0.85.0",
|
|
55
56
|
"@opentui/core": "^0.5.1",
|
|
56
57
|
"@opentui/react": "^0.5.1",
|
|
57
58
|
"react": "^19.2.8",
|
package/src/app.tsx
CHANGED
|
@@ -1041,6 +1041,8 @@ export function App({
|
|
|
1041
1041
|
}));
|
|
1042
1042
|
const [modelQuery, setModelQuery] = useState("");
|
|
1043
1043
|
const [modelSearchFocused, setModelSearchFocused] = useState(false);
|
|
1044
|
+
const [modelRefreshing, setModelRefreshing] = useState(false);
|
|
1045
|
+
const [modelRevision, setModelRevision] = useState(0);
|
|
1044
1046
|
const [, setAgentRevision] = useState(0);
|
|
1045
1047
|
const [triggersOpen, setTriggersOpen] = useState(false);
|
|
1046
1048
|
const [processTab, setProcessTab] = useState<ProcessTab>("triggers");
|
|
@@ -1321,7 +1323,7 @@ export function App({
|
|
|
1321
1323
|
modelRuntime.getAvailableSnapshot(),
|
|
1322
1324
|
modelQuery,
|
|
1323
1325
|
(providerId) => (modelRuntime as any).getProvider?.(providerId)?.name ?? "",
|
|
1324
|
-
), [modelRuntime, modelId, modelQuery, loginPage]);
|
|
1326
|
+
), [modelRuntime, modelId, modelQuery, loginPage, modelRevision]);
|
|
1325
1327
|
|
|
1326
1328
|
const inputRef = useRef<TextareaRenderable>(null);
|
|
1327
1329
|
const inputModeRef = useRef(false);
|
|
@@ -1334,6 +1336,7 @@ export function App({
|
|
|
1334
1336
|
const processTabRef = useRef<ProcessTab>("triggers");
|
|
1335
1337
|
const settingsPageRef = useRef(page);
|
|
1336
1338
|
const settingsSearchFocusedRef = useRef(settingsSearchFocused);
|
|
1339
|
+
const modelRefreshInFlightRef = useRef(false);
|
|
1337
1340
|
const focusInputAfterSwitch = useRef(false);
|
|
1338
1341
|
const activeAgentIdRef = useRef<string | null>(null);
|
|
1339
1342
|
const agentSelectorCursorRef = useRef(0);
|
|
@@ -2870,6 +2873,25 @@ export function App({
|
|
|
2870
2873
|
void loadManagedProviders();
|
|
2871
2874
|
}, [commandInput, managedProviders.length]);
|
|
2872
2875
|
|
|
2876
|
+
const refreshModels = async () => {
|
|
2877
|
+
if (modelRefreshInFlightRef.current) return;
|
|
2878
|
+
modelRefreshInFlightRef.current = true;
|
|
2879
|
+
setModelRefreshing(true);
|
|
2880
|
+
try {
|
|
2881
|
+
const result = await modelRuntime.refresh({ allowNetwork: true, force: true });
|
|
2882
|
+
if (result.errors.size > 0) {
|
|
2883
|
+
const details = [...result.errors].map(([provider, error]) => `${provider}: ${error.message}`).join("; ");
|
|
2884
|
+
append({ kind: "text", role: "error", text: `model refresh completed with errors: ${details}` });
|
|
2885
|
+
}
|
|
2886
|
+
} catch (error) {
|
|
2887
|
+
append({ kind: "text", role: "error", text: `model refresh failed: ${String(error)}` });
|
|
2888
|
+
} finally {
|
|
2889
|
+
modelRefreshInFlightRef.current = false;
|
|
2890
|
+
setModelRefreshing(false);
|
|
2891
|
+
setModelRevision((revision) => revision + 1);
|
|
2892
|
+
}
|
|
2893
|
+
};
|
|
2894
|
+
|
|
2873
2895
|
const selectModel = (model: Model<any>) => {
|
|
2874
2896
|
settingsPageRef.current = "main";
|
|
2875
2897
|
setPage("main");
|
|
@@ -5284,6 +5306,11 @@ export function App({
|
|
|
5284
5306
|
}
|
|
5285
5307
|
return;
|
|
5286
5308
|
}
|
|
5309
|
+
if (!key.ctrl && !key.meta && !key.option && printableKey === "r") {
|
|
5310
|
+
key.stopPropagation();
|
|
5311
|
+
void refreshModels();
|
|
5312
|
+
return;
|
|
5313
|
+
}
|
|
5287
5314
|
if (isModelSearchShortcut(key, modelSearchFocused)) {
|
|
5288
5315
|
key.stopPropagation();
|
|
5289
5316
|
setModelSearchFocused(true);
|
|
@@ -6170,6 +6197,7 @@ export function App({
|
|
|
6170
6197
|
models={visibleModels}
|
|
6171
6198
|
modelQuery={modelQuery}
|
|
6172
6199
|
modelSearchFocused={modelSearchFocused}
|
|
6200
|
+
modelRefreshing={modelRefreshing}
|
|
6173
6201
|
onSearchChange={updateSettingsQuery}
|
|
6174
6202
|
onModelSearchChange={setModelQuery}
|
|
6175
6203
|
onSelectModel={selectModel}
|
package/src/headless.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SessionManager,
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { mkdirSync } from "node:fs";
|
|
9
|
+
import { installModelCatalogFallbacks } from "./model-catalog";
|
|
9
10
|
import { AGENT_DIR, AUTH_PATH, MODELS_PATH, sessionDir } from "./config";
|
|
10
11
|
import { createMemoryExtension, MEMORY_EDIT_TOOL_NAME, MEMORY_READ_TOOL_NAME } from "./memory";
|
|
11
12
|
import { checkPathsForProject, loadSettings } from "./settings";
|
|
@@ -148,6 +149,7 @@ async function runPromptSession(
|
|
|
148
149
|
authPath: AUTH_PATH,
|
|
149
150
|
modelsPath: MODELS_PATH,
|
|
150
151
|
});
|
|
152
|
+
installModelCatalogFallbacks(modelRuntime);
|
|
151
153
|
if ((await modelRuntime.getAvailable()).length === 0) {
|
|
152
154
|
process.stderr.write("pum: no provider is available. Run 'pum login' first.\n");
|
|
153
155
|
return 1;
|
package/src/main.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import { createMemoryExtension } from "./memory";
|
|
|
15
15
|
import { checkPathsForProject, loadSettings } from "./settings";
|
|
16
16
|
import { setBashOutputSettingsIfPresent } from "./bash-output";
|
|
17
17
|
import { installWebSearch, webSearch } from "./web-search";
|
|
18
|
+
import { installModelCatalogFallbacks } from "./model-catalog";
|
|
18
19
|
import { identityExtension } from "./identity";
|
|
19
20
|
import { setWritingStyle, writingStyleExtension } from "./writing-style";
|
|
20
21
|
import { checkModePromptExtension, setSandboxModeSource } from "./check-mode-prompt";
|
|
@@ -97,6 +98,7 @@ export async function start(
|
|
|
97
98
|
authPath: AUTH_PATH,
|
|
98
99
|
modelsPath: MODELS_PATH,
|
|
99
100
|
});
|
|
101
|
+
installModelCatalogFallbacks(modelRuntime);
|
|
100
102
|
const loginRequired = options.login || (await modelRuntime.getAvailable()).length === 0;
|
|
101
103
|
|
|
102
104
|
const settings = loadSettings();
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Model, Provider } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
|
|
4
|
+
const ASTRA_ID = "gpt-6-astra";
|
|
5
|
+
|
|
6
|
+
/** Temporary catalog fallback until pi includes Astra. Never replace upstream metadata. */
|
|
7
|
+
export function withAstraModel(base: Provider): Provider {
|
|
8
|
+
if (base.id !== "openai" && base.id !== "openai-codex") return base;
|
|
9
|
+
if (base.getModels().some((model) => model.id === ASTRA_ID)) return base;
|
|
10
|
+
const codex = base.id === "openai-codex";
|
|
11
|
+
// https://developers.openai.com/api/docs/models/gpt-6-astra
|
|
12
|
+
const astra: Model<any> = {
|
|
13
|
+
id: ASTRA_ID,
|
|
14
|
+
name: "GPT-6 Astra",
|
|
15
|
+
provider: base.id,
|
|
16
|
+
api: codex ? "openai-codex-responses" : "openai-responses",
|
|
17
|
+
baseUrl: base.baseUrl ?? (codex ? "https://chatgpt.com/backend-api" : "https://api.openai.com/v1"),
|
|
18
|
+
reasoning: true,
|
|
19
|
+
input: ["text", "image"],
|
|
20
|
+
// Keep pi's conservative Codex context budget until that transport publishes one.
|
|
21
|
+
contextWindow: codex ? 272000 : 1050000,
|
|
22
|
+
maxTokens: 128000,
|
|
23
|
+
cost: {
|
|
24
|
+
input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5,
|
|
25
|
+
tiers: [{ inputTokensAbove: 272000, input: 20, output: 75, cacheRead: 2, cacheWrite: 25 }],
|
|
26
|
+
},
|
|
27
|
+
thinkingLevelMap: {
|
|
28
|
+
off: null, minimal: null, low: "low", medium: "medium",
|
|
29
|
+
high: "high", xhigh: "xhigh", max: "max",
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
const wrapped: Provider = Object.create(base);
|
|
33
|
+
wrapped.getModels = () => {
|
|
34
|
+
const models = base.getModels();
|
|
35
|
+
return models.some((model) => model.id === ASTRA_ID) ? models : [...models, astra];
|
|
36
|
+
};
|
|
37
|
+
return wrapped;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function installModelCatalogFallbacks(runtime: ModelRuntime): void {
|
|
41
|
+
for (const id of ["openai", "openai-codex"]) {
|
|
42
|
+
const base = runtime.getProvider(id);
|
|
43
|
+
if (!base) continue;
|
|
44
|
+
const provider = withAstraModel(base);
|
|
45
|
+
if (provider !== base) runtime.registerNativeProvider(provider);
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/settings-popup.tsx
CHANGED
|
@@ -203,6 +203,7 @@ export type PopupProps = {
|
|
|
203
203
|
models: readonly Model<any>[];
|
|
204
204
|
modelQuery?: string;
|
|
205
205
|
modelSearchFocused?: boolean;
|
|
206
|
+
modelRefreshing?: boolean;
|
|
206
207
|
onSearchChange: (value: string) => void;
|
|
207
208
|
onModelSearchChange?: (value: string) => void;
|
|
208
209
|
onSelectModel: (model: Model<any>) => void;
|
|
@@ -226,6 +227,7 @@ export function SettingsPopup({
|
|
|
226
227
|
models,
|
|
227
228
|
modelQuery = "",
|
|
228
229
|
modelSearchFocused = false,
|
|
230
|
+
modelRefreshing = false,
|
|
229
231
|
onSearchChange,
|
|
230
232
|
onModelSearchChange = () => {},
|
|
231
233
|
onSelectModel,
|
|
@@ -378,7 +380,11 @@ export function SettingsPopup({
|
|
|
378
380
|
/>}
|
|
379
381
|
</box> : null}
|
|
380
382
|
{layout.footerHeight ? <text
|
|
381
|
-
content=
|
|
383
|
+
content={modelRefreshing
|
|
384
|
+
? layout.narrow ? "Refreshing models… esc" : "Refreshing models… esc back"
|
|
385
|
+
: layout.narrow
|
|
386
|
+
? "/ search ↑↓ enter select r refresh esc"
|
|
387
|
+
: "/ search ↑↓ move enter select r refresh esc back"}
|
|
382
388
|
fg={theme.dim}
|
|
383
389
|
bg={theme.popupBg}
|
|
384
390
|
wrapMode="none"
|