@code-yeongyu/senpi 2026.9.23-3 → 2026.9.23-5

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 (178) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/bundle/chunks/{app-server-command-U5Y46LHO.js → app-server-command-BGT65PGC.js} +1 -1
  3. package/dist/bundle/chunks/{chunk-UCTYTLP3.js → chunk-3RN6BFG2.js} +2 -2
  4. package/dist/bundle/chunks/{chunk-XTXBPXZZ.js → chunk-EN6KDFY2.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-2NFHX2FJ.js → chunk-J3QXPP4T.js} +1 -1
  6. package/dist/bundle/chunks/chunk-LTIMMSDM.js +8 -0
  7. package/dist/bundle/chunks/{chunk-67Z5WNA4.js → chunk-PSMTQDII.js} +1 -1
  8. package/dist/bundle/chunks/{chunk-TH4PERSI.js → chunk-RRSND33J.js} +50 -64
  9. package/dist/bundle/chunks/{chunk-Y4AJLTFT.js → chunk-WUYBEF7C.js} +1 -1
  10. package/dist/bundle/chunks/{chunk-IDSOMHWS.js → chunk-WXIRFRXY.js} +1 -1
  11. package/dist/bundle/chunks/{chunk-R6F525XB.js → chunk-XDSQYM4F.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-TUVSARCZ.js → chunk-ZEFQYQOU.js} +1 -1
  13. package/dist/bundle/chunks/{cli-main-65WMMIRT.js → cli-main-W55CWPEI.js} +1 -1
  14. package/dist/bundle/chunks/cursor-agent.js +1 -1
  15. package/dist/bundle/chunks/{help-fast-path-VXAZDQ3Z.js → help-fast-path-QXVCHTP3.js} +1 -1
  16. package/dist/bundle/chunks/{host-command-5PEFD4N6.js → host-command-3PUZYQOK.js} +1 -1
  17. package/dist/bundle/chunks/{host-lifecycle-BXW5C4JT.js → host-lifecycle-OHPSU5VU.js} +1 -1
  18. package/dist/bundle/chunks/{interactive-host-runtime-DUNYT24Q.js → interactive-host-runtime-57LEAJPU.js} +1 -1
  19. package/dist/bundle/chunks/{interactive-mode-SCG5PQZ6.js → interactive-mode-EDJZ4CSY.js} +1 -1
  20. package/dist/bundle/chunks/{multi-session-host-QC6BZZNH.js → multi-session-host-GFSDQQUJ.js} +1 -1
  21. package/dist/bundle/chunks/{package-manager-cli-23GULCVL.js → package-manager-cli-J7244EJE.js} +1 -1
  22. package/dist/bundle/chunks/{pi-messages-B6RSUQKL.js → pi-messages-CAS467IY.js} +1 -1
  23. package/dist/bundle/chunks/{rotation-stream-ZIYWOB2N.js → rotation-stream-CZT4TRPD.js} +1 -1
  24. package/dist/bundle/chunks/{rpc-mode-ZQ4E6FFA.js → rpc-mode-75CRTY24.js} +1 -1
  25. package/dist/bundle/chunks/{session-picker-CO2ZK3EC.js → session-picker-ZIBANFBQ.js} +1 -1
  26. package/dist/bundle/chunks/session-worker.js +56 -70
  27. package/dist/bundle/cli.js +1 -1
  28. package/dist/bundle/index.js +1 -1
  29. package/dist/bundle/rpc-entry.js +1 -1
  30. package/dist/core/agent-session.d.ts +9 -2
  31. package/dist/core/agent-session.d.ts.map +1 -1
  32. package/dist/core/agent-session.js +14 -1
  33. package/dist/core/agent-session.js.map +1 -1
  34. package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +43 -26
  36. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -1
  37. package/dist/core/extensions/builtin/nested-agents-md/index.js +1 -1
  38. package/dist/core/extensions/builtin/nested-agents-md/index.js.map +1 -1
  39. package/dist/core/extensions/builtin/rule-activation/types.d.ts +2 -0
  40. package/dist/core/extensions/builtin/rule-activation/types.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/rule-activation/types.js +7 -1
  42. package/dist/core/extensions/builtin/rule-activation/types.js.map +1 -1
  43. package/dist/core/extensions/builtin/rules/index.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/rules/index.js +2 -1
  45. package/dist/core/extensions/builtin/rules/index.js.map +1 -1
  46. package/dist/core/extensions/builtin/terminal/output-format.d.ts +15 -0
  47. package/dist/core/extensions/builtin/terminal/output-format.d.ts.map +1 -1
  48. package/dist/core/extensions/builtin/terminal/output-format.js +43 -2
  49. package/dist/core/extensions/builtin/terminal/output-format.js.map +1 -1
  50. package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
  51. package/dist/core/extensions/builtin/terminal/tools/bash-output.js +4 -3
  52. package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
  53. package/dist/core/extensions/builtin/terminal/tools/bash.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/terminal/tools/bash.js +3 -3
  55. package/dist/core/extensions/builtin/terminal/tools/bash.js.map +1 -1
  56. package/dist/core/extensions/builtin/terminal/tools/context.d.ts +7 -4
  57. package/dist/core/extensions/builtin/terminal/tools/context.d.ts.map +1 -1
  58. package/dist/core/extensions/builtin/terminal/tools/context.js +5 -0
  59. package/dist/core/extensions/builtin/terminal/tools/context.js.map +1 -1
  60. package/dist/core/extensions/builtin/terminal/tools/render.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/terminal/tools/render.js +5 -2
  62. package/dist/core/extensions/builtin/terminal/tools/render.js.map +1 -1
  63. package/dist/core/extensions/builtin/webfetch/webfetch/tool.d.ts +3 -1
  64. package/dist/core/extensions/builtin/webfetch/webfetch/tool.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +9 -3
  66. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js.map +1 -1
  67. package/dist/core/model-resolver.d.ts.map +1 -1
  68. package/dist/core/model-resolver.js +87 -14
  69. package/dist/core/model-resolver.js.map +1 -1
  70. package/dist/core/tool-call-display-name.d.ts +15 -0
  71. package/dist/core/tool-call-display-name.d.ts.map +1 -0
  72. package/dist/core/tool-call-display-name.js +14 -0
  73. package/dist/core/tool-call-display-name.js.map +1 -0
  74. package/dist/core/tools/bash.d.ts.map +1 -1
  75. package/dist/core/tools/bash.js +12 -7
  76. package/dist/core/tools/bash.js.map +1 -1
  77. package/dist/core/tools/find.d.ts.map +1 -1
  78. package/dist/core/tools/find.js +9 -6
  79. package/dist/core/tools/find.js.map +1 -1
  80. package/dist/core/tools/grep/format.d.ts +3 -2
  81. package/dist/core/tools/grep/format.d.ts.map +1 -1
  82. package/dist/core/tools/grep/format.js +5 -3
  83. package/dist/core/tools/grep/format.js.map +1 -1
  84. package/dist/core/tools/grep/index.js +2 -2
  85. package/dist/core/tools/grep/index.js.map +1 -1
  86. package/dist/core/tools/ls.d.ts.map +1 -1
  87. package/dist/core/tools/ls.js +5 -3
  88. package/dist/core/tools/ls.js.map +1 -1
  89. package/dist/core/tools/model-only-text.d.ts +4 -0
  90. package/dist/core/tools/model-only-text.d.ts.map +1 -0
  91. package/dist/core/tools/model-only-text.js +12 -0
  92. package/dist/core/tools/model-only-text.js.map +1 -0
  93. package/dist/core/tools/read.d.ts.map +1 -1
  94. package/dist/core/tools/read.js +15 -5
  95. package/dist/core/tools/read.js.map +1 -1
  96. package/dist/core/tools/render-utils.d.ts.map +1 -1
  97. package/dist/core/tools/render-utils.js +2 -1
  98. package/dist/core/tools/render-utils.js.map +1 -1
  99. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  100. package/dist/core/tools/renderers/bash.js +1 -25
  101. package/dist/core/tools/renderers/bash.js.map +1 -1
  102. package/dist/core/tools/renderers/grep.d.ts.map +1 -1
  103. package/dist/core/tools/renderers/grep.js +0 -25
  104. package/dist/core/tools/renderers/grep.js.map +1 -1
  105. package/dist/core/tools/renderers/read.d.ts +8 -0
  106. package/dist/core/tools/renderers/read.d.ts.map +1 -1
  107. package/dist/core/tools/renderers/read.js +2 -14
  108. package/dist/core/tools/renderers/read.js.map +1 -1
  109. package/dist/modes/interactive/components/exploration-call.d.ts +4 -1
  110. package/dist/modes/interactive/components/exploration-call.d.ts.map +1 -1
  111. package/dist/modes/interactive/components/exploration-call.js +17 -2
  112. package/dist/modes/interactive/components/exploration-call.js.map +1 -1
  113. package/dist/modes/interactive/components/exploration-group.d.ts +3 -1
  114. package/dist/modes/interactive/components/exploration-group.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/exploration-group.js +7 -1
  116. package/dist/modes/interactive/components/exploration-group.js.map +1 -1
  117. package/dist/modes/interactive/components/exploration-rules.d.ts +5 -0
  118. package/dist/modes/interactive/components/exploration-rules.d.ts.map +1 -0
  119. package/dist/modes/interactive/components/exploration-rules.js +15 -0
  120. package/dist/modes/interactive/components/exploration-rules.js.map +1 -0
  121. package/dist/modes/interactive/components/exploration-transcript-container.d.ts.map +1 -1
  122. package/dist/modes/interactive/components/exploration-transcript-container.js +10 -1
  123. package/dist/modes/interactive/components/exploration-transcript-container.js.map +1 -1
  124. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  125. package/dist/modes/interactive/components/tool-execution.js +4 -1
  126. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  127. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  128. package/dist/modes/interactive/interactive-mode.js +3 -1
  129. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  130. package/dist/modes/json-event.d.ts +1 -0
  131. package/dist/modes/json-event.d.ts.map +1 -1
  132. package/dist/modes/json-event.js +1 -0
  133. package/dist/modes/json-event.js.map +1 -1
  134. package/docs/json.md +4 -1
  135. package/docs/rpc.md +7 -1
  136. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +26 -0
  137. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  138. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +4 -19
  139. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  140. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +8 -23
  141. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  142. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.d.ts +8 -0
  143. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.d.ts.map +1 -1
  144. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.js +27 -0
  145. package/node_modules/@earendil-works/pi-agent-core/dist/tool-name-alias.js.map +1 -1
  146. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  147. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.d.ts.map +1 -1
  148. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js +17 -1
  149. package/node_modules/@earendil-works/pi-ai/dist/api/pi-messages.js.map +1 -1
  150. package/node_modules/@earendil-works/pi-ai/dist/cursor/catalog-grouping.d.ts +18 -1
  151. package/node_modules/@earendil-works/pi-ai/dist/cursor/catalog-grouping.d.ts.map +1 -1
  152. package/node_modules/@earendil-works/pi-ai/dist/cursor/catalog-grouping.js +103 -3
  153. package/node_modules/@earendil-works/pi-ai/dist/cursor/catalog-grouping.js.map +1 -1
  154. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.d.ts +3 -1
  155. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.d.ts.map +1 -1
  156. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.js +15 -3
  157. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.js.map +1 -1
  158. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.d.ts +7 -2
  159. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.d.ts.map +1 -1
  160. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js +92 -20
  161. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js.map +1 -1
  162. package/node_modules/@earendil-works/pi-ai/dist/model.d.ts +9 -1
  163. package/node_modules/@earendil-works/pi-ai/dist/model.d.ts.map +1 -1
  164. package/node_modules/@earendil-works/pi-ai/dist/model.js.map +1 -1
  165. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts.map +1 -1
  166. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js +1 -0
  167. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js.map +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  169. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +5 -0
  171. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  173. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  174. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  175. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  176. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  177. package/package.json +7 -7
  178. package/dist/bundle/chunks/chunk-XPDV6SIX.js +0 -8
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/cursor-cli-oauth/models.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAU1D,MAAM,MAAM,6BAA6B,GAAG;IAC3C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACvC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD,CAAC;AAEF,KAAK,gCAAgC,GAAG,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAC;AAEpF,MAAM,MAAM,mCAAmC,GAAG;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,6BAA6B,CAAC;IAClD,gHAAgH;IAChH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,gCAAgC,CAAC,GAAG,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAC;CACvG,CAAC;AA+DF,eAAO,MAAM,wBAAwB,EAAE,SAAS,mBAAmB,EAA+C,CAAC;AAEnH,wFAAwF;AACxF,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAiBpF;AAsGD,0FAA0F;AAC1F,wBAAsB,4BAA4B,CACjD,OAAO,EAAE,mCAAmC,GAC1C,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC,CAkCzC"}
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/cursor-cli-oauth/models.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAU1D,MAAM,MAAM,6BAA6B,GAAG;IAC3C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACvC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,sBAAsB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAChD,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD,CAAC;AAEF,KAAK,gCAAgC,GAAG,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAC;AAEpF,MAAM,MAAM,mCAAmC,GAAG;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,6BAA6B,CAAC;IAClD,gHAAgH;IAChH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,gCAAgC,CAAC,GAAG,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAC;CACvG,CAAC;AA0EF,eAAO,MAAM,wBAAwB,EAAE,SAAS,mBAAmB,EAA+C,CAAC;AAEnH,wFAAwF;AACxF,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAiBpF;AAmHD,0FAA0F;AAC1F,wBAAsB,4BAA4B,CACjD,OAAO,EAAE,mCAAmC,GAC1C,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC,CAoCzC"}
@@ -50,6 +50,7 @@ function normalizeEntries(raw) {
50
50
  capabilityId: entry.capabilityId,
51
51
  ...(entry.thinkingMode !== undefined ? { thinkingMode: entry.thinkingMode } : {}),
52
52
  representativeVariantId: entry.representativeVariantId,
53
+ ...(entry.variantIds !== undefined ? { variantIds: entry.variantIds } : {}),
53
54
  },
54
55
  }
55
56
  : {}),
@@ -109,30 +110,14 @@ function catalogTtlMs(settings) {
109
110
  const validHours = typeof hours === "number" && Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_MODEL_CATALOG_TTL_HOURS;
110
111
  return validHours * 60 * 60 * 1_000;
111
112
  }
112
- function parseCachedCatalog(contents) {
113
- let parsed;
114
- try {
115
- parsed = JSON.parse(contents);
116
- }
117
- catch {
118
- return undefined;
119
- }
120
- if (typeof parsed !== "object" || parsed === null || !("cachedAt" in parsed) || !("models" in parsed)) {
121
- return undefined;
122
- }
123
- const cachedAt = parsed.cachedAt;
124
- const models = parsed.models;
125
- if (typeof cachedAt !== "number" || !Number.isFinite(cachedAt) || !Array.isArray(models) || models.length === 0) {
126
- return undefined;
127
- }
113
+ function parseLegacyCachedModels(models) {
128
114
  const rawCached = [];
129
115
  const seen = new Set();
130
116
  for (const candidate of models) {
131
117
  if (typeof candidate !== "object" || candidate === null || !("id" in candidate) || !("name" in candidate)) {
132
118
  return undefined;
133
119
  }
134
- const id = candidate.id;
135
- const name = candidate.name;
120
+ const { id, name } = candidate;
136
121
  if (typeof id !== "string" ||
137
122
  !MODEL_ID.test(id) ||
138
123
  typeof name !== "string" ||
@@ -143,14 +128,44 @@ function parseCachedCatalog(contents) {
143
128
  seen.add(id);
144
129
  rawCached.push({ id, label: name });
145
130
  }
146
- return { cachedAt, models: normalizeEntries(rawCached) };
131
+ return normalizeEntries(rawCached);
132
+ }
133
+ function parseCachedCatalog(contents) {
134
+ let parsed;
135
+ try {
136
+ parsed = JSON.parse(contents);
137
+ }
138
+ catch {
139
+ return undefined;
140
+ }
141
+ if (typeof parsed !== "object" || parsed === null || !("cachedAt" in parsed) || !("models" in parsed)) {
142
+ return undefined;
143
+ }
144
+ const cachedAt = parsed.cachedAt;
145
+ const listing = "listing" in parsed ? parsed.listing : undefined;
146
+ const models = parsed.models;
147
+ if (typeof cachedAt !== "number" || !Number.isFinite(cachedAt) || !Array.isArray(models) || models.length === 0)
148
+ return undefined;
149
+ if (listing === undefined) {
150
+ const legacy = parseLegacyCachedModels(models);
151
+ return legacy === undefined ? undefined : { kind: "legacy", cachedAt, models: legacy };
152
+ }
153
+ if (typeof listing !== "string")
154
+ return undefined;
155
+ // The listing is the source of truth: a grouped id alone cannot reconstruct its observed
156
+ // variants, and the stored `models` projection carries mutable state (observed context
157
+ // windows) that must not invalidate an otherwise fresh cache. Rebuild from the listing.
158
+ const rebuilt = parseCursorAgentModelsListing(listing);
159
+ if (rebuilt.length === 0)
160
+ return undefined;
161
+ return { kind: "listing", cachedAt, models: rebuilt };
147
162
  }
148
163
  async function readFreshCache(cachePath, now, ttlMs, deps) {
149
164
  try {
150
165
  const cached = parseCachedCatalog(await deps.readTextFile(cachePath));
151
166
  if (!cached || now < cached.cachedAt || now - cached.cachedAt >= ttlMs)
152
167
  return undefined;
153
- return cached.models;
168
+ return cached;
154
169
  }
155
170
  catch {
156
171
  return undefined;
@@ -170,19 +185,21 @@ export async function resolveCursorCliModelCatalog(options) {
170
185
  const cachePath = join(cacheDirectory, "models.json");
171
186
  const now = deps.now();
172
187
  const cached = await readFreshCache(cachePath, now, catalogTtlMs(settings), deps);
173
- if (cached)
174
- return cached;
188
+ if (cached?.kind === "listing")
189
+ return cached.models;
190
+ const offlineFallback = cached?.models ?? STATIC_CURSOR_CLI_MODELS;
175
191
  let temporaryDirectory;
176
192
  try {
177
193
  const executable = deps.resolveExecutable();
178
194
  temporaryDirectory = await deps.makeTemporaryDirectory(join(tmpdir(), "senpi-cursor-models-"));
179
195
  const stdoutPath = join(temporaryDirectory, "stdout.txt");
180
196
  await deps.runProbe(executable, stdoutPath, MODEL_PROBE_TIMEOUT_MS);
181
- const models = parseCursorAgentModelsListing(await deps.readTextFile(stdoutPath));
197
+ const listing = await deps.readTextFile(stdoutPath);
198
+ const models = parseCursorAgentModelsListing(listing);
182
199
  if (models.length === 0)
183
- return STATIC_CURSOR_CLI_MODELS;
200
+ return offlineFallback;
184
201
  try {
185
- await writeCache(cacheDirectory, cachePath, { cachedAt: now, models }, deps);
202
+ await writeCache(cacheDirectory, cachePath, { cachedAt: now, listing, models }, deps);
186
203
  }
187
204
  catch {
188
205
  // A read-only cache directory must not prevent provider registration.
@@ -190,7 +207,7 @@ export async function resolveCursorCliModelCatalog(options) {
190
207
  return models;
191
208
  }
192
209
  catch {
193
- return STATIC_CURSOR_CLI_MODELS;
210
+ return offlineFallback;
194
211
  }
195
212
  finally {
196
213
  if (temporaryDirectory !== undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/cursor-cli-oauth/models.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { normalizeCursorCatalog } from \"@earendil-works/pi-ai\";\nimport { resolveCursorContextWindow } from \"@earendil-works/pi-ai/utils/cursor-context-limit\";\nimport type { ProviderModelConfig } from \"../../types.ts\";\nimport { defaultCursorAgentExecutableDeps, resolveCursorAgentExecutable } from \"./executable.ts\";\n\nconst MODEL_PROBE_TIMEOUT_MS = 15_000;\nconst DEFAULT_MODEL_CATALOG_TTL_HOURS = 24;\nconst ANSI_ESCAPE_SEQUENCE = /\\u001B(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~])/g;\nconst MODEL_LINE = /^(\\S+)\\s+-\\s+(.+)$/;\nconst MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._/:+-]*$/;\nconst MISLEADING_ERROR_LINE = /^\\s*(?:error|failed|failure)(?=\\s|:|-|$)/i;\n\nexport type CursorCliModelCatalogSettings = {\n\treadonly modelCatalogTtlHours?: number;\n\treadonly executablePath?: string;\n};\n\nexport type CursorCliModelCatalogDeps = {\n\tnow: () => number;\n\tresolveExecutable: () => string;\n\trunProbe: (executable: string, stdoutPath: string, timeoutMs: number) => Promise<void>;\n\tmakeTemporaryDirectory: (prefix: string) => Promise<string>;\n\treadTextFile: (path: string) => Promise<string>;\n\tmakeDirectory: (path: string) => Promise<void>;\n\twriteTextFile: (path: string, contents: string) => Promise<void>;\n\trenameFile: (from: string, to: string) => Promise<void>;\n\tremoveDirectory: (path: string) => Promise<void>;\n};\n\ntype CursorCliModelCatalogDefaultDeps = Omit<CursorCliModelCatalogDeps, \"runProbe\">;\n\nexport type ResolveCursorCliModelCatalogOptions = {\n\treadonly agentDir: string;\n\treadonly settings?: CursorCliModelCatalogSettings;\n\t/** The caller owns the probe: it decides the HOME (and therefore the account) `cursor-agent models` runs in. */\n\treadonly deps: Partial<CursorCliModelCatalogDefaultDeps> & Pick<CursorCliModelCatalogDeps, \"runProbe\">;\n};\n\ntype CachedModelCatalog = {\n\treadonly cachedAt: number;\n\treadonly models: readonly ProviderModelConfig[];\n};\n\ntype StaticModelDefinition = {\n\treadonly id: string;\n\treadonly label: string;\n};\n\nconst STATIC_MODEL_DEFINITIONS: readonly StaticModelDefinition[] = [\n\t{ id: \"auto\", label: \"Auto\" },\n\t{ id: \"composer-2.5\", label: \"Composer 2.5 (200K context)\" },\n\t{ id: \"composer-2.5-fast\", label: \"Composer 2.5 Fast (200K context)\" },\n\t{ id: \"gpt-5.6-sol-high\", label: \"GPT 5.6 SOL High (272K context)\" },\n\t{ id: \"gpt-5.6-luna-high\", label: \"GPT 5.6 Luna High (272K context)\" },\n\t{ id: \"gpt-5.5-high\", label: \"GPT 5.5 High (272K context)\" },\n\t{ id: \"gpt-5.3-codex\", label: \"GPT 5.3 Codex (272K context)\" },\n\t{ id: \"gpt-5.2\", label: \"GPT 5.2 (272K context)\" },\n\t{ id: \"claude-opus-5-high\", label: \"Claude Opus 5 High (300K context)\" },\n\t{ id: \"claude-opus-5-thinking-high\", label: \"Claude Opus 5 Thinking High (300K context)\" },\n\t{ id: \"claude-opus-4-8-thinking-high\", label: \"Claude Opus 4.8 Thinking High (300K context)\" },\n\t{ id: \"claude-fable-5-thinking-high\", label: \"Claude Fable 5 Thinking High (300K context)\" },\n\t{ id: \"claude-sonnet-5-thinking-high\", label: \"Claude Sonnet 5 Thinking High (300K context)\" },\n\t{ id: \"gemini-3.7-flash-high\", label: \"Gemini 3.7 Flash High (1M context)\" },\n\t{ id: \"cursor-grok-4.6-high\", label: \"Cursor Grok 4.6 High (200K context)\" },\n];\n\nfunction stripAnsi(value: string): string {\n\treturn value.replace(ANSI_ESCAPE_SEQUENCE, \"\");\n}\n\nfunction normalizeEntries(raw: readonly { id: string; label: string }[]): ProviderModelConfig[] {\n\treturn normalizeCursorCatalog(\n\t\traw.map(({ id, label }) => ({ id, name: label, input: [\"text\"] as const, cursorMaxMode: false })),\n\t).map((entry) => ({\n\t\tid: entry.id,\n\t\tname: entry.name,\n\t\treasoning: entry.reasoning,\n\t\t...(entry.thinkingLevelMap ? { thinkingLevelMap: entry.thinkingLevelMap } : {}),\n\t\tinput: [\"text\"] as (\"text\" | \"image\")[],\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t\tcontextWindow: resolveCursorContextWindow(entry.id, entry.window),\n\t\tmaxTokens: 64_000,\n\t\t...(entry.representativeVariantId !== undefined && entry.representativeVariantId !== entry.id\n\t\t\t? { upstreamModelId: entry.representativeVariantId }\n\t\t\t: {}),\n\t\tcompat: {\n\t\t\t...(entry.capabilityId !== undefined && entry.representativeVariantId !== undefined\n\t\t\t\t? {\n\t\t\t\t\t\tcursorReasoning: {\n\t\t\t\t\t\t\tcapabilityId: entry.capabilityId,\n\t\t\t\t\t\t\t...(entry.thinkingMode !== undefined ? { thinkingMode: entry.thinkingMode } : {}),\n\t\t\t\t\t\t\trepresentativeVariantId: entry.representativeVariantId,\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t},\n\t}));\n}\n\nexport const STATIC_CURSOR_CLI_MODELS: readonly ProviderModelConfig[] = normalizeEntries(STATIC_MODEL_DEFINITIONS);\n\n/** Parse the complete `cursor-agent models` listing into extension provider entries. */\nexport function parseCursorAgentModelsListing(listing: string): ProviderModelConfig[] {\n\tconst plainListing = stripAnsi(listing);\n\tconst lines = plainListing.split(/\\r?\\n/);\n\tif (lines.some((line) => MISLEADING_ERROR_LINE.test(line))) return [];\n\n\tconst seen = new Set<string>();\n\tconst raw: { id: string; label: string }[] = [];\n\tfor (const rawLine of lines) {\n\t\tconst match = MODEL_LINE.exec(rawLine.trim());\n\t\tif (!match) continue;\n\t\tconst id = match[1];\n\t\tconst label = match[2].trim();\n\t\tif (!MODEL_ID.test(id) || label.length === 0 || seen.has(id)) continue;\n\t\tseen.add(id);\n\t\traw.push({ id, label });\n\t}\n\treturn normalizeEntries(raw);\n}\n\nfunction defaultDeps(settings: CursorCliModelCatalogSettings): CursorCliModelCatalogDefaultDeps {\n\treturn {\n\t\tnow: Date.now,\n\t\tresolveExecutable: () => {\n\t\t\tconst executableDeps = defaultCursorAgentExecutableDeps();\n\t\t\treturn resolveCursorAgentExecutable({\n\t\t\t\t...executableDeps,\n\t\t\t\tsettings: { executablePath: settings.executablePath },\n\t\t\t});\n\t\t},\n\t\tmakeTemporaryDirectory: (prefix) => mkdtemp(prefix),\n\t\treadTextFile: (path) => readFile(path, \"utf8\"),\n\t\tmakeDirectory: async (path) => {\n\t\t\tawait mkdir(path, { recursive: true });\n\t\t},\n\t\twriteTextFile: async (path, contents) => {\n\t\t\tawait writeFile(path, contents, \"utf8\");\n\t\t},\n\t\trenameFile: async (from, to) => {\n\t\t\tawait rename(from, to);\n\t\t},\n\t\tremoveDirectory: async (path) => {\n\t\t\tawait rm(path, { recursive: true, force: true });\n\t\t},\n\t};\n}\n\nfunction catalogTtlMs(settings: CursorCliModelCatalogSettings): number {\n\tconst hours = settings.modelCatalogTtlHours;\n\tconst validHours =\n\t\ttypeof hours === \"number\" && Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_MODEL_CATALOG_TTL_HOURS;\n\treturn validHours * 60 * 60 * 1_000;\n}\n\nfunction parseCachedCatalog(contents: string): CachedModelCatalog | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(contents);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || !(\"cachedAt\" in parsed) || !(\"models\" in parsed)) {\n\t\treturn undefined;\n\t}\n\tconst cachedAt = parsed.cachedAt;\n\tconst models = parsed.models;\n\tif (typeof cachedAt !== \"number\" || !Number.isFinite(cachedAt) || !Array.isArray(models) || models.length === 0) {\n\t\treturn undefined;\n\t}\n\n\tconst rawCached: { id: string; label: string }[] = [];\n\tconst seen = new Set<string>();\n\tfor (const candidate of models) {\n\t\tif (typeof candidate !== \"object\" || candidate === null || !(\"id\" in candidate) || !(\"name\" in candidate)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst id = candidate.id;\n\t\tconst name = candidate.name;\n\t\tif (\n\t\t\ttypeof id !== \"string\" ||\n\t\t\t!MODEL_ID.test(id) ||\n\t\t\ttypeof name !== \"string\" ||\n\t\t\tname.length === 0 ||\n\t\t\tseen.has(id)\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tseen.add(id);\n\t\trawCached.push({ id, label: name });\n\t}\n\treturn { cachedAt, models: normalizeEntries(rawCached) };\n}\n\nasync function readFreshCache(\n\tcachePath: string,\n\tnow: number,\n\tttlMs: number,\n\tdeps: CursorCliModelCatalogDeps,\n): Promise<readonly ProviderModelConfig[] | undefined> {\n\ttry {\n\t\tconst cached = parseCachedCatalog(await deps.readTextFile(cachePath));\n\t\tif (!cached || now < cached.cachedAt || now - cached.cachedAt >= ttlMs) return undefined;\n\t\treturn cached.models;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nasync function writeCache(\n\tcacheDirectory: string,\n\tcachePath: string,\n\tcatalog: CachedModelCatalog,\n\tdeps: CursorCliModelCatalogDeps,\n): Promise<void> {\n\tconst temporaryPath = `${cachePath}.${process.pid}.${catalog.cachedAt}.tmp`;\n\tawait deps.makeDirectory(cacheDirectory);\n\tawait deps.writeTextFile(temporaryPath, `${JSON.stringify(catalog, null, 2)}\\n`);\n\tawait deps.renameFile(temporaryPath, cachePath);\n}\n\n/** Resolve a cached or probed catalog, always degrading to the exact offline fallback. */\nexport async function resolveCursorCliModelCatalog(\n\toptions: ResolveCursorCliModelCatalogOptions,\n): Promise<readonly ProviderModelConfig[]> {\n\tconst settings = options.settings ?? {};\n\tconst deps: CursorCliModelCatalogDeps = { ...defaultDeps(settings), ...options.deps };\n\tconst cacheDirectory = join(options.agentDir, \"cursor-cli-oauth\");\n\tconst cachePath = join(cacheDirectory, \"models.json\");\n\tconst now = deps.now();\n\tconst cached = await readFreshCache(cachePath, now, catalogTtlMs(settings), deps);\n\tif (cached) return cached;\n\n\tlet temporaryDirectory: string | undefined;\n\ttry {\n\t\tconst executable = deps.resolveExecutable();\n\t\ttemporaryDirectory = await deps.makeTemporaryDirectory(join(tmpdir(), \"senpi-cursor-models-\"));\n\t\tconst stdoutPath = join(temporaryDirectory, \"stdout.txt\");\n\t\tawait deps.runProbe(executable, stdoutPath, MODEL_PROBE_TIMEOUT_MS);\n\t\tconst models = parseCursorAgentModelsListing(await deps.readTextFile(stdoutPath));\n\t\tif (models.length === 0) return STATIC_CURSOR_CLI_MODELS;\n\t\ttry {\n\t\t\tawait writeCache(cacheDirectory, cachePath, { cachedAt: now, models }, deps);\n\t\t} catch {\n\t\t\t// A read-only cache directory must not prevent provider registration.\n\t\t}\n\t\treturn models;\n\t} catch {\n\t\treturn STATIC_CURSOR_CLI_MODELS;\n\t} finally {\n\t\tif (temporaryDirectory !== undefined) {\n\t\t\ttry {\n\t\t\t\tawait deps.removeDirectory(temporaryDirectory);\n\t\t\t} catch {\n\t\t\t\t// Best-effort cleanup must not replace the catalog result.\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/cursor-cli-oauth/models.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { normalizeCursorCatalog } from \"@earendil-works/pi-ai\";\nimport { resolveCursorContextWindow } from \"@earendil-works/pi-ai/utils/cursor-context-limit\";\nimport type { ProviderModelConfig } from \"../../types.ts\";\nimport { defaultCursorAgentExecutableDeps, resolveCursorAgentExecutable } from \"./executable.ts\";\n\nconst MODEL_PROBE_TIMEOUT_MS = 15_000;\nconst DEFAULT_MODEL_CATALOG_TTL_HOURS = 24;\nconst ANSI_ESCAPE_SEQUENCE = /\\u001B(?:[@-Z\\\\-_]|\\[[0-?]*[ -/]*[@-~])/g;\nconst MODEL_LINE = /^(\\S+)\\s+-\\s+(.+)$/;\nconst MODEL_ID = /^[A-Za-z0-9][A-Za-z0-9._/:+-]*$/;\nconst MISLEADING_ERROR_LINE = /^\\s*(?:error|failed|failure)(?=\\s|:|-|$)/i;\n\nexport type CursorCliModelCatalogSettings = {\n\treadonly modelCatalogTtlHours?: number;\n\treadonly executablePath?: string;\n};\n\nexport type CursorCliModelCatalogDeps = {\n\tnow: () => number;\n\tresolveExecutable: () => string;\n\trunProbe: (executable: string, stdoutPath: string, timeoutMs: number) => Promise<void>;\n\tmakeTemporaryDirectory: (prefix: string) => Promise<string>;\n\treadTextFile: (path: string) => Promise<string>;\n\tmakeDirectory: (path: string) => Promise<void>;\n\twriteTextFile: (path: string, contents: string) => Promise<void>;\n\trenameFile: (from: string, to: string) => Promise<void>;\n\tremoveDirectory: (path: string) => Promise<void>;\n};\n\ntype CursorCliModelCatalogDefaultDeps = Omit<CursorCliModelCatalogDeps, \"runProbe\">;\n\nexport type ResolveCursorCliModelCatalogOptions = {\n\treadonly agentDir: string;\n\treadonly settings?: CursorCliModelCatalogSettings;\n\t/** The caller owns the probe: it decides the HOME (and therefore the account) `cursor-agent models` runs in. */\n\treadonly deps: Partial<CursorCliModelCatalogDefaultDeps> & Pick<CursorCliModelCatalogDeps, \"runProbe\">;\n};\n\ntype CachedModelCatalog = {\n\treadonly cachedAt: number;\n\treadonly listing: string;\n\treadonly models: readonly ProviderModelConfig[];\n};\n\n/**\n * A cache record read back from disk. `legacy` records predate the stored listing: they\n * cannot reconstruct derived variant ids, so they never short-circuit the probe, but they\n * remain the offline fallback when that probe fails, exactly as they were served before.\n */\ntype CachedModelRecord =\n\t| { readonly kind: \"listing\"; readonly cachedAt: number; readonly models: readonly ProviderModelConfig[] }\n\t| { readonly kind: \"legacy\"; readonly cachedAt: number; readonly models: readonly ProviderModelConfig[] };\n\ntype StaticModelDefinition = {\n\treadonly id: string;\n\treadonly label: string;\n};\n\nconst STATIC_MODEL_DEFINITIONS: readonly StaticModelDefinition[] = [\n\t{ id: \"auto\", label: \"Auto\" },\n\t{ id: \"composer-2.5\", label: \"Composer 2.5 (200K context)\" },\n\t{ id: \"composer-2.5-fast\", label: \"Composer 2.5 Fast (200K context)\" },\n\t{ id: \"gpt-5.6-sol-high\", label: \"GPT 5.6 SOL High (272K context)\" },\n\t{ id: \"gpt-5.6-luna-high\", label: \"GPT 5.6 Luna High (272K context)\" },\n\t{ id: \"gpt-5.5-high\", label: \"GPT 5.5 High (272K context)\" },\n\t{ id: \"gpt-5.3-codex\", label: \"GPT 5.3 Codex (272K context)\" },\n\t{ id: \"gpt-5.2\", label: \"GPT 5.2 (272K context)\" },\n\t{ id: \"claude-opus-5-high\", label: \"Claude Opus 5 High (300K context)\" },\n\t{ id: \"claude-opus-5-thinking-high\", label: \"Claude Opus 5 Thinking High (300K context)\" },\n\t{ id: \"claude-opus-4-8-thinking-high\", label: \"Claude Opus 4.8 Thinking High (300K context)\" },\n\t{ id: \"claude-fable-5-thinking-high\", label: \"Claude Fable 5 Thinking High (300K context)\" },\n\t{ id: \"claude-sonnet-5-thinking-high\", label: \"Claude Sonnet 5 Thinking High (300K context)\" },\n\t{ id: \"gemini-3.7-flash-high\", label: \"Gemini 3.7 Flash High (1M context)\" },\n\t{ id: \"cursor-grok-4.6-high\", label: \"Cursor Grok 4.6 High (200K context)\" },\n];\n\nfunction stripAnsi(value: string): string {\n\treturn value.replace(ANSI_ESCAPE_SEQUENCE, \"\");\n}\n\nfunction normalizeEntries(raw: readonly { id: string; label: string }[]): ProviderModelConfig[] {\n\treturn normalizeCursorCatalog(\n\t\traw.map(({ id, label }) => ({ id, name: label, input: [\"text\"] as const, cursorMaxMode: false })),\n\t).map((entry) => ({\n\t\tid: entry.id,\n\t\tname: entry.name,\n\t\treasoning: entry.reasoning,\n\t\t...(entry.thinkingLevelMap ? { thinkingLevelMap: entry.thinkingLevelMap } : {}),\n\t\tinput: [\"text\"] as (\"text\" | \"image\")[],\n\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\t\tcontextWindow: resolveCursorContextWindow(entry.id, entry.window),\n\t\tmaxTokens: 64_000,\n\t\t...(entry.representativeVariantId !== undefined && entry.representativeVariantId !== entry.id\n\t\t\t? { upstreamModelId: entry.representativeVariantId }\n\t\t\t: {}),\n\t\tcompat: {\n\t\t\t...(entry.capabilityId !== undefined && entry.representativeVariantId !== undefined\n\t\t\t\t? {\n\t\t\t\t\t\tcursorReasoning: {\n\t\t\t\t\t\t\tcapabilityId: entry.capabilityId,\n\t\t\t\t\t\t\t...(entry.thinkingMode !== undefined ? { thinkingMode: entry.thinkingMode } : {}),\n\t\t\t\t\t\t\trepresentativeVariantId: entry.representativeVariantId,\n\t\t\t\t\t\t\t...(entry.variantIds !== undefined ? { variantIds: entry.variantIds } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t},\n\t}));\n}\n\nexport const STATIC_CURSOR_CLI_MODELS: readonly ProviderModelConfig[] = normalizeEntries(STATIC_MODEL_DEFINITIONS);\n\n/** Parse the complete `cursor-agent models` listing into extension provider entries. */\nexport function parseCursorAgentModelsListing(listing: string): ProviderModelConfig[] {\n\tconst plainListing = stripAnsi(listing);\n\tconst lines = plainListing.split(/\\r?\\n/);\n\tif (lines.some((line) => MISLEADING_ERROR_LINE.test(line))) return [];\n\n\tconst seen = new Set<string>();\n\tconst raw: { id: string; label: string }[] = [];\n\tfor (const rawLine of lines) {\n\t\tconst match = MODEL_LINE.exec(rawLine.trim());\n\t\tif (!match) continue;\n\t\tconst id = match[1];\n\t\tconst label = match[2].trim();\n\t\tif (!MODEL_ID.test(id) || label.length === 0 || seen.has(id)) continue;\n\t\tseen.add(id);\n\t\traw.push({ id, label });\n\t}\n\treturn normalizeEntries(raw);\n}\n\nfunction defaultDeps(settings: CursorCliModelCatalogSettings): CursorCliModelCatalogDefaultDeps {\n\treturn {\n\t\tnow: Date.now,\n\t\tresolveExecutable: () => {\n\t\t\tconst executableDeps = defaultCursorAgentExecutableDeps();\n\t\t\treturn resolveCursorAgentExecutable({\n\t\t\t\t...executableDeps,\n\t\t\t\tsettings: { executablePath: settings.executablePath },\n\t\t\t});\n\t\t},\n\t\tmakeTemporaryDirectory: (prefix) => mkdtemp(prefix),\n\t\treadTextFile: (path) => readFile(path, \"utf8\"),\n\t\tmakeDirectory: async (path) => {\n\t\t\tawait mkdir(path, { recursive: true });\n\t\t},\n\t\twriteTextFile: async (path, contents) => {\n\t\t\tawait writeFile(path, contents, \"utf8\");\n\t\t},\n\t\trenameFile: async (from, to) => {\n\t\t\tawait rename(from, to);\n\t\t},\n\t\tremoveDirectory: async (path) => {\n\t\t\tawait rm(path, { recursive: true, force: true });\n\t\t},\n\t};\n}\n\nfunction catalogTtlMs(settings: CursorCliModelCatalogSettings): number {\n\tconst hours = settings.modelCatalogTtlHours;\n\tconst validHours =\n\t\ttypeof hours === \"number\" && Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_MODEL_CATALOG_TTL_HOURS;\n\treturn validHours * 60 * 60 * 1_000;\n}\n\nfunction parseLegacyCachedModels(models: readonly unknown[]): ProviderModelConfig[] | undefined {\n\tconst rawCached: { id: string; label: string }[] = [];\n\tconst seen = new Set<string>();\n\tfor (const candidate of models) {\n\t\tif (typeof candidate !== \"object\" || candidate === null || !(\"id\" in candidate) || !(\"name\" in candidate)) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst { id, name } = candidate;\n\t\tif (\n\t\t\ttypeof id !== \"string\" ||\n\t\t\t!MODEL_ID.test(id) ||\n\t\t\ttypeof name !== \"string\" ||\n\t\t\tname.length === 0 ||\n\t\t\tseen.has(id)\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tseen.add(id);\n\t\trawCached.push({ id, label: name });\n\t}\n\treturn normalizeEntries(rawCached);\n}\n\nfunction parseCachedCatalog(contents: string): CachedModelRecord | undefined {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(contents);\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || !(\"cachedAt\" in parsed) || !(\"models\" in parsed)) {\n\t\treturn undefined;\n\t}\n\tconst cachedAt = parsed.cachedAt;\n\tconst listing = \"listing\" in parsed ? parsed.listing : undefined;\n\tconst models = parsed.models;\n\tif (typeof cachedAt !== \"number\" || !Number.isFinite(cachedAt) || !Array.isArray(models) || models.length === 0)\n\t\treturn undefined;\n\tif (listing === undefined) {\n\t\tconst legacy = parseLegacyCachedModels(models);\n\t\treturn legacy === undefined ? undefined : { kind: \"legacy\", cachedAt, models: legacy };\n\t}\n\tif (typeof listing !== \"string\") return undefined;\n\n\t// The listing is the source of truth: a grouped id alone cannot reconstruct its observed\n\t// variants, and the stored `models` projection carries mutable state (observed context\n\t// windows) that must not invalidate an otherwise fresh cache. Rebuild from the listing.\n\tconst rebuilt = parseCursorAgentModelsListing(listing);\n\tif (rebuilt.length === 0) return undefined;\n\treturn { kind: \"listing\", cachedAt, models: rebuilt };\n}\n\nasync function readFreshCache(\n\tcachePath: string,\n\tnow: number,\n\tttlMs: number,\n\tdeps: CursorCliModelCatalogDeps,\n): Promise<CachedModelRecord | undefined> {\n\ttry {\n\t\tconst cached = parseCachedCatalog(await deps.readTextFile(cachePath));\n\t\tif (!cached || now < cached.cachedAt || now - cached.cachedAt >= ttlMs) return undefined;\n\t\treturn cached;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nasync function writeCache(\n\tcacheDirectory: string,\n\tcachePath: string,\n\tcatalog: CachedModelCatalog,\n\tdeps: CursorCliModelCatalogDeps,\n): Promise<void> {\n\tconst temporaryPath = `${cachePath}.${process.pid}.${catalog.cachedAt}.tmp`;\n\tawait deps.makeDirectory(cacheDirectory);\n\tawait deps.writeTextFile(temporaryPath, `${JSON.stringify(catalog, null, 2)}\\n`);\n\tawait deps.renameFile(temporaryPath, cachePath);\n}\n\n/** Resolve a cached or probed catalog, always degrading to the exact offline fallback. */\nexport async function resolveCursorCliModelCatalog(\n\toptions: ResolveCursorCliModelCatalogOptions,\n): Promise<readonly ProviderModelConfig[]> {\n\tconst settings = options.settings ?? {};\n\tconst deps: CursorCliModelCatalogDeps = { ...defaultDeps(settings), ...options.deps };\n\tconst cacheDirectory = join(options.agentDir, \"cursor-cli-oauth\");\n\tconst cachePath = join(cacheDirectory, \"models.json\");\n\tconst now = deps.now();\n\tconst cached = await readFreshCache(cachePath, now, catalogTtlMs(settings), deps);\n\tif (cached?.kind === \"listing\") return cached.models;\n\tconst offlineFallback = cached?.models ?? STATIC_CURSOR_CLI_MODELS;\n\n\tlet temporaryDirectory: string | undefined;\n\ttry {\n\t\tconst executable = deps.resolveExecutable();\n\t\ttemporaryDirectory = await deps.makeTemporaryDirectory(join(tmpdir(), \"senpi-cursor-models-\"));\n\t\tconst stdoutPath = join(temporaryDirectory, \"stdout.txt\");\n\t\tawait deps.runProbe(executable, stdoutPath, MODEL_PROBE_TIMEOUT_MS);\n\t\tconst listing = await deps.readTextFile(stdoutPath);\n\t\tconst models = parseCursorAgentModelsListing(listing);\n\t\tif (models.length === 0) return offlineFallback;\n\t\ttry {\n\t\t\tawait writeCache(cacheDirectory, cachePath, { cachedAt: now, listing, models }, deps);\n\t\t} catch {\n\t\t\t// A read-only cache directory must not prevent provider registration.\n\t\t}\n\t\treturn models;\n\t} catch {\n\t\treturn offlineFallback;\n\t} finally {\n\t\tif (temporaryDirectory !== undefined) {\n\t\t\ttry {\n\t\t\t\tawait deps.removeDirectory(temporaryDirectory);\n\t\t\t} catch {\n\t\t\t\t// Best-effort cleanup must not replace the catalog result.\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -65,7 +65,7 @@ export default function nestedAgentsMd(pi) {
65
65
  updateStatus(ctx, cache, sessionKey, hasErrors);
66
66
  if (widgetVisible)
67
67
  updateWidget(ctx, true, [...metaMap.values()]);
68
- const textBlock = { type: "text", text: result.injectedText };
68
+ const textBlock = { type: "text", text: result.injectedText, audience: "model" };
69
69
  return { content: [...event.content, textBlock] };
70
70
  });
71
71
  pi.on("session_compact", async (event, ctx) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/nested-agents-md/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TextContent } from \"@earendil-works/pi-ai\";\nimport type { ExtensionAPI } from \"../../types.ts\";\nimport { isReadToolResult } from \"../../types.ts\";\nimport { injectDirectoryContext } from \"./core/inject-directory-context.ts\";\nimport { InjectionCache } from \"./core/injection-cache.ts\";\nimport { getSessionKey } from \"./core/session-key.ts\";\nimport {\n\tbuildDebugRecord,\n\tclearStatus,\n\tclearWidget,\n\ttype InjectedFileMeta,\n\tupdateStatus,\n\tupdateWidget,\n} from \"./ui/reporter.ts\";\n\nconst FLAG_DISABLE = \"no-nested-agents\";\nconst COMMAND_TOGGLE = \"nested-agents\";\nconst ENTRY_TYPE_DEBUG = \"nested-agents-md:debug\";\n\nexport default function nestedAgentsMd(pi: ExtensionAPI): void {\n\tpi.registerFlag(FLAG_DISABLE, {\n\t\tdescription: \"Disable nested AGENTS.md context injection.\",\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t});\n\n\tconst cache = new InjectionCache();\n\tconst filesPerSession = new Map<string, Map<string, InjectedFileMeta>>();\n\tconst errorsPerSession = new Map<string, boolean>();\n\t// Widget visibility is extension-scoped because command registration currently has no session-local state hook.\n\tlet widgetVisible = false;\n\tlet disabled = false;\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tdisabled = pi.getFlag(FLAG_DISABLE) === true;\n\t\tif (disabled) {\n\t\t\tclearStatus(ctx);\n\t\t\tclearWidget(ctx);\n\t\t}\n\t});\n\n\tpi.on(\"tool_result\", async (event, ctx) => {\n\t\tif (disabled) return undefined;\n\t\tif (!isReadToolResult(event) || event.isError) return undefined;\n\t\tconst filePath = event.input.path;\n\t\tif (typeof filePath !== \"string\" || filePath.length === 0) return undefined;\n\t\tconst hasText = event.content.some((block) => block.type === \"text\");\n\t\tif (!hasText) return undefined;\n\n\t\tconst sessionKey = getSessionKey(ctx);\n\t\tconst result = await injectDirectoryContext({\n\t\t\tfilePath,\n\t\t\trootDir: ctx.cwd,\n\t\t\tcache,\n\t\t\tsessionKey,\n\t\t});\n\n\t\tconst errorsRecorded = errorsPerSession.get(sessionKey) === true;\n\t\tconst hasErrors = errorsRecorded || result.errors.length > 0;\n\t\tif (result.errors.length > 0) errorsPerSession.set(sessionKey, true);\n\n\t\tif (!result.injectedText) {\n\t\t\tupdateStatus(ctx, cache, sessionKey, hasErrors);\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet metaMap = filesPerSession.get(sessionKey);\n\t\tif (!metaMap) {\n\t\t\tmetaMap = new Map();\n\t\t\tfilesPerSession.set(sessionKey, metaMap);\n\t\t}\n\t\tfor (const file of result.injectedFiles) {\n\t\t\tmetaMap.set(file.absolutePath, {\n\t\t\t\tabsolutePath: file.absolutePath,\n\t\t\t\ttruncated: file.truncated,\n\t\t\t});\n\t\t}\n\n\t\tupdateStatus(ctx, cache, sessionKey, hasErrors);\n\t\tif (widgetVisible) updateWidget(ctx, true, [...metaMap.values()]);\n\n\t\tconst textBlock: TextContent = { type: \"text\", text: result.injectedText };\n\t\treturn { content: [...event.content, textBlock] };\n\t});\n\n\tpi.on(\"session_compact\", async (event, ctx) => {\n\t\t// Rejected compactions do not mutate session state; keep the cache warm.\n\t\tif (!event.accepted) return;\n\t\tconst sessionKey = getSessionKey(ctx);\n\t\tcache.clearSession(sessionKey);\n\t\tfilesPerSession.delete(sessionKey);\n\t\terrorsPerSession.delete(sessionKey);\n\t\tupdateStatus(ctx, cache, sessionKey, false);\n\t\tif (widgetVisible) updateWidget(ctx, true, []);\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tconst sessionKey = getSessionKey(ctx);\n\t\tcache.clearSession(sessionKey);\n\t\tfilesPerSession.delete(sessionKey);\n\t\terrorsPerSession.delete(sessionKey);\n\t});\n\n\tpi.registerCommand(COMMAND_TOGGLE, {\n\t\tdescription: \"Toggle the nested AGENTS.md context widget and dump cache state.\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tif (disabled) {\n\t\t\t\tctx.ui.notify(\"nested-agents-md is disabled via --no-nested-agents\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\twidgetVisible = !widgetVisible;\n\t\t\tconst sessionKey = getSessionKey(ctx);\n\t\t\tconst files = [...(filesPerSession.get(sessionKey)?.values() ?? [])];\n\t\t\tupdateWidget(ctx, widgetVisible, files);\n\t\t\tpi.appendEntry(ENTRY_TYPE_DEBUG, buildDebugRecord({ cache, sessionKey, files }));\n\t\t\tctx.ui.notify(\n\t\t\t\twidgetVisible ? \"Nested AGENTS.md context widget shown\" : \"Nested AGENTS.md context widget hidden\",\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/nested-agents-md/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TextContent } from \"@earendil-works/pi-ai\";\nimport type { ExtensionAPI } from \"../../types.ts\";\nimport { isReadToolResult } from \"../../types.ts\";\nimport { injectDirectoryContext } from \"./core/inject-directory-context.ts\";\nimport { InjectionCache } from \"./core/injection-cache.ts\";\nimport { getSessionKey } from \"./core/session-key.ts\";\nimport {\n\tbuildDebugRecord,\n\tclearStatus,\n\tclearWidget,\n\ttype InjectedFileMeta,\n\tupdateStatus,\n\tupdateWidget,\n} from \"./ui/reporter.ts\";\n\nconst FLAG_DISABLE = \"no-nested-agents\";\nconst COMMAND_TOGGLE = \"nested-agents\";\nconst ENTRY_TYPE_DEBUG = \"nested-agents-md:debug\";\n\nexport default function nestedAgentsMd(pi: ExtensionAPI): void {\n\tpi.registerFlag(FLAG_DISABLE, {\n\t\tdescription: \"Disable nested AGENTS.md context injection.\",\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t});\n\n\tconst cache = new InjectionCache();\n\tconst filesPerSession = new Map<string, Map<string, InjectedFileMeta>>();\n\tconst errorsPerSession = new Map<string, boolean>();\n\t// Widget visibility is extension-scoped because command registration currently has no session-local state hook.\n\tlet widgetVisible = false;\n\tlet disabled = false;\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tdisabled = pi.getFlag(FLAG_DISABLE) === true;\n\t\tif (disabled) {\n\t\t\tclearStatus(ctx);\n\t\t\tclearWidget(ctx);\n\t\t}\n\t});\n\n\tpi.on(\"tool_result\", async (event, ctx) => {\n\t\tif (disabled) return undefined;\n\t\tif (!isReadToolResult(event) || event.isError) return undefined;\n\t\tconst filePath = event.input.path;\n\t\tif (typeof filePath !== \"string\" || filePath.length === 0) return undefined;\n\t\tconst hasText = event.content.some((block) => block.type === \"text\");\n\t\tif (!hasText) return undefined;\n\n\t\tconst sessionKey = getSessionKey(ctx);\n\t\tconst result = await injectDirectoryContext({\n\t\t\tfilePath,\n\t\t\trootDir: ctx.cwd,\n\t\t\tcache,\n\t\t\tsessionKey,\n\t\t});\n\n\t\tconst errorsRecorded = errorsPerSession.get(sessionKey) === true;\n\t\tconst hasErrors = errorsRecorded || result.errors.length > 0;\n\t\tif (result.errors.length > 0) errorsPerSession.set(sessionKey, true);\n\n\t\tif (!result.injectedText) {\n\t\t\tupdateStatus(ctx, cache, sessionKey, hasErrors);\n\t\t\treturn undefined;\n\t\t}\n\n\t\tlet metaMap = filesPerSession.get(sessionKey);\n\t\tif (!metaMap) {\n\t\t\tmetaMap = new Map();\n\t\t\tfilesPerSession.set(sessionKey, metaMap);\n\t\t}\n\t\tfor (const file of result.injectedFiles) {\n\t\t\tmetaMap.set(file.absolutePath, {\n\t\t\t\tabsolutePath: file.absolutePath,\n\t\t\t\ttruncated: file.truncated,\n\t\t\t});\n\t\t}\n\n\t\tupdateStatus(ctx, cache, sessionKey, hasErrors);\n\t\tif (widgetVisible) updateWidget(ctx, true, [...metaMap.values()]);\n\n\t\tconst textBlock: TextContent = { type: \"text\", text: result.injectedText, audience: \"model\" };\n\t\treturn { content: [...event.content, textBlock] };\n\t});\n\n\tpi.on(\"session_compact\", async (event, ctx) => {\n\t\t// Rejected compactions do not mutate session state; keep the cache warm.\n\t\tif (!event.accepted) return;\n\t\tconst sessionKey = getSessionKey(ctx);\n\t\tcache.clearSession(sessionKey);\n\t\tfilesPerSession.delete(sessionKey);\n\t\terrorsPerSession.delete(sessionKey);\n\t\tupdateStatus(ctx, cache, sessionKey, false);\n\t\tif (widgetVisible) updateWidget(ctx, true, []);\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tconst sessionKey = getSessionKey(ctx);\n\t\tcache.clearSession(sessionKey);\n\t\tfilesPerSession.delete(sessionKey);\n\t\terrorsPerSession.delete(sessionKey);\n\t});\n\n\tpi.registerCommand(COMMAND_TOGGLE, {\n\t\tdescription: \"Toggle the nested AGENTS.md context widget and dump cache state.\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tif (disabled) {\n\t\t\t\tctx.ui.notify(\"nested-agents-md is disabled via --no-nested-agents\", \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\twidgetVisible = !widgetVisible;\n\t\t\tconst sessionKey = getSessionKey(ctx);\n\t\t\tconst files = [...(filesPerSession.get(sessionKey)?.values() ?? [])];\n\t\t\tupdateWidget(ctx, widgetVisible, files);\n\t\t\tpi.appendEntry(ENTRY_TYPE_DEBUG, buildDebugRecord({ cache, sessionKey, files }));\n\t\t\tctx.ui.notify(\n\t\t\t\twidgetVisible ? \"Nested AGENTS.md context widget shown\" : \"Nested AGENTS.md context widget hidden\",\n\t\t\t\t\"info\",\n\t\t\t);\n\t\t},\n\t});\n}\n"]}
@@ -3,6 +3,8 @@ export interface ProjectRulesActivationDetails {
3
3
  readonly kind: "project-rules";
4
4
  readonly targetPath: string;
5
5
  readonly rules: readonly string[];
6
+ /** The tool call whose result the rules were injected into; absent on entries written before it existed. */
7
+ readonly toolCallId?: string;
6
8
  }
7
9
  export interface TtsrActivationDetails {
8
10
  readonly kind: "ttsr";
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rule-activation/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAE5D,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,GAAG,gBAAgB,CAAC;CACjD;AAED,MAAM,MAAM,qBAAqB,GAAG,6BAA6B,GAAG,qBAAqB,CAAC;AAE1F,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,GAAG,SAAS,CAW5F"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rule-activation/types.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,0BAA0B,oBAAoB,CAAC;AAE5D,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,4GAA4G;IAC5G,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,GAAG,gBAAgB,CAAC;CACjD;AAED,MAAM,MAAM,qBAAqB,GAAG,6BAA6B,GAAG,qBAAqB,CAAC;AAE1F,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,GAAG,SAAS,CAW5F"}
@@ -17,7 +17,13 @@ function parseProjectRulesActivation(value) {
17
17
  const rules = stringList(Reflect.get(value, "rules"));
18
18
  if (typeof targetPath !== "string" || targetPath.length === 0 || rules === undefined)
19
19
  return undefined;
20
- return { kind: "project-rules", targetPath, rules };
20
+ const toolCallId = Reflect.get(value, "toolCallId");
21
+ return {
22
+ kind: "project-rules",
23
+ targetPath,
24
+ rules,
25
+ ...(typeof toolCallId === "string" && toolCallId.length > 0 ? { toolCallId } : {}),
26
+ };
21
27
  }
22
28
  function parseTtsrActivation(value) {
23
29
  const owner = Reflect.get(value, "owner");
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rule-activation/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAiB5D,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACxD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,IAAI,EAAE,CAAC;QACd,KAAK,eAAe;YACnB,OAAO,2BAA2B,CAAC,KAAK,CAAC,CAAC;QAC3C,KAAK,MAAM;YACV,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACnC;YACC,OAAO,SAAS,CAAC;IACnB,CAAC;AACF,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAa;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACvG,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AACrD,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACtD,IACC,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,MAAM,KAAK,CAAC;QAClB,KAAK,KAAK,SAAS;QACnB,CAAC,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK,gBAAgB,CAAC,EAC5D,CAAC;QACF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IACjC,IACC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,KAAK,CAAC,MAAM,KAAK,CAAC;QAClB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EACrE,CAAC;QACF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["export const RULE_ACTIVATION_ENTRY_TYPE = \"rule-activation\";\n\nexport interface ProjectRulesActivationDetails {\n\treadonly kind: \"project-rules\";\n\treadonly targetPath: string;\n\treadonly rules: readonly string[];\n}\n\nexport interface TtsrActivationDetails {\n\treadonly kind: \"ttsr\";\n\treadonly owner: string;\n\treadonly rules: readonly string[];\n\treadonly remediation: \"nudge\" | \"provider-error\";\n}\n\nexport type RuleActivationDetails = ProjectRulesActivationDetails | TtsrActivationDetails;\n\nexport function parseRuleActivationDetails(value: unknown): RuleActivationDetails | undefined {\n\tif (!isObject(value)) return undefined;\n\tconst kind = Reflect.get(value, \"kind\");\n\tswitch (kind) {\n\t\tcase \"project-rules\":\n\t\t\treturn parseProjectRulesActivation(value);\n\t\tcase \"ttsr\":\n\t\t\treturn parseTtsrActivation(value);\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction parseProjectRulesActivation(value: object): ProjectRulesActivationDetails | undefined {\n\tconst targetPath = Reflect.get(value, \"targetPath\");\n\tconst rules = stringList(Reflect.get(value, \"rules\"));\n\tif (typeof targetPath !== \"string\" || targetPath.length === 0 || rules === undefined) return undefined;\n\treturn { kind: \"project-rules\", targetPath, rules };\n}\n\nfunction parseTtsrActivation(value: object): TtsrActivationDetails | undefined {\n\tconst owner = Reflect.get(value, \"owner\");\n\tconst rules = stringList(Reflect.get(value, \"rules\"));\n\tconst remediation = Reflect.get(value, \"remediation\");\n\tif (\n\t\ttypeof owner !== \"string\" ||\n\t\towner.length === 0 ||\n\t\trules === undefined ||\n\t\t(remediation !== \"nudge\" && remediation !== \"provider-error\")\n\t) {\n\t\treturn undefined;\n\t}\n\treturn { kind: \"ttsr\", owner, rules, remediation };\n}\n\nfunction stringList(value: unknown): readonly string[] | undefined {\n\tif (\n\t\t!Array.isArray(value) ||\n\t\tvalue.length === 0 ||\n\t\t!value.every((entry) => typeof entry === \"string\" && entry.length > 0)\n\t) {\n\t\treturn undefined;\n\t}\n\treturn value;\n}\n\nfunction isObject(value: unknown): value is object {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rule-activation/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG,iBAAiB,CAAC;AAmB5D,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACxD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,IAAI,EAAE,CAAC;QACd,KAAK,eAAe;YACnB,OAAO,2BAA2B,CAAC,KAAK,CAAC,CAAC;QAC3C,KAAK,MAAM;YACV,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACnC;YACC,OAAO,SAAS,CAAC;IACnB,CAAC;AACF,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAa;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACvG,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACpD,OAAO;QACN,IAAI,EAAE,eAAe;QACrB,UAAU;QACV,KAAK;QACL,GAAG,CAAC,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAClF,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IACtD,IACC,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,CAAC,MAAM,KAAK,CAAC;QAClB,KAAK,KAAK,SAAS;QACnB,CAAC,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK,gBAAgB,CAAC,EAC5D,CAAC;QACF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,UAAU,CAAC,KAAc;IACjC,IACC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,KAAK,CAAC,MAAM,KAAK,CAAC;QAClB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EACrE,CAAC;QACF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC","sourcesContent":["export const RULE_ACTIVATION_ENTRY_TYPE = \"rule-activation\";\n\nexport interface ProjectRulesActivationDetails {\n\treadonly kind: \"project-rules\";\n\treadonly targetPath: string;\n\treadonly rules: readonly string[];\n\t/** The tool call whose result the rules were injected into; absent on entries written before it existed. */\n\treadonly toolCallId?: string;\n}\n\nexport interface TtsrActivationDetails {\n\treadonly kind: \"ttsr\";\n\treadonly owner: string;\n\treadonly rules: readonly string[];\n\treadonly remediation: \"nudge\" | \"provider-error\";\n}\n\nexport type RuleActivationDetails = ProjectRulesActivationDetails | TtsrActivationDetails;\n\nexport function parseRuleActivationDetails(value: unknown): RuleActivationDetails | undefined {\n\tif (!isObject(value)) return undefined;\n\tconst kind = Reflect.get(value, \"kind\");\n\tswitch (kind) {\n\t\tcase \"project-rules\":\n\t\t\treturn parseProjectRulesActivation(value);\n\t\tcase \"ttsr\":\n\t\t\treturn parseTtsrActivation(value);\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction parseProjectRulesActivation(value: object): ProjectRulesActivationDetails | undefined {\n\tconst targetPath = Reflect.get(value, \"targetPath\");\n\tconst rules = stringList(Reflect.get(value, \"rules\"));\n\tif (typeof targetPath !== \"string\" || targetPath.length === 0 || rules === undefined) return undefined;\n\tconst toolCallId = Reflect.get(value, \"toolCallId\");\n\treturn {\n\t\tkind: \"project-rules\",\n\t\ttargetPath,\n\t\trules,\n\t\t...(typeof toolCallId === \"string\" && toolCallId.length > 0 ? { toolCallId } : {}),\n\t};\n}\n\nfunction parseTtsrActivation(value: object): TtsrActivationDetails | undefined {\n\tconst owner = Reflect.get(value, \"owner\");\n\tconst rules = stringList(Reflect.get(value, \"rules\"));\n\tconst remediation = Reflect.get(value, \"remediation\");\n\tif (\n\t\ttypeof owner !== \"string\" ||\n\t\towner.length === 0 ||\n\t\trules === undefined ||\n\t\t(remediation !== \"nudge\" && remediation !== \"provider-error\")\n\t) {\n\t\treturn undefined;\n\t}\n\treturn { kind: \"ttsr\", owner, rules, remediation };\n}\n\nfunction stringList(value: unknown): readonly string[] | undefined {\n\tif (\n\t\t!Array.isArray(value) ||\n\t\tvalue.length === 0 ||\n\t\t!value.every((entry) => typeof entry === \"string\" && entry.length > 0)\n\t) {\n\t\treturn undefined;\n\t}\n\treturn value;\n}\n\nfunction isObject(value: unknown): value is object {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rules/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAgBnD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA0I/D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rules/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAgBnD,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA2I/D"}
@@ -121,8 +121,9 @@ export default function piRulesExtension(pi) {
121
121
  kind: "project-rules",
122
122
  targetPath,
123
123
  rules: rules.map((rule) => rule.relativePath),
124
+ toolCallId: event.toolCallId,
124
125
  });
125
- return { content: [...event.content, { type: "text", text: block }] };
126
+ return { content: [...event.content, { type: "text", text: block, audience: "model" }] };
126
127
  });
127
128
  }
128
129
  function isPiRulesMode(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rules/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, realpathSync } from \"node:fs\";\nimport { isAbsolute, relative } from \"node:path\";\n\nimport type { ExtensionAPI } from \"../../types.ts\";\n\nimport { appendRuleActivation, registerRuleActivationRenderer } from \"../rule-activation/index.ts\";\nimport { registerSlashCommands } from \"./commands.ts\";\nimport { configFromEnvironment } from \"./config.ts\";\nimport { createEngine } from \"./rules/engine.ts\";\nimport { findRuleCandidates } from \"./rules/finder.ts\";\nimport { findProjectRoot } from \"./rules/project-root.ts\";\nimport { extractToolPaths } from \"./rules/tool-paths.ts\";\nimport type { PiRulesConfig } from \"./rules/types.ts\";\n\ntype PiRulesMode = PiRulesConfig[\"mode\"];\n\nconst MODE_VALUES = new Set<string>([\"static\", \"dynamic\", \"both\", \"off\"]);\nconst DYNAMIC_CONTEXT_SCOPE = \"live-context\";\n\nexport default function piRulesExtension(pi: ExtensionAPI): void {\n\tpi.registerFlag(\"pi-rules-disabled\", {\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t\tdescription: \"Disable pi-rules hooks.\",\n\t});\n\tpi.registerFlag(\"pi-rules-mode\", {\n\t\ttype: \"string\",\n\t\tdefault: \"both\",\n\t\tdescription: \"Rule injection mode: static, dynamic, both, or off.\",\n\t});\n\tconst config = configFromEnvironment();\n\tconst envDisabled = config.disabled;\n\tconst engine = createEngine(config, {\n\t\tfindCandidates: findRuleCandidates,\n\t\treadFile: (path) => {\n\t\t\ttry {\n\t\t\t\treturn readFileSync(path, \"utf-8\");\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tfindProjectRoot,\n\t\textractToolPaths,\n\t});\n\tregisterSlashCommands(pi, engine);\n\tregisterRuleActivationRenderer(pi);\n\n\tfunction syncConfigFromFlags(): void {\n\t\tconst disabled = pi.getFlag(\"pi-rules-disabled\");\n\t\tconst mode = pi.getFlag(\"pi-rules-mode\");\n\n\t\tif (typeof disabled === \"boolean\") {\n\t\t\tengine.config.disabled = disabled || envDisabled;\n\t\t}\n\t\tif (typeof mode === \"string\" && isPiRulesMode(mode)) {\n\t\t\tengine.config.mode = mode;\n\t\t}\n\t}\n\n\tpi.on(\"session_start\", async (event, ctx) => {\n\t\tsyncConfigFromFlags();\n\t\tif (engine.config.disabled) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tengine.resetSession(ctx.cwd);\n\t\tpi.appendEntry(\"pi-rules.scan\", { cwd: ctx.cwd, reason: event.reason });\n\t\treturn undefined;\n\t});\n\n\tpi.on(\"session_compact\", async (event, ctx) => {\n\t\t// Rejected compactions do not mutate session state; do not reset rules.\n\t\tif (!event.accepted) return undefined;\n\t\tengine.resetSession(ctx.cwd);\n\t\tpi.appendEntry(\"pi-rules.scan\", { cwd: ctx.cwd, reason: \"compact\" });\n\t\treturn undefined;\n\t});\n\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\tsyncConfigFromFlags();\n\t\tif (engine.config.disabled || engine.config.mode === \"off\" || engine.config.mode === \"dynamic\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst loaded = engine.loadStaticRules(ctx.cwd);\n\t\tconst nativeContextPaths = new Set(\n\t\t\tevent.systemPromptOptions.contextFiles?.flatMap((contextFile) => pathKeys(contextFile.path)) ?? [],\n\t\t);\n\t\tfor (const rule of loaded.rules) {\n\t\t\tif (nativeContextPaths.has(rule.path) || nativeContextPaths.has(rule.realPath)) {\n\t\t\t\tengine.markStaticInjected(rule);\n\t\t\t}\n\t\t}\n\t\t// Deliberately NOT gated on isStaticInjected: the host re-emits this from the BASE prompt\n\t\t// every user prompt, so a prior turn's mark would drop the block from turn 2 onward. The\n\t\t// marks below serve only the dynamic tool_result path.\n\t\tconst rules = loaded.rules.filter(\n\t\t\t(rule) => !nativeContextPaths.has(rule.path) && !nativeContextPaths.has(rule.realPath),\n\t\t);\n\n\t\tif (rules.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst block = engine.formatStatic(rules);\n\t\tfor (const rule of rules) {\n\t\t\tengine.markStaticInjected(rule);\n\t\t}\n\n\t\treturn { systemPrompt: event.systemPrompt + block };\n\t});\n\n\tpi.on(\"tool_result\", async (event, ctx) => {\n\t\tsyncConfigFromFlags();\n\t\tif (engine.config.disabled || engine.config.mode === \"off\" || engine.config.mode === \"static\" || event.isError) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst targetPaths = extractToolPaths(event, ctx.cwd);\n\t\tconst firstTargetPath = targetPaths[0];\n\t\tif (firstTargetPath === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst fingerprints = engine.fingerprintDynamicTargets(ctx.cwd, targetPaths);\n\t\tconst pendingFingerprints = fingerprints.filter((target) => !engine.isDynamicTargetFingerprintCurrent(target));\n\t\tif (pendingFingerprints.length === 0) {\n\t\t\tengine.commitDynamicTargetFingerprints(fingerprints);\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst loaded = engine.loadDynamicRules(\n\t\t\tctx.cwd,\n\t\t\tpendingFingerprints.map((target) => target.targetPath),\n\t\t);\n\t\tengine.commitDynamicTargetFingerprints(fingerprints);\n\t\tconst rules = loaded.rules.filter(\n\t\t\t(rule) => !engine.isStaticInjected(rule) && !engine.isDynamicInjected(DYNAMIC_CONTEXT_SCOPE, rule),\n\t\t);\n\t\tif (rules.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst firstPendingTarget = pendingFingerprints[0]?.targetPath ?? firstTargetPath;\n\t\tconst targetPath = displayPath(ctx.cwd, firstPendingTarget);\n\t\tconst block = engine.formatDynamic(rules, targetPath);\n\t\tfor (const rule of rules) {\n\t\t\tengine.markDynamicInjected(DYNAMIC_CONTEXT_SCOPE, rule);\n\t\t}\n\t\tappendRuleActivation(pi, {\n\t\t\tkind: \"project-rules\",\n\t\t\ttargetPath,\n\t\t\trules: rules.map((rule) => rule.relativePath),\n\t\t});\n\n\t\treturn { content: [...event.content, { type: \"text\", text: block }] };\n\t});\n}\n\nfunction isPiRulesMode(value: string): value is PiRulesMode {\n\treturn MODE_VALUES.has(value);\n}\n\nfunction pathKeys(filePath: string): string[] {\n\ttry {\n\t\treturn [filePath, realpathSync.native(filePath)];\n\t} catch {\n\t\treturn [filePath];\n\t}\n}\n\nfunction displayPath(cwd: string, filePath: string): string {\n\treturn isAbsolute(filePath) ? relative(cwd, filePath) : filePath;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/rules/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync, realpathSync } from \"node:fs\";\nimport { isAbsolute, relative } from \"node:path\";\n\nimport type { ExtensionAPI } from \"../../types.ts\";\n\nimport { appendRuleActivation, registerRuleActivationRenderer } from \"../rule-activation/index.ts\";\nimport { registerSlashCommands } from \"./commands.ts\";\nimport { configFromEnvironment } from \"./config.ts\";\nimport { createEngine } from \"./rules/engine.ts\";\nimport { findRuleCandidates } from \"./rules/finder.ts\";\nimport { findProjectRoot } from \"./rules/project-root.ts\";\nimport { extractToolPaths } from \"./rules/tool-paths.ts\";\nimport type { PiRulesConfig } from \"./rules/types.ts\";\n\ntype PiRulesMode = PiRulesConfig[\"mode\"];\n\nconst MODE_VALUES = new Set<string>([\"static\", \"dynamic\", \"both\", \"off\"]);\nconst DYNAMIC_CONTEXT_SCOPE = \"live-context\";\n\nexport default function piRulesExtension(pi: ExtensionAPI): void {\n\tpi.registerFlag(\"pi-rules-disabled\", {\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t\tdescription: \"Disable pi-rules hooks.\",\n\t});\n\tpi.registerFlag(\"pi-rules-mode\", {\n\t\ttype: \"string\",\n\t\tdefault: \"both\",\n\t\tdescription: \"Rule injection mode: static, dynamic, both, or off.\",\n\t});\n\tconst config = configFromEnvironment();\n\tconst envDisabled = config.disabled;\n\tconst engine = createEngine(config, {\n\t\tfindCandidates: findRuleCandidates,\n\t\treadFile: (path) => {\n\t\t\ttry {\n\t\t\t\treturn readFileSync(path, \"utf-8\");\n\t\t\t} catch {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t\tfindProjectRoot,\n\t\textractToolPaths,\n\t});\n\tregisterSlashCommands(pi, engine);\n\tregisterRuleActivationRenderer(pi);\n\n\tfunction syncConfigFromFlags(): void {\n\t\tconst disabled = pi.getFlag(\"pi-rules-disabled\");\n\t\tconst mode = pi.getFlag(\"pi-rules-mode\");\n\n\t\tif (typeof disabled === \"boolean\") {\n\t\t\tengine.config.disabled = disabled || envDisabled;\n\t\t}\n\t\tif (typeof mode === \"string\" && isPiRulesMode(mode)) {\n\t\t\tengine.config.mode = mode;\n\t\t}\n\t}\n\n\tpi.on(\"session_start\", async (event, ctx) => {\n\t\tsyncConfigFromFlags();\n\t\tif (engine.config.disabled) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tengine.resetSession(ctx.cwd);\n\t\tpi.appendEntry(\"pi-rules.scan\", { cwd: ctx.cwd, reason: event.reason });\n\t\treturn undefined;\n\t});\n\n\tpi.on(\"session_compact\", async (event, ctx) => {\n\t\t// Rejected compactions do not mutate session state; do not reset rules.\n\t\tif (!event.accepted) return undefined;\n\t\tengine.resetSession(ctx.cwd);\n\t\tpi.appendEntry(\"pi-rules.scan\", { cwd: ctx.cwd, reason: \"compact\" });\n\t\treturn undefined;\n\t});\n\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\tsyncConfigFromFlags();\n\t\tif (engine.config.disabled || engine.config.mode === \"off\" || engine.config.mode === \"dynamic\") {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst loaded = engine.loadStaticRules(ctx.cwd);\n\t\tconst nativeContextPaths = new Set(\n\t\t\tevent.systemPromptOptions.contextFiles?.flatMap((contextFile) => pathKeys(contextFile.path)) ?? [],\n\t\t);\n\t\tfor (const rule of loaded.rules) {\n\t\t\tif (nativeContextPaths.has(rule.path) || nativeContextPaths.has(rule.realPath)) {\n\t\t\t\tengine.markStaticInjected(rule);\n\t\t\t}\n\t\t}\n\t\t// Deliberately NOT gated on isStaticInjected: the host re-emits this from the BASE prompt\n\t\t// every user prompt, so a prior turn's mark would drop the block from turn 2 onward. The\n\t\t// marks below serve only the dynamic tool_result path.\n\t\tconst rules = loaded.rules.filter(\n\t\t\t(rule) => !nativeContextPaths.has(rule.path) && !nativeContextPaths.has(rule.realPath),\n\t\t);\n\n\t\tif (rules.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst block = engine.formatStatic(rules);\n\t\tfor (const rule of rules) {\n\t\t\tengine.markStaticInjected(rule);\n\t\t}\n\n\t\treturn { systemPrompt: event.systemPrompt + block };\n\t});\n\n\tpi.on(\"tool_result\", async (event, ctx) => {\n\t\tsyncConfigFromFlags();\n\t\tif (engine.config.disabled || engine.config.mode === \"off\" || engine.config.mode === \"static\" || event.isError) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst targetPaths = extractToolPaths(event, ctx.cwd);\n\t\tconst firstTargetPath = targetPaths[0];\n\t\tif (firstTargetPath === undefined) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst fingerprints = engine.fingerprintDynamicTargets(ctx.cwd, targetPaths);\n\t\tconst pendingFingerprints = fingerprints.filter((target) => !engine.isDynamicTargetFingerprintCurrent(target));\n\t\tif (pendingFingerprints.length === 0) {\n\t\t\tengine.commitDynamicTargetFingerprints(fingerprints);\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst loaded = engine.loadDynamicRules(\n\t\t\tctx.cwd,\n\t\t\tpendingFingerprints.map((target) => target.targetPath),\n\t\t);\n\t\tengine.commitDynamicTargetFingerprints(fingerprints);\n\t\tconst rules = loaded.rules.filter(\n\t\t\t(rule) => !engine.isStaticInjected(rule) && !engine.isDynamicInjected(DYNAMIC_CONTEXT_SCOPE, rule),\n\t\t);\n\t\tif (rules.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst firstPendingTarget = pendingFingerprints[0]?.targetPath ?? firstTargetPath;\n\t\tconst targetPath = displayPath(ctx.cwd, firstPendingTarget);\n\t\tconst block = engine.formatDynamic(rules, targetPath);\n\t\tfor (const rule of rules) {\n\t\t\tengine.markDynamicInjected(DYNAMIC_CONTEXT_SCOPE, rule);\n\t\t}\n\t\tappendRuleActivation(pi, {\n\t\t\tkind: \"project-rules\",\n\t\t\ttargetPath,\n\t\t\trules: rules.map((rule) => rule.relativePath),\n\t\t\ttoolCallId: event.toolCallId,\n\t\t});\n\n\t\treturn { content: [...event.content, { type: \"text\", text: block, audience: \"model\" }] };\n\t});\n}\n\nfunction isPiRulesMode(value: string): value is PiRulesMode {\n\treturn MODE_VALUES.has(value);\n}\n\nfunction pathKeys(filePath: string): string[] {\n\ttry {\n\t\treturn [filePath, realpathSync.native(filePath)];\n\t} catch {\n\t\treturn [filePath];\n\t}\n}\n\nfunction displayPath(cwd: string, filePath: string): string {\n\treturn isAbsolute(filePath) ? relative(cwd, filePath) : filePath;\n}\n"]}
@@ -9,6 +9,7 @@
9
9
  * carriage-return/backspace redraws collapsed — then (b) tail-truncated to the
10
10
  * same budget the core bash tool enforces.
11
11
  */
12
+ import type { TextContent } from "@earendil-works/pi-ai";
12
13
  import { type TruncationResult } from "../../../tools/truncate.ts";
13
14
  /** Mirrors the core bash tool budget (`DEFAULT_MAX_LINES` / `DEFAULT_MAX_BYTES`). */
14
15
  export declare const TERMINAL_TOOL_MAX_LINES = 2000;
@@ -16,7 +17,12 @@ export declare const TERMINAL_TOOL_MAX_BYTES: number;
16
17
  /** Strip escape sequences and collapse carriage-return/backspace redraws. */
17
18
  export declare function sanitizeTerminalOutput(raw: string): string;
18
19
  export interface FormattedTerminalToolOutput {
20
+ /** Model-facing text: the kept output, then the truncation marker when content was cut. */
19
21
  readonly text: string;
22
+ /** The kept output alone, without the marker. */
23
+ readonly body: string;
24
+ /** Truncation marker addressed to the model; UIs never render it. */
25
+ readonly marker?: string;
20
26
  readonly truncated: boolean;
21
27
  readonly truncation: TruncationResult;
22
28
  }
@@ -25,4 +31,13 @@ export interface FormattedTerminalToolOutput {
25
31
  * tail (where exit status and errors live) with a marker when content was cut.
26
32
  */
27
33
  export declare function formatTerminalToolOutput(raw: string): FormattedTerminalToolOutput;
34
+ /**
35
+ * Split a tool-result text into parts so each notice becomes its own model-only
36
+ * part. Providers join a tool result's text parts with "\n", so every notice
37
+ * must sit on its own line: the newline before it (and after it, when text
38
+ * follows) is taken out of the neighbouring parts and restored by that join.
39
+ * The joined parts are therefore byte-identical to `text`. A notice that is
40
+ * absent or not line-delimited leaves `text` as one visible part.
41
+ */
42
+ export declare function splitModelOnlyNotices(text: string, notices: ReadonlyArray<string | undefined>): TextContent[];
28
43
  //# sourceMappingURL=output-format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"output-format.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/output-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAc,KAAK,gBAAgB,EAAgB,MAAM,4BAA4B,CAAC;AAE7F,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,uBAAuB,QAAY,CAAC;AAsCjD,6EAA6E;AAC7E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,MAAM,WAAW,2BAA2B;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,2BAA2B,CAajF"}
1
+ {"version":3,"file":"output-format.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/output-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAc,KAAK,gBAAgB,EAAgB,MAAM,4BAA4B,CAAC;AAE7F,qFAAqF;AACrF,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,uBAAuB,QAAY,CAAC;AAsCjD,6EAA6E;AAC7E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,MAAM,WAAW,2BAA2B;IAC3C,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,2BAA2B,CAajF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,WAAW,EAAE,CAwB7G"}
@@ -9,6 +9,7 @@
9
9
  * carriage-return/backspace redraws collapsed — then (b) tail-truncated to the
10
10
  * same budget the core bash tool enforces.
11
11
  */
12
+ import { modelOnlyText } from "../../../tools/model-only-text.js";
12
13
  import { formatSize, truncateTail } from "../../../tools/truncate.js";
13
14
  /** Mirrors the core bash tool budget (`DEFAULT_MAX_LINES` / `DEFAULT_MAX_BYTES`). */
14
15
  export const TERMINAL_TOOL_MAX_LINES = 2000;
@@ -64,11 +65,51 @@ export function formatTerminalToolOutput(raw) {
64
65
  maxBytes: TERMINAL_TOOL_MAX_BYTES,
65
66
  });
66
67
  if (!truncation.truncated) {
67
- return { text: sanitized, truncated: false, truncation };
68
+ return { text: sanitized, body: sanitized, truncated: false, truncation };
68
69
  }
69
70
  const marker = truncation.lastLinePartial
70
71
  ? `[Showing last ${formatSize(truncation.outputBytes)} of a single line; earlier output dropped]`
71
72
  : `[Showing lines ${truncation.totalLines - truncation.outputLines + 1}-${truncation.totalLines} of ${truncation.totalLines}; earlier output dropped]`;
72
- return { text: `${truncation.content}\n\n${marker}`, truncated: true, truncation };
73
+ return { text: `${truncation.content}\n\n${marker}`, body: truncation.content, marker, truncated: true, truncation };
74
+ }
75
+ /**
76
+ * Split a tool-result text into parts so each notice becomes its own model-only
77
+ * part. Providers join a tool result's text parts with "\n", so every notice
78
+ * must sit on its own line: the newline before it (and after it, when text
79
+ * follows) is taken out of the neighbouring parts and restored by that join.
80
+ * The joined parts are therefore byte-identical to `text`. A notice that is
81
+ * absent or not line-delimited leaves `text` as one visible part.
82
+ */
83
+ export function splitModelOnlyNotices(text, notices) {
84
+ const parts = [];
85
+ let cursor = 0;
86
+ // True when the previous part is a notice whose trailing newline was consumed.
87
+ let afterNotice = false;
88
+ for (const notice of notices) {
89
+ if (notice === undefined || notice.length === 0)
90
+ continue;
91
+ const index = text.indexOf(notice, cursor);
92
+ if (index < 0)
93
+ continue;
94
+ const end = index + notice.length;
95
+ if (end < text.length && text[end] !== "\n")
96
+ continue;
97
+ if (afterNotice && index === cursor) {
98
+ parts.push(modelOnlyText(notice));
99
+ }
100
+ else if (index > cursor && text[index - 1] === "\n") {
101
+ parts.push({ type: "text", text: text.slice(cursor, index - 1) }, modelOnlyText(notice));
102
+ }
103
+ else {
104
+ continue;
105
+ }
106
+ cursor = end < text.length ? end + 1 : end;
107
+ afterNotice = end < text.length;
108
+ }
109
+ if (parts.length === 0)
110
+ return [{ type: "text", text }];
111
+ if (cursor < text.length || text[text.length - 1] === "\n")
112
+ parts.push({ type: "text", text: text.slice(cursor) });
113
+ return parts;
73
114
  }
74
115
  //# sourceMappingURL=output-format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"output-format.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/output-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAyB,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE7F,qFAAqF;AACrF,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAC5C,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,GAAG,IAAI,CAAC;AAEjD,2EAA2E;AAC3E,MAAM,WAAW,GAAG,oCAAoC,CAAC;AACzD,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAC/C,MAAM,4BAA4B,GAAG,8CAA8C,CAAC;AACpF,qFAAqF;AACrF,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAE/D,SAAS,oBAAoB,CAAC,IAAY;IACzC,OAAO,IAAI;SACT,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;SACxB,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC;SACzC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,IAAY;IACpC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,GAAG,CAAC,CAAC;QACZ,CAAC;aAAM,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACxB,IAAI,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;YACnB,MAAM,IAAI,CAAC,CAAC;QACb,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACvB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,sBAAsB,CAAC,GAAW;IACjD,OAAO,oBAAoB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrG,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAW;IACnD,MAAM,SAAS,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IACxD,MAAM,UAAU,GAAG,YAAY,CAAC,SAAS,EAAE;QAC1C,QAAQ,EAAE,uBAAuB;QACjC,QAAQ,EAAE,uBAAuB;KACjC,CAAC,CAAC;IACH,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAC3B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IAC1D,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,CAAC,eAAe;QACxC,CAAC,CAAC,iBAAiB,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,4CAA4C;QACjG,CAAC,CAAC,kBAAkB,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,OAAO,UAAU,CAAC,UAAU,2BAA2B,CAAC;IACxJ,OAAO,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,OAAO,OAAO,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AACpF,CAAC","sourcesContent":["/**\n * Model-facing formatting for PTY terminal output.\n *\n * The PTY bash tools capture a raw terminal stream: escape sequences, and one\n * redraw frame per spinner tick. Returned unbounded (up to the 1 MB session\n * buffer), a single `gh run view --log-failed` injected ~1M chars of ANSI soup\n * into the conversation and forced emergency compactions. Before output\n * reaches the model it is (a) sanitized — escape sequences stripped and\n * carriage-return/backspace redraws collapsed — then (b) tail-truncated to the\n * same budget the core bash tool enforces.\n */\n\nimport { formatSize, type TruncationResult, truncateTail } from \"../../../tools/truncate.ts\";\n\n/** Mirrors the core bash tool budget (`DEFAULT_MAX_LINES` / `DEFAULT_MAX_BYTES`). */\nexport const TERMINAL_TOOL_MAX_LINES = 2000;\nexport const TERMINAL_TOOL_MAX_BYTES = 50 * 1024;\n\n// Order matters: OSC can contain characters that later patterns would eat.\nconst OSC_PATTERN = /\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g;\nconst CSI_PATTERN = /\\x1b\\[[0-?]*[ -/]*[@-~]/g;\nconst DESIGNATE_AND_SINGLE_PATTERN = /\\x1b(?:[()#%*+][0-9A-Za-z]|[0-9<=>@-Z\\\\-_])/g;\n// `\\x08` (backspace) and `\\r` are excluded: the redraw fold applies their semantics.\nconst C0_CONTROL_PATTERN = /[\\x00-\\x07\\x0b\\x0c\\x0e-\\x1f\\x7f]/g;\n\nfunction stripEscapeSequences(text: string): string {\n\treturn text\n\t\t.replace(OSC_PATTERN, \"\")\n\t\t.replace(CSI_PATTERN, \"\")\n\t\t.replace(DESIGNATE_AND_SINGLE_PATTERN, \"\")\n\t\t.replace(C0_CONTROL_PATTERN, \"\");\n}\n\n/**\n * Fold terminal redraw semantics into a flat line: `\\r` returns the cursor to\n * column 0 so spinner/progress frames overwrite each other, and `\\b` steps the\n * cursor back one cell. Only the final visible state survives.\n */\nfunction collapseRedraws(line: string): string {\n\tconst cells: string[] = [];\n\tlet cursor = 0;\n\tfor (const ch of line) {\n\t\tif (ch === \"\\r\") {\n\t\t\tcursor = 0;\n\t\t} else if (ch === \"\\b\") {\n\t\t\tif (cursor > 0) cursor -= 1;\n\t\t} else {\n\t\t\tcells[cursor] = ch;\n\t\t\tcursor += 1;\n\t\t}\n\t}\n\treturn cells.join(\"\");\n}\n\n/** Strip escape sequences and collapse carriage-return/backspace redraws. */\nexport function sanitizeTerminalOutput(raw: string): string {\n\treturn stripEscapeSequences(raw).replace(/\\r\\n/g, \"\\n\").split(\"\\n\").map(collapseRedraws).join(\"\\n\");\n}\n\nexport interface FormattedTerminalToolOutput {\n\treadonly text: string;\n\treadonly truncated: boolean;\n\treadonly truncation: TruncationResult;\n}\n\n/**\n * Sanitize `raw` PTY output and bound it to the core-bash budget, keeping the\n * tail (where exit status and errors live) with a marker when content was cut.\n */\nexport function formatTerminalToolOutput(raw: string): FormattedTerminalToolOutput {\n\tconst sanitized = sanitizeTerminalOutput(raw).trimEnd();\n\tconst truncation = truncateTail(sanitized, {\n\t\tmaxLines: TERMINAL_TOOL_MAX_LINES,\n\t\tmaxBytes: TERMINAL_TOOL_MAX_BYTES,\n\t});\n\tif (!truncation.truncated) {\n\t\treturn { text: sanitized, truncated: false, truncation };\n\t}\n\tconst marker = truncation.lastLinePartial\n\t\t? `[Showing last ${formatSize(truncation.outputBytes)} of a single line; earlier output dropped]`\n\t\t: `[Showing lines ${truncation.totalLines - truncation.outputLines + 1}-${truncation.totalLines} of ${truncation.totalLines}; earlier output dropped]`;\n\treturn { text: `${truncation.content}\\n\\n${marker}`, truncated: true, truncation };\n}\n"]}
1
+ {"version":3,"file":"output-format.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/terminal/output-format.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Model-facing formatting for PTY terminal output.\n *\n * The PTY bash tools capture a raw terminal stream: escape sequences, and one\n * redraw frame per spinner tick. Returned unbounded (up to the 1 MB session\n * buffer), a single `gh run view --log-failed` injected ~1M chars of ANSI soup\n * into the conversation and forced emergency compactions. Before output\n * reaches the model it is (a) sanitized — escape sequences stripped and\n * carriage-return/backspace redraws collapsed — then (b) tail-truncated to the\n * same budget the core bash tool enforces.\n */\n\nimport type { TextContent } from \"@earendil-works/pi-ai\";\nimport { modelOnlyText } from \"../../../tools/model-only-text.ts\";\nimport { formatSize, type TruncationResult, truncateTail } from \"../../../tools/truncate.ts\";\n\n/** Mirrors the core bash tool budget (`DEFAULT_MAX_LINES` / `DEFAULT_MAX_BYTES`). */\nexport const TERMINAL_TOOL_MAX_LINES = 2000;\nexport const TERMINAL_TOOL_MAX_BYTES = 50 * 1024;\n\n// Order matters: OSC can contain characters that later patterns would eat.\nconst OSC_PATTERN = /\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)/g;\nconst CSI_PATTERN = /\\x1b\\[[0-?]*[ -/]*[@-~]/g;\nconst DESIGNATE_AND_SINGLE_PATTERN = /\\x1b(?:[()#%*+][0-9A-Za-z]|[0-9<=>@-Z\\\\-_])/g;\n// `\\x08` (backspace) and `\\r` are excluded: the redraw fold applies their semantics.\nconst C0_CONTROL_PATTERN = /[\\x00-\\x07\\x0b\\x0c\\x0e-\\x1f\\x7f]/g;\n\nfunction stripEscapeSequences(text: string): string {\n\treturn text\n\t\t.replace(OSC_PATTERN, \"\")\n\t\t.replace(CSI_PATTERN, \"\")\n\t\t.replace(DESIGNATE_AND_SINGLE_PATTERN, \"\")\n\t\t.replace(C0_CONTROL_PATTERN, \"\");\n}\n\n/**\n * Fold terminal redraw semantics into a flat line: `\\r` returns the cursor to\n * column 0 so spinner/progress frames overwrite each other, and `\\b` steps the\n * cursor back one cell. Only the final visible state survives.\n */\nfunction collapseRedraws(line: string): string {\n\tconst cells: string[] = [];\n\tlet cursor = 0;\n\tfor (const ch of line) {\n\t\tif (ch === \"\\r\") {\n\t\t\tcursor = 0;\n\t\t} else if (ch === \"\\b\") {\n\t\t\tif (cursor > 0) cursor -= 1;\n\t\t} else {\n\t\t\tcells[cursor] = ch;\n\t\t\tcursor += 1;\n\t\t}\n\t}\n\treturn cells.join(\"\");\n}\n\n/** Strip escape sequences and collapse carriage-return/backspace redraws. */\nexport function sanitizeTerminalOutput(raw: string): string {\n\treturn stripEscapeSequences(raw).replace(/\\r\\n/g, \"\\n\").split(\"\\n\").map(collapseRedraws).join(\"\\n\");\n}\n\nexport interface FormattedTerminalToolOutput {\n\t/** Model-facing text: the kept output, then the truncation marker when content was cut. */\n\treadonly text: string;\n\t/** The kept output alone, without the marker. */\n\treadonly body: string;\n\t/** Truncation marker addressed to the model; UIs never render it. */\n\treadonly marker?: string;\n\treadonly truncated: boolean;\n\treadonly truncation: TruncationResult;\n}\n\n/**\n * Sanitize `raw` PTY output and bound it to the core-bash budget, keeping the\n * tail (where exit status and errors live) with a marker when content was cut.\n */\nexport function formatTerminalToolOutput(raw: string): FormattedTerminalToolOutput {\n\tconst sanitized = sanitizeTerminalOutput(raw).trimEnd();\n\tconst truncation = truncateTail(sanitized, {\n\t\tmaxLines: TERMINAL_TOOL_MAX_LINES,\n\t\tmaxBytes: TERMINAL_TOOL_MAX_BYTES,\n\t});\n\tif (!truncation.truncated) {\n\t\treturn { text: sanitized, body: sanitized, truncated: false, truncation };\n\t}\n\tconst marker = truncation.lastLinePartial\n\t\t? `[Showing last ${formatSize(truncation.outputBytes)} of a single line; earlier output dropped]`\n\t\t: `[Showing lines ${truncation.totalLines - truncation.outputLines + 1}-${truncation.totalLines} of ${truncation.totalLines}; earlier output dropped]`;\n\treturn { text: `${truncation.content}\\n\\n${marker}`, body: truncation.content, marker, truncated: true, truncation };\n}\n\n/**\n * Split a tool-result text into parts so each notice becomes its own model-only\n * part. Providers join a tool result's text parts with \"\\n\", so every notice\n * must sit on its own line: the newline before it (and after it, when text\n * follows) is taken out of the neighbouring parts and restored by that join.\n * The joined parts are therefore byte-identical to `text`. A notice that is\n * absent or not line-delimited leaves `text` as one visible part.\n */\nexport function splitModelOnlyNotices(text: string, notices: ReadonlyArray<string | undefined>): TextContent[] {\n\tconst parts: TextContent[] = [];\n\tlet cursor = 0;\n\t// True when the previous part is a notice whose trailing newline was consumed.\n\tlet afterNotice = false;\n\tfor (const notice of notices) {\n\t\tif (notice === undefined || notice.length === 0) continue;\n\t\tconst index = text.indexOf(notice, cursor);\n\t\tif (index < 0) continue;\n\t\tconst end = index + notice.length;\n\t\tif (end < text.length && text[end] !== \"\\n\") continue;\n\t\tif (afterNotice && index === cursor) {\n\t\t\tparts.push(modelOnlyText(notice));\n\t\t} else if (index > cursor && text[index - 1] === \"\\n\") {\n\t\t\tparts.push({ type: \"text\", text: text.slice(cursor, index - 1) }, modelOnlyText(notice));\n\t\t} else {\n\t\t\tcontinue;\n\t\t}\n\t\tcursor = end < text.length ? end + 1 : end;\n\t\tafterNotice = end < text.length;\n\t}\n\tif (parts.length === 0) return [{ type: \"text\", text }];\n\tif (cursor < text.length || text[text.length - 1] === \"\\n\") parts.push({ type: \"text\", text: text.slice(cursor) });\n\treturn parts;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"bash-output.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/bash-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAGN,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAEvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG3E,eAAO,MAAM,gBAAgB;;;;EAQ3B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAwB9D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,mBAAmB;;;;;;;;;;yBAShC,MAAM,SAAS,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;;;EA8BxF"}
1
+ {"version":3,"file":"bash-output.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/bash-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI5C,OAAO,EAIN,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EAEvB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAG3E,eAAO,MAAM,gBAAgB;;;;EAQ3B,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAwB9D,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,mBAAmB;;;;;;;;;;yBAShC,MAAM,SAAS,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC;;;EAmCxF"}
@@ -1,7 +1,7 @@
1
1
  import { Type } from "typebox";
2
2
  import { formatTerminalToolOutput } from "../output-format.js";
3
3
  import { safeRegExp, TERMINAL_OUTPUT_TOOL } from "../shared.js";
4
- import { errorResult, resolveTerminalId, textResult, } from "./context.js";
4
+ import { errorResult, noticedResult, resolveTerminalId, textResult, } from "./context.js";
5
5
  import { renderBashOutputCall, renderBashOutputResult } from "./render.js";
6
6
  import { describeExit } from "./spawn.js";
7
7
  export const bashOutputSchema = Type.Object({
@@ -62,8 +62,9 @@ export function createBashOutputTool(ctx) {
62
62
  }
63
63
  const delta = runtime.readDelta();
64
64
  const formatted = formatTerminalToolOutput(applyFilter(delta.text, input.filter));
65
- const dropped = delta.droppedChars > 0 ? `[${delta.droppedChars} earlier chars dropped]\n` : "";
66
- return textResult(`${prefix}${statusLine(runtime)}\n${dropped}${formatted.text || "(no new output)"}`, extra);
65
+ const droppedNotice = delta.droppedChars > 0 ? `[${delta.droppedChars} earlier chars dropped]` : undefined;
66
+ const dropped = droppedNotice === undefined ? "" : `${droppedNotice}\n`;
67
+ return noticedResult(`${prefix}${statusLine(runtime)}\n${dropped}${formatted.text || "(no new output)"}`, [droppedNotice, formatted.marker], extra);
67
68
  },
68
69
  renderCall: renderBashOutputCall,
69
70
  renderResult: renderBashOutputResult,