@pensar/apex 2.6.1 → 2.7.0-canary.21cb83dc

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 (226) hide show
  1. package/build/{agent-yby11zaq.js → agent-68t2wzmc.js} +6 -6
  2. package/build/agent-jhekpern.js +13 -0
  3. package/build/{agent-ayreaj6z.js → agent-qvreg8a4.js} +5 -5
  4. package/build/{agent-runs-g37vkrjr.js → agent-runs-30dvbvmm.js} +18 -18
  5. package/build/agent-vybw255f.js +13 -0
  6. package/build/{apps-xn6km9yn.js → apps-na0y0992.js} +13 -13
  7. package/build/{auth-d105wc64.js → auth-dz2c5thx.js} +13 -13
  8. package/build/{blackboxAgent-s56sgx9b.js → blackboxAgent-2mwvaajb.js} +6 -6
  9. package/build/{blackboxPentest-ps78v3w7.js → blackboxPentest-s2mfze9p.js} +17 -12
  10. package/build/{cli-kb21qhvm.js → cli-1pmfnz1a.js} +4 -3
  11. package/build/{cli-ae8jqxqr.js → cli-563yntrb.js} +2 -2
  12. package/build/{cli-7eea4e2y.js → cli-5ab346kd.js} +6 -6
  13. package/build/{cli-1ve18npr.js → cli-72y76bpg.js} +3 -2
  14. package/build/{cli-fy6vh2a7.js → cli-98wmmphf.js} +1 -1
  15. package/build/{cli-c05y640n.js → cli-9jscnhtg.js} +1 -1
  16. package/build/{cli-3sp6nd8d.js → cli-9kk9tm58.js} +2 -2
  17. package/build/{cli-d37a42dc.js → cli-9pz9wjt2.js} +101 -18
  18. package/build/{cli-dpsw2fe6.js → cli-a6x4jm5a.js} +476 -193
  19. package/build/{cli-az3xq70v.js → cli-cfrdr25z.js} +1 -1
  20. package/build/{cli-ngq9dr8n.js → cli-fdrrbb9p.js} +3 -3
  21. package/build/{cli-k2nwxta0.js → cli-t09zj814.js} +2 -2
  22. package/build/{cli-qgz1q9g5.js → cli-t4q4t4dx.js} +223 -76
  23. package/build/{cli-xtc2t6me.js → cli-vdd1s3x9.js} +103 -39
  24. package/build/{cli-gx71jcke.js → cli-vn4cwac3.js} +1 -1
  25. package/build/{cli-65g6rcat.js → cli-w9nw2a4z.js} +1 -1
  26. package/build/{cli-n2en7j5b.js → cli-z7ysyrr4.js} +1 -1
  27. package/build/{cli-709rvekb.js → cli-zbbtr4sb.js} +4 -2
  28. package/build/cli.js +70 -40
  29. package/build/{config-zpqkd5cz.js → config-nkzm9arw.js} +1 -1
  30. package/build/{fastStrike-mcjdvwez.js → fastStrike-16jw1s61.js} +67 -15
  31. package/build/{fixes-kytx5dmj.js → fixes-6wxk22v5.js} +13 -13
  32. package/build/{index-4vqdgb42.js → index-36p56pcf.js} +1473 -1217
  33. package/build/{index-je6hcsqr.js → index-6nejymsc.js} +1 -1
  34. package/build/{index-fv64eb32.js → index-asm4y7e4.js} +2 -2
  35. package/build/{index-n65z6v7b.js → index-bfmn52za.js} +6 -6
  36. package/build/{index-qv4cc65p.js → index-bt4r13q7.js} +13 -13
  37. package/build/{index-dr3tg1w3.js → index-gfq1rp1q.js} +1 -1
  38. package/build/{index-wqetp6gq.js → index-j9pxgrzd.js} +12 -7
  39. package/build/{index-r9yjkfrv.js → index-n9kapn1x.js} +4 -4
  40. package/build/{index-ns4m4s01.js → index-tr6evhyg.js} +4 -4
  41. package/build/{issues-eb3q9d3a.js → issues-7rmf4ker.js} +13 -13
  42. package/build/{knowledge-fqsvtx0f.js → knowledge-s2sxh8nb.js} +13 -13
  43. package/build/lib/{chunk-1azdancq.js → chunk-0dnx79b1.js} +1 -1
  44. package/build/lib/{chunk-4hwzqthr.js → chunk-161ak2am.js} +3 -2
  45. package/build/lib/{chunk-z137x08b.js → chunk-1rqbhx4b.js} +7 -5
  46. package/build/lib/{chunk-1yct6cra.js → chunk-3hjvdf67.js} +1 -1
  47. package/build/lib/{chunk-r2dkp4tb.js → chunk-5j8pp0xf.js} +1 -1
  48. package/build/lib/{chunk-3c00n0cy.js → chunk-77se1d35.js} +8 -3
  49. package/build/lib/{chunk-yrnrbjes.js → chunk-7tft4b8b.js} +4 -2
  50. package/build/lib/{chunk-w66ce82a.js → chunk-8njjwwcw.js} +4 -2
  51. package/build/lib/{chunk-v3dk9j7k.js → chunk-aq9eyxas.js} +80 -21
  52. package/build/lib/{chunk-9tt2021m.js → chunk-d05cvnv8.js} +9 -9
  53. package/build/lib/{chunk-e8yy2k73.js → chunk-f3sq4896.js} +28 -2
  54. package/build/lib/{chunk-1fy8azee.js → chunk-fasqtjq1.js} +1 -1
  55. package/build/lib/{chunk-1jvwqnem.js → chunk-gm3b9j4y.js} +104 -40
  56. package/build/lib/{chunk-r4j8ybn9.js → chunk-hys7nemr.js} +87 -36
  57. package/build/lib/{chunk-7pwa92rn.js → chunk-j9668h16.js} +1 -1
  58. package/build/lib/{chunk-ba41jh41.js → chunk-kcw4cpr3.js} +418 -133
  59. package/build/lib/{chunk-z96pbjhd.js → chunk-m4swxy5r.js} +13 -6
  60. package/build/lib/{chunk-xhdpgpkm.js → chunk-mbsde9d0.js} +2 -2
  61. package/build/lib/{chunk-9yksqgzr.js → chunk-mpvjfvz0.js} +20 -13
  62. package/build/lib/{chunk-2qsq76xq.js → chunk-qkz5sm0d.js} +128 -34
  63. package/build/lib/{chunk-nhfax4gb.js → chunk-rfyjqybp.js} +1 -1
  64. package/build/lib/{chunk-mg03jdmt.js → chunk-vsnwzne5.js} +1 -1
  65. package/build/lib/{chunk-acgbyf5h.js → chunk-ydxhkgjw.js} +3 -2
  66. package/build/lib/{chunk-bb0ekhkw.js → chunk-z8kej0sp.js} +69 -18
  67. package/build/lib/core/agents/agentRuntime.d.ts +2 -1
  68. package/build/lib/core/agents/defineAgent.d.ts +3 -2
  69. package/build/lib/core/agents/offSecAgent/index.d.ts +3 -1
  70. package/build/lib/core/agents/offSecAgent/index.js +10 -8
  71. package/build/lib/core/agents/offSecAgent/prompt.d.ts +3 -1
  72. package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +2 -1
  73. package/build/lib/core/agents/offSecAgent/subagentSpawner.js +5 -5
  74. package/build/lib/core/agents/offSecAgent/tools/index.js +4 -4
  75. package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +11 -1
  76. package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +2 -1
  77. package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +20 -1
  78. package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +4 -7
  79. package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +7 -1
  80. package/build/lib/core/agents/offSecAgent/tools/types.d.ts +2 -1
  81. package/build/lib/core/agents/offSecAgent/types.d.ts +7 -1
  82. package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +9 -9
  83. package/build/lib/core/agents/specialized/attackSurface/schemas.js +9 -9
  84. package/build/lib/core/agents/specialized/authenticationAgent/index.js +10 -10
  85. package/build/lib/core/agents/specialized/codeAgent/agent.js +9 -9
  86. package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
  87. package/build/lib/core/agents/specialized/findingJudge/agent.js +8 -8
  88. package/build/lib/core/agents/specialized/findingJudge/index.js +3 -3
  89. package/build/lib/core/agents/specialized/patching/index.js +10 -10
  90. package/build/lib/core/agents/specialized/pentest/agent.d.ts +18 -4
  91. package/build/lib/core/agents/specialized/pentest/agent.js +11 -9
  92. package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +9 -9
  93. package/build/lib/core/ai/ai.d.ts +23 -6
  94. package/build/lib/core/ai/ai.js +2 -2
  95. package/build/lib/core/ai/index.js +2 -2
  96. package/build/lib/core/ai/models/claude-capabilities.d.ts +3 -2
  97. package/build/lib/core/ai/models/concentrate.d.ts +1 -0
  98. package/build/lib/core/ai/models/index.d.ts +1 -1
  99. package/build/lib/core/ai/providers/concentrate.d.ts +1 -0
  100. package/build/lib/core/ai/utils.d.ts +2 -0
  101. package/build/lib/core/ai/utils.js +4 -2
  102. package/build/lib/core/api/index.d.ts +3 -0
  103. package/build/lib/core/api/index.js +22 -19
  104. package/build/lib/core/api/targetedPentest.d.ts +1 -0
  105. package/build/lib/core/api/targetedPentest.js +10 -10
  106. package/build/lib/core/config/config.d.ts +3 -0
  107. package/build/lib/core/config/index.js +1 -1
  108. package/build/lib/core/findings/registry.js +3 -3
  109. package/build/lib/core/session/index.d.ts +12 -0
  110. package/build/lib/core/session/index.js +2 -2
  111. package/build/lib/core/session/persistence.d.ts +7 -0
  112. package/build/lib/core/session/persistence.js +7 -3
  113. package/build/lib/core/workflows/fastStrike.d.ts +3 -1
  114. package/build/lib/core/workflows/pentest.d.ts +8 -1
  115. package/build/lib/core/workflows/pentest.js +15 -15
  116. package/build/lib/core/workflows/seams.js +4 -4
  117. package/build/lib/core/workflows/whiteboxAttackSurface.js +11 -11
  118. package/build/lib/index.js +22 -22
  119. package/build/{logs-x62b7hk1.js → logs-70fyr848.js} +13 -13
  120. package/build/{native-rollout-evidence-c9yhfqcs.js → native-rollout-evidence-ntensvzb.js} +3 -3
  121. package/build/{offesecAgent-5whnpeta.js → offesecAgent-kk62tf60.js} +4 -4
  122. package/build/pentest-4jgznn7h.js +22 -0
  123. package/build/{pentests-e0f32aqx.js → pentests-3e41hj08.js} +13 -13
  124. package/build/recordedRunClient-w34kcch1.js +11 -0
  125. package/build/{targetedPentest-rcbs4nvp.js → targetedPentest-t9snxcj2.js} +18 -11
  126. package/build/{targets-xj9g3q0z.js → targets-1pyf21c7.js} +13 -13
  127. package/build/threatModel-48y5qjze.js +21 -0
  128. package/build/{uninstall-7s784q4a.js → uninstall-fbydd9t6.js} +1 -1
  129. package/build/{upload-4gvhwmvr.js → upload-sbhwe2fq.js} +4 -4
  130. package/build/{utils-cjrz64pc.js → utils-23903gcj.js} +3 -3
  131. package/package.json +1 -1
  132. package/src/cli.ts +48 -14
  133. package/src/core/agents/agentRuntime.test.ts +38 -0
  134. package/src/core/agents/agentRuntime.ts +5 -2
  135. package/src/core/agents/defineAgent.ts +3 -1
  136. package/src/core/agents/offSecAgent/index.ts +3 -0
  137. package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +92 -5
  138. package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +70 -31
  139. package/src/core/agents/offSecAgent/prompt.test.ts +46 -0
  140. package/src/core/agents/offSecAgent/prompt.ts +52 -8
  141. package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +38 -0
  142. package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +64 -0
  143. package/src/core/agents/offSecAgent/subagentSpawner.ts +12 -1
  144. package/src/core/agents/offSecAgent/tools/delegateAuth.ts +1 -0
  145. package/src/core/agents/offSecAgent/tools/listMemories.test.ts +230 -0
  146. package/src/core/agents/offSecAgent/tools/listMemories.ts +73 -6
  147. package/src/core/agents/offSecAgent/tools/reportError.test.ts +6 -0
  148. package/src/core/agents/offSecAgent/tools/reportError.ts +10 -2
  149. package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +2 -0
  150. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.test.ts +57 -0
  151. package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +19 -1
  152. package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +5 -2
  153. package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +1 -0
  154. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +72 -2
  155. package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +32 -11
  156. package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +25 -5
  157. package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +1 -0
  158. package/src/core/agents/offSecAgent/tools/types.ts +6 -1
  159. package/src/core/agents/offSecAgent/tools/updateTask.ts +5 -1
  160. package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +11 -4
  161. package/src/core/agents/offSecAgent/types.ts +9 -0
  162. package/src/core/agents/specialized/codeAgent/agent.test.ts +7 -0
  163. package/src/core/agents/specialized/codeAgent/agent.ts +9 -2
  164. package/src/core/agents/specialized/codeAgent/prompts.ts +6 -0
  165. package/src/core/agents/specialized/pentest/agent.test.ts +116 -0
  166. package/src/core/agents/specialized/pentest/agent.ts +178 -49
  167. package/src/core/agents/specialized/pentest/results.test.ts +59 -4
  168. package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +1 -0
  169. package/src/core/ai/ai.operator-context.bench.test.ts +1408 -0
  170. package/src/core/ai/ai.recorded-context.test.ts +74 -0
  171. package/src/core/ai/ai.test.ts +54 -0
  172. package/src/core/ai/ai.ts +312 -82
  173. package/src/core/ai/compactionTelemetry.test.ts +2 -2
  174. package/src/core/ai/models/anthropic.ts +6 -0
  175. package/src/core/ai/models/bedrock.ts +18 -0
  176. package/src/core/ai/models/claude-capabilities.ts +29 -8
  177. package/src/core/ai/models/concentrate.ts +52 -0
  178. package/src/core/ai/models/index.ts +25 -4
  179. package/src/core/ai/models/models.test.ts +154 -0
  180. package/src/core/ai/models/openrouter.ts +43 -1
  181. package/src/core/ai/providers/anthropic-models.test.ts +60 -1
  182. package/src/core/ai/providers/bedrock-models.test.ts +81 -0
  183. package/src/core/ai/providers/concentrate.test.ts +31 -0
  184. package/src/core/ai/providers/concentrate.ts +11 -0
  185. package/src/core/ai/providers/gateway-models.test.ts +16 -6
  186. package/src/core/ai/utils.native-rollout-evidence.test.ts +1 -0
  187. package/src/core/ai/utils.ts +189 -143
  188. package/src/core/api/blackboxPentest.ts +9 -1
  189. package/src/core/api/index.ts +9 -0
  190. package/src/core/api/targetedPentest.ts +19 -5
  191. package/src/core/config/config.test.ts +35 -0
  192. package/src/core/config/config.ts +5 -0
  193. package/src/core/observability/telemetry.test.ts +2 -3
  194. package/src/core/session/index.ts +5 -0
  195. package/src/core/session/persistence.test.ts +62 -0
  196. package/src/core/session/persistence.ts +56 -0
  197. package/src/core/workflows/fastStrike.seams.test.ts +149 -0
  198. package/src/core/workflows/fastStrike.test.ts +18 -1
  199. package/src/core/workflows/fastStrike.ts +103 -11
  200. package/src/core/workflows/pentest.test.ts +104 -0
  201. package/src/core/workflows/pentest.ts +110 -7
  202. package/src/core/workflows/seams.test.ts +13 -0
  203. package/src/core/workflows/seams.ts +5 -1
  204. package/src/core/workflows/whiteboxAttackSurface.ts +1 -0
  205. package/src/tui/components/chat/loading-indicator.tsx +20 -4
  206. package/src/tui/components/chat/message-list.tsx +1 -0
  207. package/src/tui/components/commands/__tests__/advanced-settings-lifecycle.tsx +176 -0
  208. package/src/tui/components/commands/advanced-settings.test.ts +28 -0
  209. package/src/tui/components/commands/advanced-settings.tsx +237 -19
  210. package/src/tui/components/operator-dashboard/index.tsx +46 -9
  211. package/src/tui/components/operator-dashboard/logic.test.ts +106 -0
  212. package/src/tui/components/operator-dashboard/logic.ts +42 -2
  213. package/src/tui/components/operator-dashboard/run-events.test.ts +3 -1
  214. package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +18 -0
  215. package/src/tui/components/shared/action-label.test.ts +9 -0
  216. package/src/tui/components/shared/compacting-icon.tsx +18 -0
  217. package/src/tui/components/shared/index.ts +1 -0
  218. package/src/tui/components/shared/tool-registry.ts +1 -0
  219. package/src/tui/components/shared/tool-renderer.tsx +21 -7
  220. package/src/tui/context/route.tsx +1 -0
  221. package/src/tui/keybindings/registry.ts +2 -0
  222. package/build/agent-6w7egcf1.js +0 -13
  223. package/build/agent-9zx75feh.js +0 -13
  224. package/build/pentest-e61hc38s.js +0 -22
  225. package/build/recordedRunClient-dp032c32.js +0 -11
  226. package/build/threatModel-jkxfa40s.js +0 -21
package/src/core/ai/ai.ts CHANGED
@@ -51,6 +51,9 @@ import {
51
51
  } from "./compactionTelemetry";
52
52
  import {
53
53
  estimateMessageTokens,
54
+ estimateTokens,
55
+ estimateToolsOverheadTokens,
56
+ type FitContextResult,
54
57
  fitMessagesToContext,
55
58
  truncateWithMarker,
56
59
  } from "./contextManagement";
@@ -60,6 +63,7 @@ import {
60
63
  runWithInferenceRecorder,
61
64
  } from "./inference-attempt";
62
65
  import {
66
+ type ClaudeThinkingEffort,
63
67
  getClaudeCapabilities,
64
68
  getMaxOutputTokens,
65
69
  getModelInfo,
@@ -69,11 +73,15 @@ import {
69
73
  runWithNativeRolloutOperation,
70
74
  withNativeRolloutEvidenceModel,
71
75
  } from "./native-rollout-evidence";
72
- import { CONCENTRATE_GLM_5_3_MODEL_ID } from "./providers/concentrate";
76
+ import {
77
+ CONCENTRATE_GLM_5_3_MODEL_ID,
78
+ CONCENTRATE_REASONING_MODEL_IDS,
79
+ } from "./providers/concentrate";
73
80
  import { OPENAI_PRO_TIMEOUT_MS } from "./providers/openai-pro-fetch";
74
81
  import { STREAM_DEBUG } from "./streamTelemetry";
75
82
  import {
76
83
  type AIAuthConfig,
84
+ CompactionRejectedError,
77
85
  checkIfContextLengthError,
78
86
  createSummarizationStream,
79
87
  getProviderModel,
@@ -136,11 +144,11 @@ export const DEFAULT_OPENAI_REASONING_EFFORT: OpenAIReasoningEffort = "medium";
136
144
 
137
145
  /**
138
146
  * Anthropic adaptive-thinking effort hint. Soft guidance for how much the model
139
- * reasons (see AWS "adaptive thinking"): `low` minimizes thinking, `max`
140
- * reasons hardest (`high` is the model default). Only meaningful for models that
141
- * support adaptive thinking (Claude Opus/Sonnet 4.6+); ignored elsewhere.
147
+ * reasons (see AWS "adaptive thinking"): `low` minimizes thinking while
148
+ * `xhigh` and `max` allow progressively deeper work. Only meaningful for
149
+ * models that support adaptive thinking; ignored elsewhere.
142
150
  */
143
- export type ThinkingEffort = "low" | "medium" | "high" | "xhigh" | "max";
151
+ export type ThinkingEffort = ClaudeThinkingEffort;
144
152
 
145
153
  const OPENAI_REASONING_MODEL_IDS = new Set([
146
154
  "gpt-6.1-sol",
@@ -676,6 +684,43 @@ function createToolExecutionGate(): {
676
684
  };
677
685
  }
678
686
 
687
+ class InStreamCompactionRequired extends Error {
688
+ constructor(readonly fitted: FitContextResult) {
689
+ super("In-stream compaction required before provider dispatch");
690
+ }
691
+ }
692
+
693
+ // Retry once after new tool output, then let fitting handle this turn.
694
+ const MAX_PROACTIVE_SUMMARY_FAILURES = 2;
695
+
696
+ function shouldSummarizeBeforeFitting(
697
+ messages: ModelMessage[],
698
+ contextWindow: number,
699
+ maxOutputTokens: number,
700
+ overheadTokens: number,
701
+ safetyMarginTokens: number,
702
+ system?: string,
703
+ toolChoice?: ToolChoice<ToolSet>,
704
+ ): boolean {
705
+ const historyBudget =
706
+ contextWindow -
707
+ maxOutputTokens -
708
+ overheadTokens -
709
+ safetyMarginTokens -
710
+ (system ? estimateTokens(system) : 0);
711
+ const estimatedHistoryTokens = estimateMessageTokens(messages);
712
+ // Forced-tool replays missed the summary cache; keep their fitted history path.
713
+ return (
714
+ historyBudget > 0 &&
715
+ messages.length > 2 &&
716
+ toolChoice !== "required" &&
717
+ typeof toolChoice !== "object" &&
718
+ estimatedHistoryTokens >=
719
+ Math.max(100_000, Math.floor(historyBudget * 0.8)) &&
720
+ estimatedHistoryTokens <= historyBudget
721
+ );
722
+ }
723
+
679
724
  // Helper function to wrap a stream with error handling for async errors
680
725
  function wrapStreamWithErrorHandler(
681
726
  originalStream: StreamTextResult<ToolSet, never>,
@@ -786,7 +831,10 @@ function wrapStreamWithErrorHandler(
786
831
 
787
832
  // Check context length FIRST — these should never be retried
788
833
  // as-is; the prompt must be reduced via summarization.
789
- const isCtxError = checkIfContextLengthError(error);
834
+ const isCtxError =
835
+ error instanceof InStreamCompactionRequired ||
836
+ error instanceof CompactionRejectedError ||
837
+ checkIfContextLengthError(error);
790
838
 
791
839
  // A latched persistence failure is terminal: retrying would
792
840
  // dispatch a turn whose context was never durably committed.
@@ -893,42 +941,50 @@ function wrapStreamWithErrorHandler(
893
941
  }
894
942
 
895
943
  if (isCtxError) {
896
- let currentMessages: ModelMessage[] = messagesContainer.current;
897
- try {
898
- const response = await originalStream.response;
899
- if (response.messages && response.messages.length > 0) {
900
- currentMessages = response.messages as ModelMessage[];
944
+ let currentMessages: ModelMessage[] =
945
+ error instanceof InStreamCompactionRequired
946
+ ? error.fitted.messages
947
+ : messagesContainer.current;
948
+ if (!(error instanceof InStreamCompactionRequired)) {
949
+ try {
950
+ const response = await originalStream.response;
951
+ if (response.messages && response.messages.length > 0) {
952
+ currentMessages = response.messages as ModelMessage[];
953
+ }
954
+ } catch {
955
+ // Fall back to container messages.
901
956
  }
902
- } catch {
903
- // Fall back to container messages.
904
957
  }
905
958
 
906
- const fitted = fitMessagesToContext(currentMessages, {
907
- contextWindow: getContextWindow(
908
- opts.model,
909
- opts.authConfig?.customProviders,
910
- opts.authConfig?.hoonifyModels,
911
- ),
912
- maxOutputTokens: getMaxOutputTokens(
913
- opts.model,
914
- opts.authConfig?.customProviders,
915
- opts.authConfig?.hoonifyModels,
916
- ),
917
- system: applySequentialToolCallPolicy(
918
- opts.system,
919
- opts.tools,
920
- opts.model,
921
- ),
922
- tools: opts.tools,
923
- sessionPath: opts.sessionPath,
924
- telemetry: {
925
- trigger: "context_overflow",
926
- model: opts.model,
927
- sessionId: opts.sessionId,
928
- restartDepth: opts._restartDepth,
929
- previous: opts._compaction?.last,
930
- },
931
- });
959
+ const fitted =
960
+ error instanceof InStreamCompactionRequired
961
+ ? error.fitted
962
+ : fitMessagesToContext(currentMessages, {
963
+ contextWindow: getContextWindow(
964
+ opts.model,
965
+ opts.authConfig?.customProviders,
966
+ opts.authConfig?.hoonifyModels,
967
+ ),
968
+ maxOutputTokens: getMaxOutputTokens(
969
+ opts.model,
970
+ opts.authConfig?.customProviders,
971
+ opts.authConfig?.hoonifyModels,
972
+ ),
973
+ system: applySequentialToolCallPolicy(
974
+ opts.system,
975
+ opts.tools,
976
+ opts.model,
977
+ ),
978
+ tools: opts.tools,
979
+ sessionPath: opts.sessionPath,
980
+ telemetry: {
981
+ trigger: "context_overflow",
982
+ model: opts.model,
983
+ sessionId: opts.sessionId,
984
+ restartDepth: opts._restartDepth,
985
+ previous: opts._compaction?.last,
986
+ },
987
+ });
932
988
 
933
989
  if (fitted.compaction && opts._compaction)
934
990
  opts._compaction.last = fitted.compaction;
@@ -944,7 +1000,11 @@ function wrapStreamWithErrorHandler(
944
1000
  // `MAX_RESTART_DEPTH` finally trips.
945
1001
  let postReactiveDepth = opts._restartDepth ?? 0;
946
1002
 
947
- if (fitted.fitsBudget && fitted.modified) {
1003
+ if (
1004
+ !(error instanceof CompactionRejectedError) &&
1005
+ fitted.fitsBudget &&
1006
+ fitted.modified
1007
+ ) {
948
1008
  if (!silent) {
949
1009
  log.warn(
950
1010
  `Context length error — Layer 1+2 reduced to ~${fitted.estimatedInputTokens} tokens, retrying`,
@@ -1017,12 +1077,19 @@ function wrapStreamWithErrorHandler(
1017
1077
  }
1018
1078
 
1019
1079
  try {
1080
+ if (error instanceof CompactionRejectedError) throw error;
1020
1081
  const summarizationStream = createSummarizationStream(
1021
1082
  messagesForSummary,
1022
1083
  {
1023
1084
  ...opts,
1024
1085
  _restartDepth: postReactiveDepth,
1025
- _compactionTrigger: "context_overflow",
1086
+ _compactionTrigger:
1087
+ error instanceof InStreamCompactionRequired
1088
+ ? "proactive"
1089
+ : "context_overflow",
1090
+ _proactiveSummaryMayFit:
1091
+ error instanceof InStreamCompactionRequired &&
1092
+ error.fitted.fitsBudget,
1026
1093
  },
1027
1094
  model,
1028
1095
  );
@@ -1030,7 +1097,7 @@ function wrapStreamWithErrorHandler(
1030
1097
  yield chunk;
1031
1098
  }
1032
1099
  } catch (summarizeError) {
1033
- // Only fall through to minimal-restart for context-length
1100
+ // Fall through to minimal restart for context or rejected-summary
1034
1101
  // errors. For everything else — auth failures, network
1035
1102
  // errors, abort signals, the typed terminal
1036
1103
  // `ContextLengthExhaustedError`, or non-`Error` throws —
@@ -1043,7 +1110,10 @@ function wrapStreamWithErrorHandler(
1043
1110
  (summarizeError instanceof Error &&
1044
1111
  summarizeError.name === "AbortError") ||
1045
1112
  opts.abortSignal?.aborted ||
1046
- !checkIfContextLengthError(summarizeError)
1113
+ !(
1114
+ checkIfContextLengthError(summarizeError) ||
1115
+ summarizeError instanceof CompactionRejectedError
1116
+ )
1047
1117
  ) {
1048
1118
  throw summarizeError;
1049
1119
  }
@@ -1181,7 +1251,12 @@ export function modelSupportsAdaptiveThinking(modelId: string): boolean {
1181
1251
  }
1182
1252
 
1183
1253
  export function modelSupportsOpenAIReasoning(modelId: string): boolean {
1184
- if (modelId === CONCENTRATE_GLM_5_3_MODEL_ID) return true;
1254
+ if (
1255
+ modelId === CONCENTRATE_GLM_5_3_MODEL_ID ||
1256
+ CONCENTRATE_REASONING_MODEL_IDS.has(modelId)
1257
+ ) {
1258
+ return true;
1259
+ }
1185
1260
  const { provider } = getModelInfo(modelId);
1186
1261
  if (!["openai", "openrouter", "concentrate"].includes(provider)) return false;
1187
1262
  modelId = modelId.replace(/^(openai\/|concentrate:)/, "");
@@ -1201,6 +1276,9 @@ export function getOpenAIReasoningEfforts(
1201
1276
  if (modelId === CONCENTRATE_GLM_5_3_MODEL_ID) {
1202
1277
  return ["low", "high", "max"];
1203
1278
  }
1279
+ if (CONCENTRATE_REASONING_MODEL_IDS.has(modelId)) {
1280
+ return ["low", "medium", "high", "xhigh", "max"];
1281
+ }
1204
1282
  if (/^concentrate:gpt-5\.4-(?:mini|nano)$/.test(modelId)) {
1205
1283
  return ["none", "low", "medium", "high"];
1206
1284
  }
@@ -1279,12 +1357,19 @@ export type ReasoningProviderOptions = {
1279
1357
  effort?: ThinkingEffort;
1280
1358
  };
1281
1359
  bedrock?: {
1282
- reasoningConfig: {
1283
- type: "adaptive";
1284
- display: "summarized";
1285
- // Bedrock's channel for the adaptive effort hint (maps to Claude's
1286
- // output_config.effort). Omitted when no level requested.
1287
- maxReasoningEffort?: ThinkingEffort;
1360
+ reasoningConfig:
1361
+ | {
1362
+ type: "adaptive";
1363
+ display: "summarized";
1364
+ // Bedrock's channel for the adaptive effort hint (maps to Claude's
1365
+ // output_config.effort).
1366
+ maxReasoningEffort?: ThinkingEffort;
1367
+ }
1368
+ | { type: "disabled" };
1369
+ /** Explicitly forwarded for Anthropic because the Bedrock SDK currently
1370
+ * drops reasoningConfig.type="disabled" instead of sending it. */
1371
+ additionalModelRequestFields?: {
1372
+ thinking: { type: "disabled" };
1288
1373
  };
1289
1374
  };
1290
1375
  openai?: OpenAIResponsesProviderOptions;
@@ -1360,8 +1445,8 @@ export function buildReasoningProviderOptions(
1360
1445
  enableThinking?: boolean;
1361
1446
  /**
1362
1447
  * Adaptive-thinking effort hint for Anthropic models that support it
1363
- * (Opus/Sonnet 4.6+). Ignored on models without adaptive support. When
1364
- * omitted the model uses its own default (`high`).
1448
+ * Ignored on models without adaptive support. When omitted, models in the
1449
+ * Claude capability registry use their documented provider default.
1365
1450
  */
1366
1451
  thinkingEffort?: ThinkingEffort | null;
1367
1452
  openAIReasoningEffort?: OpenAIReasoningEffort | null;
@@ -1371,11 +1456,12 @@ export function buildReasoningProviderOptions(
1371
1456
  if (claude) {
1372
1457
  const provider = getModelInfo(model).provider;
1373
1458
  const thinking = !!opts.enableThinking || claude.alwaysOnThinking;
1459
+ const effort = opts.thinkingEffort ?? claude.defaultEffort;
1374
1460
  if (provider === "openrouter") {
1375
1461
  return {
1376
1462
  openrouter: {
1377
1463
  reasoning: {
1378
- effort: thinking ? (opts.thinkingEffort ?? "high") : "none",
1464
+ effort: thinking ? effort : "none",
1379
1465
  enabled: thinking,
1380
1466
  },
1381
1467
  },
@@ -1385,19 +1471,38 @@ export function buildReasoningProviderOptions(
1385
1471
  return {
1386
1472
  openai: {
1387
1473
  forceReasoning: true,
1388
- reasoningEffort: thinking ? (opts.thinkingEffort ?? "high") : "none",
1474
+ reasoningEffort: thinking ? effort : "none",
1389
1475
  reasoningSummary: "auto",
1390
1476
  },
1391
1477
  };
1392
1478
  }
1393
1479
  return {
1394
1480
  anthropic: {
1395
- thinking:
1396
- opts.enableThinking || claude.alwaysOnThinking
1397
- ? { type: "adaptive", display: "summarized" }
1398
- : { type: "disabled" },
1399
- ...(opts.thinkingEffort ? { effort: opts.thinkingEffort } : {}),
1481
+ thinking: thinking
1482
+ ? { type: "adaptive", display: "summarized" }
1483
+ : { type: "disabled" },
1484
+ ...(thinking ? { effort } : {}),
1400
1485
  },
1486
+ ...(provider === "bedrock"
1487
+ ? {
1488
+ bedrock: {
1489
+ reasoningConfig: thinking
1490
+ ? {
1491
+ type: "adaptive" as const,
1492
+ display: "summarized" as const,
1493
+ maxReasoningEffort: effort,
1494
+ }
1495
+ : { type: "disabled" as const },
1496
+ ...(!thinking
1497
+ ? {
1498
+ additionalModelRequestFields: {
1499
+ thinking: { type: "disabled" as const },
1500
+ },
1501
+ }
1502
+ : {}),
1503
+ },
1504
+ }
1505
+ : {}),
1401
1506
  };
1402
1507
  }
1403
1508
  const useThinking =
@@ -1474,6 +1579,11 @@ export interface StreamResponseOpts {
1474
1579
  * new messages (not the full pre-summarization history).
1475
1580
  */
1476
1581
  onSummarized?: (summary: string) => void;
1582
+ /** The fitted context and prior generated-message count for transcript writers. */
1583
+ onContextPrepared?: (context: {
1584
+ messages: ModelMessage[];
1585
+ responseMessageOffset: number;
1586
+ }) => void;
1477
1587
  /**
1478
1588
  * Deprecated host shim: fires only when a cache counter is `> 0`.
1479
1589
  * Apex internals read cache from {@link normalizeStepUsage} on the step.
@@ -1506,6 +1616,8 @@ export interface StreamResponseOpts {
1506
1616
  /** Shared only by continuations of this stream, never across agents. */
1507
1617
  _compaction?: { last?: CompactionLink };
1508
1618
  _compactionTrigger?: CompactionTrigger;
1619
+ _proactiveSummaryMayFit?: boolean;
1620
+ _proactiveSummaryFailures?: number;
1509
1621
  }
1510
1622
 
1511
1623
  const NATIVE_STREAM_RECOVERY = Symbol("native-stream-recovery");
@@ -1581,13 +1693,19 @@ function streamResponseWithinOperation(
1581
1693
  thinkingEffort,
1582
1694
  openAIReasoningEffort,
1583
1695
  sessionId,
1696
+ sessionPath,
1584
1697
  contextRecorder,
1698
+ onContextPrepared,
1585
1699
  } = opts;
1586
1700
 
1587
- // Recorded-run base, captured once at the first prepareStep of this
1588
- // streamText call; only used to reconstruct cumulative prefixes in
1589
- // onStepFinish (response.messages excludes the input base).
1701
+ // response.messages accumulates generated steps after prepareStep rewrites
1702
+ // provider context. The offset prevents transcript writers from appending
1703
+ // older, pre-fit messages again.
1590
1704
  let recorderBase: ModelMessage[] | undefined;
1705
+ let responseMessageLength = 0;
1706
+ const estimatedOverheadTokens = estimateToolsOverheadTokens(tools) + 1_000;
1707
+ let preparedInputEstimateTokens = 0;
1708
+ let inputSafetyMarginTokens = 10_000;
1591
1709
 
1592
1710
  // Wrap onStepFinish to fire cache metrics and the usage callback for every
1593
1711
  // step. Must be async so that callers returning a Promise (persistence,
@@ -1596,6 +1714,18 @@ function streamResponseWithinOperation(
1596
1714
  // provider metadata is never parsed twice.
1597
1715
  const onStepFinish: typeof userOnStepFinish = async (step) => {
1598
1716
  const stepUsage = normalizeStepUsage(step);
1717
+ if (
1718
+ preparedInputEstimateTokens > 0 &&
1719
+ stepUsage.inputTokens > 0 &&
1720
+ step.response?.id !== "summarization" &&
1721
+ step.response?.id !== "tool-repair"
1722
+ ) {
1723
+ // Fixed system and tool overhead is already reserved by the fitter; calibrate only the remaining tokenization gap.
1724
+ inputSafetyMarginTokens = Math.max(
1725
+ inputSafetyMarginTokens,
1726
+ stepUsage.inputTokens - preparedInputEstimateTokens + 10_000,
1727
+ );
1728
+ }
1599
1729
  // Canonical commit precedes every downstream use; the recorder latches
1600
1730
  // rejections (SDK notify() swallows them) until the next turn gate or
1601
1731
  // agent-side flush turns the latch into a stream failure.
@@ -1611,10 +1741,20 @@ function streamResponseWithinOperation(
1611
1741
  throw new Error("context recorder active without a committed base");
1612
1742
  }
1613
1743
  await contextRecorder.checkpoint({
1614
- messages: [...recorderBase, ...step.response.messages],
1744
+ messages: [
1745
+ ...recorderBase,
1746
+ ...step.response.messages.slice(responseMessageLength),
1747
+ ],
1615
1748
  system: effectiveSystem ?? null,
1616
1749
  });
1617
1750
  }
1751
+ if (
1752
+ step.response?.id !== "summarization" &&
1753
+ step.response?.id !== "tool-repair" &&
1754
+ step.response?.messages
1755
+ ) {
1756
+ responseMessageLength = step.response.messages.length;
1757
+ }
1618
1758
  if (
1619
1759
  onCacheMetrics &&
1620
1760
  (stepUsage.cacheReadTokens > 0 || stepUsage.cacheWriteTokens > 0)
@@ -1661,22 +1801,54 @@ function streamResponseWithinOperation(
1661
1801
  model,
1662
1802
  );
1663
1803
 
1804
+ const contextWindow = getContextWindow(
1805
+ model,
1806
+ authConfig?.customProviders,
1807
+ authConfig?.hoonifyModels,
1808
+ );
1809
+ const maxOutputTokens = getMaxOutputTokens(
1810
+ model,
1811
+ authConfig?.customProviders,
1812
+ authConfig?.hoonifyModels,
1813
+ );
1814
+ if (
1815
+ messages &&
1816
+ !opts._proactiveSummaryFailures &&
1817
+ shouldSummarizeBeforeFitting(
1818
+ messages,
1819
+ contextWindow,
1820
+ maxOutputTokens,
1821
+ estimatedOverheadTokens,
1822
+ inputSafetyMarginTokens,
1823
+ systemWithToolPolicy,
1824
+ toolChoice,
1825
+ )
1826
+ ) {
1827
+ return wrapStreamWithErrorHandler(
1828
+ createSummarizationStream(
1829
+ messages,
1830
+ {
1831
+ ...opts,
1832
+ _compactionTrigger: "proactive",
1833
+ _proactiveSummaryMayFit: true,
1834
+ },
1835
+ providerModel,
1836
+ ),
1837
+ { current: messages },
1838
+ opts,
1839
+ providerModel,
1840
+ silent,
1841
+ );
1842
+ }
1843
+
1664
1844
  // Proactive context fit: compact before the call. Reactive recovery
1665
1845
  // below is a safety net for tokenizer drift the estimate doesn't catch.
1666
1846
  let fittedMessages = messages;
1667
1847
  let proactiveFitFailed = false;
1668
1848
  if (messages && messages.length > 0) {
1669
1849
  const fitted = fitMessagesToContext(messages, {
1670
- contextWindow: getContextWindow(
1671
- model,
1672
- authConfig?.customProviders,
1673
- authConfig?.hoonifyModels,
1674
- ),
1675
- maxOutputTokens: getMaxOutputTokens(
1676
- model,
1677
- authConfig?.customProviders,
1678
- authConfig?.hoonifyModels,
1679
- ),
1850
+ contextWindow,
1851
+ maxOutputTokens,
1680
1852
  system: systemWithToolPolicy,
1681
1853
  tools,
1682
1854
  sessionPath: opts.sessionPath,
@@ -1723,7 +1895,11 @@ function streamResponseWithinOperation(
1723
1895
  return wrapStreamWithErrorHandler(
1724
1896
  createSummarizationStream(
1725
1897
  fittedMessages,
1726
- { ...opts, _compactionTrigger: "proactive" },
1898
+ {
1899
+ ...opts,
1900
+ _compactionTrigger: "proactive",
1901
+ _proactiveSummaryMayFit: false,
1902
+ },
1727
1903
  providerModel,
1728
1904
  ),
1729
1905
  messagesContainer,
@@ -1818,28 +1994,82 @@ function streamResponseWithinOperation(
1818
1994
  // Awaiting inside prepareStep keeps streamResponse synchronous; the
1819
1995
  // SDK awaits it before provider dispatch, so every turn and every
1820
1996
  // recovery re-entry commits before use.
1821
- prepareStep: async (opts) => {
1822
- if (contextRecorder) {
1823
- if (recorderBase === undefined) {
1824
- recorderBase = structuredClone(opts.messages);
1997
+ prepareStep: async (step) => {
1998
+ let stepMessages = step.messages;
1999
+ if (step.stepNumber > 0) {
2000
+ if (
2001
+ (opts._proactiveSummaryFailures ?? 0) <
2002
+ MAX_PROACTIVE_SUMMARY_FAILURES &&
2003
+ shouldSummarizeBeforeFitting(
2004
+ stepMessages,
2005
+ contextWindow,
2006
+ maxOutputTokens,
2007
+ estimatedOverheadTokens,
2008
+ inputSafetyMarginTokens,
2009
+ effectiveSystem,
2010
+ toolChoice,
2011
+ )
2012
+ ) {
2013
+ messagesContainer.current = stepMessages;
2014
+ throw new InStreamCompactionRequired({
2015
+ messages: stepMessages,
2016
+ estimatedInputTokens: estimateMessageTokens(stepMessages),
2017
+ modified: false,
2018
+ fitsBudget: true,
2019
+ });
2020
+ }
2021
+ const fitted = fitMessagesToContext(stepMessages, {
2022
+ contextWindow,
2023
+ maxOutputTokens,
2024
+ system: effectiveSystem,
2025
+ tools,
2026
+ overheadTokens: estimatedOverheadTokens,
2027
+ sessionPath,
2028
+ safetyMarginTokens: inputSafetyMarginTokens,
2029
+ telemetry: {
2030
+ trigger: "proactive",
2031
+ model,
2032
+ sessionId,
2033
+ restartDepth,
2034
+ previous: compactionState.last,
2035
+ },
2036
+ });
2037
+ if (fitted.compaction) compactionState.last = fitted.compaction;
2038
+ stepMessages = fitted.messages;
2039
+ if (!fitted.fitsBudget) {
2040
+ messagesContainer.current = stepMessages;
2041
+ throw new InStreamCompactionRequired(fitted);
1825
2042
  }
2043
+ }
2044
+ if (contextRecorder) {
2045
+ recorderBase = structuredClone(stepMessages);
1826
2046
  // Full logical context each turn — accepted step outputs must
1827
2047
  // survive into every later commit, and system is explicit (when
1828
2048
  // cached into messages, effectiveSystem is undefined → null).
1829
2049
  await contextRecorder.checkpoint({
1830
- messages: opts.messages,
2050
+ messages: stepMessages,
1831
2051
  system: effectiveSystem ?? null,
1832
2052
  });
1833
2053
  }
1834
2054
  // Update the container with the latest messages
1835
- messagesContainer.current = opts.messages;
2055
+ messagesContainer.current = stepMessages;
2056
+ preparedInputEstimateTokens =
2057
+ estimateMessageTokens(stepMessages) +
2058
+ estimatedOverheadTokens +
2059
+ (effectiveSystem ? estimateTokens(effectiveSystem) : 0);
2060
+ onContextPrepared?.({
2061
+ messages: stepMessages,
2062
+ responseMessageOffset: responseMessageLength,
2063
+ });
1836
2064
  // Mark the last message so the growing conversation caches incrementally
1837
2065
  if (cacheBreakpoint) {
1838
2066
  return {
1839
- messages: withCachedLastMessage(opts.messages, cacheBreakpoint),
2067
+ messages: withCachedLastMessage(stepMessages, cacheBreakpoint),
1840
2068
  };
1841
2069
  }
1842
- return undefined;
2070
+ return stepMessages === step.messages
2071
+ ? undefined
2072
+ : { messages: stepMessages };
1843
2073
  },
1844
2074
  onError: async ({ error }: { error: unknown }) => {
1845
2075
  // Never throw here: the SDK calls this inside its stream pipeline,
@@ -387,7 +387,7 @@ describe("compaction observability", () => {
387
387
  ).toBe(true);
388
388
  });
389
389
 
390
- it("preserves tool evidence omitted by the summarizer and links its actual request and response", async () => {
390
+ it("passes tool evidence to the summarizer and links its actual request and response", async () => {
391
391
  const summaryModel = model();
392
392
  state.model = summaryModel;
393
393
  const messages: ModelMessage[] = [
@@ -422,7 +422,7 @@ describe("compaction observability", () => {
422
422
  expect(summary.attributes["ai.prompt.messages"]).toContain(
423
423
  "continue researching",
424
424
  );
425
- expect(summary.attributes["ai.prompt.messages"]).not.toContain(
425
+ expect(summary.attributes["ai.prompt.messages"]).toContain(
426
426
  "evidence evidence",
427
427
  );
428
428
  expect(summary.attributes["ai.response.text"]).toBe("A compact summary");
@@ -112,6 +112,12 @@ export const ANTHROPIC_MODELS: ModelInfo[] = [
112
112
  provider: "anthropic",
113
113
  contextLength: 1000000,
114
114
  },
115
+ {
116
+ id: "claude-haiku-5-5",
117
+ name: "Claude Haiku 5.5",
118
+ provider: "anthropic",
119
+ contextLength: 1000000,
120
+ },
115
121
  {
116
122
  id: "claude-sonnet-5-5",
117
123
  name: "Claude Sonnet 5.5",
@@ -465,6 +465,18 @@ export const BEDROCK_MODELS: ModelInfo[] = [
465
465
  provider: "bedrock",
466
466
  contextLength: 262000,
467
467
  },
468
+ {
469
+ id: "anthropic.claude-haiku-5-5",
470
+ name: "Claude Haiku 5.5 (Bedrock)",
471
+ provider: "bedrock",
472
+ contextLength: 1000000,
473
+ },
474
+ {
475
+ id: "us.anthropic.claude-haiku-5-5",
476
+ name: "Claude Haiku 5.5 (US)",
477
+ provider: "bedrock",
478
+ contextLength: 1000000,
479
+ },
468
480
  {
469
481
  id: "anthropic.claude-opus-4-8",
470
482
  name: "Claude Opus 4.8 (Bedrock)",
@@ -597,4 +609,10 @@ export const BEDROCK_MODELS: ModelInfo[] = [
597
609
  provider: "bedrock",
598
610
  contextLength: 1000000,
599
611
  },
612
+ {
613
+ id: "global.anthropic.claude-haiku-5-5",
614
+ name: "Claude Haiku 5.5 (Global)",
615
+ provider: "bedrock",
616
+ contextLength: 1000000,
617
+ },
600
618
  ];