@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
@@ -16,6 +16,8 @@ import { runPipeline } from "./pipeline.js";
16
16
  import { appendProjectChange, } from "./project-changes.js";
17
17
  import { syncGitGuardRecord } from "./git-guard.js";
18
18
  import { scheduleWordIndexPersist } from "./word-index.js";
19
+ import { RUNTIME_CONFIG } from "./runtime-config.js";
20
+ const AUTHORITATIVE_CONTENT_MAX_BYTES = RUNTIME_CONFIG.pipeline.lspMaxFileBytes;
19
21
  function parseDiffRanges(diff) {
20
22
  const changedLines = [];
21
23
  for (const line of diff.split("\n")) {
@@ -207,6 +209,8 @@ export async function handleToolResult(deps) {
207
209
  : path.resolve(workspaceRoot, rawFilePath)
208
210
  : rawFilePath;
209
211
  const behaviorWarnings = agentBehaviorRecord(event.toolName, filePath);
212
+ const syntheticWriteContent = [];
213
+ let syntheticAttachmentBytes = 0;
210
214
  // Bash writes (redirects, tee, sed -i, cp/mv, touch, git checkout/restore) —
211
215
  // these change file content but never go through the edit tool, so bash
212
216
  // early-returns before the dispatch pipeline below. For each in-project file
@@ -224,11 +228,59 @@ export async function handleToolResult(deps) {
224
228
  for (const wp of written) {
225
229
  if (!getFlag("no-read-guard"))
226
230
  deps.readGuard?.recordWritten(wp);
227
- await handleToolResult({
231
+ const receipt = runtime.recordMutationToolReceipt;
232
+ const autofixMode = receipt
233
+ ? receipt.call(runtime, wp, "write").autofixMode
234
+ : "immediate";
235
+ const syntheticResult = await handleToolResult({
228
236
  ...deps,
229
237
  event: { ...event, toolName: "write", input: { path: wp } },
230
238
  _bypassDebounce: true,
239
+ _autofixMode: autofixMode,
231
240
  });
241
+ if (syntheticResult) {
242
+ // The per-attachment cap bounds each file, but a multi-file bash
243
+ // write (`sed -i` over globs, `;`-chained rewrites) appends one
244
+ // attachment per path — share ONE authoritative-content budget
245
+ // across the whole command so the aggregate tool result stays
246
+ // bounded too. Past the budget, degrade to the re-read warning.
247
+ for (const block of syntheticResult.content.slice(event.content.length)) {
248
+ const blockBytes = typeof block.text === "string"
249
+ ? Buffer.byteLength(block.text, "utf-8")
250
+ : 0;
251
+ const isAuthoritativeAttachment = typeof block.text === "string" &&
252
+ block.text.startsWith("pi-lens applied autofix to ");
253
+ if (isAuthoritativeAttachment &&
254
+ syntheticAttachmentBytes + blockBytes >
255
+ AUTHORITATIVE_CONTENT_MAX_BYTES) {
256
+ // S3e (#1432 review): this is the SECOND
257
+ // `authoritative_content_attachment_decision` row for `wp` —
258
+ // the synthetic `handleToolResult` call above already logged
259
+ // an "attached" row for the same path under its per-file
260
+ // cap. This outer, aggregate-budget row is logged later and
261
+ // is the one that matches what the caller actually sees
262
+ // (the re-read warning below, not the attachment), so it
263
+ // wins for `wp`; the inner "attached" row is a stale
264
+ // per-file view superseded by this shared-budget decision.
265
+ logLatency({
266
+ type: "phase",
267
+ phase: "authoritative_content_attachment_decision",
268
+ filePath: wp,
269
+ durationMs: 0,
270
+ metadata: { path: wp, bytes: blockBytes, decision: "aggregate-budget-degraded" },
271
+ });
272
+ syntheticWriteContent.push({
273
+ type: "text",
274
+ text: `⚠️ **File was modified by auto-format/fix. You MUST re-read ${wp} before making any further edits — the aggregate authoritative content for this command is too large to attach.**`,
275
+ });
276
+ continue;
277
+ }
278
+ if (isAuthoritativeAttachment) {
279
+ syntheticAttachmentBytes += blockBytes;
280
+ }
281
+ syntheticWriteContent.push(block);
282
+ }
283
+ }
232
284
  }
233
285
  if (event.isError !== true && !getFlag("no-read-guard")) {
234
286
  for (const span of extractReadPathsFromCommand(command, workspaceRoot)) {
@@ -277,7 +329,9 @@ export async function handleToolResult(deps) {
277
329
  }
278
330
  if (event.toolName !== "write" && event.toolName !== "edit") {
279
331
  dbg(`tool_result: skipped turn tracking - toolName="${event.toolName}" (not write/edit)`);
280
- return;
332
+ return syntheticWriteContent.length > 0
333
+ ? { content: [...event.content, ...syntheticWriteContent] }
334
+ : undefined;
281
335
  }
282
336
  if (!filePath) {
283
337
  dbg(`tool_result: skipped turn tracking - no filePath for toolName="${event.toolName}"`);
@@ -324,6 +378,16 @@ export async function handleToolResult(deps) {
324
378
  });
325
379
  return { content: event.content, isError: true };
326
380
  }
381
+ // Must happen before debounce admission: latestDeps intentionally retains only
382
+ // the latest event, but write -> edit is a sticky turn transition.
383
+ const receipt = runtime.recordMutationToolReceipt;
384
+ const autofixMode = deps._bypassDebounce
385
+ ? (deps._autofixMode ?? (event.toolName === "edit" ? "deferred" : "immediate"))
386
+ : receipt
387
+ ? receipt.call(runtime, filePath, event.toolName).autofixMode
388
+ : event.toolName === "edit"
389
+ ? "deferred"
390
+ : "immediate";
327
391
  // Coalesce sequential edits to the same file into one pipeline run against
328
392
  // the final state. Only the debounce-fired call (with _bypassDebounce=true)
329
393
  // proceeds to the pipeline body; in-window callers share its promise.
@@ -332,6 +396,7 @@ export async function handleToolResult(deps) {
332
396
  if (debounceMs > 0) {
333
397
  return scheduleDebounced(filePath, debounceMs, {
334
398
  ...deps,
399
+ _autofixMode: autofixMode,
335
400
  _telemetryParticipantIds: [readGuardCorrelationId],
336
401
  _telemetryParticipantTotal: 1,
337
402
  });
@@ -469,12 +534,15 @@ export async function handleToolResult(deps) {
469
534
  cwd: dispatchCwd,
470
535
  projectRoot: turnStateCwd,
471
536
  toolName: event.toolName,
537
+ autofixMode,
472
538
  modifiedRanges,
473
539
  telemetry: {
474
540
  model: runtime.telemetryModel,
475
541
  sessionId: runtime.telemetrySessionId,
476
542
  turnIndex: runtime.turnIndex,
477
543
  writeIndex,
544
+ modelId: runtime.telemetryModelId,
545
+ provider: runtime.telemetryProviderId,
478
546
  },
479
547
  getFlag,
480
548
  getFlagSource,
@@ -638,11 +706,19 @@ export async function handleToolResult(deps) {
638
706
  }
639
707
  }
640
708
  }
709
+ let autofixNewlyQueued = false;
710
+ if (!result.isError && autofixMode === "deferred" && nodeFs.existsSync(filePath)) {
711
+ autofixNewlyQueued =
712
+ runtime.deferMutation?.call(runtime, filePath, dispatchCwd, event.toolName, turnStateCwd, "autofix", deps.sessionId) ?? false;
713
+ dbg(`tool_result: queued deferred autofix for ${filePath}`);
714
+ }
715
+ let formatQueued = false;
641
716
  if (!result.isError &&
642
717
  !getFlag("no-autoformat", filePath) &&
643
- !getFlag("immediate-format") &&
718
+ (autofixMode === "deferred" || !getFlag("immediate-format")) &&
644
719
  nodeFs.existsSync(filePath)) {
645
720
  const isNewlyQueued = runtime.deferFormat(filePath, dispatchCwd, event.toolName, turnStateCwd, deps.sessionId);
721
+ formatQueued = true;
646
722
  dbg(`tool_result: queued deferred format for ${filePath}`);
647
723
  logLatency({
648
724
  type: "phase",
@@ -652,19 +728,21 @@ export async function handleToolResult(deps) {
652
728
  durationMs: 0,
653
729
  metadata: { cwd: dispatchCwd },
654
730
  });
655
- // #673: only publish on first queue entry — a re-touch of an already
656
- // queued file (a second edit before agent_end) is a structural no-op
657
- // for a listener that just wants to know "has this file entered the
658
- // queue", so re-emitting would be spam with zero new information.
659
- if (isNewlyQueued) {
731
+ // Publish a file's first queue entry and each newly added kind. A same-kind
732
+ // re-touch before agent_end carries no new information and stays silent.
733
+ if (isNewlyQueued || autofixNewlyQueued) {
660
734
  publishFormatQueued({
661
735
  filePath,
662
736
  cwd: dispatchCwd,
663
737
  tool: event.toolName,
664
738
  dbg,
739
+ kinds: autofixMode === "deferred" ? ["autofix", "format"] : ["format"],
665
740
  });
666
741
  }
667
742
  }
743
+ if (autofixNewlyQueued && !formatQueued) {
744
+ publishFormatQueued({ filePath, cwd: dispatchCwd, tool: event.toolName, kinds: ["autofix"], dbg });
745
+ }
668
746
  for (const changedFile of result.changedFiles ?? []) {
669
747
  const resolvedChanged = path.resolve(changedFile);
670
748
  if (!nodeFs.existsSync(resolvedChanged))
@@ -764,9 +842,45 @@ export async function handleToolResult(deps) {
764
842
  result: output ? "completed" : "no_output",
765
843
  });
766
844
  runtime.reportedThisTurn.add(filePath);
767
- if (!output)
845
+ const postMutation = result.postMutation;
846
+ const attachAuthoritativeContent = postMutation !== undefined &&
847
+ Buffer.byteLength(postMutation.content, "utf-8") <= AUTHORITATIVE_CONTENT_MAX_BYTES;
848
+ if (postMutation) {
849
+ const bytes = Buffer.byteLength(postMutation.content, "utf-8");
850
+ // S3e (#1432 review): when this call is the synthetic per-file
851
+ // `handleToolResult` recursion a multi-file bash write drives (see the
852
+ // bash branch above), the OUTER aggregate-budget loop may log a SECOND
853
+ // `authoritative_content_attachment_decision` row for this same
854
+ // `filePath` right after this one, downgrading an "attached" here to
855
+ // "aggregate-budget-degraded" once the shared budget is exhausted.
856
+ // Both rows are intentional (this one reflects the per-file cap
857
+ // decision; the outer one reflects the aggregate-budget decision that
858
+ // can override it) — the outer row, logged later, wins for that path.
859
+ logLatency({
860
+ type: "phase",
861
+ phase: "authoritative_content_attachment_decision",
862
+ filePath: postMutation.filePath,
863
+ durationMs: 0,
864
+ metadata: { path: postMutation.filePath, bytes, decision: attachAuthoritativeContent ? "attached" : "size-capped" },
865
+ });
866
+ }
867
+ const returnedContent = attachAuthoritativeContent
868
+ ? [
869
+ ...event.content,
870
+ {
871
+ type: "text",
872
+ text: `pi-lens applied autofix to ${postMutation.filePath}. The following full content is authoritative for subsequent edits:\n\n${postMutation.content}`,
873
+ },
874
+ ]
875
+ : event.content;
876
+ if (postMutation && !attachAuthoritativeContent) {
877
+ output = `${output ? `${output}\n\n` : ""}⚠️ **File was modified by auto-format/fix. You MUST re-read ${postMutation.filePath} before making any further edits — the authoritative content is too large to attach.**`;
878
+ }
879
+ if (!output && !result.postMutation)
768
880
  return;
769
881
  return {
770
- content: [...event.content, { type: "text", text: output }],
882
+ content: output
883
+ ? [...returnedContent, { type: "text", text: output }]
884
+ : returnedContent,
771
885
  };
772
886
  }