pi-ui-extend 0.1.18 → 0.1.20

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.
Files changed (70) hide show
  1. package/dist/app/app.js +8 -6
  2. package/dist/app/constants.d.ts +1 -0
  3. package/dist/app/constants.js +2 -1
  4. package/dist/app/input/voice-controller.js +16 -12
  5. package/dist/app/popup/popup-menu-controller.d.ts +1 -5
  6. package/dist/app/popup/popup-menu-controller.js +7 -8
  7. package/dist/app/process.js +7 -0
  8. package/dist/app/rendering/conversation-entry-renderer.js +17 -16
  9. package/dist/app/rendering/conversation-viewport.js +4 -35
  10. package/dist/app/rendering/editor-layout-renderer.d.ts +5 -1
  11. package/dist/app/rendering/editor-layout-renderer.js +29 -20
  12. package/dist/app/rendering/popup-menu-renderer.d.ts +1 -5
  13. package/dist/app/rendering/popup-menu-renderer.js +24 -34
  14. package/dist/app/rendering/render-controller.d.ts +2 -0
  15. package/dist/app/rendering/render-controller.js +40 -49
  16. package/dist/app/rendering/render-text.js +2 -2
  17. package/dist/app/rendering/status-line-renderer.d.ts +2 -0
  18. package/dist/app/rendering/status-line-renderer.js +75 -29
  19. package/dist/app/rendering/tab-line-renderer.js +3 -3
  20. package/dist/app/runtime.js +29 -3
  21. package/dist/app/screen/file-link-opener.d.ts +2 -0
  22. package/dist/app/screen/file-link-opener.js +84 -17
  23. package/dist/app/screen/mouse-controller.d.ts +1 -3
  24. package/dist/app/screen/mouse-controller.js +14 -14
  25. package/dist/app/screen/screen-styler.js +1 -1
  26. package/dist/app/session/lazy-session-manager.d.ts +1 -1
  27. package/dist/app/session/lazy-session-manager.js +64 -52
  28. package/dist/app/session/queued-message-controller.d.ts +6 -0
  29. package/dist/app/session/queued-message-controller.js +9 -1
  30. package/dist/app/session/queued-message-entries.d.ts +8 -0
  31. package/dist/app/session/queued-message-entries.js +41 -0
  32. package/dist/app/session/session-lifecycle-controller.d.ts +9 -1
  33. package/dist/app/session/session-lifecycle-controller.js +45 -11
  34. package/dist/app/session/tabs-controller.d.ts +11 -1
  35. package/dist/app/session/tabs-controller.js +197 -30
  36. package/dist/app/terminal/terminal-controller.d.ts +2 -0
  37. package/dist/app/terminal/terminal-controller.js +7 -5
  38. package/dist/app/types.d.ts +1 -1
  39. package/dist/schemas/pi-tools-suite-schema.d.ts +3 -0
  40. package/dist/schemas/pi-tools-suite-schema.js +3 -0
  41. package/dist/theme.d.ts +11 -0
  42. package/dist/theme.js +26 -4
  43. package/extensions/question/tui.ts +1 -1
  44. package/extensions/session-title/config.ts +3 -3
  45. package/extensions/session-title/index.ts +60 -5
  46. package/external/pi-tools-suite/README.md +3 -2
  47. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +1 -0
  48. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +1 -0
  49. package/external/pi-tools-suite/src/async-subagents/core/config.ts +0 -3
  50. package/external/pi-tools-suite/src/async-subagents/core/notifications.ts +64 -0
  51. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +1 -0
  52. package/external/pi-tools-suite/src/async-subagents/index.ts +54 -8
  53. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +4 -4
  54. package/external/pi-tools-suite/src/config.ts +13 -0
  55. package/external/pi-tools-suite/src/dcp/state.ts +9 -4
  56. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +5 -1
  57. package/external/pi-tools-suite/src/glm-coding-discipline/index.ts +683 -0
  58. package/external/pi-tools-suite/src/index.ts +1 -0
  59. package/external/pi-tools-suite/src/lib/lsp.ts +2 -5
  60. package/external/pi-tools-suite/src/lsp/_shared/config.ts +2 -0
  61. package/external/pi-tools-suite/src/lsp/_shared/types.ts +2 -0
  62. package/external/pi-tools-suite/src/lsp/manager.ts +15 -9
  63. package/external/pi-tools-suite/src/telegram-mirror/ipc.ts +1 -0
  64. package/external/pi-tools-suite/src/todo/index.ts +81 -4
  65. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +5 -0
  66. package/external/pi-tools-suite/src/tool-descriptions.ts +4 -4
  67. package/package.json +3 -14
  68. package/schemas/pi-tools-suite.json +19 -0
  69. package/apps/desktop-tauri/README.md +0 -103
  70. package/apps/desktop-tauri/bin/pix-desktop.mjs +0 -89
@@ -9,13 +9,16 @@ import { createStartupInfoMessage, isEmptyStartupSession } from "../cli/startup-
9
9
  import { tabPanelRows } from "../rendering/tab-line-renderer.js";
10
10
  const TAB_STATE_VERSION = 3;
11
11
  const MAX_RESTORED_TABS = 8;
12
+ const BACKGROUND_PREWARM_TAB_LIMIT = 2;
12
13
  const TAB_ATTENTION_BLINK_KEY = "tab-attention";
13
14
  const LOADING_TAB_TITLE_PATTERN = /^loading(?:…|\.\.\.)?$/iu;
14
15
  export class AppTabsController {
15
16
  host;
16
17
  tabItems = [];
17
18
  runtimesByTabId = new Map();
19
+ runtimeLoadsByTabId = new Map();
18
20
  runtimeSubscriptionsByTabId = new Map();
21
+ runtimeRefreshTimersByTabId = new Map();
19
22
  inputStatesByTabId = new Map();
20
23
  deferredUserMessagesByTabId = new Map();
21
24
  activeTabId;
@@ -24,6 +27,8 @@ export class AppTabsController {
24
27
  restored = false;
25
28
  retentionCleanupRunning = false;
26
29
  retentionCleanupScheduled = false;
30
+ prewarmScheduled = false;
31
+ prewarmRunning = false;
27
32
  constructor(host) {
28
33
  this.host = host;
29
34
  }
@@ -192,6 +197,7 @@ export class AppTabsController {
192
197
  this.settleStartupTabPlaceholders();
193
198
  await this.saveTabs();
194
199
  this.scheduleProjectSessionRetention();
200
+ this.scheduleTabPrewarm();
195
201
  return;
196
202
  }
197
203
  let restoredRuntime = runtime;
@@ -200,7 +206,7 @@ export class AppTabsController {
200
206
  this.host.render();
201
207
  try {
202
208
  restoredRuntime = await this.host.createRuntimeForSession(desiredPath);
203
- await this.host.activateRuntime(restoredRuntime);
209
+ await this.host.activateRuntime(restoredRuntime, { awaitExtensions: false });
204
210
  }
205
211
  catch {
206
212
  this.host.showToast("Could not restore the previous active tab", "warning");
@@ -224,6 +230,7 @@ export class AppTabsController {
224
230
  this.restoreInputState(this.activeTabId);
225
231
  await this.saveTabs();
226
232
  this.scheduleProjectSessionRetention();
233
+ this.scheduleTabPrewarm();
227
234
  }
228
235
  async openNewTab() {
229
236
  if (this.pendingActiveTabId) {
@@ -239,31 +246,72 @@ export class AppTabsController {
239
246
  this.syncActiveTabFromRuntime();
240
247
  this.storeActiveInputState();
241
248
  this.storeActiveDeferredUserMessages();
242
- this.host.setStatus("starting new tab");
243
- this.host.render();
244
- const newRuntime = await this.host.createRuntimeForNewSession();
245
- const existingTab = this.findTabForSession(newRuntime.session);
246
- const tab = existingTab ?? this.tabFromSession(newRuntime.session, { titlePlaceholder: "new" });
247
- if (!existingTab)
248
- this.tabItems.push(tab);
249
+ const previousTabId = this.activeTabId;
250
+ const previousRuntime = runtime;
251
+ const tab = {
252
+ id: createId("tab"),
253
+ title: "new",
254
+ titlePlaceholder: "new",
255
+ status: "active",
256
+ activity: "thinking",
257
+ };
258
+ this.tabItems.push(tab);
249
259
  this.activeTabId = tab.id;
250
260
  this.pendingActiveTabId = tab.id;
251
261
  this.clearTabAttention(tab);
252
- this.updateTabFromSession(tab, newRuntime.session);
253
- this.setRuntimeForTab(tab.id, newRuntime);
254
262
  this.restoreInputState(tab.id);
255
263
  this.host.closeMenusForTabSwitch?.();
264
+ this.host.resetSessionView();
265
+ this.restoreDeferredUserMessages(tab.id);
266
+ this.host.setSessionActivity("thinking");
267
+ this.host.setStatus("starting new tab");
268
+ this.host.render();
269
+ let newRuntime;
256
270
  try {
257
- await this.host.activateRuntime(newRuntime);
271
+ newRuntime = await this.host.createRuntimeForNewSession();
258
272
  }
259
- finally {
273
+ catch (error) {
260
274
  if (this.pendingActiveTabId === tab.id)
261
275
  this.pendingActiveTabId = undefined;
276
+ this.removeTab(tab.id);
277
+ this.activeTabId = previousTabId;
278
+ if (previousTabId)
279
+ this.restoreInputState(previousTabId);
280
+ this.host.closeMenusForTabSwitch?.();
281
+ this.host.resetSessionView();
282
+ if (previousTabId)
283
+ this.restoreDeferredUserMessages(previousTabId);
284
+ this.host.loadSessionHistory();
285
+ this.host.setSessionStatus(previousRuntime.session);
286
+ this.host.setSessionActivity(this.sessionActivity(previousRuntime.session));
287
+ this.host.render();
288
+ throw error;
289
+ }
290
+ const existingTab = this.findTabForSession(newRuntime.session);
291
+ const targetTab = existingTab && existingTab.id !== tab.id ? existingTab : tab;
292
+ if (targetTab !== tab)
293
+ this.removeTab(tab.id);
294
+ this.activeTabId = targetTab.id;
295
+ this.pendingActiveTabId = targetTab.id;
296
+ this.clearTabAttention(targetTab);
297
+ this.updateTabFromSession(targetTab, newRuntime.session);
298
+ this.setRuntimeForTab(targetTab.id, newRuntime);
299
+ this.restoreInputState(targetTab.id);
300
+ this.host.resetSessionView();
301
+ this.restoreDeferredUserMessages(targetTab.id);
302
+ this.host.setSessionActivity("thinking");
303
+ this.host.render();
304
+ try {
305
+ await this.host.activateRuntime(newRuntime, { awaitExtensions: false });
306
+ }
307
+ finally {
308
+ if (this.pendingActiveTabId === targetTab.id)
309
+ this.pendingActiveTabId = undefined;
262
310
  }
263
311
  void this.saveTabs();
264
312
  this.scheduleProjectSessionRetention();
265
313
  this.host.resetSessionView();
266
- this.restoreDeferredUserMessages(tab.id);
314
+ this.restoreDeferredUserMessages(targetTab.id);
267
315
  if (isEmptyStartupSession(newRuntime)) {
268
316
  this.host.addEntry({ id: createId("system"), kind: "system", text: createStartupInfoMessage(newRuntime) });
269
317
  }
@@ -306,27 +354,50 @@ export class AppTabsController {
306
354
  const previousRuntime = runtime;
307
355
  this.host.setStatus("opening session tab");
308
356
  this.host.render();
357
+ const tab = {
358
+ id: createId("tab"),
359
+ title: basename(resolvedSessionPath, extname(resolvedSessionPath)) || "loading",
360
+ titlePlaceholder: "loading",
361
+ status: "active",
362
+ activity: "thinking",
363
+ sessionPath: resolvedSessionPath,
364
+ };
365
+ this.tabItems.push(tab);
366
+ this.activeTabId = tab.id;
367
+ this.pendingActiveTabId = tab.id;
368
+ this.clearTabAttention(tab);
369
+ this.restoreInputState(tab.id);
370
+ this.host.closeMenusForTabSwitch?.();
371
+ this.host.resetSessionView();
372
+ this.restoreDeferredUserMessages(tab.id);
373
+ this.host.setSessionActivity("thinking");
374
+ this.host.render();
309
375
  let newRuntime;
310
376
  try {
311
377
  newRuntime = await this.host.createRuntimeForSession(resolvedSessionPath);
312
378
  }
313
379
  catch {
380
+ this.pendingActiveTabId = undefined;
381
+ this.removeTab(tab.id);
382
+ this.activeTabId = previousTabId;
383
+ if (previousTabId)
384
+ this.restoreInputState(previousTabId);
385
+ this.host.closeMenusForTabSwitch?.();
386
+ this.host.resetSessionView();
387
+ if (previousTabId)
388
+ this.restoreDeferredUserMessages(previousTabId);
389
+ this.host.loadSessionHistory();
314
390
  this.host.showToast("Could not open session tab", "warning");
315
391
  this.host.setSessionStatus(previousRuntime.session);
392
+ this.host.setSessionActivity(this.sessionActivity(previousRuntime.session));
316
393
  this.host.render();
317
394
  return false;
318
395
  }
319
- const tab = this.tabFromSession(newRuntime.session, { titlePlaceholder: "loading" });
320
- this.tabItems.push(tab);
321
- this.activeTabId = tab.id;
322
- this.pendingActiveTabId = tab.id;
323
- this.clearTabAttention(tab);
324
396
  this.updateTabFromSession(tab, newRuntime.session);
325
397
  this.setRuntimeForTab(tab.id, newRuntime);
326
- this.restoreInputState(tab.id);
327
- this.host.closeMenusForTabSwitch?.();
398
+ this.host.render();
328
399
  try {
329
- await this.host.activateRuntime(newRuntime);
400
+ await this.host.activateRuntime(newRuntime, { awaitExtensions: false });
330
401
  }
331
402
  catch {
332
403
  this.pendingActiveTabId = undefined;
@@ -337,7 +408,7 @@ export class AppTabsController {
337
408
  this.host.closeMenusForTabSwitch?.();
338
409
  if (this.host.runtime() !== previousRuntime) {
339
410
  try {
340
- await this.host.activateRuntime(previousRuntime);
411
+ await this.host.activateRuntime(previousRuntime, { awaitExtensions: false });
341
412
  }
342
413
  catch {
343
414
  // Keep the best available runtime below and surface the switch failure.
@@ -362,6 +433,7 @@ export class AppTabsController {
362
433
  this.setRuntimeForTab(tab.id, newRuntime);
363
434
  this.restoreInputState(tab.id);
364
435
  void this.saveTabs();
436
+ this.scheduleTabPrewarm();
365
437
  await this.loadActiveSessionHistory(newRuntime);
366
438
  return true;
367
439
  }
@@ -435,8 +507,12 @@ export class AppTabsController {
435
507
  this.inputStatesByTabId.set(tab.id, this.inputStateFromText(result.selectedText));
436
508
  this.restoreInputState(tab.id);
437
509
  this.host.closeMenusForTabSwitch?.();
510
+ this.host.resetSessionView();
511
+ this.restoreDeferredUserMessages(tab.id);
512
+ this.host.setSessionActivity("thinking");
513
+ this.host.render();
438
514
  try {
439
- await this.host.activateRuntime(forkRuntime);
515
+ await this.host.activateRuntime(forkRuntime, { awaitExtensions: false });
440
516
  }
441
517
  catch {
442
518
  this.pendingActiveTabId = undefined;
@@ -447,7 +523,7 @@ export class AppTabsController {
447
523
  this.host.closeMenusForTabSwitch?.();
448
524
  if (this.host.runtime() !== previousRuntime) {
449
525
  try {
450
- await this.host.activateRuntime(previousRuntime);
526
+ await this.host.activateRuntime(previousRuntime, { awaitExtensions: false });
451
527
  }
452
528
  catch {
453
529
  // Keep the best available runtime below and surface the switch failure.
@@ -472,6 +548,7 @@ export class AppTabsController {
472
548
  this.restoreInputState(tab.id);
473
549
  void this.saveTabs();
474
550
  this.scheduleProjectSessionRetention();
551
+ this.scheduleTabPrewarm();
475
552
  await this.loadActiveSessionHistory(forkRuntime);
476
553
  this.host.addEntry({ id: createId("system"), kind: "system", text: `Forked from entry ${entryId} in a new tab.` });
477
554
  this.host.setSessionStatus(forkRuntime.session);
@@ -520,7 +597,7 @@ export class AppTabsController {
520
597
  targetRuntime = await this.runtimeForTab(target);
521
598
  if (!targetRuntime)
522
599
  throw new Error("Could not load tab runtime");
523
- await this.host.activateRuntime(targetRuntime);
600
+ await this.host.activateRuntime(targetRuntime, { awaitExtensions: false });
524
601
  }
525
602
  catch {
526
603
  this.pendingActiveTabId = undefined;
@@ -534,7 +611,7 @@ export class AppTabsController {
534
611
  this.host.closeMenusForTabSwitch?.();
535
612
  if (this.host.runtime() !== previousRuntime) {
536
613
  try {
537
- await this.host.activateRuntime(previousRuntime);
614
+ await this.host.activateRuntime(previousRuntime, { awaitExtensions: false });
538
615
  }
539
616
  catch {
540
617
  // Keep the best available runtime below and surface the switch failure.
@@ -558,6 +635,7 @@ export class AppTabsController {
558
635
  this.setRuntimeForTab(target.id, targetRuntime);
559
636
  this.restoreInputState(target.id);
560
637
  void this.saveTabs();
638
+ this.scheduleTabPrewarm();
561
639
  await this.loadActiveSessionHistory(targetRuntime);
562
640
  }
563
641
  async closeTab(tabId) {
@@ -605,7 +683,7 @@ export class AppTabsController {
605
683
  const nextRuntime = await this.runtimeForTab(nextTab);
606
684
  if (!nextRuntime)
607
685
  return;
608
- await this.host.activateRuntime(nextRuntime);
686
+ await this.host.activateRuntime(nextRuntime, { awaitExtensions: false });
609
687
  this.tabItems.splice(index, 1);
610
688
  this.deleteRuntimeForTab(tabId);
611
689
  this.inputStatesByTabId.delete(tabId);
@@ -619,6 +697,7 @@ export class AppTabsController {
619
697
  this.host.closeMenusForTabSwitch?.();
620
698
  void this.host.disposeRuntime(runtime);
621
699
  void this.saveTabs();
700
+ this.scheduleTabPrewarm();
622
701
  await this.loadActiveSessionHistory(nextRuntime);
623
702
  }
624
703
  async replaceLastTabWithNewSession(tabId) {
@@ -739,11 +818,16 @@ export class AppTabsController {
739
818
  }
740
819
  deleteRuntimeForTab(tabId) {
741
820
  this.runtimesByTabId.delete(tabId);
821
+ this.runtimeLoadsByTabId.delete(tabId);
822
+ this.clearRuntimeRefreshTimers(tabId);
742
823
  const subscription = this.runtimeSubscriptionsByTabId.get(tabId);
743
824
  subscription?.unsubscribe();
744
825
  this.runtimeSubscriptionsByTabId.delete(tabId);
745
826
  }
746
827
  clearRuntimeSubscriptions() {
828
+ for (const tabId of this.runtimeRefreshTimersByTabId.keys()) {
829
+ this.clearRuntimeRefreshTimers(tabId);
830
+ }
747
831
  for (const subscription of this.runtimeSubscriptionsByTabId.values()) {
748
832
  subscription.unsubscribe();
749
833
  }
@@ -755,6 +839,9 @@ export class AppTabsController {
755
839
  return;
756
840
  existing?.unsubscribe();
757
841
  const unsubscribe = runtime.session.subscribe((event) => {
842
+ if (this.shouldScheduleDelayedSyncForRuntimeEvent(event)) {
843
+ this.scheduleDelayedRuntimeSync(tabId, runtime);
844
+ }
758
845
  if (!this.shouldSyncTabFromRuntimeEvent(event))
759
846
  return;
760
847
  this.syncTabFromObservedRuntime(tabId, runtime);
@@ -768,6 +855,35 @@ export class AppTabsController {
768
855
  || event.type === "compaction_start"
769
856
  || event.type === "compaction_end";
770
857
  }
858
+ shouldScheduleDelayedSyncForRuntimeEvent(event) {
859
+ return event.type === "agent_end"
860
+ || event.type === "turn_end"
861
+ || event.type === "compaction_end";
862
+ }
863
+ scheduleDelayedRuntimeSync(tabId, runtime) {
864
+ this.clearRuntimeRefreshTimers(tabId);
865
+ for (const delayMs of [0, 100, 500, 1500, 3000]) {
866
+ const timer = setTimeout(() => {
867
+ this.runtimeRefreshTimersByTabId.get(tabId)?.delete(timer);
868
+ this.syncTabFromObservedRuntime(tabId, runtime);
869
+ }, delayMs);
870
+ timer.unref?.();
871
+ let timers = this.runtimeRefreshTimersByTabId.get(tabId);
872
+ if (!timers) {
873
+ timers = new Set();
874
+ this.runtimeRefreshTimersByTabId.set(tabId, timers);
875
+ }
876
+ timers.add(timer);
877
+ }
878
+ }
879
+ clearRuntimeRefreshTimers(tabId) {
880
+ const timers = this.runtimeRefreshTimersByTabId.get(tabId);
881
+ if (!timers)
882
+ return;
883
+ for (const timer of timers)
884
+ clearTimeout(timer);
885
+ this.runtimeRefreshTimersByTabId.delete(tabId);
886
+ }
771
887
  syncTabFromObservedRuntime(tabId, runtime) {
772
888
  const tab = this.tabItems.find((item) => item.id === tabId);
773
889
  if (!tab) {
@@ -827,13 +943,32 @@ export class AppTabsController {
827
943
  const existing = this.runtimesByTabId.get(tab.id);
828
944
  if (existing)
829
945
  return existing;
946
+ const loading = this.runtimeLoadsByTabId.get(tab.id);
947
+ if (loading)
948
+ return await loading;
830
949
  if (!tab.sessionPath) {
831
950
  this.host.showToast("Tab has no persisted session path", "warning");
832
951
  return undefined;
833
952
  }
834
- const runtime = await this.host.createRuntimeForSession(tab.sessionPath);
835
- this.setRuntimeForTab(tab.id, runtime);
836
- return runtime;
953
+ const expectedPath = resolve(tab.sessionPath);
954
+ const pending = (async () => {
955
+ const runtime = await this.host.createRuntimeForSession(expectedPath);
956
+ const liveTab = this.tabItems.find((item) => item.id === tab.id);
957
+ if (!liveTab || !liveTab.sessionPath || resolve(liveTab.sessionPath) !== expectedPath) {
958
+ void this.host.disposeRuntime(runtime);
959
+ return undefined;
960
+ }
961
+ this.setRuntimeForTab(tab.id, runtime);
962
+ return runtime;
963
+ })();
964
+ this.runtimeLoadsByTabId.set(tab.id, pending);
965
+ try {
966
+ return await pending;
967
+ }
968
+ finally {
969
+ if (this.runtimeLoadsByTabId.get(tab.id) === pending)
970
+ this.runtimeLoadsByTabId.delete(tab.id);
971
+ }
837
972
  }
838
973
  findTabForSession(session, options = {}) {
839
974
  const sessionPath = this.sessionPath(session);
@@ -1146,6 +1281,38 @@ export class AppTabsController {
1146
1281
  void this.cleanupOldProjectSessions();
1147
1282
  }, 0);
1148
1283
  }
1284
+ scheduleTabPrewarm() {
1285
+ if (this.host.options.noSession || this.prewarmScheduled || this.prewarmRunning)
1286
+ return;
1287
+ this.prewarmScheduled = true;
1288
+ setTimeout(() => {
1289
+ this.prewarmScheduled = false;
1290
+ void this.prewarmTabs();
1291
+ }, 0);
1292
+ }
1293
+ async prewarmTabs() {
1294
+ if (this.prewarmRunning || this.pendingActiveTabId || !this.host.isRunning())
1295
+ return;
1296
+ this.prewarmRunning = true;
1297
+ try {
1298
+ let warmed = 0;
1299
+ for (const tab of this.tabItems) {
1300
+ if (warmed >= BACKGROUND_PREWARM_TAB_LIMIT)
1301
+ break;
1302
+ if (tab.id === this.activeTabId || !tab.sessionPath)
1303
+ continue;
1304
+ if (this.runtimesByTabId.has(tab.id) || this.runtimeLoadsByTabId.has(tab.id))
1305
+ continue;
1306
+ const runtime = await this.runtimeForTab(tab).catch(() => undefined);
1307
+ if (!runtime)
1308
+ continue;
1309
+ warmed += 1;
1310
+ }
1311
+ }
1312
+ finally {
1313
+ this.prewarmRunning = false;
1314
+ }
1315
+ }
1149
1316
  async cleanupOldProjectSessions() {
1150
1317
  if (this.retentionCleanupRunning)
1151
1318
  return;
@@ -24,6 +24,8 @@ export declare class AppTerminalController {
24
24
  private terminalEnabled;
25
25
  private interactiveSuspended;
26
26
  private stopPromise;
27
+ private readonly enterInteractiveSequence;
28
+ private readonly exitInteractiveSequence;
27
29
  constructor(host: AppTerminalControllerHost);
28
30
  isSuspended(): boolean;
29
31
  enableTerminal(): void;
@@ -1,10 +1,12 @@
1
1
  import { ANSI_RESET } from "../../theme.js";
2
- import { DISABLE_BRACKETED_PASTE, DISABLE_TERMINAL_KEY_REPORTING, DISABLE_TERMINAL_WRAP, CLEAR_TERMINAL, ENABLE_BRACKETED_PASTE, ENABLE_TERMINAL_KEY_REPORTING, ENABLE_TERMINAL_WRAP, HIDE_CURSOR, RUNTIME_DISPOSE_GRACE_MS, SHOW_CURSOR, } from "../constants.js";
2
+ import { DISABLE_BRACKETED_PASTE, DISABLE_TERMINAL_KEY_REPORTING, DISABLE_TERMINAL_WRAP, CLEAR_TERMINAL, ENABLE_BRACKETED_PASTE, ENABLE_TERMINAL_KEY_REPORTING, ENABLE_TERMINAL_WRAP, HIDE_CURSOR, RESET_TERMINAL_VIEWPORT_STATE, RUNTIME_DISPOSE_GRACE_MS, SHOW_CURSOR, } from "../constants.js";
3
3
  export class AppTerminalController {
4
4
  host;
5
5
  terminalEnabled = false;
6
6
  interactiveSuspended = false;
7
7
  stopPromise;
8
+ enterInteractiveSequence = `${ANSI_RESET}${RESET_TERMINAL_VIEWPORT_STATE}${CLEAR_TERMINAL}\x1b[?1049h${RESET_TERMINAL_VIEWPORT_STATE}${CLEAR_TERMINAL}${ENABLE_TERMINAL_KEY_REPORTING}${ENABLE_BRACKETED_PASTE}${DISABLE_TERMINAL_WRAP}\x1b[?1002h\x1b[?1006h${HIDE_CURSOR}`;
9
+ exitInteractiveSequence = `${ANSI_RESET}${RESET_TERMINAL_VIEWPORT_STATE}${DISABLE_TERMINAL_KEY_REPORTING}${DISABLE_BRACKETED_PASTE}${ENABLE_TERMINAL_WRAP}\x1b[?1006l\x1b[?1002l\x1b[?1049l${SHOW_CURSOR}`;
8
10
  constructor(host) {
9
11
  this.host = host;
10
12
  }
@@ -19,7 +21,7 @@ export class AppTerminalController {
19
21
  process.stdin.resume();
20
22
  process.stdin.on("data", this.onInputData);
21
23
  process.stdout.on("resize", this.onResize);
22
- process.stdout.write(`${ANSI_RESET}${CLEAR_TERMINAL}\x1b[?1049h${CLEAR_TERMINAL}${ENABLE_TERMINAL_KEY_REPORTING}${ENABLE_BRACKETED_PASTE}${DISABLE_TERMINAL_WRAP}\x1b[?1002h\x1b[?1006h${HIDE_CURSOR}`);
24
+ process.stdout.write(this.enterInteractiveSequence);
23
25
  process.on("exit", this.restoreTerminal);
24
26
  }
25
27
  async stop() {
@@ -66,7 +68,7 @@ export class AppTerminalController {
66
68
  return;
67
69
  this.terminalEnabled = false;
68
70
  this.interactiveSuspended = false;
69
- process.stdout.write(`${ANSI_RESET}${DISABLE_TERMINAL_KEY_REPORTING}${DISABLE_BRACKETED_PASTE}${ENABLE_TERMINAL_WRAP}\x1b[?1006l\x1b[?1002l\x1b[?1049l${SHOW_CURSOR}`);
71
+ process.stdout.write(this.exitInteractiveSequence);
70
72
  if (process.stdin.isTTY)
71
73
  process.stdin.setRawMode(false);
72
74
  };
@@ -77,7 +79,7 @@ export class AppTerminalController {
77
79
  process.stdin.off("data", this.onInputData);
78
80
  process.stdin.pause();
79
81
  process.stdout.off("resize", this.onResize);
80
- process.stdout.write(`${ANSI_RESET}${DISABLE_TERMINAL_KEY_REPORTING}${DISABLE_BRACKETED_PASTE}${ENABLE_TERMINAL_WRAP}\x1b[?1006l\x1b[?1002l\x1b[?1049l${SHOW_CURSOR}`);
82
+ process.stdout.write(this.exitInteractiveSequence);
81
83
  if (process.stdin.isTTY)
82
84
  process.stdin.setRawMode(false);
83
85
  }
@@ -91,7 +93,7 @@ export class AppTerminalController {
91
93
  process.stdin.on("data", this.onInputData);
92
94
  process.stdout.on("resize", this.onResize);
93
95
  this.host.resetRenderOutputBuffer();
94
- process.stdout.write(`${ANSI_RESET}${CLEAR_TERMINAL}\x1b[?1049h${CLEAR_TERMINAL}${ENABLE_TERMINAL_KEY_REPORTING}${ENABLE_BRACKETED_PASTE}${DISABLE_TERMINAL_WRAP}\x1b[?1002h\x1b[?1006h${HIDE_CURSOR}`);
96
+ process.stdout.write(this.enterInteractiveSequence);
95
97
  this.host.render();
96
98
  }
97
99
  async stopInternal() {
@@ -447,7 +447,7 @@ export type EditorLayout = {
447
447
  belowEditorLines: readonly RenderedLine[];
448
448
  inputStartRow: number;
449
449
  inputSeparatorRow: number;
450
- inputBottomSeparatorRow: number;
450
+ inputBottomSeparatorRow?: number;
451
451
  bodyHeight: number;
452
452
  };
453
453
  export type MouseEvent = {
@@ -10,6 +10,7 @@ export declare const PiToolsSuiteConfigSchema: Type.TObject<{
10
10
  enabled: Type.TOptional<Type.TBoolean>;
11
11
  disabledModules: Type.TOptional<Type.TArray<Type.TString>>;
12
12
  todoThinking: Type.TOptional<Type.TBoolean>;
13
+ lookupModel: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
13
14
  terminalBell: Type.TOptional<Type.TObject<{
14
15
  sound: Type.TOptional<Type.TBoolean>;
15
16
  }>>;
@@ -170,6 +171,8 @@ export declare const PiToolsSuiteConfigSchema: Type.TObject<{
170
171
  languageIdByExtension: Type.TOptional<Type.TRecord<"^.*$", Type.TString>>;
171
172
  startupTimeoutMs: Type.TOptional<Type.TNumber>;
172
173
  diagnosticsWaitMs: Type.TOptional<Type.TNumber>;
174
+ pullDiagnostics: Type.TOptional<Type.TBoolean>;
175
+ waitForPublishDiagnostics: Type.TOptional<Type.TBoolean>;
173
176
  initializationOptions: Type.TOptional<Type.TUnknown>;
174
177
  settings: Type.TOptional<Type.TUnknown>;
175
178
  }>>>;
@@ -190,6 +190,8 @@ const LspServerConfig = Type.Object({
190
190
  languageIdByExtension: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "File extension → language ID mapping, e.g. {'.ts': 'typescript'}." })),
191
191
  startupTimeoutMs: Type.Optional(Type.Number({ description: "Server startup timeout in ms.", minimum: 1000 })),
192
192
  diagnosticsWaitMs: Type.Optional(Type.Number({ description: "Wait time for diagnostics after file change.", minimum: 0 })),
193
+ pullDiagnostics: Type.Optional(Type.Boolean({ description: "When false, skip textDocument/diagnostic pull requests and rely on published diagnostics. Useful for servers where pull diagnostics is slow or incomplete." })),
194
+ waitForPublishDiagnostics: Type.Optional(Type.Boolean({ description: "When false, do not wait for a fresh textDocument/publishDiagnostics notification after file change. diagnosticsWaitMs still bounds the wait when enabled." })),
193
195
  initializationOptions: Type.Optional(Type.Unknown({ description: "LSP initialization options passed to the server." })),
194
196
  settings: Type.Optional(Type.Unknown({ description: "LSP workspace/settings passed to the server." })),
195
197
  }, { description: "LSP server configuration." });
@@ -204,6 +206,7 @@ export const PiToolsSuiteConfigSchema = Type.Object({
204
206
  enabled: Type.Optional(Type.Boolean({ description: "Enable or disable the entire pi-tools-suite extension." })),
205
207
  disabledModules: Type.Optional(Type.Array(Type.String(), { description: "List of disabled module names (e.g. ['lsp', 'prompt-commands'])." })),
206
208
  todoThinking: Type.Optional(Type.Boolean({ description: "Enable per-todo thinking levels and automatic thinking switch/restore when tasks become in-progress/completed." })),
209
+ lookupModel: Type.Optional(Type.Union([Type.String(), Type.Null()], { description: "Vision-capable provider/model used by GLM's lookup tool; unset or null disables lookup." })),
207
210
  terminalBell: Type.Optional(TerminalBellConfig),
208
211
  dcp: Type.Optional(DcpConfig),
209
212
  asyncSubagents: Type.Optional(AsyncSubagentsConfig),
package/dist/theme.d.ts CHANGED
@@ -13,6 +13,9 @@ export type Theme = {
13
13
  inputForeground: string;
14
14
  inputBackground: string;
15
15
  inputBorder: string;
16
+ inputBorderWidgetBackground: string;
17
+ tabBorder: string;
18
+ assistantMessageBackground: string;
16
19
  userMessageBackground: string;
17
20
  inputCursorBackground: string;
18
21
  popupForeground: string;
@@ -33,6 +36,14 @@ export type Theme = {
33
36
  toolMutation: string;
34
37
  toolSearch: string;
35
38
  toolTitle: string;
39
+ toolBash: string;
40
+ toolRead: string;
41
+ toolIndex: string;
42
+ toolEdit: string;
43
+ toolWeb: string;
44
+ toolMeta: string;
45
+ thinkingForeground: string;
46
+ userForeground: string;
36
47
  thinkingXHigh: string;
37
48
  modelOpenAI: string;
38
49
  statusDotBase: string;
package/dist/theme.js CHANGED
@@ -10,11 +10,14 @@ export const THEMES = {
10
10
  headerForeground: "#c9d1d9",
11
11
  headerBackground: "#161b22",
12
12
  statusForeground: "#8b949e",
13
- statusBackground: "#090d13",
13
+ statusBackground: "#0f1520",
14
14
  inputForeground: "#f0f6fc",
15
15
  inputBackground: "#090d13",
16
16
  inputBorder: "#30363d",
17
- userMessageBackground: "#1e1e1e",
17
+ inputBorderWidgetBackground: "#2a2f36",
18
+ tabBorder: "#7d8590",
19
+ assistantMessageBackground: "#161b22",
20
+ userMessageBackground: "#262224",
18
21
  inputCursorBackground: "#7fb3c8",
19
22
  popupForeground: "#e6edf3",
20
23
  popupBackground: "#1e1e1e",
@@ -34,6 +37,14 @@ export const THEMES = {
34
37
  toolMutation: "#d47aa2",
35
38
  toolSearch: "#a889d6",
36
39
  toolTitle: "#9aa7b4",
40
+ toolBash: "#c99670",
41
+ toolRead: "#6daa8a",
42
+ toolIndex: "#7a9ec7",
43
+ toolEdit: "#c76a8a",
44
+ toolWeb: "#8a9cc7",
45
+ toolMeta: "#8b8fa3",
46
+ thinkingForeground: "#b8a0d4",
47
+ userForeground: "#88b4dc",
37
48
  thinkingXHigh: "#ff8a86",
38
49
  modelOpenAI: "#c8b45a",
39
50
  statusDotBase: "#30363d",
@@ -52,11 +63,14 @@ export const THEMES = {
52
63
  headerForeground: "#0f172a",
53
64
  headerBackground: "#e2e8f0",
54
65
  statusForeground: "#475569",
55
- statusBackground: "#f8fafc",
66
+ statusBackground: "#edf0f4",
56
67
  inputForeground: "#0f172a",
57
68
  inputBackground: "#f8fafc",
58
69
  inputBorder: "#334155",
59
- userMessageBackground: "#ffffff",
70
+ inputBorderWidgetBackground: "#f1f5f9",
71
+ tabBorder: "#64748b",
72
+ assistantMessageBackground: "#eef2f7",
73
+ userMessageBackground: "#f9f0ee",
60
74
  inputCursorBackground: "#0284c7",
61
75
  popupForeground: "#0f172a",
62
76
  popupBackground: "#ffffff",
@@ -76,6 +90,14 @@ export const THEMES = {
76
90
  toolMutation: "#a33a68",
77
91
  toolSearch: "#6d52a5",
78
92
  toolTitle: "#526070",
93
+ toolBash: "#8a6535",
94
+ toolRead: "#3d7a56",
95
+ toolIndex: "#3a6d96",
96
+ toolEdit: "#963a5e",
97
+ toolWeb: "#4a6096",
98
+ toolMeta: "#6b7280",
99
+ thinkingForeground: "#6b5491",
100
+ userForeground: "#4a78b5",
79
101
  thinkingXHigh: "#cf333d",
80
102
  modelOpenAI: "#75671f",
81
103
  statusDotBase: "#334155",
@@ -269,7 +269,7 @@ export async function runQuestionnaire(questions: NormalizedQuestion[], ctx: Que
269
269
  }
270
270
 
271
271
  function advanceAfterAnswer(): void {
272
- if (!pixCapabilities?.delegatedEditorInput && questions.length === 1) {
272
+ if (questions.length === 1) {
273
273
  submitCompleteSelections();
274
274
  return;
275
275
  }
@@ -26,9 +26,9 @@ const DEFAULT_CONFIG: SessionTitleConfig = {
26
26
  maxInputChars: 2000,
27
27
  maxTitleChars: 80,
28
28
  maxTokens: 32,
29
- maxRetries: 1,
30
- generationAttempts: 2,
31
- retryDelayMs: 2500,
29
+ maxRetries: 2,
30
+ generationAttempts: 3,
31
+ retryDelayMs: 3000,
32
32
  temperature: 0.2,
33
33
  timeoutMs: 12_000,
34
34
  terminalTitle: true,