pi-ui-extend 1.0.30 → 1.0.31

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/dist/app/app.d.ts CHANGED
@@ -86,6 +86,7 @@ export declare class PiUiExtendApp {
86
86
  private setInput;
87
87
  private resetInputAfterProgrammaticEdit;
88
88
  private restoreTabInputState;
89
+ private restoreEditorSnapshot;
89
90
  private clearPersistedInputDraft;
90
91
  private insertVoiceTranscript;
91
92
  private pasteInternalClipboard;
package/dist/app/app.js CHANGED
@@ -345,6 +345,13 @@ export class PiUiExtendApp {
345
345
  restoreSessionStatus: () => this.restoreSessionStatus(),
346
346
  setInput: (value) => this.setInput(value),
347
347
  getInput: () => this.input,
348
+ setInputState: (state) => this.restoreTabInputState(state),
349
+ getInputState: () => this.inputEditor.draftState,
350
+ setInputSnapshot: (snapshot) => this.restoreEditorSnapshot(snapshot),
351
+ getInputSnapshot: () => ({
352
+ text: this.inputEditor.expandedText,
353
+ images: this.inputEditor.images.map((image) => ({ ...image })),
354
+ }),
348
355
  get entries() { return app.entries; },
349
356
  deleteConversationEntry: (entryId) => this.conversationViewport.deleteEntry(entryId),
350
357
  });
@@ -982,6 +989,11 @@ export class PiUiExtendApp {
982
989
  this.inputEditor.restoreDraftState(state);
983
990
  this.autocompleteController.dispose();
984
991
  }
992
+ restoreEditorSnapshot(snapshot) {
993
+ this.restoreTabInputState({ text: snapshot.text, cursor: snapshot.text.length });
994
+ for (const image of snapshot.images)
995
+ this.inputEditor.attachImage(image.data, image.mimeType);
996
+ }
985
997
  async clearPersistedInputDraft() {
986
998
  await this.tabsController.setInputStateForTab(this.tabsController.activeInputTabId(), { text: "", cursor: 0 });
987
999
  }
@@ -1,6 +1,7 @@
1
1
  import { type Theme } from "../../theme.js";
2
2
  import { type ToastKind, type ToastNotifier } from "../../ui.js";
3
- import type { Entry, ExtensionInputMouseEvent, ExtensionWidgetRegistration, ExtensionWidgetTheme, PixExtensionUIContext, PixMenuController, WidgetPlacement, WidgetTuiHandle } from "../types.js";
3
+ import type { InputEditorDraftState } from "../../input-editor.js";
4
+ import type { Entry, ExtensionInputMouseEvent, ExtensionWidgetRegistration, ExtensionWidgetTheme, PixExtensionUIContext, PixEditorSnapshot, PixMenuController, WidgetPlacement, WidgetTuiHandle } from "../types.js";
4
5
  export type ExtensionTerminalInputResult = {
5
6
  consume: boolean;
6
7
  data?: string;
@@ -21,6 +22,10 @@ export type ExtensionUiControllerHost = {
21
22
  restoreSessionStatus(): void;
22
23
  setInput(value: string): void;
23
24
  getInput(): string;
25
+ setInputState(state: InputEditorDraftState): void;
26
+ getInputState(): InputEditorDraftState;
27
+ setInputSnapshot(snapshot: PixEditorSnapshot): void;
28
+ getInputSnapshot(): PixEditorSnapshot;
24
29
  readonly entries: readonly Entry[];
25
30
  deleteConversationEntry(entryId: string): void;
26
31
  };
@@ -242,16 +242,25 @@ export class ExtensionUiController {
242
242
  pasteToEditor: (text) => {
243
243
  if (!this.isScopeActive(contextScopeKey))
244
244
  return;
245
- this.host.setInput(text);
245
+ this.host.setInputState({ text, cursor: text.length });
246
246
  renderIfRunning();
247
247
  },
248
248
  setEditorText: (text) => {
249
249
  if (!this.isScopeActive(contextScopeKey))
250
250
  return;
251
- this.host.setInput(text);
251
+ this.host.setInputState({ text, cursor: text.length });
252
252
  renderIfRunning();
253
253
  },
254
254
  getEditorText: () => this.isScopeActive(contextScopeKey) ? this.host.getInput() : "",
255
+ getEditorSnapshot: () => this.isScopeActive(contextScopeKey)
256
+ ? this.host.getInputSnapshot()
257
+ : { text: "", images: [] },
258
+ setEditorSnapshot: (snapshot) => {
259
+ if (!this.isScopeActive(contextScopeKey))
260
+ return;
261
+ this.host.setInputSnapshot(snapshot);
262
+ renderIfRunning();
263
+ },
255
264
  editor: async (title, prefill) => await this.editorDialog(title, prefill, contextScopeKey),
256
265
  addAutocompleteProvider: () => undefined,
257
266
  setEditorComponent: () => undefined,
@@ -396,7 +405,7 @@ export class ExtensionUiController {
396
405
  const scopeKey = this.normalizeScopeKey(options.scopeKey);
397
406
  if (this.activeCustomUis.has(scopeKey))
398
407
  throw new Error("Another extension custom UI is already active.");
399
- const savedInput = options.savedInput ?? (this.isScopeActive(scopeKey) ? this.host.getInput() : undefined);
408
+ const savedInput = options.savedInput ?? (this.isScopeActive(scopeKey) ? this.host.getInputState() : undefined);
400
409
  return await new Promise((resolve, reject) => {
401
410
  const active = {
402
411
  key: CUSTOM_UI_WIDGET_KEY,
@@ -408,7 +417,7 @@ export class ExtensionUiController {
408
417
  };
409
418
  this.activeCustomUis.set(scopeKey, active);
410
419
  if (options.editorInput !== undefined)
411
- this.host.setInput(options.editorInput);
420
+ this.host.setInputState({ text: options.editorInput, cursor: options.editorInput.length });
412
421
  const done = (value) => {
413
422
  if (active.settled || this.activeCustomUis.get(scopeKey) !== active)
414
423
  return;
@@ -449,8 +458,8 @@ export class ExtensionUiController {
449
458
  return;
450
459
  active.settled = true;
451
460
  this.activeCustomUis.delete(active.scopeKey);
452
- if (this.isScopeActive(active.scopeKey) && active.savedInput !== undefined && this.host.getInput() !== active.savedInput) {
453
- this.host.setInput(active.savedInput);
461
+ if (this.isScopeActive(active.scopeKey) && active.savedInput !== undefined) {
462
+ this.host.setInputState(active.savedInput);
454
463
  }
455
464
  try {
456
465
  active.component?.dispose?.();
@@ -477,7 +486,7 @@ export class ExtensionUiController {
477
486
  return undefined;
478
487
  const active = this.activeCustomUis.get(scopeKey);
479
488
  if (active && active.savedInput === undefined)
480
- active.savedInput = this.host.getInput();
489
+ active.savedInput = this.host.getInputState();
481
490
  return active;
482
491
  }
483
492
  isScopeActive(scopeKey) {
@@ -437,6 +437,11 @@ export type AboveInputRenderer = {
437
437
  set(key: string, content: ExtensionWidgetContent): void;
438
438
  clear(key: string): void;
439
439
  };
440
+ /** Attachment-aware editor content with virtual paste/image tags resolved. */
441
+ export type PixEditorSnapshot = {
442
+ text: string;
443
+ images: readonly ImageContent[];
444
+ };
440
445
  export type PixExtensionUIContext = Omit<ExtensionUIContext, "notify"> & {
441
446
  notify(message: string, type?: ToastKind): void;
442
447
  toast: ToastNotifier;
@@ -444,6 +449,8 @@ export type PixExtensionUIContext = Omit<ExtensionUIContext, "notify"> & {
444
449
  renderAboveInput(key: string, content: ExtensionWidgetContent): void;
445
450
  showMenu<T>(items: readonly PixMenuItem<T>[], options: PixMenuOptions): Promise<T | undefined>;
446
451
  menu: PixMenuController;
452
+ getEditorSnapshot(): PixEditorSnapshot;
453
+ setEditorSnapshot(snapshot: PixEditorSnapshot): void;
447
454
  };
448
455
  export type WidgetTuiHandle = {
449
456
  requestRender(force?: boolean): void;
@@ -20,7 +20,8 @@ export default function questionExtension(pi) {
20
20
  const selections = await runQuestionnaire(questions, ctx);
21
21
  if (selections == null)
22
22
  return createQuestionToolResult(createCanceledQuestionResult("user_canceled"), questions);
23
- return createQuestionToolResult(createSuccessfulQuestionResult(questions, selections), questions);
23
+ const images = selections.flatMap((selection) => "customText" in selection ? selection.images ?? [] : []);
24
+ return createQuestionToolResult(createSuccessfulQuestionResult(questions, selections), questions, images);
24
25
  },
25
26
  });
26
27
  }
@@ -1,6 +1,6 @@
1
- import type { CanceledQuestionResult, NormalizedQuestion, QuestionResultDetails, QuestionSelection, QuestionToolResult, SuccessfulQuestionResult } from "./types.js";
1
+ import type { CanceledQuestionResult, NormalizedQuestion, QuestionImageContent, QuestionResultDetails, QuestionSelection, QuestionToolResult, SuccessfulQuestionResult } from "./types.js";
2
2
  export declare function createSuccessfulQuestionResult(questions: NormalizedQuestion[], selections: QuestionSelection[]): SuccessfulQuestionResult;
3
3
  export declare function createCanceledQuestionResult(reason: CanceledQuestionResult["reason"], questions?: NormalizedQuestion[]): CanceledQuestionResult;
4
4
  export declare function createFallbackPrompt(questions: NormalizedQuestion[]): string;
5
5
  export declare function summarizeQuestionResult(result: QuestionResultDetails, questions?: NormalizedQuestion[]): string;
6
- export declare function createQuestionToolResult(details: QuestionResultDetails, questions?: NormalizedQuestion[]): QuestionToolResult;
6
+ export declare function createQuestionToolResult(details: QuestionResultDetails, questions?: NormalizedQuestion[], images?: QuestionImageContent[]): QuestionToolResult;
@@ -1,4 +1,7 @@
1
1
  import { throwInvalid } from "./contract.js";
2
+ function formatAttachedImages(count) {
3
+ return `${count} image${count === 1 ? "" : "s"} attached`;
4
+ }
2
5
  export function createSuccessfulQuestionResult(questions, selections) {
3
6
  const selectionsById = new Map(selections.map((selection) => [selection.id, selection]));
4
7
  return {
@@ -8,13 +11,16 @@ export function createSuccessfulQuestionResult(questions, selections) {
8
11
  throwInvalid(`Missing answer for question "${question.id}".`, "Retry after collecting one answer for each normalized question.");
9
12
  if ("customText" in selection) {
10
13
  const customText = selection.customText.trim();
11
- if (!customText)
12
- throwInvalid(`Custom Answer for question "${question.id}" is empty after trimming.`, "Retry with non-empty Custom Answer text or choose a predefined Choice.");
14
+ const imageCount = selection.images?.length ?? 0;
15
+ if (!customText && imageCount === 0)
16
+ throwInvalid(`Custom Answer for question "${question.id}" is empty after trimming.`, "Retry with non-empty Custom Answer text, an image, or choose a predefined Choice.");
17
+ const label = customText || formatAttachedImages(imageCount);
13
18
  return {
14
19
  id: question.id,
15
- value: customText,
16
- label: customText,
20
+ value: customText || label,
21
+ label,
17
22
  wasCustom: true,
23
+ ...(imageCount > 0 ? { imageCount } : {}),
18
24
  };
19
25
  }
20
26
  const choiceIndex = question.choices.findIndex((choice) => choice.value === selection.choiceValue);
@@ -68,17 +74,23 @@ export function summarizeQuestionResult(result, questions = []) {
68
74
  const questionLabels = new Map(questions.map((question) => [question.id, question.label]));
69
75
  return `question answers: ${result.answers.map((answer) => {
70
76
  const questionLabel = questionLabels.get(answer.id) ?? answer.id;
71
- if (answer.wasCustom)
72
- return `${questionLabel}: ${answer.label} (custom answer)`;
77
+ if (answer.wasCustom) {
78
+ const imageLabel = answer.imageCount ? formatAttachedImages(answer.imageCount) : undefined;
79
+ const imageSuffix = imageLabel && answer.label !== imageLabel ? `; ${imageLabel}` : "";
80
+ return `${questionLabel}: ${answer.label} (custom answer${imageSuffix})`;
81
+ }
73
82
  return `${questionLabel}: ${answer.label} (choice ${answer.index})`;
74
83
  }).join("; ")}.`;
75
84
  }
76
- export function createQuestionToolResult(details, questions = []) {
85
+ export function createQuestionToolResult(details, questions = [], images = []) {
77
86
  return {
78
- content: [{
87
+ content: [
88
+ {
79
89
  type: "text",
80
90
  text: summarizeQuestionResult(details, questions),
81
- }],
91
+ },
92
+ ...images.map((image) => ({ ...image })),
93
+ ],
82
94
  details,
83
95
  };
84
96
  }
@@ -97,6 +97,9 @@ function formatHeader(title, width) {
97
97
  function clampIndex(index, length) {
98
98
  return Math.max(0, Math.min(Math.max(0, length - 1), index));
99
99
  }
100
+ function formatAttachedImages(count) {
101
+ return `${count} image${count === 1 ? "" : "s"} attached`;
102
+ }
100
103
  export async function runQuestionnaire(questions, ctx) {
101
104
  return ctx.ui.custom((tui, theme, _keybindings, done) => {
102
105
  const selections = new Map();
@@ -104,6 +107,7 @@ export async function runQuestionnaire(questions, ctx) {
104
107
  const reviewSubmitIndex = questions.length;
105
108
  const pixCapabilities = tui.pix;
106
109
  const usesSharedEditor = Boolean(pixCapabilities?.delegatedEditorInput && ctx.ui.setEditorText && ctx.ui.getEditorText);
110
+ const usesEditorSnapshots = Boolean(usesSharedEditor && ctx.ui.setEditorSnapshot && ctx.ui.getEditorSnapshot);
107
111
  let questionIndex = 0;
108
112
  let selectedChoiceIndex = 0;
109
113
  let selectedReviewIndex = reviewSubmitIndex;
@@ -129,21 +133,45 @@ export async function runQuestionnaire(questions, ctx) {
129
133
  function sharedEditorText() {
130
134
  if (usesSharedEditor)
131
135
  return ctx.ui.getEditorText?.() ?? "";
132
- return customDrafts.get(currentQuestion().id) ?? "";
136
+ return customDrafts.get(currentQuestion().id)?.text ?? "";
137
+ }
138
+ function sharedEditorSnapshot() {
139
+ if (usesEditorSnapshots) {
140
+ const snapshot = ctx.ui.getEditorSnapshot?.();
141
+ if (snapshot)
142
+ return cloneEditorSnapshot(snapshot);
143
+ }
144
+ return { text: sharedEditorText(), images: [] };
145
+ }
146
+ function cloneEditorSnapshot(snapshot) {
147
+ return {
148
+ text: snapshot.text,
149
+ images: snapshot.images.map((image) => ({ ...image })),
150
+ };
151
+ }
152
+ function setSharedEditorSnapshot(snapshot) {
153
+ const cloned = cloneEditorSnapshot(snapshot);
154
+ customDrafts.set(currentQuestion().id, cloned);
155
+ if (usesEditorSnapshots)
156
+ ctx.ui.setEditorSnapshot?.(cloned);
157
+ else if (usesSharedEditor)
158
+ ctx.ui.setEditorText?.(cloned.text);
133
159
  }
134
160
  function setSharedEditorText(text) {
135
- customDrafts.set(currentQuestion().id, text);
161
+ customDrafts.set(currentQuestion().id, { text, images: [] });
136
162
  if (usesSharedEditor)
137
163
  ctx.ui.setEditorText?.(text);
138
164
  }
139
165
  function clearSharedEditorText() {
140
- if (usesSharedEditor)
166
+ if (usesEditorSnapshots)
167
+ ctx.ui.setEditorSnapshot?.({ text: "", images: [] });
168
+ else if (usesSharedEditor)
141
169
  ctx.ui.setEditorText?.("");
142
170
  }
143
171
  function captureCustomDraft() {
144
172
  if (mode !== "custom")
145
173
  return;
146
- customDrafts.set(currentQuestion().id, sharedEditorText());
174
+ customDrafts.set(currentQuestion().id, sharedEditorSnapshot());
147
175
  }
148
176
  function getCompleteSelections() {
149
177
  const orderedSelections = [];
@@ -172,8 +200,12 @@ export async function runQuestionnaire(questions, ctx) {
172
200
  submitCompleteSelections();
173
201
  }
174
202
  function formatReviewAnswerLabel(question, selection) {
175
- if ("customText" in selection)
176
- return `${CUSTOM_ANSWER_LABEL}: ${selection.customText}`;
203
+ if ("customText" in selection) {
204
+ const imageCount = selection.images?.length ?? 0;
205
+ const text = selection.customText || formatAttachedImages(imageCount);
206
+ const imageSuffix = selection.customText && imageCount > 0 ? ` (+${formatAttachedImages(imageCount)})` : "";
207
+ return `${CUSTOM_ANSWER_LABEL}: ${text}${imageSuffix}`;
208
+ }
177
209
  return question.choices.find((choice) => choice.value === selection.choiceValue)?.label ?? "Unknown";
178
210
  }
179
211
  function selectionIndexForQuestion(question) {
@@ -250,11 +282,13 @@ export async function runQuestionnaire(questions, ctx) {
250
282
  function enterCustomMode() {
251
283
  const question = currentQuestion();
252
284
  const existing = selections.get(question.id);
253
- const prefill = existing && "customText" in existing ? existing.customText : customDrafts.get(question.id) ?? "";
285
+ const prefill = existing && "customText" in existing
286
+ ? { text: existing.customText, images: existing.images ?? [] }
287
+ : customDrafts.get(question.id) ?? { text: "", images: [] };
254
288
  mode = "custom";
255
289
  selectedChoiceIndex = customAnswerIndex(question);
256
290
  customError = undefined;
257
- setSharedEditorText(prefill);
291
+ setSharedEditorSnapshot(prefill);
258
292
  refresh();
259
293
  }
260
294
  function selectChoice(index) {
@@ -271,15 +305,19 @@ export async function runQuestionnaire(questions, ctx) {
271
305
  enterCustomMode();
272
306
  }
273
307
  function submitCustomAnswer() {
274
- const text = sharedEditorText();
275
- const trimmed = text.trim();
276
- if (!trimmed) {
308
+ const snapshot = sharedEditorSnapshot();
309
+ const trimmed = snapshot.text.trim();
310
+ if (!trimmed && snapshot.images.length === 0) {
277
311
  customError = "Custom Answer cannot be empty.";
278
312
  refresh();
279
313
  return;
280
314
  }
281
315
  const question = currentQuestion();
282
- selections.set(question.id, { id: question.id, customText: trimmed });
316
+ selections.set(question.id, {
317
+ id: question.id,
318
+ customText: trimmed,
319
+ ...(snapshot.images.length > 0 ? { images: snapshot.images } : {}),
320
+ });
283
321
  customDrafts.delete(question.id);
284
322
  clearSharedEditorText();
285
323
  advanceAfterAnswer();
@@ -30,6 +30,7 @@ export interface PredefinedQuestionSelection {
30
30
  export interface CustomQuestionSelection {
31
31
  id: string;
32
32
  customText: string;
33
+ images?: QuestionImageContent[];
33
34
  }
34
35
  export type QuestionSelection = PredefinedQuestionSelection | CustomQuestionSelection;
35
36
  export interface QuestionAnswer {
@@ -38,6 +39,7 @@ export interface QuestionAnswer {
38
39
  label: string;
39
40
  wasCustom: boolean;
40
41
  index?: number;
42
+ imageCount?: number;
41
43
  }
42
44
  export interface SuccessfulQuestionResult {
43
45
  answers: QuestionAnswer[];
@@ -54,8 +56,17 @@ export interface TextContent {
54
56
  type: "text";
55
57
  text: string;
56
58
  }
59
+ export interface QuestionImageContent {
60
+ type: "image";
61
+ data: string;
62
+ mimeType: string;
63
+ }
64
+ export interface QuestionEditorSnapshot {
65
+ text: string;
66
+ images: QuestionImageContent[];
67
+ }
57
68
  export interface QuestionToolResult {
58
- content: TextContent[];
69
+ content: Array<TextContent | QuestionImageContent>;
59
70
  details: QuestionResultDetails;
60
71
  }
61
72
  export interface QuestionUiContext {
@@ -64,6 +75,8 @@ export interface QuestionUiContext {
64
75
  custom<T>(factory: (tui: QuestionTui, theme: QuestionTheme, keybindings: unknown, done: (value: T) => void) => QuestionComponent): Promise<T | undefined>;
65
76
  setEditorText?(text: string): void;
66
77
  getEditorText?(): string;
78
+ setEditorSnapshot?(snapshot: QuestionEditorSnapshot): void;
79
+ getEditorSnapshot?(): QuestionEditorSnapshot;
67
80
  notify?(message: string, level: "info" | "warning" | "error"): void;
68
81
  };
69
82
  }
package/dist/sdk.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { isToastKind, Toast, TOAST_KINDS } from "./ui.js";
2
2
  export type { ToastEntry, ToastKind, ToastNotifier, ToastState } from "./ui.js";
3
- export type { AboveInputRenderer, ExtensionWidgetComponent, ExtensionWidgetContent, ExtensionWidgetFactory, PixMenuController, PixMenuItem, PixMenuOptions, PixMenuSelectOptions, PixMenuVariant, PixExtensionUIContext, WidgetPlacement, WidgetTuiHandle, } from "./app/types.js";
3
+ export type { AboveInputRenderer, ExtensionWidgetComponent, ExtensionWidgetContent, ExtensionWidgetFactory, PixMenuController, PixMenuItem, PixMenuOptions, PixMenuSelectOptions, PixMenuVariant, PixExtensionUIContext, PixEditorSnapshot, WidgetPlacement, WidgetTuiHandle, } from "./app/types.js";
@@ -370,7 +370,7 @@ The canonical use case is an **`oracle`** role that consults a flagship model fr
370
370
  "description": "Cross-provider second opinion: consult a flagship from a different provider than the parent to pressure-test a hard decision. Read-only; advise, do not edit.",
371
371
  "model": "openai-codex/gpt-5.6-sol",
372
372
  "fallbackModels": ["zai/glm-5.3"],
373
- "thinking": "xhigh",
373
+ "thinking": "max",
374
374
  "modelByParent": {
375
375
  "zai/*": { "model": "openai-codex/gpt-5.6-sol", "fallbackModels": ["zai/glm-5.3"] },
376
376
  "openai-codex/*": "zai/glm-5.3",
@@ -244,7 +244,7 @@
244
244
  "description": "Oracle: cross-provider flagship second opinion for hard or high-stakes uncertainty. Use sparingly to pressure-test architecture, plans, root-cause hypotheses, risk/security calls, or final recommendations when independent disagreement is valuable. Read-only; advise, do not edit.",
245
245
  "model": "openai-codex/gpt-5.6-sol",
246
246
  "fallbackModels": ["zai/glm-5.3"],
247
- "thinking": "xhigh",
247
+ "thinking": "max",
248
248
  "tools": ["read", "grep", "bash"],
249
249
  "modelByParent": {
250
250
  "zai/*": { "model": "openai-codex/gpt-5.6-sol", "fallbackModels": ["zai/glm-5.3"] },
@@ -250,7 +250,7 @@ const BUILTIN_CONFIG: SubagentConfig = {
250
250
  "antigravity/*": { model: "zai/glm-5.3", fallbackModels: ["openai-codex/gpt-5.6-sol"] },
251
251
  "anthropic/*": { model: "openai-codex/gpt-5.6-sol", fallbackModels: ["zai/glm-5.3"] },
252
252
  },
253
- thinking: "xhigh",
253
+ thinking: "max",
254
254
  tools: ["read", "grep", "bash"],
255
255
  promptAppend: "You are an oracle: a flagship model from a different provider giving a second opinion to the parent agent. Give a concise, decisive recommendation with key tradeoffs and risks. Disagree when warranted; do not rubber-stamp. Do not edit unless explicitly asked.",
256
256
  },
@@ -341,7 +341,7 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
341
341
  "description": "Oracle: cross-provider flagship second opinion for hard or high-stakes uncertainty. Use sparingly to pressure-test architecture, plans, root-cause hypotheses, risk/security calls, or final recommendations when independent disagreement is valuable. Read-only; advise, do not edit.",
342
342
  "model": "openai-codex/gpt-5.6-sol",
343
343
  "fallbackModels": ["zai/glm-5.3"],
344
- "thinking": "xhigh",
344
+ "thinking": "max",
345
345
  "tools": ["read", "grep", "bash"],
346
346
  "modelByParent": {
347
347
  "zai/*": { "model": "openai-codex/gpt-5.6-sol", "fallbackModels": ["zai/glm-5.3"] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ui-extend",
3
- "version": "1.0.30",
3
+ "version": "1.0.31",
4
4
  "description": "Pix: a workspace-first terminal UI for Pi with tabs, readable tool activity, voice input, and bundled agent tools.",
5
5
  "private": false,
6
6
  "repository": {