pi-ui-extend 0.1.38 → 0.1.41

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 (64) hide show
  1. package/dist/app/app.d.ts +0 -1
  2. package/dist/app/app.js +28 -21
  3. package/dist/app/constants.js +1 -1
  4. package/dist/app/input/input-action-controller.d.ts +1 -0
  5. package/dist/app/input/input-action-controller.js +3 -0
  6. package/dist/app/input/input-controller.d.ts +1 -0
  7. package/dist/app/input/input-controller.js +40 -12
  8. package/dist/app/model/model-usage-status.js +4 -2
  9. package/dist/app/process.js +11 -0
  10. package/dist/app/rendering/conversation-tool-renderer.js +4 -6
  11. package/dist/app/session/request-history.js +2 -0
  12. package/dist/app/session/session-event-controller.d.ts +13 -0
  13. package/dist/app/session/session-event-controller.js +27 -0
  14. package/dist/app/session/tabs-controller.d.ts +8 -0
  15. package/dist/app/session/tabs-controller.js +37 -6
  16. package/dist/app/workspace/workspace-actions-controller.d.ts +1 -0
  17. package/dist/app/workspace/workspace-actions-controller.js +2 -1
  18. package/dist/bundled-extensions/terminal-bell/index.js +55 -1
  19. package/dist/config.js +1 -1
  20. package/dist/default-pix-config.js +1 -1
  21. package/dist/markdown-format.js +14 -25
  22. package/dist/terminal-width.d.ts +14 -0
  23. package/dist/terminal-width.js +31 -2
  24. package/dist/theme.js +2 -2
  25. package/external/pi-tools-suite/README.md +34 -9
  26. package/external/pi-tools-suite/package.json +3 -3
  27. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +35 -21
  28. package/external/pi-tools-suite/src/async-subagents/commands.ts +1 -1
  29. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +2 -2
  30. package/external/pi-tools-suite/src/async-subagents/core/config.ts +70 -12
  31. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +1 -1
  32. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +1 -1
  33. package/external/pi-tools-suite/src/async-subagents/core/types.ts +1 -1
  34. package/external/pi-tools-suite/src/async-subagents/index.ts +6 -6
  35. package/external/pi-tools-suite/src/async-subagents/lib.ts +1 -1
  36. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +4 -2
  37. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +2 -2
  38. package/external/pi-tools-suite/src/{glm-coding-discipline → coding-discipline}/index.ts +17 -8
  39. package/external/pi-tools-suite/src/config.ts +1 -1
  40. package/external/pi-tools-suite/src/dcp/auto-compress.ts +368 -0
  41. package/external/pi-tools-suite/src/dcp/compress-tool.ts +3 -0
  42. package/external/pi-tools-suite/src/dcp/config.ts +23 -0
  43. package/external/pi-tools-suite/src/dcp/index.ts +112 -7
  44. package/external/pi-tools-suite/src/dcp/prompts.ts +8 -0
  45. package/external/pi-tools-suite/src/dcp/state.ts +41 -0
  46. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +30 -22
  47. package/external/pi-tools-suite/src/index.ts +2 -1
  48. package/external/pi-tools-suite/src/session-name/index.ts +37 -0
  49. package/external/pi-tools-suite/src/tool-descriptions.ts +16 -4
  50. package/package.json +4 -4
  51. package/skills/skill-creator/SKILL.md +36 -40
  52. package/skills/skill-creator/eval-viewer/viewer.html +2 -2
  53. package/skills/skill-creator/references/schemas.md +1 -1
  54. package/skills/skill-creator/scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  55. package/skills/skill-creator/scripts/__pycache__/aggregate_benchmark.cpython-314.pyc +0 -0
  56. package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-314.pyc +0 -0
  57. package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-314.pyc +0 -0
  58. package/skills/skill-creator/scripts/__pycache__/package_skill.cpython-314.pyc +0 -0
  59. package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-314.pyc +0 -0
  60. package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-314.pyc +0 -0
  61. package/skills/skill-creator/scripts/__pycache__/utils.cpython-314.pyc +0 -0
  62. package/skills/skill-creator/scripts/generate_report.py +1 -1
  63. package/skills/skill-creator/scripts/improve_description.py +14 -24
  64. package/skills/skill-creator/scripts/run_eval.py +89 -82
@@ -51,6 +51,7 @@ import {
51
51
  import { summarizeDcpState, writeDcpDebugLog } from "./debug-log.js"
52
52
  import type { DcpNudgeType } from "./pruner-types.js"
53
53
  import { registerCompressTool } from "./compress-tool.js"
54
+ import { decideAutoCompress, createAutoCompressionBlock } from "./auto-compress.js"
54
55
  import { DCP_STATS_MESSAGE_TYPE, registerCommands } from "./commands.js"
55
56
  import { normalizeDcpContextUsage } from "./ui.js"
56
57
  import { safeGetContextUsage } from "../context-usage.js"
@@ -417,6 +418,21 @@ export default async function dcpModule(pi: ExtensionAPI): Promise<void> {
417
418
  return finishContext("unknown-context-percent", prunedMessages, { clearedAnchors })
418
419
  }
419
420
 
421
+ // Record the observed context window on EVERY context event (before
422
+ // any early return) so a mid-session model/window downgrade is
423
+ // detectable even when earlier passes were below threshold. We
424
+ // snapshot the previous value first so the downgrade check below
425
+ // compares against the window the prior pass actually saw.
426
+ const currentContextWindow = usage.contextWindow
427
+ const previousContextWindow = state.lastContextWindow
428
+ if (
429
+ typeof currentContextWindow === "number" &&
430
+ Number.isFinite(currentContextWindow) &&
431
+ currentContextWindow > 0
432
+ ) {
433
+ state.lastContextWindow = currentContextWindow
434
+ }
435
+
420
436
  const ctxModel = (ctx as any).model
421
437
  const provider = ctxModel?.provider ?? ctxModel?.providerId ?? ctxModel?.providerID
422
438
  const model = ctxModel?.id ?? ctxModel?.model ?? ctxModel?.modelId ?? ctxModel?.modelID
@@ -448,13 +464,30 @@ export default async function dcpModule(pi: ExtensionAPI): Promise<void> {
448
464
  if (msg.role === "toolResult") toolCallsSinceLastUser++
449
465
  }
450
466
 
451
- const nudgeType = getNudgeType(
452
- contextPercent,
453
- state,
454
- effectiveConfig,
455
- toolCallsSinceLastUser,
456
- thresholds,
457
- )
467
+ // Switch-aware pre-emptive nudge: detect a mid-session context-window
468
+ // downgrade (e.g. model switch from a 1M window to a 275K window).
469
+ // Inherited tokens that were cheap on the larger window can suddenly
470
+ // sit above minContextPercent on the smaller one. When that happens,
471
+ // force a strong nudge on this pass so the model is told to compress
472
+ // before the smaller window fills, instead of waiting for cadence.
473
+ const windowDowngraded =
474
+ typeof previousContextWindow === "number" &&
475
+ Number.isFinite(previousContextWindow) &&
476
+ previousContextWindow > 0 &&
477
+ typeof currentContextWindow === "number" &&
478
+ Number.isFinite(currentContextWindow) &&
479
+ currentContextWindow < previousContextWindow * 0.9 &&
480
+ contextPercent > thresholds.minContextPercent
481
+
482
+ const nudgeType = windowDowngraded
483
+ ? "context-strong"
484
+ : getNudgeType(
485
+ contextPercent,
486
+ state,
487
+ effectiveConfig,
488
+ toolCallsSinceLastUser,
489
+ thresholds,
490
+ )
458
491
 
459
492
  const manualEmergencyOnly =
460
493
  state.manualMode &&
@@ -483,6 +516,78 @@ export default async function dcpModule(pi: ExtensionAPI): Promise<void> {
483
516
  }, ctx)
484
517
  }
485
518
 
519
+ // Track consecutive ignored context-strong nudges for the
520
+ // auto-compress fallback. A strong nudge is "ignored" if it fires
521
+ // again on a later context event without a successful compress in
522
+ // between. Reset on non-strong nudges and when pressure drops below
523
+ // the emergency threshold (handled by the below-threshold early
524
+ // return above, which clears anchors; counter is also reset on any
525
+ // successful compress in the compress tool).
526
+ if (nudgeType === "context-strong" || nudgeType === "context-soft") {
527
+ state.consecutiveIgnoredStrongNudges += 1
528
+ } else if (nudgeType === null) {
529
+ state.consecutiveIgnoredStrongNudges = 0
530
+ }
531
+
532
+ // Auto-compress fallback: if the model has ignored enough strong
533
+ // nudges while above the emergency threshold, DCP creates a
534
+ // compression block itself instead of nudging again.
535
+ if (!manualEmergencyOnly) {
536
+ const autoDecision = decideAutoCompress(
537
+ state,
538
+ effectiveConfig,
539
+ contextPercent,
540
+ thresholds.maxContextPercent,
541
+ candidate,
542
+ )
543
+ if (autoDecision.shouldFire && candidate) {
544
+ try {
545
+ const autoResult = await createAutoCompressionBlock({
546
+ candidate,
547
+ topic: "Auto-compressed slice",
548
+ state,
549
+ config: effectiveConfig,
550
+ messages: prunedMessages,
551
+ modelRegistry: (ctx as any).modelRegistry,
552
+ signal: (ctx as any).signal,
553
+ })
554
+ // Re-apply pruning so the new block takes effect on this
555
+ // same context pass instead of the next one.
556
+ prunedMessages = applyPruning(prunedMessages, state, effectiveConfig)
557
+ const clearedAnchors = clearDcpNudgeAnchors(state)
558
+ state.consecutiveIgnoredStrongNudges = 0
559
+ await saveDcpState(ctx, state)
560
+ writeDcpDebugLog(effectiveConfig, "compress.auto", {
561
+ trigger: autoDecision.reason,
562
+ blockId: `b${autoResult.blockId}`,
563
+ summaryMode: autoResult.summaryMode,
564
+ summarizerModelRef: autoResult.summarizerModelRef,
565
+ summarizerAttempts: autoResult.summarizerAttempts,
566
+ summaryTokens: autoResult.summaryTokens,
567
+ removedTokenEstimate: autoResult.removedTokenEstimate,
568
+ candidate,
569
+ clearedAnchors,
570
+ state: summarizeDcpState(state),
571
+ }, ctx)
572
+ return finishContext("compress.auto", prunedMessages, {
573
+ candidate,
574
+ messageCandidates,
575
+ contextPercent,
576
+ thresholds,
577
+ clearedAnchors,
578
+ })
579
+ } catch (error) {
580
+ writeDcpDebugLog(effectiveConfig, "compress.auto_failed", {
581
+ trigger: autoDecision.reason,
582
+ error: error instanceof Error ? error.message : String(error),
583
+ candidate,
584
+ state: summarizeDcpState(state),
585
+ }, ctx)
586
+ // Fall through to normal nudge emission on failure.
587
+ }
588
+ }
589
+ }
590
+
486
591
  if (nudgeType && !manualEmergencyOnly) {
487
592
  const nudgeText = appendConcreteNudgeGuidance(
488
593
  baseNudgeText(nudgeType),
@@ -30,6 +30,8 @@ Use \`compress\` as context-pressure housekeeping, not as a reflex after every s
30
30
 
31
31
  A closed slice is any finished implementation, verification, config edit, answered exploration, dead-end debugging branch, or test/log inspection. Passing logs are summary-only: preserve command, pass/fail, key failures if any, and whether follow-up is needed; never keep a full passing log in live context. Treat large shell/read/repo/web outputs as disposable evidence once their facts are extracted.
32
32
 
33
+ Completed todo/task/subtask milestones are strong boundary signals, not automatic triggers. When context pressure makes compression useful, prefer the range covering a just-finished todo item if it is closed and non-trivial; do not compress merely because a todo was completed while context remains low.
34
+
33
35
  Before compressing while work is unfinished, ensure one \`todo in_progress\` captures the active objective and next step.
34
36
 
35
37
  When a \`<dcp-system-reminder>\` appears, treat it as a context-pressure signal. Follow critical/high-context reminders promptly. For routine reminders, compress only if a genuinely closed, useful-to-summarize slice exists; otherwise continue the next atomic step and re-check later.
@@ -183,6 +185,7 @@ You MUST use the \`compress\` tool now. Do not continue normal exploration until
183
185
 
184
186
  If you are in the middle of a critical atomic operation, finish that atomic step first, then compress immediately.
185
187
  If any closed slice exists (finished implementation, verification, config/doc edit, answered exploration, dead end, or test/log inspection), compress it before replying or starting another task. Passing logs should become command + pass/fail + follow-up status only.
188
+ Recently completed todo/task/subtask items are preferred boundaries when they form a high-yield closed slice.
186
189
 
187
190
  RANGE STRATEGY (MANDATORY)
188
191
  Prioritize one large, closed, high-yield compression range first.
@@ -210,6 +213,7 @@ ACTION REQUIRED: Context usage is high.
210
213
  Before doing more exploration, look for a high-yield closed range that no longer needs to stay raw. Compress it now if one is safe and useful.
211
214
 
212
215
  This is context-pressure guidance, not a request to compress tiny or still-needed slices. If completed research, implementation, verification, config/doc edit, CI-log inspection, or dead-end debugging is large enough to reduce signal, call the \`compress\` tool before continuing normal work.
216
+ Recently completed todo/task/subtask items are preferred candidates when they form a non-trivial closed slice; do not compress merely because a tiny todo was completed.
213
217
  High-priority stale shell/read/repo/web outputs should be compressed once no exact raw text is needed. Passing logs should not remain raw after they are understood.
214
218
 
215
219
  RANGE SELECTION
@@ -230,6 +234,8 @@ CONTEXT CHECK: Evaluate whether compression would materially improve the live co
230
234
 
231
235
  If a range is cleanly closed, non-trivial, and unlikely to be needed verbatim again, use the \`compress\` tool. If direction has shifted, consider whether earlier ranges are now less relevant.
232
236
 
237
+ If a todo/task/subtask was just completed, treat that completed work as a preferred compression boundary when it is large enough and no longer needed raw; completion alone is not a reason to compress while context is still low.
238
+
233
239
  Do not compress just because a small slice closed while context is still low. Prefer compression before another large batch of searches, reads, CI log fetches, or tests when a high-yield stale slice exists.
234
240
  High-priority stale shell/read/repo/web outputs and understood passing logs should be compressed once no exact raw text is needed.
235
241
 
@@ -247,6 +253,8 @@ CONTEXT CHECK: You've been iterating for a while after the last user message.
247
253
 
248
254
  Pause before the next large non-atomic tool batch. If there is a closed portion that is unlikely to be referenced immediately and is worth summarizing (for example, finished research before implementation, completed config edit, completed CI-log triage, a verified fix, or a dead-end investigation), use the \`compress\` tool on it.
249
255
 
256
+ If a todo/task/subtask was just completed, prefer that completed work as the compression boundary when it is non-trivial and safe to summarize; do not compress merely because the todo status changed.
257
+
250
258
  Avoid accumulating large tool outputs while a high-yield completed slice remains raw. If only small or still-needed ranges are closed, continue the next atomic step and re-check later.
251
259
 
252
260
  Prefer multiple short, closed ranges over one large range when several independent slices are ready.
@@ -218,6 +218,24 @@ export interface DcpState {
218
218
  nextNudgeAnchorId: number
219
219
  /** Diagnostic/telemetry snapshot for the latest emitted reminder. */
220
220
  lastNudge?: DcpLastNudge
221
+ /**
222
+ * The context window observed on the previous `context` event, used to
223
+ * detect a mid-session model/window downgrade (e.g. switch from a 1M model
224
+ * to a 275K model). When the window shrinks and inherited tokens already
225
+ * exceed `minContextPercent`, the context handler forces a pre-emptive
226
+ * strong nudge so the model is told to compress before the window fills.
227
+ * `undefined` until the first context event records a window.
228
+ */
229
+ lastContextWindow?: number
230
+ /**
231
+ * How many consecutive `context-strong` nudges have been emitted without a
232
+ * subsequent successful `compress` (model- or DCP-initiated). When this
233
+ * reaches `compress.autoCompress.patience` while context is above the
234
+ * emergency threshold, the auto-compress fallback creates a block without
235
+ * waiting for the model. Reset to 0 on any successful compression, when
236
+ * pressure drops below the emergency threshold, or on a window change.
237
+ */
238
+ consecutiveIgnoredStrongNudges: number
221
239
  }
222
240
 
223
241
  // ---------------------------------------------------------------------------
@@ -247,6 +265,8 @@ export function createState(): DcpState {
247
265
  nudgeAnchors: [],
248
266
  nextNudgeAnchorId: 1,
249
267
  lastNudge: undefined,
268
+ lastContextWindow: undefined,
269
+ consecutiveIgnoredStrongNudges: 0,
250
270
  }
251
271
  }
252
272
 
@@ -276,6 +296,8 @@ export function resetState(state: DcpState): void {
276
296
  state.nudgeAnchors = []
277
297
  state.nextNudgeAnchorId = 1
278
298
  state.lastNudge = undefined
299
+ state.lastContextWindow = undefined
300
+ state.consecutiveIgnoredStrongNudges = 0
279
301
  }
280
302
 
281
303
  /**
@@ -412,6 +434,17 @@ export interface SerializedDcpState {
412
434
  nudgeCounter?: number
413
435
  /** Persisted since v?.?. Diagnostic turn of the last emitted nudge. */
414
436
  lastNudgeTurn?: number
437
+ /**
438
+ * Persisted so a mid-session window downgrade (model switch to a smaller
439
+ * context window) is still detectable after a pi process restart / resume.
440
+ */
441
+ lastContextWindow?: number
442
+ /**
443
+ * Persisted so the auto-compress fallback's patience counter survives a pi
444
+ * process restart / resume, preventing a stuck "model ignores strong nudges"
445
+ * state from being silently cleared by a reload.
446
+ */
447
+ consecutiveIgnoredStrongNudges?: number
415
448
  /** Hash of the last persisted serialized state, used for dedup. */
416
449
  _stateHash?: string
417
450
  }
@@ -583,6 +616,8 @@ export function serializeState(state: DcpState): SerializedDcpState {
583
616
  currentTurn: state.currentTurn,
584
617
  nudgeCounter: state.nudgeCounter,
585
618
  lastNudgeTurn: state.lastNudgeTurn,
619
+ lastContextWindow: state.lastContextWindow,
620
+ consecutiveIgnoredStrongNudges: state.consecutiveIgnoredStrongNudges,
586
621
  }
587
622
  }
588
623
 
@@ -743,6 +778,12 @@ export function restoreState(state: DcpState, data: unknown): void {
743
778
  if (typeof saved.currentTurn === "number" && Number.isFinite(saved.currentTurn) && saved.currentTurn >= 0) {
744
779
  state.currentTurn = Math.floor(saved.currentTurn)
745
780
  }
781
+ if (typeof saved.lastContextWindow === "number" && Number.isFinite(saved.lastContextWindow) && saved.lastContextWindow > 0) {
782
+ state.lastContextWindow = saved.lastContextWindow
783
+ }
784
+ if (typeof saved.consecutiveIgnoredStrongNudges === "number" && Number.isFinite(saved.consecutiveIgnoredStrongNudges) && saved.consecutiveIgnoredStrongNudges >= 0) {
785
+ state.consecutiveIgnoredStrongNudges = Math.floor(saved.consecutiveIgnoredStrongNudges)
786
+ }
746
787
  }
747
788
 
748
789
  // ---------------------------------------------------------------------------
@@ -8,7 +8,8 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
8
8
  // When true, todo items may carry a per-task thinking level and the todo
9
9
  // module will switch/restore Pi's thinking level as in-progress tasks change.
10
10
  "todoThinking": true,
11
- // Vision-capable model used by GLM's lookup tool. Remove or set to null to disable lookup.
11
+ // Vision-capable model used by the coding-discipline lookup tool for blind-model
12
+ // screenshot/image questions. Remove or set to null to disable lookup.
12
13
  "lookupModel": "openai-codex/gpt-5.4-mini",
13
14
  "terminalBell": { "sound": true },
14
15
  // comment-checker: nudges the agent to remove AI-slop code comments it just
@@ -38,10 +39,10 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
38
39
  "debugLog": { "maxBytes": 5242880, "maxBackups": 3 },
39
40
  "manualMode": { "enabled": false, "automaticStrategies": true },
40
41
  "modelOverrides": {
41
- "openai-codex/gpt-5.5": {
42
+ "openai-codex/gpt-5*": {
42
43
  "compress": {
43
- "minContextPercent": "28%",
44
- "maxContextPercent": "48%"
44
+ "minContextPercent": "26%",
45
+ "maxContextPercent": "46%"
45
46
  }
46
47
  },
47
48
  "openai-codex/gpt-5.4-mini": {
@@ -92,8 +93,15 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
92
93
  "maxContextPercent": "55%",
93
94
  "nudgeFrequency": 1,
94
95
  "iterationNudgeThreshold": 4,
96
+ "nudgeForce": "strong",
95
97
  "autoCandidates": { "minContextPercent": 0.2 },
96
- "messageMode": { "minContextPercent": 0.2 }
98
+ "messageMode": { "minContextPercent": 0.2 },
99
+ "autoCompress": {
100
+ "enabled": false,
101
+ "patience": 2,
102
+ "summarizerModel": ["zai/glm-5.2", "zai/glm-4.5-air"],
103
+ "timeoutMs": 20000
104
+ }
97
105
  }
98
106
  },
99
107
  "asyncSubagents": {
@@ -101,7 +109,7 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
101
109
  "routing": { "enabled": true, "model": "zai/glm-4.5-air", "maxTaskChars": 1200, "maxTokens": 512, "maxRetries": 1, "timeoutMs": 12000, "debug": false },
102
110
  "presets": {
103
111
  "cheap": {
104
- "description": "Use cheap GLM/Gemini Flash models for text/code roles; keep vision on the enabled GPT vision model.",
112
+ "description": "Use cheap GLM/Gemini Flash models for text/code roles.",
105
113
  "types": {
106
114
  "quick": { "model": "zai/glm-4.5-air", "thinking": "off" },
107
115
  "scan": { "model": "zai/glm-4.5-air", "thinking": "off" },
@@ -115,8 +123,7 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
115
123
  "tests": { "model": "zai/glm-5-turbo", "thinking": "medium" },
116
124
  "review": { "model": "zai/glm-5.2", "thinking": "high" },
117
125
  "implement": { "model": "zai/glm-5.2", "thinking": "high" },
118
- "deep": { "model": "zai/glm-5.2", "thinking": "high" },
119
- "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
126
+ "deep": { "model": "zai/glm-5.2", "thinking": "high" }
120
127
  }
121
128
  },
122
129
  "gpt": {
@@ -166,8 +173,7 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
166
173
  "model": "openai-codex/gpt-5.5",
167
174
  "fallbackModels": ["zai/glm-5.2"],
168
175
  "thinking": "high"
169
- },
170
- "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
176
+ }
171
177
  }
172
178
  },
173
179
  "deep": {
@@ -217,8 +223,7 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
217
223
  "model": "antigravity/antigravity-claude-opus-4-6-thinking",
218
224
  "fallbackModels": ["openai-codex/gpt-5.5", "zai/glm-5.2"],
219
225
  "thinking": "high"
220
- },
221
- "vision": { "model": "openai-codex/gpt-5.4-mini", "thinking": "off" }
226
+ }
222
227
  }
223
228
  }
224
229
  },
@@ -278,16 +283,19 @@ export const DEFAULT_PI_TOOLS_SUITE_CONFIG_JSONC = String.raw`{
278
283
  "fallbackModels": ["zai/glm-5.2"],
279
284
  "thinking": "high"
280
285
  },
281
- "vision": {
282
- "description": "Use only when task has imagePaths, screenshots, or asks to inspect visible UI/image content for a text-only parent.",
283
- "model": "openai-codex/gpt-5.4-mini",
284
- "thinking": "off",
285
- "promptAppend": [
286
- "You are a vision helper for a parent model that may not be able to see images.",
287
- "Inspect any attached images and any image paths mentioned in the task/scope. Describe concrete visible details, UI state, text, layout, errors, and uncertainties.",
288
- "If focus instructions are provided, prioritize them, but still mention other important visible findings.",
289
- "Do not make code changes. Return a compact visual description that the parent agent can rely on."
290
- ]
286
+ "oracle": {
287
+ "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.",
288
+ "model": "openai-codex/gpt-5.5",
289
+ "fallbackModels": ["zai/glm-5.2"],
290
+ "thinking": "xhigh",
291
+ "tools": ["read", "grep", "bash"],
292
+ "modelByParent": {
293
+ "zai/*": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"] },
294
+ "openai-codex/*": { "model": "zai/glm-5.2", "fallbackModels": ["openai-codex/gpt-5.5"] },
295
+ "antigravity/*": { "model": "zai/glm-5.2", "fallbackModels": ["openai-codex/gpt-5.5"] },
296
+ "anthropic/*": { "model": "openai-codex/gpt-5.5", "fallbackModels": ["zai/glm-5.2"] }
297
+ },
298
+ "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."
291
299
  }
292
300
  }
293
301
  },
@@ -10,11 +10,12 @@ type ExtensionModule = {
10
10
  };
11
11
 
12
12
  const MODULES: Array<{ name: string; load: () => Promise<ExtensionModule> }> = [
13
- { name: "glm-coding-discipline", load: () => import("./glm-coding-discipline/index") },
13
+ { name: "coding-discipline", load: () => import("./coding-discipline/index") },
14
14
  { name: "ast-grep", load: () => import("./ast-grep/index") },
15
15
  { name: "async-subagents", load: () => import("./async-subagents/index") },
16
16
  { name: "lsp", load: () => import("./lsp/index") },
17
17
  { name: "comment-checker", load: () => import("./comment-checker/index") },
18
+ { name: "session-name", load: () => import("./session-name/index") },
18
19
  { name: "repo-discovery", load: () => import("./repo-discovery/index") },
19
20
  { name: "antigravity-auth", load: () => import("./antigravity-auth/index") },
20
21
  { name: "opencode-import", load: () => import("./opencode-import/index") },
@@ -0,0 +1,37 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { Type } from "typebox";
3
+
4
+ import { SESSION_NAME_TOOL_DESCRIPTION } from "../tool-descriptions.js";
5
+
6
+ type SessionNameParams = {
7
+ name?: string;
8
+ };
9
+
10
+ function formatCurrentName(name: string | undefined): string {
11
+ return name ? `Current session name: ${name}` : "No session name is set.";
12
+ }
13
+
14
+ export default function sessionName(pi: ExtensionAPI): void {
15
+ pi.registerTool({
16
+ ...SESSION_NAME_TOOL_DESCRIPTION,
17
+ parameters: Type.Object({
18
+ name: Type.Optional(Type.String({ description: "New session name to set. Omit to read the current session name." })),
19
+ }),
20
+ async execute(_toolCallId: string, params: SessionNameParams) {
21
+ const nextName = params.name?.trim();
22
+
23
+ if (!nextName) {
24
+ return {
25
+ content: [{ type: "text", text: formatCurrentName(pi.getSessionName()) }],
26
+ details: { changed: false, sessionName: pi.getSessionName() ?? null },
27
+ };
28
+ }
29
+
30
+ pi.setSessionName(nextName);
31
+ return {
32
+ content: [{ type: "text", text: `Session name set: ${nextName}` }],
33
+ details: { changed: true, sessionName: nextName },
34
+ };
35
+ },
36
+ });
37
+ }
@@ -73,7 +73,7 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
73
73
  description: [
74
74
  "Manage isolated async sub-agents for large, parallel, context-heavy work.",
75
75
  "Presets from async-subagents config and /subagent-preset choose role model/thinking/args; AGENTS_PRESET or /subagent-preset session <name> overrides the current session; /subagent-preset init creates a sample config.",
76
- "Omit subagentType so the router chooses a configured role unless the user or task requires a role, vision, or deterministic override.",
76
+ "Omit subagentType so the router chooses a configured role unless the user or task requires a role or deterministic override.",
77
77
  repoDiscovery
78
78
  ? "Use for broad independent tracks, review axes, or hypotheses even though repo_* tools are available."
79
79
  : "Use first for broad codebase discovery split into tracks, review axes, or incident-triage hypotheses when repo_* tools are unavailable.",
@@ -82,7 +82,7 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
82
82
  ].join(" "),
83
83
  promptSnippet:
84
84
  "Use subagents action='spawn' for multiple independent agents, explicit delegate/parallelize/split work requests, or one large review/debug track that should stay out of the parent context. " +
85
- "Usually omit subagentType so the router chooses; set it only for user-named roles, vision/image handling, deterministic tests, or another concrete override. Avoid trivial reads/edits and do not call status/wait immediately after spawn just for progress. " +
85
+ "Usually omit subagentType so the router chooses; set it only for user-named roles, deterministic tests, or another concrete override. Avoid trivial reads/edits and do not call status/wait immediately after spawn just for progress. " +
86
86
  (repoDiscovery
87
87
  ? "For one semantic code-discovery question, use repo_search; for independent tracks/hypotheses/review axes, delegate even when repo_* tools exist. Read result only after completion when findings are needed."
88
88
  : "For one focused code-discovery question, use direct read/grep. Without repo_* tools, spawn several focused scan/quick agents first for broad multi-track discovery, incident triage, release readiness, risk strategy, or parallel reviews. Read result only after completion when findings are needed."),
@@ -97,8 +97,9 @@ export function asyncSubagentToolDescriptions(options: ToolDescriptionSetOptions
97
97
  : "For incident triage, release readiness, or risk/test strategy with separate hypotheses or review tracks and no repo_* tools, call action='spawn' as the first discovery step; direct read/grep can follow.",
98
98
  "Do not use subagents for exact-string lookups, known-file edits, typo/text replacements, obvious one-file changes, or interactive user input; use the cheapest direct path.",
99
99
  "Spawn multiple focused agents in one action='spawn' call for independent questions; for synthetic tests or bounded probes, pass timeoutSeconds slightly above expected runtime.",
100
- "For spawn, leave subagentType unset so the router chooses from configured roles. Set it only for user-named roles, vision/image handling, deterministic tests, or another concrete override.",
101
- "Use subagentType='vision' with imagePaths and optional focus when images/screenshots/diagrams need visual inspection.",
100
+ "For spawn, leave subagentType unset so the router chooses from configured roles. Set it only for user-named roles, deterministic tests, or another concrete override.",
101
+ "Use subagentType='oracle' sparingly for cross-provider second opinions on high-stakes uncertainty or final plan/risk checks.",
102
+ "For screenshot/image inspection by blind models, use lookup; subagents only receive imagePaths when a broader delegated track genuinely needs them.",
102
103
  "If the user asks to start, run, launch, or test parallel sub-agents, call action='spawn' and then stop; completion/failure notifications will wake the parent so do not immediately call action='status' or action='wait' just to see whether agents finished.",
103
104
  "Use status for non-blocking progress/recovery, wait only when requested or needed, and result only after completion; registry mappings can recover latest runDir/agentId after reload.",
104
105
  "Result output is compact with artifact links; inspect raw artifacts only when full details are necessary.",
@@ -251,6 +252,17 @@ export const TODO_TOOL_DESCRIPTION: ToolDescription = {
251
252
  ],
252
253
  };
253
254
 
255
+ export const SESSION_NAME_TOOL_DESCRIPTION: ToolDescription = {
256
+ name: "session_name",
257
+ label: "Session Name",
258
+ description: "Show or set the current session name so the agent can retitle the active session without relying on slash-command parsing.",
259
+ promptSnippet: "Use session_name to rename the current session directly when the task calls for updating the session title.",
260
+ promptGuidelines: [
261
+ "Pass a short, user-meaningful name when renaming the current session.",
262
+ "Call without a name only when you need to read the current session name.",
263
+ ],
264
+ };
265
+
254
266
  export const WEB_SEARCH_TOOL_DESCRIPTIONS = {
255
267
  webSearch: {
256
268
  name: "web_search",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ui-extend",
3
- "version": "0.1.38",
3
+ "version": "0.1.41",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -64,9 +64,9 @@
64
64
  "prepublishOnly": "npm run check && npm run build:pix && npm run generate-schemas"
65
65
  },
66
66
  "dependencies": {
67
- "@earendil-works/pi-ai": "^0.79.6",
68
- "@earendil-works/pi-coding-agent": "^0.79.6",
69
- "@earendil-works/pi-tui": "^0.79.6",
67
+ "@earendil-works/pi-ai": "0.79.7",
68
+ "@earendil-works/pi-coding-agent": "0.79.7",
69
+ "@earendil-works/pi-tui": "0.79.7",
70
70
  "@mariozechner/clipboard": "^0.3.9",
71
71
  "jsonc-parser": "3.3.1",
72
72
  "typebox": "1.1.38",