@code-yeongyu/senpi 2026.10.11 → 2026.10.12

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 (188) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/bundle/chunks/anthropic-messages-GYG6YZY5.js +10 -0
  3. package/dist/bundle/chunks/{app-server-command-NHDOL4HQ.js → app-server-command-OL6PPUOA.js} +1 -1
  4. package/dist/bundle/chunks/{azure-openai-responses-EJF2XJ7Q.js → azure-openai-responses-GU6FOSJR.js} +1 -1
  5. package/dist/bundle/chunks/{chunk-ZABLYCHT.js → chunk-46JEQXD7.js} +1 -1
  6. package/dist/bundle/chunks/{chunk-VE3D74HN.js → chunk-55VKYA5V.js} +1 -1
  7. package/dist/bundle/chunks/{chunk-7UNN3YHW.js → chunk-5AVFHHDF.js} +230 -221
  8. package/dist/bundle/chunks/{chunk-2JE2DOIF.js → chunk-5DPTM5E2.js} +1 -1
  9. package/dist/bundle/chunks/chunk-5UPUSGP5.js +2 -0
  10. package/dist/bundle/chunks/chunk-5VMRMY6N.js +16 -0
  11. package/dist/bundle/chunks/{chunk-7X3VRAM3.js → chunk-7P3QPVTF.js} +1 -1
  12. package/dist/bundle/chunks/{chunk-T6IYKWIH.js → chunk-BUGZZVQW.js} +1 -1
  13. package/dist/bundle/chunks/chunk-DL7EFVT2.js +12 -0
  14. package/dist/bundle/chunks/chunk-E5YUVGLQ.js +11 -0
  15. package/dist/bundle/chunks/{chunk-RLFQICEY.js → chunk-EQJY2B5F.js} +1 -1
  16. package/dist/bundle/chunks/{chunk-VYKBN4MR.js → chunk-GC2RU5TS.js} +1 -1
  17. package/dist/bundle/chunks/chunk-GPKEJOTQ.js +202 -0
  18. package/dist/bundle/chunks/{chunk-KCVSSFOV.js → chunk-GQ5PWY6P.js} +4 -3
  19. package/dist/bundle/chunks/{chunk-4GPJOGXZ.js → chunk-I4AEC6MH.js} +1 -1
  20. package/dist/bundle/chunks/chunk-JXSFGRBE.js +8 -0
  21. package/dist/bundle/chunks/{chunk-YGCMN2CX.js → chunk-NAY5LB53.js} +1 -1
  22. package/dist/bundle/chunks/{chunk-EBOD66AS.js → chunk-PBN2CATL.js} +2 -2
  23. package/dist/bundle/chunks/chunk-PFNBY43W.js +2 -0
  24. package/dist/bundle/chunks/{chunk-CY2B4QRR.js → chunk-PUWEQK2W.js} +1 -1
  25. package/dist/bundle/chunks/{chunk-5DBPUQSX.js → chunk-SF6AUNQZ.js} +1 -1
  26. package/dist/bundle/chunks/{chunk-G2XAU4BX.js → chunk-TMTT5CLA.js} +1 -1
  27. package/dist/bundle/chunks/{chunk-2JKDZXIY.js → chunk-VWFKLW73.js} +2 -2
  28. package/dist/bundle/chunks/chunk-WZMSBRTW.js +2 -0
  29. package/dist/bundle/chunks/{chunk-55HLKZSB.js → chunk-XVJRRLGN.js} +1 -1
  30. package/dist/bundle/chunks/chunk-Z2PWWBBD.js +31 -0
  31. package/dist/bundle/chunks/cli-main-RSIYAR4J.js +3 -0
  32. package/dist/bundle/chunks/{cloudflare-workers-ai-system-one-SWVU2AXJ.js → cloudflare-workers-ai-system-one-LVWRKOUD.js} +1 -1
  33. package/dist/bundle/chunks/{google-generative-ai-XG4YX6FQ.js → google-generative-ai-JMGYPTXL.js} +1 -1
  34. package/dist/bundle/chunks/{google-vertex-BBOZ6OUB.js → google-vertex-CI4FPIU7.js} +1 -1
  35. package/dist/bundle/chunks/{help-fast-path-EZ3KEW6V.js → help-fast-path-M7KMBM6K.js} +1 -1
  36. package/dist/bundle/chunks/host-command-6UR3DSFR.js +2 -0
  37. package/dist/bundle/chunks/host-lifecycle-2BKCCK5X.js +3 -0
  38. package/dist/bundle/chunks/host-lifecycle.js +10 -7
  39. package/dist/bundle/chunks/host-session-command-A2MZHEBP.js +19 -0
  40. package/dist/bundle/chunks/interactive-mode-EJBMTJOH.js +2 -0
  41. package/dist/bundle/chunks/{list-tips-WE2YGQM6.js → list-tips-BEQAOA4N.js} +1 -1
  42. package/dist/bundle/chunks/{mistral-conversations-RB3ZBCGE.js → mistral-conversations-5OD7662T.js} +1 -1
  43. package/dist/bundle/chunks/{multi-session-host-V4CUQLV7.js → multi-session-host-YYMLHCUA.js} +4 -4
  44. package/dist/bundle/chunks/{openai-codex-responses-DBTPUXRW.js → openai-codex-responses-FN3UPAFI.js} +1 -1
  45. package/dist/bundle/chunks/{openai-completions-QAJQC4BY.js → openai-completions-KMUQ3T4M.js} +1 -1
  46. package/dist/bundle/chunks/{openai-responses-UBJWCEHG.js → openai-responses-V4Q5GVLO.js} +1 -1
  47. package/dist/bundle/chunks/{package-manager-cli-WTRDT4ZH.js → package-manager-cli-6A5ZP26E.js} +1 -1
  48. package/dist/bundle/chunks/rotation-stream-PT6LLBMJ.js +2 -0
  49. package/dist/bundle/chunks/rpc-mode-XNYJPYUM.js +2 -0
  50. package/dist/bundle/chunks/runtime-session-ZBHFMT65.js +2 -0
  51. package/dist/bundle/chunks/{schedule-command-PJVRKAKD.js → schedule-command-FYQG2MFX.js} +1 -1
  52. package/dist/bundle/chunks/session-control-endpoint-7TX4XHN4.js +3 -0
  53. package/dist/bundle/chunks/session-picker-4RNB27SM.js +2 -0
  54. package/dist/bundle/chunks/session-worker.js +418 -412
  55. package/dist/bundle/chunks/{typesafe-system-one-B5TW3FHM.js → typesafe-system-one-WUHW7HR4.js} +1 -1
  56. package/dist/bundle/cli.js +1 -1
  57. package/dist/bundle/index.js +1 -1
  58. package/dist/bundle/rpc-entry.js +1 -1
  59. package/dist/bundle/runtime-manifest.json +1 -1
  60. package/dist/core/agent-session.d.ts +11 -2
  61. package/dist/core/agent-session.js +152 -43
  62. package/dist/core/compaction/compaction.js +2 -0
  63. package/dist/core/extensions/builtin/anthropic-web-search/index.js +22 -14
  64. package/dist/core/extensions/builtin/cache-keepalive/index.js +8 -4
  65. package/dist/core/extensions/builtin/cache-keepalive/session-prewarm.d.ts +2 -1
  66. package/dist/core/extensions/builtin/cache-keepalive/session-prewarm.js +55 -7
  67. package/dist/core/extensions/builtin/cache-keepalive/startup-activity-gate.d.ts +8 -0
  68. package/dist/core/extensions/builtin/cache-keepalive/startup-activity-gate.js +25 -0
  69. package/dist/core/extensions/builtin/compaction/index.js +13 -5
  70. package/dist/core/extensions/builtin/compaction/speculative.d.ts +1 -0
  71. package/dist/core/extensions/builtin/compaction/speculative.js +8 -4
  72. package/dist/core/extensions/builtin/moved-path-guard/tool-classes.js +1 -0
  73. package/dist/core/extensions/builtin/openai-web-search/index.d.ts +4 -1
  74. package/dist/core/extensions/builtin/openai-web-search/index.js +54 -17
  75. package/dist/core/extensions/builtin/server-search-fallback.d.ts +12 -0
  76. package/dist/core/extensions/builtin/server-search-fallback.js +50 -0
  77. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +10 -1
  78. package/dist/core/extensions/builtin/terminal/runtime-session.js +48 -4
  79. package/dist/core/extensions/builtin/websearch/index.d.ts +1 -1
  80. package/dist/core/extensions/builtin/websearch/index.js +119 -18
  81. package/dist/core/extensions/builtin/websearch/server-search.d.ts +11 -0
  82. package/dist/core/extensions/builtin/websearch/server-search.js +230 -0
  83. package/dist/core/extensions/builtin/websearch/websearch/native.d.ts +2 -0
  84. package/dist/core/extensions/builtin/websearch/websearch/search-model.js +15 -0
  85. package/dist/core/extensions/runner.d.ts +8 -1
  86. package/dist/core/extensions/runner.js +56 -7
  87. package/dist/core/extensions/types.d.ts +12 -2
  88. package/dist/core/extensions/user-activity.d.ts +10 -0
  89. package/dist/core/extensions/user-activity.js +20 -0
  90. package/dist/core/model-config-schema.d.ts +34 -0
  91. package/dist/core/model-config-schema.js +2 -0
  92. package/dist/core/model-config.js +1 -0
  93. package/dist/core/reserved-keywords.d.ts +28 -0
  94. package/dist/core/reserved-keywords.js +81 -0
  95. package/dist/core/retry-fallback/candidates.d.ts +1 -1
  96. package/dist/core/retry-fallback/candidates.js +1 -12
  97. package/dist/core/retry-fallback/controller.d.ts +3 -1
  98. package/dist/core/retry-fallback/controller.js +11 -5
  99. package/dist/core/retry-fallback/cooldown.d.ts +2 -1
  100. package/dist/core/retry-fallback/cooldown.js +21 -12
  101. package/dist/core/retry-fallback/unusable.d.ts +2 -0
  102. package/dist/core/retry-fallback/unusable.js +14 -2
  103. package/dist/core/retry-fallback/usage-limit.d.ts +10 -1
  104. package/dist/core/retry-fallback/usage-limit.js +39 -2
  105. package/dist/core/session-title-generator.js +2 -0
  106. package/dist/index.d.ts +2 -1
  107. package/dist/index.js +1 -0
  108. package/dist/modes/index.d.ts +1 -1
  109. package/dist/modes/interactive/components/assistant-render-descriptors.js +7 -1
  110. package/dist/modes/interactive/composer-user-activity.d.ts +6 -0
  111. package/dist/modes/interactive/composer-user-activity.js +30 -0
  112. package/dist/modes/interactive/grok/chrome.js +2 -1
  113. package/dist/modes/interactive/interactive-mode.js +15 -6
  114. package/dist/modes/interactive/session-control-registry.js +8 -1
  115. package/dist/modes/interactive/theme/reserved-keyword-rainbow.d.ts +11 -0
  116. package/dist/modes/interactive/theme/reserved-keyword-rainbow.js +50 -0
  117. package/dist/modes/interactive/theme/theme.d.ts +3 -1
  118. package/dist/modes/interactive/theme/theme.js +9 -0
  119. package/dist/modes/print-mode.js +7 -2
  120. package/dist/modes/rpc/connection-handler.js +43 -11
  121. package/dist/modes/rpc/custom-capability.d.ts +2 -0
  122. package/dist/modes/rpc/custom-capability.js +2 -0
  123. package/dist/modes/rpc/host-admission-proxy.d.ts +9 -0
  124. package/dist/modes/rpc/host-admission-proxy.js +211 -0
  125. package/dist/modes/rpc/host-admission.d.ts +21 -0
  126. package/dist/modes/rpc/host-admission.js +59 -0
  127. package/dist/modes/rpc/host-capabilities.js +2 -1
  128. package/dist/modes/rpc/host-client-occupancy.d.ts +2 -1
  129. package/dist/modes/rpc/host-client-occupancy.js +7 -0
  130. package/dist/modes/rpc/host-decision.d.ts +2 -2
  131. package/dist/modes/rpc/host-decision.js +4 -0
  132. package/dist/modes/rpc/host-ensure-types.d.ts +2 -0
  133. package/dist/modes/rpc/host-ensure.js +31 -3
  134. package/dist/modes/rpc/host-lifecycle-drain.d.ts +2 -1
  135. package/dist/modes/rpc/host-lifecycle-drain.js +3 -1
  136. package/dist/modes/rpc/host-lifecycle-proxy.d.ts +3 -2
  137. package/dist/modes/rpc/host-lifecycle-proxy.js +10 -19
  138. package/dist/modes/rpc/host-lifecycle-shutdown.d.ts +2 -0
  139. package/dist/modes/rpc/host-lifecycle-shutdown.js +3 -1
  140. package/dist/modes/rpc/host-lifecycle-ticker.d.ts +11 -0
  141. package/dist/modes/rpc/host-lifecycle-ticker.js +27 -0
  142. package/dist/modes/rpc/host-lifecycle.js +15 -23
  143. package/dist/modes/rpc/host-outcome.js +1 -0
  144. package/dist/modes/rpc/host-protocol-info.d.ts +2 -0
  145. package/dist/modes/rpc/host-protocol-info.js +1 -0
  146. package/dist/modes/rpc/host-session-runner.js +22 -1
  147. package/dist/modes/rpc/host-status-all.js +1 -0
  148. package/dist/modes/rpc/host-status.d.ts +1 -0
  149. package/dist/modes/rpc/host-status.js +5 -1
  150. package/dist/modes/rpc/rpc-client.d.ts +7 -1
  151. package/dist/modes/rpc/rpc-client.js +12 -0
  152. package/dist/modes/rpc/rpc-types.d.ts +49 -1
  153. package/dist/modes/rpc/rpc-types.js +5 -0
  154. package/dist/modes/rpc/session-command-activity.js +2 -0
  155. package/dist/modes/rpc/session-held.js +3 -0
  156. package/docs/extensions.md +13 -1
  157. package/docs/providers.md +88 -0
  158. package/docs/rpc.md +150 -5
  159. package/docs/web-search.md +3 -1
  160. package/package.json +7 -7
  161. package/dist/bundle/chunks/anthropic-messages-5QQJVQPZ.js +0 -9
  162. package/dist/bundle/chunks/chunk-2A3MS2Z7.js +0 -8
  163. package/dist/bundle/chunks/chunk-6SBY4ZWC.js +0 -2
  164. package/dist/bundle/chunks/chunk-6WCOSBI7.js +0 -2
  165. package/dist/bundle/chunks/chunk-E75RO54O.js +0 -14
  166. package/dist/bundle/chunks/chunk-JMYO34RK.js +0 -2
  167. package/dist/bundle/chunks/chunk-KRNXUTZJ.js +0 -2
  168. package/dist/bundle/chunks/chunk-L4HVJWIF.js +0 -11
  169. package/dist/bundle/chunks/chunk-N5WLLHUE.js +0 -2
  170. package/dist/bundle/chunks/chunk-NEFNNQSV.js +0 -9
  171. package/dist/bundle/chunks/chunk-PWIYVLIA.js +0 -9
  172. package/dist/bundle/chunks/chunk-Q3R44CGT.js +0 -5
  173. package/dist/bundle/chunks/chunk-QIARZN7G.js +0 -31
  174. package/dist/bundle/chunks/chunk-QKU6JOA4.js +0 -2
  175. package/dist/bundle/chunks/chunk-QYWXDE47.js +0 -2
  176. package/dist/bundle/chunks/chunk-XHELSLN3.js +0 -188
  177. package/dist/bundle/chunks/chunk-YDJ4HGWO.js +0 -16
  178. package/dist/bundle/chunks/chunk-ZJHRDQCH.js +0 -2
  179. package/dist/bundle/chunks/cli-main-TUBNBZNK.js +0 -3
  180. package/dist/bundle/chunks/host-command-WO7VKJBJ.js +0 -2
  181. package/dist/bundle/chunks/host-lifecycle-X2J5FTJL.js +0 -3
  182. package/dist/bundle/chunks/host-session-command-ICBOH5RP.js +0 -19
  183. package/dist/bundle/chunks/interactive-mode-UGDNCGQ5.js +0 -2
  184. package/dist/bundle/chunks/rotation-stream-OODURUGZ.js +0 -2
  185. package/dist/bundle/chunks/rpc-mode-22MCT32O.js +0 -2
  186. package/dist/bundle/chunks/runtime-session-I4NDNGC5.js +0 -2
  187. package/dist/bundle/chunks/session-control-endpoint-SFPRQBDO.js +0 -3
  188. package/dist/bundle/chunks/session-picker-3ZQPV7Z3.js +0 -2
@@ -78,6 +78,7 @@ declare const ModelDefinitionSchema: Type.TObject<{
78
78
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
79
79
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
80
80
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
81
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
81
82
  supportsStore: Type.TOptional<Type.TBoolean>;
82
83
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
83
84
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -142,6 +143,7 @@ declare const ModelDefinitionSchema: Type.TObject<{
142
143
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
143
144
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
144
145
  }>, Type.TObject<{
146
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
145
147
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
146
148
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
147
149
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -234,6 +236,7 @@ declare const ModelOverrideSchema: Type.TObject<{
234
236
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
235
237
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
236
238
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
239
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
237
240
  supportsStore: Type.TOptional<Type.TBoolean>;
238
241
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
239
242
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -298,6 +301,7 @@ declare const ModelOverrideSchema: Type.TObject<{
298
301
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
299
302
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
300
303
  }>, Type.TObject<{
304
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
301
305
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
302
306
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
303
307
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -348,6 +352,7 @@ declare const ProviderConfigSchema: Type.TObject<{
348
352
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
349
353
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
350
354
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
355
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
351
356
  supportsStore: Type.TOptional<Type.TBoolean>;
352
357
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
353
358
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -412,6 +417,7 @@ declare const ProviderConfigSchema: Type.TObject<{
412
417
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
413
418
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
414
419
  }>, Type.TObject<{
420
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
415
421
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
416
422
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
417
423
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -512,6 +518,7 @@ declare const ProviderConfigSchema: Type.TObject<{
512
518
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
513
519
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
514
520
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
521
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
515
522
  supportsStore: Type.TOptional<Type.TBoolean>;
516
523
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
517
524
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -576,6 +583,7 @@ declare const ProviderConfigSchema: Type.TObject<{
576
583
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
577
584
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
578
585
  }>, Type.TObject<{
586
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
579
587
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
580
588
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
581
589
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -668,6 +676,7 @@ declare const ProviderConfigSchema: Type.TObject<{
668
676
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
669
677
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
670
678
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
679
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
671
680
  supportsStore: Type.TOptional<Type.TBoolean>;
672
681
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
673
682
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -732,6 +741,7 @@ declare const ProviderConfigSchema: Type.TObject<{
732
741
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
733
742
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
734
743
  }>, Type.TObject<{
744
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
735
745
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
736
746
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
737
747
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -795,6 +805,7 @@ declare const ModelsConfigSchema: Type.TObject<{
795
805
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
796
806
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
797
807
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
808
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
798
809
  supportsStore: Type.TOptional<Type.TBoolean>;
799
810
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
800
811
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -859,6 +870,7 @@ declare const ModelsConfigSchema: Type.TObject<{
859
870
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
860
871
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
861
872
  }>, Type.TObject<{
873
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
862
874
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
863
875
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
864
876
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -959,6 +971,7 @@ declare const ModelsConfigSchema: Type.TObject<{
959
971
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
960
972
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
961
973
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
974
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
962
975
  supportsStore: Type.TOptional<Type.TBoolean>;
963
976
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
964
977
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -1023,6 +1036,7 @@ declare const ModelsConfigSchema: Type.TObject<{
1023
1036
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
1024
1037
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
1025
1038
  }>, Type.TObject<{
1039
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
1026
1040
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1027
1041
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
1028
1042
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -1115,6 +1129,7 @@ declare const ModelsConfigSchema: Type.TObject<{
1115
1129
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
1116
1130
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
1117
1131
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
1132
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
1118
1133
  supportsStore: Type.TOptional<Type.TBoolean>;
1119
1134
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1120
1135
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -1179,6 +1194,7 @@ declare const ModelsConfigSchema: Type.TObject<{
1179
1194
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
1180
1195
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
1181
1196
  }>, Type.TObject<{
1197
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
1182
1198
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1183
1199
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
1184
1200
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -1243,6 +1259,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1243
1259
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
1244
1260
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
1245
1261
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
1262
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
1246
1263
  supportsStore: Type.TOptional<Type.TBoolean>;
1247
1264
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1248
1265
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -1307,6 +1324,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1307
1324
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
1308
1325
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
1309
1326
  }>, Type.TObject<{
1327
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
1310
1328
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1311
1329
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
1312
1330
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -1407,6 +1425,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1407
1425
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
1408
1426
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
1409
1427
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
1428
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
1410
1429
  supportsStore: Type.TOptional<Type.TBoolean>;
1411
1430
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1412
1431
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -1471,6 +1490,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1471
1490
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
1472
1491
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
1473
1492
  }>, Type.TObject<{
1493
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
1474
1494
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1475
1495
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
1476
1496
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -1563,6 +1583,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1563
1583
  extraBody: Type.TOptional<Type.TRecord<"^.*$", Type.TUnknown>>;
1564
1584
  cacheRetention: Type.TOptional<Type.TUnion<[Type.TLiteral<"none">, Type.TLiteral<"short">, Type.TLiteral<"long">]>>;
1565
1585
  compat: Type.TOptional<Type.TUnion<[Type.TObject<{
1586
+ supportsWebSearch: Type.TOptional<Type.TBoolean>;
1566
1587
  supportsStore: Type.TOptional<Type.TBoolean>;
1567
1588
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1568
1589
  supportsReasoningEffort: Type.TOptional<Type.TBoolean>;
@@ -1627,6 +1648,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1627
1648
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
1628
1649
  supportsForcedToolChoice: Type.TOptional<Type.TBoolean>;
1629
1650
  }>, Type.TObject<{
1651
+ webSearchToolType: Type.TOptional<Type.TUnion<[Type.TLiteral<"web_search">, Type.TLiteral<"web_search_preview">]>>;
1630
1652
  supportsDeveloperRole: Type.TOptional<Type.TBoolean>;
1631
1653
  sessionAffinityFormat: Type.TOptional<Type.TUnion<[Type.TLiteral<"openai">, Type.TLiteral<"openai-nosession">, Type.TLiteral<"openrouter">]>>;
1632
1654
  supportsLongCacheRetention: Type.TOptional<Type.TBoolean>;
@@ -1690,6 +1712,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1690
1712
  extraBody?: Record<string, unknown> | undefined;
1691
1713
  cacheRetention?: "long" | "none" | "short" | undefined;
1692
1714
  compat?: {
1715
+ supportsWebSearch?: boolean | undefined;
1693
1716
  supportsStore?: boolean | undefined;
1694
1717
  supportsDeveloperRole?: boolean | undefined;
1695
1718
  supportsReasoningEffort?: boolean | undefined;
@@ -1754,6 +1777,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1754
1777
  supportsLongCacheRetention?: boolean | undefined;
1755
1778
  supportsForcedToolChoice?: boolean | undefined;
1756
1779
  } | {
1780
+ webSearchToolType?: "web_search" | "web_search_preview" | undefined;
1757
1781
  supportsDeveloperRole?: boolean | undefined;
1758
1782
  sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter" | undefined;
1759
1783
  supportsLongCacheRetention?: boolean | undefined;
@@ -1854,6 +1878,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1854
1878
  extraBody?: Record<string, unknown> | undefined;
1855
1879
  cacheRetention?: "long" | "none" | "short" | undefined;
1856
1880
  compat?: {
1881
+ supportsWebSearch?: boolean | undefined;
1857
1882
  supportsStore?: boolean | undefined;
1858
1883
  supportsDeveloperRole?: boolean | undefined;
1859
1884
  supportsReasoningEffort?: boolean | undefined;
@@ -1918,6 +1943,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
1918
1943
  supportsLongCacheRetention?: boolean | undefined;
1919
1944
  supportsForcedToolChoice?: boolean | undefined;
1920
1945
  } | {
1946
+ webSearchToolType?: "web_search" | "web_search_preview" | undefined;
1921
1947
  supportsDeveloperRole?: boolean | undefined;
1922
1948
  sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter" | undefined;
1923
1949
  supportsLongCacheRetention?: boolean | undefined;
@@ -2010,6 +2036,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2010
2036
  extraBody?: Record<string, unknown> | undefined;
2011
2037
  cacheRetention?: "long" | "none" | "short" | undefined;
2012
2038
  compat?: {
2039
+ supportsWebSearch?: boolean | undefined;
2013
2040
  supportsStore?: boolean | undefined;
2014
2041
  supportsDeveloperRole?: boolean | undefined;
2015
2042
  supportsReasoningEffort?: boolean | undefined;
@@ -2074,6 +2101,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2074
2101
  supportsLongCacheRetention?: boolean | undefined;
2075
2102
  supportsForcedToolChoice?: boolean | undefined;
2076
2103
  } | {
2104
+ webSearchToolType?: "web_search" | "web_search_preview" | undefined;
2077
2105
  supportsDeveloperRole?: boolean | undefined;
2078
2106
  sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter" | undefined;
2079
2107
  supportsLongCacheRetention?: boolean | undefined;
@@ -2137,6 +2165,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2137
2165
  extraBody?: Record<string, unknown> | undefined;
2138
2166
  cacheRetention?: "long" | "none" | "short" | undefined;
2139
2167
  compat?: {
2168
+ supportsWebSearch?: boolean | undefined;
2140
2169
  supportsStore?: boolean | undefined;
2141
2170
  supportsDeveloperRole?: boolean | undefined;
2142
2171
  supportsReasoningEffort?: boolean | undefined;
@@ -2201,6 +2230,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2201
2230
  supportsLongCacheRetention?: boolean | undefined;
2202
2231
  supportsForcedToolChoice?: boolean | undefined;
2203
2232
  } | {
2233
+ webSearchToolType?: "web_search" | "web_search_preview" | undefined;
2204
2234
  supportsDeveloperRole?: boolean | undefined;
2205
2235
  sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter" | undefined;
2206
2236
  supportsLongCacheRetention?: boolean | undefined;
@@ -2301,6 +2331,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2301
2331
  extraBody?: Record<string, unknown> | undefined;
2302
2332
  cacheRetention?: "long" | "none" | "short" | undefined;
2303
2333
  compat?: {
2334
+ supportsWebSearch?: boolean | undefined;
2304
2335
  supportsStore?: boolean | undefined;
2305
2336
  supportsDeveloperRole?: boolean | undefined;
2306
2337
  supportsReasoningEffort?: boolean | undefined;
@@ -2365,6 +2396,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2365
2396
  supportsLongCacheRetention?: boolean | undefined;
2366
2397
  supportsForcedToolChoice?: boolean | undefined;
2367
2398
  } | {
2399
+ webSearchToolType?: "web_search" | "web_search_preview" | undefined;
2368
2400
  supportsDeveloperRole?: boolean | undefined;
2369
2401
  sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter" | undefined;
2370
2402
  supportsLongCacheRetention?: boolean | undefined;
@@ -2457,6 +2489,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2457
2489
  extraBody?: Record<string, unknown> | undefined;
2458
2490
  cacheRetention?: "long" | "none" | "short" | undefined;
2459
2491
  compat?: {
2492
+ supportsWebSearch?: boolean | undefined;
2460
2493
  supportsStore?: boolean | undefined;
2461
2494
  supportsDeveloperRole?: boolean | undefined;
2462
2495
  supportsReasoningEffort?: boolean | undefined;
@@ -2521,6 +2554,7 @@ export declare const validateModelsConfig: import("typebox/compile").Validator<{
2521
2554
  supportsLongCacheRetention?: boolean | undefined;
2522
2555
  supportsForcedToolChoice?: boolean | undefined;
2523
2556
  } | {
2557
+ webSearchToolType?: "web_search" | "web_search_preview" | undefined;
2524
2558
  supportsDeveloperRole?: boolean | undefined;
2525
2559
  sessionAffinityFormat?: "openai" | "openai-nosession" | "openrouter" | undefined;
2526
2560
  supportsLongCacheRetention?: boolean | undefined;
@@ -101,6 +101,7 @@ const ModelInputLimitsSchema = Type.Object({
101
101
  })),
102
102
  });
103
103
  const OpenAICompletionsCompatSchema = Type.Object({
104
+ supportsWebSearch: Type.Optional(Type.Boolean()),
104
105
  supportsStore: Type.Optional(Type.Boolean()),
105
106
  supportsDeveloperRole: Type.Optional(Type.Boolean()),
106
107
  supportsReasoningEffort: Type.Optional(Type.Boolean()),
@@ -138,6 +139,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
138
139
  supportsForcedToolChoice: Type.Optional(Type.Boolean()),
139
140
  });
140
141
  const OpenAIResponsesCompatSchema = Type.Object({
142
+ webSearchToolType: Type.Optional(Type.Union([Type.Literal("web_search"), Type.Literal("web_search_preview")])),
141
143
  supportsDeveloperRole: Type.Optional(Type.Boolean()),
142
144
  sessionAffinityFormat: Type.Optional(Type.Union([Type.Literal("openai"), Type.Literal("openai-nosession"), Type.Literal("openrouter")])),
143
145
  supportsLongCacheRetention: Type.Optional(Type.Boolean()),
@@ -109,6 +109,7 @@ const OpenAICompletionsCompatSchema = Type.Object({
109
109
  });
110
110
 
111
111
  const OpenAIResponsesCompatSchema = Type.Object({
112
+ webSearchToolType: Type.Optional(Type.Union([Type.Literal("web_search"), Type.Literal("web_search_preview")])),
112
113
  supportsDeveloperRole: Type.Optional(Type.Boolean()),
113
114
  sessionAffinityFormat: Type.Optional(
114
115
  Type.Union([Type.Literal("openai"), Type.Literal("openai-nosession"), Type.Literal("openrouter")]),
@@ -0,0 +1,28 @@
1
+ /**
2
+ * OmO's reserved prompt keywords (`ulw`, `mass ulw`, `ulw plan`, `ulw loop`, `ulw research`).
3
+ *
4
+ * Mirrors the omo-senpi engine's arming rules (omo `packages/omo-senpi/src/components/
5
+ * {ultrawork/index.ts, skill-pointers/index.ts, skill-pointers/strip-quoted-regions.ts}`),
6
+ * so the editor paints a keyword only where the engine would arm it: case-insensitive,
7
+ * spaces or hyphens between words, the mass aliases (`mulw`, `meth`, `ulw mass`), and
8
+ * each name only as a word of its own (not inside an identifier, path, file name or URL).
9
+ * Overlapping mentions all match ("ulw research" is both `ultrawork` and `ulw-research`).
10
+ * Injected blocks, fenced/inline code, relay headers, block quotes, double quotes, URLs
11
+ * and negated invocations ("do not load mass-ulw") are quoted material, never a request.
12
+ */
13
+ export type ReservedKeyword = "ultrawork" | "mass-ulw" | "ulw-plan" | "ulw-loop" | "ulw-research";
14
+ export interface ReservedKeywordSpan {
15
+ readonly keyword: ReservedKeyword;
16
+ /** UTF-16 offsets into the searched text, end exclusive. */
17
+ readonly start: number;
18
+ readonly end: number;
19
+ }
20
+ export interface ReservedKeywordRun {
21
+ readonly start: number;
22
+ readonly end: number;
23
+ }
24
+ /** Every reserved-keyword mention outside quoted regions, in text order. */
25
+ export declare function findReservedKeywordSpans(text: string): ReservedKeywordSpan[];
26
+ /** Overlapping mentions ("mass ulw research" is three) merge into one painted run, as on the desktop. */
27
+ export declare function findReservedKeywordRuns(text: string): ReservedKeywordRun[];
28
+ //# sourceMappingURL=reserved-keywords.d.ts.map
@@ -0,0 +1,81 @@
1
+ const MASS_ULW_ALIAS = String.raw `(?:mass[\s-]*ulw|ulw[\s-]*mass|mulw|meth)`;
2
+ // A keyword glued to a preceding identifier segment (`mass-ulw`, `senpi-ulw-loop`, `.omo/ulw/`, `v1.ulw`)
3
+ // names something else; so does one that opens a path (`ulw-execute/ledger.jsonl`).
4
+ const NOT_AFTER_IDENTIFIER = String.raw `(?<![A-Za-z0-9][-_.]|[/\\])`;
5
+ const NOT_BEFORE_PATH = String.raw `(?![-_.]?[A-Za-z0-9]*[/\\])`;
6
+ // The name must end its own word (`ulw-plan-refactor`, `ulw-plan.md` do not count); a chained
7
+ // skill word may follow if it, too, ends there (`mass ulw-loop`).
8
+ const SEGMENT_END = String.raw `(?![-_.]?[A-Za-z0-9])`;
9
+ const NOT_INTO_IDENTIFIER = String.raw `(?![-_](?!(?:loop|plan|research|execute|mass)${SEGMENT_END})[A-Za-z0-9]|\.[A-Za-z0-9])`;
10
+ const keywordPattern = (body) => new RegExp(String.raw `${NOT_AFTER_IDENTIFIER}\b${body}\b${NOT_INTO_IDENTIFIER}${NOT_BEFORE_PATH}`, "gi");
11
+ const RESERVED_KEYWORD_PATTERNS = [
12
+ ["ultrawork", keywordPattern("(?:ultrawork|ulw)")],
13
+ ["mass-ulw", keywordPattern(MASS_ULW_ALIAS)],
14
+ ["ulw-plan", keywordPattern(String.raw `ulw[\s-]*plan`)],
15
+ ["ulw-loop", keywordPattern(String.raw `ulw[\s-]*loop`)],
16
+ ["ulw-research", keywordPattern(String.raw `(?:ulw|${MASS_ULW_ALIAS})[\s-]*research`)],
17
+ ];
18
+ /** Cheap pre-check: every reserved keyword contains one of these stems. */
19
+ const RESERVED_KEYWORD_STEM = /ulw|ultrawork|mulw|meth/i;
20
+ const INJECTED_BLOCK = /<(omo-[a-z0-9-]+-pointer|ultrawork-mode|omo-ultrawork-reminder)>[\s\S]*?<\/\1>/gi;
21
+ const INLINE_CODE = /(?<!`)(`+)(?!`)[\s\S]*?(?<!`)\1(?!`)/g;
22
+ // A relayed message opens with a sender header (`[REPORT]`, `[a -> b]`, `Name (id), recipient:`): all of it is someone else's text.
23
+ const RELAY_HEADER = /^\s*(?:\[(?:[A-Z][A-Z0-9_-]{2,}|[\w.-]+ -> [\w.-]+)\]|[A-Z][\w.-]{0,30} \([^()\s]{1,40}\)(?:, | to )[\w.-]{1,40}:)/;
24
+ const BLOCK_QUOTE_LINE = /^ {0,3}>[^\n]*/gm;
25
+ const DOUBLE_QUOTED = /"[^"\n]*"|\u201C[^\u201D\n]*\u201D/g;
26
+ const URL_SPAN = /(?<![a-z0-9+.-])[a-z][a-z0-9+.-]*:\/\/[^\s<>"'`]+/gi;
27
+ // A keyword named only to forbid it ("Do not load mass-ulw or ulw-research") is masked up to the next clause break.
28
+ const NEGATED_INVOCATION = /\b(?:do\s+not|don['\u2019]t|dont|never)\s+(?:load|use|run|invoke|start|trigger|arm|enable)\b[^.;:!?\n]*?(?=[.;:!?\n]|\s(?:but|then|instead|however)\b|\s[\u2014\u2013-]\s|$)/gi;
29
+ function maskRegion(region) {
30
+ return region.replace(/[^\r\n]/g, "\0");
31
+ }
32
+ /** Quoted, injected, relayed and negated regions are masked (offsets kept) before matching. */
33
+ function maskQuotedRegions(text) {
34
+ if (RELAY_HEADER.test(text))
35
+ return maskRegion(text);
36
+ let visible = text.replace(INJECTED_BLOCK, maskRegion);
37
+ const fenceStart = /^ {0,3}(`{3,}|~{3,})[^\n]*(?:\n|$)/gm;
38
+ for (let opening = fenceStart.exec(visible); opening !== null; opening = fenceStart.exec(visible)) {
39
+ const fence = opening[1] ?? "```";
40
+ const fenceEnd = new RegExp(`^ {0,3}${fence[0]}{${fence.length},}[ \\t]*\\r?$`, "gm");
41
+ fenceEnd.lastIndex = fenceStart.lastIndex;
42
+ const closing = fenceEnd.exec(visible);
43
+ const end = closing === null ? visible.length : fenceEnd.lastIndex;
44
+ visible = visible.slice(0, opening.index) + maskRegion(visible.slice(opening.index, end)) + visible.slice(end);
45
+ fenceStart.lastIndex = end;
46
+ }
47
+ return visible
48
+ .replace(INLINE_CODE, maskRegion)
49
+ .replace(BLOCK_QUOTE_LINE, maskRegion)
50
+ .replace(DOUBLE_QUOTED, maskRegion)
51
+ .replace(URL_SPAN, maskRegion)
52
+ .replace(NEGATED_INVOCATION, maskRegion);
53
+ }
54
+ /** Every reserved-keyword mention outside quoted regions, in text order. */
55
+ export function findReservedKeywordSpans(text) {
56
+ if (!RESERVED_KEYWORD_STEM.test(text))
57
+ return [];
58
+ const visible = maskQuotedRegions(text);
59
+ const spans = [];
60
+ for (const [keyword, pattern] of RESERVED_KEYWORD_PATTERNS) {
61
+ for (const match of visible.matchAll(pattern)) {
62
+ spans.push({ keyword, start: match.index, end: match.index + match[0].length });
63
+ }
64
+ }
65
+ return spans.sort((left, right) => left.start - right.start || right.end - left.end);
66
+ }
67
+ /** Overlapping mentions ("mass ulw research" is three) merge into one painted run, as on the desktop. */
68
+ export function findReservedKeywordRuns(text) {
69
+ const runs = [];
70
+ for (const span of findReservedKeywordSpans(text)) {
71
+ const previous = runs.at(-1);
72
+ if (previous !== undefined && span.start <= previous.end) {
73
+ previous.end = Math.max(previous.end, span.end);
74
+ }
75
+ else {
76
+ runs.push({ start: span.start, end: span.end });
77
+ }
78
+ }
79
+ return runs;
80
+ }
81
+ //# sourceMappingURL=reserved-keywords.js.map
@@ -11,7 +11,7 @@ export interface CandidateFilters {
11
11
  isSuppressed(base: string): boolean;
12
12
  isAuthAvailable(provider: string): boolean;
13
13
  isCircuitOpen(base: string): boolean;
14
- /** Providers whose account-wide usage limit or billing failure this session saw: their entries are tried last. */
14
+ /** Providers whose account-wide usage limit or billing failure this session saw: their entries are skipped. */
15
15
  isProviderSpent?(provider: string): boolean;
16
16
  skip(candidate: string, skipReason: string): void;
17
17
  }
@@ -1,17 +1,6 @@
1
1
  import { baseSelector, candidatesAfter, formatSelector, parseFallbackSelector, } from "./chains.js";
2
2
  export function firstUsableCandidate(entries, current, filters) {
3
- const isSpent = filters.isProviderSpent;
4
- if (!isSpent)
5
- return scanCandidates(entries, current, filters, () => false);
6
- let avoided = false;
7
- const elsewhere = scanCandidates(entries, current, filters, (provider) => {
8
- const spent = isSpent(provider);
9
- avoided ||= spent;
10
- return spent;
11
- });
12
- if (elsewhere || !avoided)
13
- return elsewhere;
14
- return scanCandidates(entries, current, filters, () => false);
3
+ return scanCandidates(entries, current, filters, (provider) => filters.isProviderSpent?.(provider) ?? false);
15
4
  }
16
5
  function scanCandidates(entries, current, filters, avoidProvider) {
17
6
  let probe;
@@ -14,6 +14,8 @@ export declare class RetryFallbackController {
14
14
  constructor(deps: RetryFallbackControllerDeps);
15
15
  get activeState(): Readonly<ActiveFallbackState> | undefined;
16
16
  get exhaustedChainKey(): string | undefined;
17
+ isProviderSpent(provider: string): boolean;
18
+ accountError(provider: string): string | undefined;
17
19
  resetTurn(): void;
18
20
  clear(): void;
19
21
  private canonicalChains;
@@ -68,7 +70,7 @@ export declare class RetryFallbackController {
68
70
  tryFallback(reason: FallbackReason, failure: {
69
71
  errorMessage?: string;
70
72
  retryAfterMs?: number;
71
- }): Promise<boolean>;
73
+ }, failureObserved?: boolean): Promise<boolean>;
72
74
  private applyCandidate;
73
75
  /** The chain governing `current`, when fallback is enabled; a model's own chain wins over an active one. */
74
76
  private managedChainKey;
@@ -18,6 +18,12 @@ export class RetryFallbackController {
18
18
  get exhaustedChainKey() {
19
19
  return this.lastExhaustedChainKey;
20
20
  }
21
+ isProviderSpent(provider) {
22
+ return this.unusable.isProviderSpent(provider);
23
+ }
24
+ accountError(provider) {
25
+ return this.unusable.accountError(provider);
26
+ }
21
27
  resetTurn() {
22
28
  this.triedSelectors.clear();
23
29
  this.lastExhaustedChainKey = undefined;
@@ -149,9 +155,9 @@ export class RetryFallbackController {
149
155
  * half-open probe - independent of whether a fallback candidate remains.
150
156
  */
151
157
  noteHealthFailure(model, thinkingLevel, failure) {
158
+ this.unusable.note(model, failure);
152
159
  if (!this.managedChainKey({ model, thinkingLevel }))
153
160
  return;
154
- this.unusable.note(model, failure);
155
161
  this.probes.noteFailure(formatSelector(model), failure);
156
162
  }
157
163
  /**
@@ -174,21 +180,21 @@ export class RetryFallbackController {
174
180
  await this.applyCandidate(current, candidate, "transient");
175
181
  return true;
176
182
  }
177
- async tryFallback(reason, failure) {
183
+ async tryFallback(reason, failure, failureObserved = true) {
178
184
  const current = this.deps.getCurrentSelector();
179
185
  // Only a transient failure that spent its budget opens the shared circuit
180
186
  // here; billing/quota exhaustion is recorded at the failure itself (see
181
187
  // noteHealthFailure) so the final chain entry opens too.
182
- if (current && reason === "transient")
188
+ if (failureObserved && current && reason === "transient")
183
189
  this.noteHealthFailure(current.model, current.thinkingLevel, failure);
184
190
  const limit = usageLimitScope(failure.errorMessage);
185
- if (current)
191
+ if (failureObserved && current)
186
192
  this.unusable.note(current.model, failure);
187
193
  let candidate = this.nextCandidate(false, true);
188
194
  if (!current || !candidate)
189
195
  return false;
190
196
  const currentBase = formatSelector(current.model);
191
- if (reason === "transient" || reason === "hard-error" || reason === "billing") {
197
+ if (failureObserved && (reason === "transient" || reason === "hard-error" || reason === "billing")) {
192
198
  this.deps.cooldowns.note(currentBase, failure);
193
199
  this.deps.logger.info("cooldown_noted", { selector: currentBase, errorMessage: failure.errorMessage });
194
200
  }
@@ -7,7 +7,8 @@ export declare class SelectorCooldowns {
7
7
  private readonly expiresAtBySelector;
8
8
  private readonly now;
9
9
  private readonly random;
10
- constructor(now: () => number, random?: () => number);
10
+ private readonly wallNow;
11
+ constructor(now: () => number, random?: () => number, wallNow?: () => number);
11
12
  note(baseSelector: string, failure: {
12
13
  retryAfterMs?: number;
13
14
  errorMessage?: string;
@@ -1,14 +1,17 @@
1
+ import { MAX_SLOT_BLOCK_MS } from "@earendil-works/pi-ai/auth/pool/failover";
1
2
  import { stripProviderRequestIds } from "@earendil-works/pi-ai/utils/retry";
3
+ import { usageLimitResetMs } from "../credential-pool/reset-time.js";
2
4
  /**
3
5
  * Runtime-only selector suppression. A SelectorCooldowns instance belongs to one
4
6
  * AgentSession and is deliberately never persisted to settings or session files.
5
7
  * Callers provide canonical base selectors without a thinking suffix.
6
8
  */
7
9
  export class SelectorCooldowns {
8
- constructor(now, random = Math.random) {
10
+ constructor(now, random = Math.random, wallNow = now) {
9
11
  this.expiresAtBySelector = new Map();
10
12
  this.now = now;
11
13
  this.random = random;
14
+ this.wallNow = wallNow;
12
15
  }
13
16
  note(baseSelector, failure) {
14
17
  this.expiresAtBySelector.set(baseSelector, this.now() + this.durationFor(failure));
@@ -29,18 +32,24 @@ export class SelectorCooldowns {
29
32
  this.expiresAtBySelector.clear();
30
33
  }
31
34
  durationFor({ retryAfterMs, errorMessage }) {
32
- if (retryAfterMs !== undefined && Number.isFinite(retryAfterMs) && retryAfterMs > 0) {
33
- return retryAfterMs;
34
- }
35
+ // Reset hints are wall-clock times; the stored deadline remains monotonic.
36
+ const resetMs = errorMessage === undefined ? undefined : usageLimitResetMs(errorMessage, this.wallNow());
35
37
  const message = stripProviderRequestIds(errorMessage ?? "").toLowerCase();
36
- if (/usage[- ]limit|quota|insufficient_quota|billing|credits[-_ ]required|credits are required/.test(message))
37
- return 30 * 60_000;
38
- if (/rate[ -]?limit|429|too many requests/.test(message))
39
- return 30_000;
40
- if (/overloaded|capacity/.test(message))
41
- return 45_000 + this.capacityJitterMs();
42
- if (/5xx|\b5\d\d\b|server|internal error/.test(message))
43
- return 20_000;
38
+ const defaultMs = /usage[- ]limit|quota|insufficient_quota|billing|credits[-_ ]required|credits are required/.test(message)
39
+ ? 30 * 60_000
40
+ : /rate[ -]?limit|429|too many requests/.test(message)
41
+ ? 30_000
42
+ : /overloaded|capacity/.test(message)
43
+ ? 45_000 + this.capacityJitterMs()
44
+ : /5xx|\b5\d\d\b|server|internal error/.test(message)
45
+ ? 20_000
46
+ : undefined;
47
+ const retryMs = retryAfterMs !== undefined && Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? retryAfterMs : undefined;
48
+ const hintedMs = Math.max(resetMs ?? 0, retryMs ?? 0);
49
+ if (hintedMs > 0)
50
+ return Math.min(MAX_SLOT_BLOCK_MS, hintedMs);
51
+ if (defaultMs !== undefined)
52
+ return defaultMs;
44
53
  // A reason-less forbidden rejection is a per-request blip on a healthy credential (senpi#2376).
45
54
  if (/"type"\s*:\s*"forbidden"[^}]*request not allowed|request not allowed[^}]*"type"\s*:\s*"forbidden"/.test(message)) {
46
55
  return 60_000;
@@ -10,9 +10,11 @@ import type { SelectorCooldowns } from "./cooldown.ts";
10
10
  */
11
11
  export declare class UnusableEntries {
12
12
  private readonly cooldowns;
13
+ private readonly accountErrors;
13
14
  constructor(cooldowns: SelectorCooldowns);
14
15
  note(model: Model<Api>, failure: CircuitFailure): void;
15
16
  isProviderSpent(provider: string): boolean;
17
+ accountError(provider: string): string | undefined;
16
18
  isUnusable(model: Model<Api>): boolean;
17
19
  clear(model: Model<Api>): void;
18
20
  }
@@ -10,12 +10,16 @@ import { usageLimitScope } from "./usage-limit.js";
10
10
  */
11
11
  export class UnusableEntries {
12
12
  constructor(cooldowns) {
13
+ this.accountErrors = new Map();
13
14
  this.cooldowns = cooldowns;
14
15
  }
15
16
  note(model, failure) {
16
17
  const scope = usageLimitScope(failure.errorMessage);
17
- if (scope === "account")
18
+ if (scope === "account") {
18
19
  this.cooldowns.note(accountKey(model.provider), failure);
20
+ if (failure.errorMessage)
21
+ this.accountErrors.set(model.provider, failure.errorMessage);
22
+ }
19
23
  else if (scope === "model" || isHealthExhaustionFailure(failure.errorMessage)) {
20
24
  this.cooldowns.note(entryKey(model), failure);
21
25
  }
@@ -23,15 +27,23 @@ export class UnusableEntries {
23
27
  isProviderSpent(provider) {
24
28
  return this.cooldowns.isSuppressed(accountKey(provider));
25
29
  }
30
+ accountError(provider) {
31
+ return this.isProviderSpent(provider) ? this.accountErrors.get(provider) : undefined;
32
+ }
26
33
  isUnusable(model) {
27
34
  return this.isProviderSpent(model.provider) || this.cooldowns.isSuppressed(entryKey(model));
28
35
  }
29
36
  clear(model) {
30
- this.cooldowns.clear(accountKey(model.provider));
37
+ // A deliberate model selection allows the user to probe after reauth/reset.
31
38
  this.cooldowns.clear(entryKey(model));
39
+ this.cooldowns.clear(accountKey(model.provider));
40
+ this.accountErrors.delete(model.provider);
32
41
  }
33
42
  }
34
43
  function accountKey(provider) {
44
+ // This is a provider-name approximation, not resolved credential/account identity.
45
+ // Gateways with distinct model accounts share a key; pooled slots can reset earlier
46
+ // than the last surfaced account error. Explicit /model selection permits a probe.
35
47
  return `account:${provider}`;
36
48
  }
37
49
  function entryKey(model) {