@herbertgao/pi-extensions 2026.8.5 → 2026.8.6

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 (155) hide show
  1. package/README.md +5 -5
  2. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -1
  3. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -1
  4. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +74 -5
  5. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +48 -6
  6. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  7. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +10 -0
  8. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  9. package/node_modules/@herbertgao/pi-subagents/src/agent-runner.ts +5 -2
  10. package/node_modules/@herbertgao/pi-subagents/src/ui/fleet-list.ts +15 -6
  11. package/node_modules/@herbertgao/pi-subagents/src/worktree.ts +9 -5
  12. package/node_modules/@juicesharp/rpiv-ask-user-question/README.md +2 -0
  13. package/node_modules/@juicesharp/rpiv-ask-user-question/ask-user-question.ts +20 -0
  14. package/node_modules/@juicesharp/rpiv-ask-user-question/docs/hosts.md +6 -0
  15. package/node_modules/@juicesharp/rpiv-ask-user-question/package.json +2 -2
  16. package/node_modules/@narumitw/pi-btw/README.md +24 -17
  17. package/node_modules/@narumitw/pi-btw/package.json +5 -5
  18. package/node_modules/@narumitw/pi-btw/src/btw.ts +4 -2
  19. package/node_modules/@narumitw/pi-btw/src/menu.ts +33 -13
  20. package/node_modules/@narumitw/pi-btw/src/settings.ts +22 -2
  21. package/node_modules/pi-lens/CHANGELOG.md +95 -0
  22. package/node_modules/pi-lens/dist/clients/advisory-provenance.js +314 -0
  23. package/node_modules/pi-lens/dist/clients/agent-nudge.js +14 -7
  24. package/node_modules/pi-lens/dist/clients/biome-client.js +121 -13
  25. package/node_modules/pi-lens/dist/clients/bus-events-logger.js +62 -6
  26. package/node_modules/pi-lens/dist/clients/bus-publish.js +11 -3
  27. package/node_modules/pi-lens/dist/clients/cascade-format.js +57 -2
  28. package/node_modules/pi-lens/dist/clients/console-guard-install.js +16 -4
  29. package/node_modules/pi-lens/dist/clients/dead-code-client.js +135 -30
  30. package/node_modules/pi-lens/dist/clients/dependency-checker.js +19 -7
  31. package/node_modules/pi-lens/dist/clients/diagnostic-dispositions.js +6 -4
  32. package/node_modules/pi-lens/dist/clients/diagnostics-publish.js +10 -3
  33. package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +114 -10
  34. package/node_modules/pi-lens/dist/clients/dispatch/integration.js +101 -16
  35. package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +37 -4
  36. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +226 -0
  37. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +69 -0
  38. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +230 -50
  39. package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/toolchain-availability.js +97 -0
  40. package/node_modules/pi-lens/dist/clients/disposition-publish.js +10 -3
  41. package/node_modules/pi-lens/dist/clients/eval-timestamp.js +17 -0
  42. package/node_modules/pi-lens/dist/clients/extension-log.js +296 -3
  43. package/node_modules/pi-lens/dist/clients/fix-worklog.js +5 -1
  44. package/node_modules/pi-lens/dist/clients/format-events-publish.js +39 -8
  45. package/node_modules/pi-lens/dist/clients/git-guard.js +18 -21
  46. package/node_modules/pi-lens/dist/clients/go-client.js +21 -39
  47. package/node_modules/pi-lens/dist/clients/govulncheck-client.js +116 -7
  48. package/node_modules/pi-lens/dist/clients/host-ports.js +1 -1
  49. package/node_modules/pi-lens/dist/clients/installer/index.js +5 -1
  50. package/node_modules/pi-lens/dist/clients/jscpd-client.js +4 -9
  51. package/node_modules/pi-lens/dist/clients/knip-client.js +51 -14
  52. package/node_modules/pi-lens/dist/clients/latency-logger.js +50 -1
  53. package/node_modules/pi-lens/dist/clients/lens-events.js +56 -25
  54. package/node_modules/pi-lens/dist/clients/lens-flag-registry.js +8 -0
  55. package/node_modules/pi-lens/dist/clients/live-bus-emitter.js +45 -2
  56. package/node_modules/pi-lens/dist/clients/lsp/aggregation.js +30 -4
  57. package/node_modules/pi-lens/dist/clients/lsp/cascade-tier.js +58 -13
  58. package/node_modules/pi-lens/dist/clients/lsp/client.js +169 -8
  59. package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +29 -0
  60. package/node_modules/pi-lens/dist/clients/lsp/index.js +264 -66
  61. package/node_modules/pi-lens/dist/clients/lsp/server.js +247 -60
  62. package/node_modules/pi-lens/dist/clients/lsp/tsserver-sync.js +96 -0
  63. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/classification.js +21 -5
  64. package/node_modules/pi-lens/dist/clients/lsp/wait-policy/strategies.js +18 -3
  65. package/node_modules/pi-lens/dist/clients/mcp/analyze.js +4 -0
  66. package/node_modules/pi-lens/dist/clients/mcp/session.js +30 -17
  67. package/node_modules/pi-lens/dist/clients/model-provider.js +53 -0
  68. package/node_modules/pi-lens/dist/clients/pipeline.js +29 -2
  69. package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +1 -1
  70. package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +23 -2
  71. package/node_modules/pi-lens/dist/clients/review-graph/query.js +24 -0
  72. package/node_modules/pi-lens/dist/clients/run-duration.js +55 -0
  73. package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +153 -7
  74. package/node_modules/pi-lens/dist/clients/runtime-context.js +104 -11
  75. package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +170 -22
  76. package/node_modules/pi-lens/dist/clients/runtime-session.js +28 -5
  77. package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +124 -10
  78. package/node_modules/pi-lens/dist/clients/runtime-turn.js +388 -35
  79. package/node_modules/pi-lens/dist/clients/rust-client.js +21 -37
  80. package/node_modules/pi-lens/dist/clients/security-scan-client.js +88 -5
  81. package/node_modules/pi-lens/dist/clients/sg-runner.js +141 -23
  82. package/node_modules/pi-lens/dist/clients/smells-rollup.js +18 -11
  83. package/node_modules/pi-lens/dist/clients/startup-timing.js +7 -1
  84. package/node_modules/pi-lens/dist/clients/test-runner-client.js +431 -24
  85. package/node_modules/pi-lens/dist/clients/tool-policy.js +2 -0
  86. package/node_modules/pi-lens/dist/clients/tool-set-policy.js +76 -0
  87. package/node_modules/pi-lens/dist/clients/warm-attach.js +17 -0
  88. package/node_modules/pi-lens/dist/clients/word-index.js +305 -33
  89. package/node_modules/pi-lens/dist/index.js +4827 -1633
  90. package/node_modules/pi-lens/dist/mcp/server.js +8 -2
  91. package/node_modules/pi-lens/dist/tools/activate-tools.js +17 -5
  92. package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +9 -4
  93. package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +5 -2
  94. package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +3 -2
  95. package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +62 -7
  96. package/node_modules/pi-lens/dist/tools/symbol-search.js +1 -1
  97. package/node_modules/pi-lens/docs/agent-guide.md +38 -13
  98. package/node_modules/pi-lens/docs/ast-grep_rules_catalog.md +11 -3
  99. package/node_modules/pi-lens/docs/features.md +14 -1
  100. package/node_modules/pi-lens/docs/globalconfig.md +11 -0
  101. package/node_modules/pi-lens/docs/servercapabilities.md +1 -1
  102. package/node_modules/pi-lens/docs/settings.md +6 -0
  103. package/node_modules/pi-lens/docs/usage.md +23 -5
  104. package/node_modules/pi-lens/docs/word-index.md +35 -0
  105. package/node_modules/pi-lens/package.json +1 -1
  106. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-chained-type-assertions-test.yml +8 -0
  107. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-js-test.yml +9 -0
  108. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-conditional-empty-object-spread-test.yml +9 -0
  109. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-js-test.yml +7 -0
  110. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-apply-test.yml +7 -0
  111. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-js-test.yml +8 -0
  112. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-reflect-get-test.yml +8 -0
  113. package/node_modules/pi-lens/rules/ast-grep-rules/rule-tests/no-unknown-laundering-test.yml +11 -0
  114. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-chained-type-assertions.yml +21 -0
  115. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread-js.yml +21 -0
  116. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-conditional-empty-object-spread.yml +29 -0
  117. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply-js.yml +9 -0
  118. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-apply.yml +9 -0
  119. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get-js.yml +13 -0
  120. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-reflect-get.yml +16 -0
  121. package/node_modules/pi-lens/rules/ast-grep-rules/rules/no-unknown-laundering.yml +27 -0
  122. package/node_modules/pi-lens/scripts/analyze-pi-lens-logs.mjs +79 -0
  123. package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +10 -11
  124. package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +22 -22
  125. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/SKILL.md +8 -114
  126. package/node_modules/pi-lens/skills/pi-lens-write-ast-grep-rule/reference.md +129 -0
  127. package/node_modules/pi-lens/skills/pi-lens-write-tree-sitter-rule/SKILL.md +3 -1
  128. package/node_modules/pi-mcp-adapter/CHANGELOG.md +13 -0
  129. package/node_modules/pi-mcp-adapter/README.md +4 -1
  130. package/node_modules/pi-mcp-adapter/agent-dir.ts +12 -4
  131. package/node_modules/pi-mcp-adapter/cli.js +25 -4
  132. package/node_modules/pi-mcp-adapter/config.ts +4 -4
  133. package/node_modules/pi-mcp-adapter/direct-tools.ts +18 -15
  134. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +2 -1
  135. package/node_modules/pi-mcp-adapter/metadata-cache.ts +18 -8
  136. package/node_modules/pi-mcp-adapter/package.json +2 -1
  137. package/node_modules/pi-mcp-adapter/request-headers-command.ts +336 -0
  138. package/node_modules/pi-mcp-adapter/server-manager.ts +5 -0
  139. package/node_modules/pi-mcp-adapter/tool-metadata.ts +38 -18
  140. package/node_modules/pi-mcp-adapter/types.ts +93 -10
  141. package/node_modules/pi-web-access/CHANGELOG.md +14 -0
  142. package/node_modules/pi-web-access/README.md +18 -12
  143. package/node_modules/pi-web-access/auth-fetch.ts +148 -0
  144. package/node_modules/pi-web-access/chrome-cookies.ts +110 -23
  145. package/node_modules/pi-web-access/curator-page.ts +5 -3
  146. package/node_modules/pi-web-access/curator-server.ts +2 -1
  147. package/node_modules/pi-web-access/extract.ts +106 -34
  148. package/node_modules/pi-web-access/fetch-params.ts +17 -3
  149. package/node_modules/pi-web-access/firecrawl.ts +172 -12
  150. package/node_modules/pi-web-access/gemini-search.ts +18 -4
  151. package/node_modules/pi-web-access/index.ts +120 -48
  152. package/node_modules/pi-web-access/package.json +2 -2
  153. package/node_modules/pi-web-access/summary-review.ts +11 -5
  154. package/node_modules/pi-web-access/youtube-extract.ts +2 -2
  155. package/package.json +9 -9
package/README.md CHANGED
@@ -11,13 +11,13 @@ pi install npm:@herbertgao/pi-extensions
11
11
  The package bundles 11 active `@herbertgao/*` child packages, including `@herbertgao/pi-cc-extensions`, plus the following upstream packages under their original names:
12
12
 
13
13
  - `@dietrichgebert/ponytail@4.9.0`
14
- - `@juicesharp/rpiv-ask-user-question@2.5.2`
15
- - `@narumitw/pi-btw@0.51.0`
14
+ - `@juicesharp/rpiv-ask-user-question@2.6.0`
15
+ - `@narumitw/pi-btw@0.52.0`
16
16
  - `@pi-plugins/fast-mode@0.1.9`
17
- - `pi-mcp-adapter@2.25.0`
17
+ - `pi-mcp-adapter@2.26.0`
18
18
  - `pi-footer@0.5.1`
19
- - `pi-lens@4.0.0`
20
- - `pi-web-access@0.22.0`
19
+ - `pi-lens@4.0.1`
20
+ - `pi-web-access@0.23.0`
21
21
  - `remote-pi@0.7.0`
22
22
  - `@czottmann/pi-automode@1.11.0`
23
23
 
@@ -40,7 +40,7 @@ Run `/reload` after installation.
40
40
  | Claude Code Output | Tool summaries, expand/collapse, rich edit/write diffs, and `on` / `compact` / `off` modes | `/ccstyle` |
41
41
  | Fullscreen mode | Tool card/group click-to-toggle, previews, hover highlight, and a back-to-bottom button | `TUIMODE=fullscreen` or `--tui-mode fullscreen` |
42
42
  | Settings panel | `Style / Diff / Thinking / Feature` tabs with startup, wheel, and optional feature toggles | `/ccstyle` |
43
- | Context inspection | Usage breakdown and previews for the system prompt, tools, skills, and messages | `/context` |
43
+ | Context inspection | Usage breakdown and previews for the system prompt, tools, tool results, skills, and messages | `/context` |
44
44
  | Session/Subagent references | Search and inject effective context from previous Sessions or existing SubAgents | `@` |
45
45
  | Theme | Bundled CC Dark and CC Light themes | `/theme` |
46
46
 
@@ -40,7 +40,7 @@ pi install npm:@herbertgao/pi-cc-extensions
40
40
  | Claude Code UI | 工具摘要、折叠展开、rich edit/write diff,以及 `on` / `compact` / `off` 三种模式 | `/ccstyle` |
41
41
  | Fullscreen mode | 工具卡/group 点击展开与收起、预览、hover 高亮、回到底部按钮 | `TUIMODE=fullscreen` 或 `--tui-mode fullscreen` |
42
42
  | 配置面板 | `Style / Diff / Thinking / Feature` 四页签,含启动头、滚轮步进及可选功能开关 | `/ccstyle` |
43
- | 上下文检查 | 查看上下文占用,并预览 System prompt、Tools、Skills 和消息内容 | `/context` |
43
+ | 上下文检查 | 查看上下文占用,并预览 System prompt、Tools、Tool results、Skills 和消息内容 | `/context` |
44
44
  | Session/Subagent 引用 | 搜索并注入历史 Session 或现有 SubAgent 的有效上下文 | `@` |
45
45
  | 主题 | 随包提供内置 CC Dark、CC Light 主题 | `/theme` |
46
46
 
@@ -12,7 +12,7 @@ export type ContextPart = {
12
12
  color: "accent" | "success" | "warning" | "muted" | "dim";
13
13
  };
14
14
 
15
- type PreviewKey = "systemPrompt" | "tools" | "contextFiles" | "skills";
15
+ type PreviewKey = "systemPrompt" | "tools" | "toolResults" | "contextFiles" | "skills";
16
16
 
17
17
  type ContextPreview = {
18
18
  key: PreviewKey;
@@ -303,6 +303,48 @@ export function scaleParts(parts: ContextPart[], target: number): ContextPart[]
303
303
  return scaled;
304
304
  }
305
305
 
306
+ export function resolveUsedTokens(
307
+ usage: { tokens: number | null; percent: number | null } | undefined,
308
+ estimated: number,
309
+ contextWindow: number,
310
+ ): number {
311
+ const reported = usage?.tokens;
312
+ const fromPercent =
313
+ usage?.percent !== null && usage?.percent !== undefined && contextWindow > 0
314
+ ? Math.round((usage.percent / 100) * contextWindow)
315
+ : undefined;
316
+ let resolved = reported ?? fromPercent ?? estimated;
317
+ if (reported !== null && reported !== undefined && fromPercent !== undefined) {
318
+ const tolerance = Math.max(32, Math.round(contextWindow * 0.001));
319
+ if (Math.abs(reported - fromPercent) > tolerance) resolved = fromPercent;
320
+ }
321
+ if (estimated > 0 && resolved < estimated * 0.25) return estimated;
322
+ return resolved;
323
+ }
324
+
325
+ export function capParts(parts: ContextPart[], target: number, fixedPrefix = 0): ContextPart[] {
326
+ const fixed = parts.slice(0, fixedPrefix);
327
+ const variable = parts.slice(fixedPrefix);
328
+ const fixedTokens = fixed.reduce((sum, part) => sum + part.tokens, 0);
329
+ const variableTarget = Math.max(0, target - fixedTokens);
330
+ const estimated = variable.reduce((sum, part) => sum + part.tokens, 0);
331
+ if (estimated <= variableTarget || estimated === 0) return parts;
332
+ if (variableTarget === 0) return [...fixed, ...variable.map((part) => ({ ...part, tokens: 0 }))];
333
+ let previous = 0;
334
+ let cumulative = 0;
335
+ const capped = variable.map((part, index) => {
336
+ cumulative += part.tokens;
337
+ const next =
338
+ index === variable.length - 1
339
+ ? variableTarget
340
+ : Math.round((cumulative / estimated) * variableTarget);
341
+ const tokens = next - previous;
342
+ previous = next;
343
+ return { ...part, tokens };
344
+ });
345
+ return [...fixed, ...capped];
346
+ }
347
+
306
348
  export function formatTokens(tokens: number): string {
307
349
  if (tokens < 1_000) return String(tokens);
308
350
  if (tokens < 100_000) return `${(tokens / 1_000).toFixed(1)}k`;
@@ -326,13 +368,14 @@ type ContextBreakdown = {
326
368
  parts: ContextPart[];
327
369
  options: SystemPromptOptions;
328
370
  systemPrompt: string;
371
+ toolResults: string;
329
372
  };
330
373
 
331
374
  /**
332
375
  * Single pass over prompt options + session entries. Returns options/systemPrompt
333
376
  * so the /context UI does not re-fetch or re-stringify the same sources.
334
377
  */
335
- function collectContextBreakdown(ctx: ExtensionCommandContext): ContextBreakdown {
378
+ export function collectContextBreakdown(ctx: ExtensionCommandContext): ContextBreakdown {
336
379
  const options = (ctx.getSystemPromptOptions?.() ?? {}) as SystemPromptOptions;
337
380
  const systemPrompt = typeof ctx.getSystemPrompt === "function" ? ctx.getSystemPrompt() : "";
338
381
 
@@ -361,12 +404,16 @@ function collectContextBreakdown(ctx: ExtensionCommandContext): ContextBreakdown
361
404
  let assistant = 0;
362
405
  let toolResults = 0;
363
406
  let summaries = 0;
407
+ const toolResultPreview: string[] = [];
364
408
  for (const entry of ctx.sessionManager.buildContextEntries()) {
365
409
  if (entry.type === "message") {
366
410
  const tokens = estimateTokens(entry.message);
367
411
  if (entry.message.role === "user") user += tokens;
368
412
  else if (entry.message.role === "assistant") assistant += tokens;
369
413
  else toolResults += tokens;
414
+ if (entry.message.role === "toolResult") {
415
+ toolResultPreview.push(`## Tool result\n\n${JSON.stringify(entry.message, null, 2)}`);
416
+ }
370
417
  } else if (entry.type === "compaction" || entry.type === "branch_summary") {
371
418
  summaries += tokenEstimate(entry);
372
419
  }
@@ -388,6 +435,7 @@ function collectContextBreakdown(ctx: ExtensionCommandContext): ContextBreakdown
388
435
  parts: parts.filter((part) => part.tokens > 0),
389
436
  options,
390
437
  systemPrompt,
438
+ toolResults: toolResultPreview.join("\n\n") || "No tool results in the current context.",
391
439
  };
392
440
  }
393
441
 
@@ -398,10 +446,25 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
398
446
  const usage = ctx.getContextUsage();
399
447
  const contextWindow = usage?.contextWindow ?? ctx.model?.contextWindow ?? 0;
400
448
  const breakdown = collectContextBreakdown(ctx);
401
- const used = usage?.tokens ?? breakdown.parts.reduce((sum, part) => sum + part.tokens, 0);
402
- const parts = scaleParts(breakdown.parts, used);
449
+ const estimated = breakdown.parts.reduce((sum, part) => sum + part.tokens, 0);
450
+ const fixedParts = breakdown.parts.filter(
451
+ (part) => part.label === "System prompt" || part.label === "Tools",
452
+ );
453
+ const variableParts = breakdown.parts.filter(
454
+ (part) => part.label !== "System prompt" && part.label !== "Tools",
455
+ );
456
+ const orderedParts = [...fixedParts, ...variableParts];
457
+ const fixedTokens = fixedParts.reduce((sum, part) => sum + part.tokens, 0);
458
+ const used = Math.max(resolveUsedTokens(usage, estimated, contextWindow), fixedTokens);
459
+ const parts = capParts(orderedParts, used, fixedParts.length);
460
+ const attributed = parts.reduce((sum, part) => sum + part.tokens, 0);
461
+ const other = Math.max(0, used - attributed);
403
462
  const free = Math.max(0, contextWindow - used);
404
- const allParts = [...parts, { label: "Free space", tokens: free, color: "dim" as const }];
463
+ const allParts = [
464
+ ...parts,
465
+ { label: "Other", tokens: other, color: "muted" as const },
466
+ { label: "Free space", tokens: free, color: "dim" as const },
467
+ ];
405
468
 
406
469
  if (ctx.mode !== "tui") {
407
470
  const lines = allParts.map((part) => `${part.label}: ${formatTokens(part.tokens)} tokens`);
@@ -456,6 +519,12 @@ export default function contextUsageExtension(pi: ExtensionAPI) {
456
519
  title: "Tools",
457
520
  content: toolContent.join("\n\n") || "No active tools.",
458
521
  },
522
+ {
523
+ key: "toolResults",
524
+ label: "Tool results",
525
+ title: "Tool Results",
526
+ content: breakdown.toolResults,
527
+ },
459
528
  {
460
529
  key: "contextFiles",
461
530
  label: "Context files",
@@ -80,14 +80,20 @@ function renderAdmonition(lines: string[], i: number): { output: string[]; next:
80
80
  // 裸 URL 转可点击超链接
81
81
  // ============================================================================
82
82
 
83
- const URL_RE = /(?<!<)(?<!\]\()https?:\/\/[^\s<>'"|,。;:!?、」』】]+/g;
84
- const TRIM_URL_RE = /[.,;:!?】」』"']+$/;
83
+ const URL_RE = /(?<!<)(?<!\]\()https?:\/\/[^\s<>'"|,。;:!?、」』】()【】《》『』「」]+/g;
84
+ const TRIM_URL_RE = /[.,;:!?】」』"'》)}]+$/;
85
85
 
86
86
  /** 去掉 URL 尾部标点;括号按平衡保留(如 Wikipedia 链接含括号)。 */
87
87
  function trimUrl(url: string): string {
88
88
  let t = url.replace(TRIM_URL_RE, "");
89
- while (t.endsWith(")") && (t.match(/\(/g)?.length ?? 0) < (t.match(/\)/g)?.length ?? 0)) {
90
- t = t.slice(0, -1);
89
+ while (true) {
90
+ if (t.endsWith(")") && (t.match(/\(/g)?.length ?? 0) < (t.match(/\)/g)?.length ?? 0)) {
91
+ t = t.slice(0, -1);
92
+ } else if (t.endsWith("]") && (t.match(/\[/g)?.length ?? 0) < (t.match(/\]/g)?.length ?? 0)) {
93
+ t = t.slice(0, -1);
94
+ } else {
95
+ break;
96
+ }
91
97
  }
92
98
  return t;
93
99
  }
@@ -246,6 +252,41 @@ function deCircled(markdown: string): string {
246
252
  return output.join("\n");
247
253
  }
248
254
 
255
+ // ============================================================================
256
+ // 跨行链接规整
257
+ // ============================================================================
258
+
259
+ const MULTILINE_LINK_RE = /\[([^\]\n]*(?:\n[^\]\n]*)+)\](?=\()/g;
260
+
261
+ /** 防止 Markdown 跨行链接把行尾填充空格纳入 OSC 8 点击区。 */
262
+ function normalizeMultilineLinks(markdown: string): string {
263
+ const out: string[] = [];
264
+ let prose: string[] = [];
265
+ let fence: MarkdownFence | undefined;
266
+ const flush = () => {
267
+ if (prose.length === 0) return;
268
+ out.push(
269
+ prose.join("\n").replace(MULTILINE_LINK_RE, (_match, label: string) => {
270
+ const normalized = label.replace(/[ \t]*\n[ \t]*/g, " ").trim();
271
+ return `[${normalized}]`;
272
+ }),
273
+ );
274
+ prose = [];
275
+ };
276
+ for (const line of markdown.split("\n")) {
277
+ const scanned = scanMarkdownFence(line, fence);
278
+ fence = scanned.fence;
279
+ if (scanned.protected) {
280
+ flush();
281
+ out.push(line);
282
+ } else {
283
+ prose.push(line);
284
+ }
285
+ }
286
+ flush();
287
+ return out.join("\n");
288
+ }
289
+
249
290
  // ============================================================================
250
291
  // 注册
251
292
  // ============================================================================
@@ -255,8 +296,9 @@ export default function (pi: ExtensionAPI): void {
255
296
  // 所以三个转换合并为一次注册,内部按序链式执行。
256
297
  pi.registerMarkdownTransformer((markdown, context) => {
257
298
  const { messageType, isStreaming = false } = context ?? {};
258
- // 与官方推荐一致:thinking 块与流式中间态不转换
259
- if (messageType === "assistant-thinking" || isStreaming) return markdown;
299
+ if (messageType === "assistant-thinking") return markdown;
300
+ markdown = normalizeMultilineLinks(markdown);
301
+ if (isStreaming) return markdown;
260
302
  // 0. 圈数字转半角括号(Nerd Font 补丁字形缺陷规避)
261
303
  markdown = deCircled(markdown);
262
304
  // 1. Mermaid 方言渲染
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-cc-extensions",
3
- "version": "0.8.54",
3
+ "version": "0.8.55",
4
4
  "license": "MIT",
5
5
  "description": "A Pi productivity suite with Claude Code-style UI, context inspection, and agent/session references",
6
6
  "type": "module",
@@ -69,8 +69,8 @@
69
69
  },
70
70
  "x-upstream": {
71
71
  "package": "pi-cc-extensions",
72
- "version": "0.8.56",
72
+ "version": "0.8.60",
73
73
  "repository": "https://github.com/minuque/pi-cc-extensions",
74
- "commit": "6d83911f78628167b3ddb6e2e45787b829b54f55"
74
+ "commit": "268e017acde0b2adb65cefef3c740e37b1aef202"
75
75
  }
76
76
  }
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.15.5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#72](https://github.com/HerbertGao/pi-extensions/pull/72) [`5a133d4`](https://github.com/HerbertGao/pi-extensions/commit/5a133d4753532beef884b093638bceacf0545629) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Bypass Git signing for internal worktree preservation commits so interactive signers cannot time out and discard agent changes.
8
+
9
+ - [#73](https://github.com/HerbertGao/pi-extensions/pull/73) [`553f466`](https://github.com/HerbertGao/pi-extensions/commit/553f4662b968cacaf969c1f4e588d29403c54bfc) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Use the primary text color across selected FleetView rows while preserving configured agent badges, and record the reviewed post-0.16.1 upstream range.
10
+
11
+ - [#66](https://github.com/HerbertGao/pi-extensions/pull/66) [`af56b15`](https://github.com/HerbertGao/pi-extensions/commit/af56b159ce7081daf191d12da6afe3155e2b52b0) Thanks [@HerbertGao](https://github.com/HerbertGao)! - Sync the applicable pi-subagents 0.16.1 compatibility fix and advance the reviewed upstream baseline to bb47763 while preserving the local fork behavior.
12
+
3
13
  ## 0.15.4
4
14
 
5
15
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@herbertgao/pi-subagents",
3
- "version": "0.15.4",
3
+ "version": "0.15.5",
4
4
  "description": "Claude Code-style autonomous subagents for Pi, with HerbertGao-maintained UI extensions.",
5
5
  "keywords": [
6
6
  "agent",
@@ -68,8 +68,8 @@
68
68
  },
69
69
  "x-upstream": {
70
70
  "package": "@tintinweb/pi-subagents",
71
- "version": "0.16.0",
71
+ "version": "0.16.1",
72
72
  "repository": "https://github.com/tintinweb/pi-subagents",
73
- "commit": "bcbd602"
73
+ "commit": "bb47763"
74
74
  }
75
75
  }
@@ -958,18 +958,21 @@ export async function runAgent(
958
958
  type SessionOptions = NonNullable<Parameters<typeof createAgentSession>[0]>
959
959
  const parentModelRuntime = (
960
960
  ctx.modelRegistry as unknown as {
961
- runtime?: SessionOptions["modelRuntime"] | null
961
+ runtime?: unknown
962
962
  }
963
963
  ).runtime
964
964
  const sessionOpts: SessionOptions & {
965
965
  modelRegistry: ExtensionContext["modelRegistry"]
966
+ modelRuntime?: unknown
966
967
  } = {
967
968
  cwd: effectiveCwd,
968
969
  agentDir,
969
970
  sessionManager,
970
971
  settingsManager,
971
972
  modelRegistry: ctx.modelRegistry,
972
- ...(parentModelRuntime != null && { modelRuntime: parentModelRuntime }),
973
+ ...(parentModelRuntime != null && {
974
+ modelRuntime: parentModelRuntime as never,
975
+ }),
973
976
  model,
974
977
  tools: sessionTools,
975
978
  customTools: nestedTools,
@@ -19,7 +19,7 @@ import {
19
19
  truncateToWidth,
20
20
  visibleWidth,
21
21
  } from "@earendil-works/pi-tui"
22
- import { renderAgentName } from "../agent-color.js"
22
+ import { hasAgentBadge, renderAgentName } from "../agent-color.js"
23
23
  import type { AgentManager } from "../agent-manager.js"
24
24
  import type { AgentRecord } from "../types.js"
25
25
  import { getLifetimeTotal } from "../usage.js"
@@ -476,15 +476,24 @@ export class FleetList {
476
476
  width: number,
477
477
  theme: Theme,
478
478
  ): string {
479
- const left = ` ${this.bullet(rosterIndex, sel, theme)} ${renderAgentName(record.type, theme, { fallbackColor: "muted" })} ${record.description}`
479
+ const selected = rosterIndex === sel
480
+ const name = renderAgentName(
481
+ record.type,
482
+ theme,
483
+ selected
484
+ ? { fallbackColor: "text", bold: hasAgentBadge(record.type) }
485
+ : { fallbackColor: "muted" },
486
+ )
487
+ const description = selected
488
+ ? theme.fg("text", record.description)
489
+ : record.description
490
+ const left = ` ${this.bullet(rosterIndex, sel, theme)} ${name} ${description}`
480
491
  const tokens = getLifetimeTotal(
481
492
  this.agentActivity.get(record.id)?.lifetimeUsage ?? record.lifetimeUsage,
482
493
  )
483
494
  const elapsedMs = (record.completedAt ?? Date.now()) - record.startedAt // freezes once finished
484
- const right = theme.fg(
485
- "dim",
486
- `${formatFleetElapsed(elapsedMs)} · ${formatFleetTokens(tokens)}`,
487
- )
495
+ const stats = `${formatFleetElapsed(elapsedMs)} · ${formatFleetTokens(tokens)}`
496
+ const right = selected ? theme.fg("text", stats) : theme.fg("dim", stats)
488
497
  return rightAlign(left, right, width)
489
498
  }
490
499
  }
@@ -134,11 +134,15 @@ export function cleanupWorktree(
134
134
  // Truncate description for commit message (no shell sanitization needed — execFileSync uses argv)
135
135
  const safeDesc = agentDescription.slice(0, 200)
136
136
  const commitMsg = `pi-agent: ${safeDesc}`
137
- execFileSync("git", ["commit", "--no-verify", "-m", commitMsg], {
138
- cwd: worktree.path,
139
- stdio: "pipe",
140
- timeout: 10000,
141
- })
137
+ execFileSync(
138
+ "git",
139
+ ["commit", "--no-verify", "--no-gpg-sign", "-m", commitMsg],
140
+ {
141
+ cwd: worktree.path,
142
+ stdio: "pipe",
143
+ timeout: 10000,
144
+ },
145
+ )
142
146
  } else {
143
147
  const currentSha = execFileSync("git", ["rev-parse", "HEAD"], {
144
148
  cwd: worktree.path,
@@ -27,6 +27,8 @@ Nothing to set up — the tool is live as soon as Pi restarts. Hand the model a
27
27
 
28
28
  Rather than picking a strategy on your behalf, the model calls `ask_user_question` and a dialog takes over the bottom of your terminal. Move with `↑`/`↓`, choose with `Enter`, press `n` to attach a note, or land on the `Type something.` row to answer in your own words. While typing, `Shift+Enter` adds a line, `Ctrl+G` opens Pi's configured external editor, and `Ctrl+U` clears the draft; browsing another option and returning keeps what you wrote. `Esc` abandons the questionnaire entirely.
29
29
 
30
+ When the questionnaire begins waiting in an interactive TTY, it emits one standard terminal BEL (`\x07`). Your terminal configuration determines whether that appears as an audible alert, a visual alert, or nothing; redirected and non-TTY output is untouched.
31
+
30
32
  ![Single question in the dialog: the tab strip reads Feature Type, Design Tab, Testing, Release, Submit; the question Which real development task are we planning right now? sits above four numbered options — Bug fix (Recommended), New feature, Refactor, Perf tuning — each with a one-line description, followed by the appended Type something. row](https://raw.githubusercontent.com/juicesharp/rpiv-mono/main/packages/rpiv-ask-user-question/docs/single-question.jpg)
31
33
 
32
34
  When the model asks several things at once, `Tab` moves between them and a Submit tab reviews everything before it goes back:
@@ -61,6 +61,24 @@ const ERROR_SESSION_LOAD_FAILED =
61
61
  const ERROR_STALE_MODULE_CACHE =
62
62
  "Error: the questionnaire UI cannot load — the host's module cache went stale after an earlier failed load (typically dependencies replaced on disk mid-session). This is unrecoverable within the current Pi process. The user never saw the questions — do NOT treat this as a decline. Ask the questions as plain chat text instead, and tell the user to restart Pi to restore this tool.";
63
63
 
64
+ /** Standard terminal bell — same byte rpiv-warp exports as OSC_TERMINATOR. */
65
+ export const BEL = "\x07";
66
+
67
+ /**
68
+ * Emit one portable terminal attention signal without touching redirected output.
69
+ * Writes to stdout rather than rpiv-warp's `/dev/tty` transport: the `isTTY` gate
70
+ * both proves an interactive terminal owns the coming wait and keeps the byte out
71
+ * of piped RPC transports (VS Code pendant, Zed) — a `/dev/tty` write would ring
72
+ * even when the questionnaire renders in a remote host's own UI.
73
+ */
74
+ function emitTerminalAttention(): void {
75
+ try {
76
+ if (process.stdout.isTTY) process.stdout.write(BEL);
77
+ } catch {
78
+ // Terminal attention is best effort; the questionnaire must still proceed.
79
+ }
80
+ }
81
+
64
82
  /** Delay before the background session-graph pre-warm; mirrors rpiv-workflow's /wf prewarm. */
65
83
  export const PREWARM_DELAY_MS = 2000;
66
84
 
@@ -173,6 +191,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
173
191
  if ((ctx as { mode?: string }).mode === "rpc" && hasDialogUI(ctx.ui)) {
174
192
  emitAskUserBlockedEvent(pi, true);
175
193
  try {
194
+ emitTerminalAttention();
176
195
  return buildQuestionnaireResponse(await runRpcQuestionnaire(ctx.ui, typed), typed);
177
196
  } finally {
178
197
  emitAskUserBlockedEvent(pi, false);
@@ -232,6 +251,7 @@ Preview content is rendered as markdown in a monospace box. Multi-line text with
232
251
 
233
252
  emitAskUserBlockedEvent(pi, true);
234
253
  try {
254
+ emitTerminalAttention();
235
255
  const result = await ctx.ui.custom<QuestionnaireResult>(
236
256
  (tui, theme, keybindings, done) => {
237
257
  const session = new QuestionnaireSession({
@@ -11,6 +11,12 @@ render at all.
11
11
  | RPC / ACP host (VS Code pendant, Zed, Paseo) | `ask_user_question` in its tool list | A sequence of the host's own native select and input dialogs |
12
12
  | Non-interactive run (no UI) | Nothing — the tool is removed | Nothing |
13
13
 
14
+ ### Terminal attention
15
+
16
+ After UI availability and questionnaire validation succeed, the package emits exactly one standard terminal BEL (`\x07`) immediately before the interactive wait begins. The signal is sent to `stdout` only when `process.stdout.isTTY` is true, so redirected output and non-TTY RPC streams stay untouched. A TTY-backed RPC dialog walker receives the same signal as the TUI path.
17
+
18
+ The BEL is best effort: if the synchronous terminal write fails, the questionnaire continues and its existing prompt/blocked lifecycle and result envelope are unchanged. The terminal configuration decides whether the BEL is audible, visual, or ignored. No BEL is emitted for missing UI, invalid questionnaires, or a failed TUI session load.
19
+
14
20
  ### Non-interactive runs
15
21
 
16
22
  A `before_agent_start` hook reconciles the active tool set against `ctx.hasUI` before every
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juicesharp/rpiv-ask-user-question",
3
- "version": "2.5.2",
3
+ "version": "2.6.0",
4
4
  "description": "Pi extension. A structured questionnaire the model can put to you when it would otherwise guess, with typed options instead of free-form replies.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -93,7 +93,7 @@
93
93
  }
94
94
  },
95
95
  "dependencies": {
96
- "@juicesharp/rpiv-config": "^2.5.2",
96
+ "@juicesharp/rpiv-config": "^2.6.0",
97
97
  "typebox": "^1.1.24"
98
98
  }
99
99
  }
@@ -18,7 +18,7 @@ Use it when you want to ask a temporary question, inspect context, or get a shor
18
18
  - Optionally brings the latest answer, a question-to-end suffix, an exact line range, or the entire side thread into the main editor.
19
19
  - Uses the current session branch as context.
20
20
  - Uses Pi's current model or an independent model selected in `pi-btw.json`.
21
- - Uses a pi-btw thinking level that can be changed with Pi's configured thinking shortcut and remembered for next time.
21
+ - Uses a pi-btw thinking level that can either start from the main thread or use a fixed remembered value.
22
22
  - Does not append the side question or answer to the main conversation.
23
23
  - Works as an independently installable npm Pi extension package.
24
24
 
@@ -61,7 +61,7 @@ Examples:
61
61
  Running `/btw` alone opens a menu with **Start side thread** selected first.
62
62
  When the current Pi session has non-empty side threads in memory, **Resume side thread** opens a bounded searchable choice list.
63
63
  Search matches the displayed first question and question count while returning the thread's raw in-memory ID.
64
- **Settings** changes the starting thinking level and whether shortcut changes are remembered.
64
+ **Settings** changes the starting thinking level and whether shortcut changes for fixed levels are remembered.
65
65
  Each Resume row keeps the first question as its fixed title, shows its question count, and the list is ordered by the newest recorded answer or visible error.
66
66
  Opening and closing a thread without a new result does not reorder it.
67
67
  `/btw <question>` bypasses this menu and always starts a fresh side thread.
@@ -79,9 +79,11 @@ Previous side questions and answers remain available to the model and visible fo
79
79
  invocation. The side-thread header shows its current thinking level. Press Pi's configured
80
80
  `app.thinking.cycle` shortcut (`Shift+Tab` by default) in the composer to cycle the levels
81
81
  supported by the side-thread model; every later question uses the displayed level until it is
82
- changed again. By default, each shortcut change is also written to `pi-btw.json` for the next
83
- invocation. Turn **Remember thinking level changes** off in Settings to keep changes local to the
84
- current side thread. Neither path changes the main session's thinking level.
82
+ changed again. When a fixed thinking level is selected, each shortcut change is also written to
83
+ `pi-btw.json` for the next invocation by default. Turn **Remember thinking level changes** off in
84
+ Settings to keep fixed-level changes local to the current side thread. When **Same as main thread** is
85
+ selected, shortcut changes are always local to the current side thread. Neither path changes the main
86
+ session's thinking level.
85
87
  While a response is running, the transcript and composer remain visible above an `Answering…`
86
88
  status.
87
89
  Type another question and press `Enter` to queue it as `Steering`; queued questions are shown in
@@ -148,18 +150,23 @@ cannot be found or authenticated, pi-btw warns and falls back to the current ses
148
150
  If neither model is available, `/btw` reports an error and stops. This selection affects only
149
151
  `/btw`; it does not change the main session model.
150
152
 
151
- Pi calls its reasoning setting the **thinking level**. `thinkingLevel` sets pi-btw's starting
152
- level; accepted values are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. When the
153
- field is absent for backward compatibility, the next invocation starts from the current session
154
- level. The initial value and shortcut cycle are clamped to the selected side model's capabilities
155
- using Pi's model rules. Pi-btw does not read, write, or change the main session's
156
- `defaultThinkingLevel`.
157
-
158
- `rememberThinkingLevelChanges` controls only persistence and defaults to `true` when omitted. A
159
- side-thread shortcut always changes that side thread immediately. When remembering is on, the
160
- concrete level is written for the next invocation; when off, `pi-btw.json` stays unchanged. If a
161
- shortcut write fails, the local change remains active and pi-btw warns that it was not remembered.
162
- A failed Settings-screen save instead restores the previous displayed value.
153
+ Pi calls its reasoning setting the **thinking level**. In Settings, choose **Same as main thread**
154
+ to start each new side thread from the main thread's current thinking level. This is stored by
155
+ omitting `thinkingLevel` from `pi-btw.json`.
156
+
157
+ Set `thinkingLevel` only when you want a fixed pi-btw starting level. Accepted fixed values are
158
+ `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. The initial value and shortcut cycle
159
+ are clamped to the selected side model's capabilities using Pi's model rules. Resumed side threads
160
+ keep their own local thinking level instead of re-syncing with the main thread. Pi-btw does not read,
161
+ write, or change the main session's `defaultThinkingLevel`.
162
+
163
+ `rememberThinkingLevelChanges` controls only persistence for fixed thinking levels and defaults to
164
+ `true` when omitted. A side-thread shortcut always changes that side thread immediately. When a fixed
165
+ thinking level is selected and remembering is on, the concrete level is written for the next
166
+ invocation; when off, `pi-btw.json` stays unchanged. When **Same as main thread** is selected,
167
+ shortcut changes stay local even when remembering is on. If a shortcut write fails, the local change
168
+ remains active and pi-btw warns that it was not remembered. A failed Settings-screen save instead
169
+ restores the previous displayed value.
163
170
 
164
171
  A missing settings file is a side-effect-free read: pi-btw creates it only after a Settings change
165
172
  or a remembered shortcut change. Saves are ordered within the Pi process and published atomically
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@narumitw/pi-btw",
3
- "version": "0.51.0",
3
+ "version": "0.52.0",
4
4
  "description": "Pi extension that adds a /btw side-question command.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -31,10 +31,10 @@
31
31
  "typecheck": "tsc --noEmit"
32
32
  },
33
33
  "devDependencies": {
34
- "@biomejs/biome": "2.5.7",
35
- "@earendil-works/pi-ai": "0.84.1",
36
- "@earendil-works/pi-coding-agent": "0.84.1",
37
- "@earendil-works/pi-tui": "0.84.1",
34
+ "@biomejs/biome": "2.5.8",
35
+ "@earendil-works/pi-ai": "0.84.2",
36
+ "@earendil-works/pi-coding-agent": "0.84.2",
37
+ "@earendil-works/pi-tui": "0.84.2",
38
38
  "typescript": "7.0.2"
39
39
  },
40
40
  "repository": {
@@ -194,7 +194,7 @@ export async function loadBtwThinkingLevel(
194
194
 
195
195
  options.warn?.(
196
196
  sanitizeSingleLine(
197
- `pi-btw settings ignored: ${settings.reason}; expected optional model "provider/model-id", thinkingLevel "${BTW_THINKING_LEVELS.join('" | "')}", and boolean rememberThinkingLevelChanges. Using current Pi thinking level.`,
197
+ `pi-btw settings ignored: ${settings.reason}; expected optional model "provider/model-id", omitted thinkingLevel for Same as main thread or thinkingLevel "${BTW_THINKING_LEVELS.join('" | "')}", and boolean rememberThinkingLevelChanges. Using current Pi thinking level.`,
198
198
  ),
199
199
  );
200
200
  return currentThinkingLevel;
@@ -265,6 +265,7 @@ export default function btw(pi: ExtensionAPI, dependencies: BtwExtensionDependen
265
265
  }
266
266
 
267
267
  const settings = await loadSettings(ctx);
268
+ const sameAsMainThinkingLevel = settings.thinkingLevel === undefined;
268
269
  const resolution = await resolveModel(settings, ctx);
269
270
  if (resolution.kind === "cancelled") {
270
271
  notifySafely(ctx, "Cancelled", "info");
@@ -302,7 +303,8 @@ export default function btw(pi: ExtensionAPI, dependencies: BtwExtensionDependen
302
303
  initialQuestion: question || undefined,
303
304
  selected: resolution.selected,
304
305
  thinkingLevel: state.thinkingLevel,
305
- rememberThinkingLevelChanges: effectiveRememberThinkingLevelChanges(settings),
306
+ rememberThinkingLevelChanges:
307
+ !sameAsMainThinkingLevel && effectiveRememberThinkingLevelChanges(settings),
306
308
  state,
307
309
  ctx: fullscreenCtx,
308
310
  });