@fickydev/pigent 0.1.8 → 0.1.9

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 CHANGED
@@ -45,6 +45,7 @@
45
45
  - Added session-scoped model and thinking level overrides with `/model` and `/thinking` Telegram commands.
46
46
  - Planned Telegram inline-button model and thinking level pickers backed by configured model choices.
47
47
  - Added Telegram inline-button pickers for `/model` and `/thinking`, callback handling, and bot command menu registration.
48
+ - Changed `/model` picker to use Pi's currently available models automatically when explicit `modelChoices` are not configured.
48
49
  - Kept daemon process alive after startup so CLI runs do not exit after `pigent ready`.
49
50
 
50
51
  ### Changed
package/PLAN.md CHANGED
@@ -214,7 +214,7 @@ Telegram should also provide inline-button pickers so non-technical users can ch
214
214
  - after selection, bot should answer the callback and update or send a confirmation message
215
215
  - daemon startup should register Telegram bot commands through `setMyCommands` so users see Pigent commands in the Telegram command menu
216
216
 
217
- Model choices should be explicitly configured instead of exposing every Pi model blindly:
217
+ Model choices default to Pi's currently available models from `ModelRegistry.getAvailable()`. Operators may optionally configure explicit choices to curate or rename the button list:
218
218
 
219
219
  ```yaml
220
220
  modelChoices:
@@ -226,7 +226,7 @@ modelChoices:
226
226
  label: GPT-4.1
227
227
  ```
228
228
 
229
- The command and callback handlers should validate agent access and model availability through a Pi model resolver. They must not reveal API keys, auth values, raw environment variables, or provider secrets.
229
+ The command and callback handlers should validate agent access and model availability through a Pi model resolver. They must not reveal API keys, auth values, raw environment variables, or provider secrets. The available-model fallback should use Pi auth presence checks only and never print secret values.
230
230
 
231
231
  ## Prompt Composition
232
232
 
package/TODO.md CHANGED
@@ -184,9 +184,10 @@
184
184
  - [x] `/thinking` show thinking level for default chat agent session
185
185
  - [x] `/thinking <level>` set thinking level for default chat agent session
186
186
  - [x] `/thinking default` clear thinking level for default chat agent session
187
- - [x] `/model` inline button picker for configured model choices
187
+ - [x] `/model` inline button picker for currently available Pi models
188
188
  - [x] `/thinking` inline button picker
189
189
  - [x] Handle model picker callback and persist selected session model
190
+ - [x] Fallback to Pi `ModelRegistry.getAvailable()` when `modelChoices` is empty
190
191
  - [x] Handle thinking picker callback and persist selected session thinking level
191
192
  - [x] Add `Use default` model/thinking buttons
192
193
  - [ ] `/model <agentId> <provider/modelId>` set model for an explicit agent session
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fickydev/pigent",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Autonomous multi-agent daemon using Pi as core execution engine.",
package/pigent.yaml CHANGED
@@ -1,7 +1,8 @@
1
1
  telegramChats: []
2
2
  modelChoices: []
3
3
 
4
- # Add choices to show buttons for /model.
4
+ # Optional. If empty, /model uses Pi's currently available models automatically.
5
+ # Add choices to curate or rename buttons for /model.
5
6
  #
6
7
  # modelChoices:
7
8
  # - id: anthropic/claude-sonnet-4-5
@@ -1,6 +1,7 @@
1
1
  import type { InboundMessage, InlineKeyboardButton } from "../channels/types";
2
2
  import type { ModelChoiceConfig } from "../config/schemas";
3
3
  import type { Repositories } from "../db/repositories";
4
+ import { getAvailableModelChoices } from "../pi/PiAvailableModels";
4
5
  import { isValidModelRef } from "../pi/PiModelResolver";
5
6
  import type { AgentRegistry } from "./AgentRegistry";
6
7
 
@@ -96,16 +97,18 @@ export class BotCommandHandler {
96
97
  const value = args.join(" ").trim();
97
98
 
98
99
  if (!value) {
100
+ const choices = await this.loadModelChoices();
101
+
99
102
  return {
100
103
  handled: true,
101
104
  text: [
102
105
  `Agent: ${sessionResult.agentId}`,
103
106
  `Session model: ${sessionResult.session.model ?? "default"}`,
104
- this.modelChoices.length > 0
107
+ choices.length > 0
105
108
  ? "Choose a model:"
106
- : "No model choices configured. Use /model <provider/modelId> to set manually.",
109
+ : "No available Pi models found. Configure provider auth or use /model <provider/modelId> manually.",
107
110
  ].join("\n"),
108
- inlineKeyboard: this.modelChoices.length > 0 ? this.modelKeyboard() : undefined,
111
+ inlineKeyboard: choices.length > 0 ? this.modelKeyboard(choices) : undefined,
109
112
  };
110
113
  }
111
114
 
@@ -156,12 +159,13 @@ export class BotCommandHandler {
156
159
  const sessionResult = await this.getDefaultSession(message);
157
160
  if (!sessionResult.ok) return sessionResult.message;
158
161
 
162
+ const choices = await this.loadModelChoices();
159
163
  const choiceIndex = Number(message.text.slice("/model:set:".length));
160
- if (!Number.isSafeInteger(choiceIndex) || choiceIndex < 0 || choiceIndex >= this.modelChoices.length) {
164
+ if (!Number.isSafeInteger(choiceIndex) || choiceIndex < 0 || choiceIndex >= choices.length) {
161
165
  return "Unknown model choice.";
162
166
  }
163
167
 
164
- const choice = this.modelChoices[choiceIndex];
168
+ const choice = choices[choiceIndex];
165
169
  const session = await this.repositories.sessions.updateModel(sessionResult.session.id, choice.id);
166
170
  return `Session model for ${session.agentId} set to ${choice.label} (${session.model}).`;
167
171
  }
@@ -195,13 +199,19 @@ export class BotCommandHandler {
195
199
  return `Session thinking level cleared for ${session.agentId}. Current: ${session.thinkingLevel ?? "default"}`;
196
200
  }
197
201
 
198
- private modelKeyboard(): InlineKeyboardButton[][] {
202
+ private async loadModelChoices(): Promise<ModelChoiceConfig[]> {
203
+ if (this.modelChoices.length > 0) return this.modelChoices;
204
+
205
+ return getAvailableModelChoices();
206
+ }
207
+
208
+ private modelKeyboard(choices: ModelChoiceConfig[]): InlineKeyboardButton[][] {
199
209
  const rows = chunk(
200
- this.modelChoices.map((choice, index) => ({
210
+ choices.map((choice, index) => ({
201
211
  text: choice.label,
202
212
  callbackData: `model:set:${index}`,
203
213
  })),
204
- 2,
214
+ 1,
205
215
  );
206
216
 
207
217
  rows.push([{ text: "Use default", callbackData: "model:default" }]);
@@ -0,0 +1,23 @@
1
+ import { AuthStorage, ModelRegistry } from "@earendil-works/pi-coding-agent";
2
+ import type { Model } from "@earendil-works/pi-ai";
3
+
4
+ export type AvailableModelChoice = {
5
+ id: string;
6
+ label: string;
7
+ };
8
+
9
+ export async function getAvailableModelChoices(): Promise<AvailableModelChoice[]> {
10
+ const authStorage = AuthStorage.create();
11
+ const modelRegistry = ModelRegistry.create(authStorage);
12
+ const models = await modelRegistry.getAvailable();
13
+
14
+ return models.map(toChoice).sort((a, b) => a.label.localeCompare(b.label));
15
+ }
16
+
17
+ function toChoice(model: Model<any>): AvailableModelChoice {
18
+ const modelWithName = model as Model<any> & { name?: string };
19
+ const id = `${model.provider}/${model.id}`;
20
+ const label = modelWithName.name ? `${modelWithName.name} (${id})` : id;
21
+
22
+ return { id, label };
23
+ }