@ponythewhite/base-context 1.0.0 → 1.0.1

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 (285) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/NOTICE +0 -5
  3. package/README.md +44 -27
  4. package/dist/NOTICE +0 -5
  5. package/dist/base-context-runtime/pyproject.toml +1 -1
  6. package/dist/base-context-runtime/src/rlm/__init__.py +7 -5
  7. package/dist/base-context-runtime/test/test_subagent_registry.py +28 -1
  8. package/dist/base-context-runtime/uv.lock +1 -1
  9. package/dist/build-info.json +1 -1
  10. package/dist/bundle/amazon-bedrock.js +21 -0
  11. package/dist/bundle/{anthropic-YY4S534V.js → anthropic-UV5GZDGU.js} +5 -5
  12. package/dist/bundle/{azure-openai-responses-JRP6MIM3.js → azure-openai-responses-WSNPXLYC.js} +6 -6
  13. package/dist/bundle/{bundled-modules-P5HARVZA.js → bundled-modules-TXUMJ2XQ.js} +71 -16
  14. package/dist/bundle/chunk-27QCJ2AZ.js +2578 -0
  15. package/dist/bundle/{chunk-MZHTNNVA.js → chunk-4GXPNXOJ.js} +8 -0
  16. package/dist/bundle/{chunk-SC5KG5R4.js → chunk-DGNX7HQ3.js} +1 -7
  17. package/dist/bundle/{chunk-A53O363M.js → chunk-GRJFXLBM.js} +0 -1
  18. package/dist/bundle/chunk-HKINIBP4.js +29 -0
  19. package/dist/bundle/{chunk-UDSW6FNG.js → chunk-KTNETTHI.js} +1 -1
  20. package/dist/bundle/{chunk-BYVP7LWV.js → chunk-S3INFPZZ.js} +1861 -4662
  21. package/dist/bundle/{chunk-73DCPU3D.js → chunk-V3L54UD6.js} +1019 -3427
  22. package/dist/bundle/{chunk-U7AUVG3B.js → chunk-VSIYFWYI.js} +24 -6
  23. package/dist/bundle/{chunk-IW77OXFS.js → chunk-W46VZFI6.js} +0 -2099
  24. package/dist/bundle/{cli-main-O4X7GFGY.js → cli-main-BYMSRPH6.js} +8 -6
  25. package/dist/bundle/cli.js +2 -2
  26. package/dist/bundle/{google-AUTVGDGZ.js → google-T32FMD4G.js} +6 -6
  27. package/dist/bundle/{google-vertex-Q75D7RKS.js → google-vertex-56KP6I3B.js} +3 -3
  28. package/dist/bundle/{main-XKOW2BV6.js → main-ODOYCEQC.js} +7 -5
  29. package/dist/bundle/{mistral-7ESB7QUD.js → mistral-WGBMYIVU.js} +5 -5
  30. package/dist/bundle/{openai-codex-responses-LV3QRPN4.js → openai-codex-responses-PIV2RQ3G.js} +6 -6
  31. package/dist/bundle/{openai-completions-OJECLPXJ.js → openai-completions-64C3JPR4.js} +11 -22
  32. package/dist/bundle/{openai-responses-FN6HZMBC.js → openai-responses-UI7RSTDV.js} +6 -6
  33. package/dist/cli/args.d.ts.map +1 -1
  34. package/dist/cli/args.js +108 -59
  35. package/dist/cli/args.js.map +1 -1
  36. package/dist/cli/daemon-ps.d.ts +4 -3
  37. package/dist/cli/daemon-ps.d.ts.map +1 -1
  38. package/dist/cli/daemon-ps.js +6 -1
  39. package/dist/cli/daemon-ps.js.map +1 -1
  40. package/dist/cli/product-doctor.d.ts +0 -1
  41. package/dist/cli/product-doctor.d.ts.map +1 -1
  42. package/dist/cli/product-doctor.js +6 -3
  43. package/dist/cli/product-doctor.js.map +1 -1
  44. package/dist/config.d.ts +0 -3
  45. package/dist/config.d.ts.map +1 -1
  46. package/dist/config.js +0 -8
  47. package/dist/config.js.map +1 -1
  48. package/dist/core/agent-session-config.d.ts +1 -2
  49. package/dist/core/agent-session-config.d.ts.map +1 -1
  50. package/dist/core/agent-session-config.js +0 -3
  51. package/dist/core/agent-session-config.js.map +1 -1
  52. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  53. package/dist/core/agent-session-runtime.js +5 -4
  54. package/dist/core/agent-session-runtime.js.map +1 -1
  55. package/dist/core/agent-session-services.d.ts +5 -3
  56. package/dist/core/agent-session-services.d.ts.map +1 -1
  57. package/dist/core/agent-session-services.js +2 -27
  58. package/dist/core/agent-session-services.js.map +1 -1
  59. package/dist/core/agent-session.d.ts +10 -6
  60. package/dist/core/agent-session.d.ts.map +1 -1
  61. package/dist/core/agent-session.js +107 -107
  62. package/dist/core/agent-session.js.map +1 -1
  63. package/dist/core/auth-storage.d.ts +8 -35
  64. package/dist/core/auth-storage.d.ts.map +1 -1
  65. package/dist/core/auth-storage.js +33 -243
  66. package/dist/core/auth-storage.js.map +1 -1
  67. package/dist/core/cron-jobs.d.ts.map +1 -1
  68. package/dist/core/cron-jobs.js +7 -1
  69. package/dist/core/cron-jobs.js.map +1 -1
  70. package/dist/core/inference-coordinator.d.ts +6 -1
  71. package/dist/core/inference-coordinator.d.ts.map +1 -1
  72. package/dist/core/inference-coordinator.js +18 -2
  73. package/dist/core/inference-coordinator.js.map +1 -1
  74. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  75. package/dist/core/kernel/bootstrap.js +2 -2
  76. package/dist/core/kernel/bootstrap.js.map +1 -1
  77. package/dist/core/kernel/repl-manager.d.ts +2 -0
  78. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  79. package/dist/core/kernel/repl-manager.js +44 -1
  80. package/dist/core/kernel/repl-manager.js.map +1 -1
  81. package/dist/core/logging.d.ts +4 -3
  82. package/dist/core/logging.d.ts.map +1 -1
  83. package/dist/core/logging.js +6 -6
  84. package/dist/core/logging.js.map +1 -1
  85. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  86. package/dist/core/mcp/mcp-manager.js +3 -3
  87. package/dist/core/mcp/mcp-manager.js.map +1 -1
  88. package/dist/core/model-registry.d.ts +1 -16
  89. package/dist/core/model-registry.d.ts.map +1 -1
  90. package/dist/core/model-registry.js +88 -196
  91. package/dist/core/model-registry.js.map +1 -1
  92. package/dist/core/model-resolver.d.ts +2 -19
  93. package/dist/core/model-resolver.d.ts.map +1 -1
  94. package/dist/core/model-resolver.js +36 -266
  95. package/dist/core/model-resolver.js.map +1 -1
  96. package/dist/core/provider-contracts.d.ts +5 -6
  97. package/dist/core/provider-contracts.d.ts.map +1 -1
  98. package/dist/core/provider-contracts.js +25 -36
  99. package/dist/core/provider-contracts.js.map +1 -1
  100. package/dist/core/provider-display-names.d.ts.map +1 -1
  101. package/dist/core/provider-display-names.js +0 -2
  102. package/dist/core/provider-display-names.js.map +1 -1
  103. package/dist/core/rlm-max-subagents.d.ts +32 -0
  104. package/dist/core/rlm-max-subagents.d.ts.map +1 -0
  105. package/dist/core/rlm-max-subagents.js +50 -0
  106. package/dist/core/rlm-max-subagents.js.map +1 -0
  107. package/dist/core/rlm-runtime.d.ts +3 -3
  108. package/dist/core/rlm-runtime.d.ts.map +1 -1
  109. package/dist/core/rlm-runtime.js.map +1 -1
  110. package/dist/core/sdk.d.ts +4 -3
  111. package/dist/core/sdk.d.ts.map +1 -1
  112. package/dist/core/sdk.js +27 -14
  113. package/dist/core/sdk.js.map +1 -1
  114. package/dist/core/settings-manager.d.ts +3 -16
  115. package/dist/core/settings-manager.d.ts.map +1 -1
  116. package/dist/core/settings-manager.js +12 -44
  117. package/dist/core/settings-manager.js.map +1 -1
  118. package/dist/core/slash-commands.d.ts.map +1 -1
  119. package/dist/core/slash-commands.js +6 -6
  120. package/dist/core/slash-commands.js.map +1 -1
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js.map +1 -1
  124. package/dist/installer.mjs +2 -3
  125. package/dist/main.d.ts.map +1 -1
  126. package/dist/main.js +39 -34
  127. package/dist/main.js.map +1 -1
  128. package/dist/modes/acp/acp-mode.d.ts.map +1 -1
  129. package/dist/modes/acp/acp-mode.js +1 -1
  130. package/dist/modes/acp/acp-mode.js.map +1 -1
  131. package/dist/modes/agent-connection/daemon-agent-connection.d.ts +6 -2
  132. package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
  133. package/dist/modes/agent-connection/daemon-agent-connection.js +13 -2
  134. package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
  135. package/dist/modes/agent-connection/in-process-agent-connection.d.ts +2 -0
  136. package/dist/modes/agent-connection/in-process-agent-connection.d.ts.map +1 -1
  137. package/dist/modes/agent-connection/in-process-agent-connection.js +6 -0
  138. package/dist/modes/agent-connection/in-process-agent-connection.js.map +1 -1
  139. package/dist/modes/agent-connection/types.d.ts +3 -0
  140. package/dist/modes/agent-connection/types.d.ts.map +1 -1
  141. package/dist/modes/agent-connection/types.js.map +1 -1
  142. package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
  143. package/dist/modes/agents-view/agents-view-mode.js +2 -19
  144. package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
  145. package/dist/modes/daemon/daemon-mode.d.ts +5 -0
  146. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  147. package/dist/modes/daemon/daemon-mode.js +107 -12
  148. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  149. package/dist/modes/daemon/daemon-protocol.d.ts +194 -165
  150. package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
  151. package/dist/modes/daemon/daemon-protocol.js +16 -11
  152. package/dist/modes/daemon/daemon-protocol.js.map +1 -1
  153. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.d.ts +19 -0
  154. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.d.ts.map +1 -0
  155. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.js +74 -0
  156. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.js.map +1 -0
  157. package/dist/modes/daemon/daemon-rlm-capacity.d.ts +19 -0
  158. package/dist/modes/daemon/daemon-rlm-capacity.d.ts.map +1 -0
  159. package/dist/modes/daemon/daemon-rlm-capacity.js +85 -0
  160. package/dist/modes/daemon/daemon-rlm-capacity.js.map +1 -0
  161. package/dist/modes/daemon/daemon-session-summarizer.d.ts +2 -3
  162. package/dist/modes/daemon/daemon-session-summarizer.d.ts.map +1 -1
  163. package/dist/modes/daemon/daemon-session-summarizer.js +32 -23
  164. package/dist/modes/daemon/daemon-session-summarizer.js.map +1 -1
  165. package/dist/modes/daemon/daemon-state-root.d.ts +15 -0
  166. package/dist/modes/daemon/daemon-state-root.d.ts.map +1 -0
  167. package/dist/modes/daemon/daemon-state-root.js +40 -0
  168. package/dist/modes/daemon/daemon-state-root.js.map +1 -0
  169. package/dist/modes/daemon/daemon-supervisor-ownership.d.ts +2 -0
  170. package/dist/modes/daemon/daemon-supervisor-ownership.d.ts.map +1 -1
  171. package/dist/modes/daemon/daemon-supervisor-ownership.js +27 -0
  172. package/dist/modes/daemon/daemon-supervisor-ownership.js.map +1 -1
  173. package/dist/modes/daemon/daemon-supervisor.d.ts +10 -1
  174. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  175. package/dist/modes/daemon/daemon-supervisor.js +213 -20
  176. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  177. package/dist/modes/daemon/daemon-worker-protocol.d.ts +28 -1
  178. package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
  179. package/dist/modes/daemon/daemon-worker-protocol.js +16 -2
  180. package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
  181. package/dist/modes/daemon/saved-session-catalog.d.ts.map +1 -1
  182. package/dist/modes/daemon/saved-session-catalog.js +2 -0
  183. package/dist/modes/daemon/saved-session-catalog.js.map +1 -1
  184. package/dist/modes/interactive/auth-flows.d.ts +0 -6
  185. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  186. package/dist/modes/interactive/auth-flows.js +6 -126
  187. package/dist/modes/interactive/auth-flows.js.map +1 -1
  188. package/dist/modes/interactive/components/configuration-menu.d.ts +3 -0
  189. package/dist/modes/interactive/components/configuration-menu.d.ts.map +1 -1
  190. package/dist/modes/interactive/components/configuration-menu.js +13 -2
  191. package/dist/modes/interactive/components/configuration-menu.js.map +1 -1
  192. package/dist/modes/interactive/components/footer.d.ts +1 -1
  193. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  194. package/dist/modes/interactive/components/footer.js +2 -2
  195. package/dist/modes/interactive/components/footer.js.map +1 -1
  196. package/dist/modes/interactive/components/index.d.ts +0 -1
  197. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  198. package/dist/modes/interactive/components/index.js +0 -1
  199. package/dist/modes/interactive/components/index.js.map +1 -1
  200. package/dist/modes/interactive/components/login-dialog.d.ts +0 -1
  201. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  202. package/dist/modes/interactive/components/login-dialog.js +3 -40
  203. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  204. package/dist/modes/interactive/components/model-selector.d.ts +2 -0
  205. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  206. package/dist/modes/interactive/components/model-selector.js +13 -2
  207. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  208. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  209. package/dist/modes/interactive/components/oauth-selector.js +2 -11
  210. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  211. package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
  212. package/dist/modes/interactive/feature-hints.js +0 -4
  213. package/dist/modes/interactive/feature-hints.js.map +1 -1
  214. package/dist/modes/interactive/interactive-mode.d.ts +3 -12
  215. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  216. package/dist/modes/interactive/interactive-mode.js +81 -507
  217. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  218. package/dist/modes/interactive/onboarding.d.ts +0 -3
  219. package/dist/modes/interactive/onboarding.d.ts.map +1 -1
  220. package/dist/modes/interactive/onboarding.js +0 -17
  221. package/dist/modes/interactive/onboarding.js.map +1 -1
  222. package/dist/modes/rpc/rpc-client.d.ts +2 -1
  223. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  224. package/dist/modes/rpc/rpc-client.js +24 -11
  225. package/dist/modes/rpc/rpc-client.js.map +1 -1
  226. package/dist/node/amazon-bedrock.d.ts +2 -0
  227. package/dist/node/amazon-bedrock.d.ts.map +1 -0
  228. package/dist/node/amazon-bedrock.js +6 -0
  229. package/dist/node/amazon-bedrock.js.map +1 -0
  230. package/dist/product-identity.d.ts +0 -1
  231. package/dist/product-identity.d.ts.map +1 -1
  232. package/dist/product-identity.js +0 -1
  233. package/dist/product-identity.js.map +1 -1
  234. package/docs/development.md +1 -1
  235. package/docs/fork-philosophy.md +4 -8
  236. package/docs/installation.md +83 -62
  237. package/docs/providers.md +23 -25
  238. package/docs/quickstart.md +19 -10
  239. package/docs/rlm-runtime.md +13 -5
  240. package/docs/rpc.md +4 -4
  241. package/docs/sdk.md +20 -11
  242. package/docs/sessions.md +0 -1
  243. package/docs/settings.md +9 -42
  244. package/docs/skills.md +0 -1
  245. package/docs/upstream-0.9.5.md +171 -0
  246. package/docs/usage.md +14 -10
  247. package/package.json +4 -4
  248. package/dist/core/agent-traces.d.ts +0 -149
  249. package/dist/core/agent-traces.d.ts.map +0 -1
  250. package/dist/core/agent-traces.js +0 -904
  251. package/dist/core/agent-traces.js.map +0 -1
  252. package/dist/core/prime-inference-auth.d.ts +0 -70
  253. package/dist/core/prime-inference-auth.d.ts.map +0 -1
  254. package/dist/core/prime-inference-auth.js +0 -352
  255. package/dist/core/prime-inference-auth.js.map +0 -1
  256. package/dist/core/prime-inference-model-selection.d.ts +0 -16
  257. package/dist/core/prime-inference-model-selection.d.ts.map +0 -1
  258. package/dist/core/prime-inference-model-selection.js +0 -16
  259. package/dist/core/prime-inference-model-selection.js.map +0 -1
  260. package/dist/core/prime-inference-models.d.ts +0 -6
  261. package/dist/core/prime-inference-models.d.ts.map +0 -1
  262. package/dist/core/prime-inference-models.js +0 -62
  263. package/dist/core/prime-inference-models.js.map +0 -1
  264. package/dist/core/telemetry.d.ts +0 -95
  265. package/dist/core/telemetry.d.ts.map +0 -1
  266. package/dist/core/telemetry.js +0 -637
  267. package/dist/core/telemetry.js.map +0 -1
  268. package/dist/modes/interactive/components/prime-onboarding-splash.d.ts +0 -39
  269. package/dist/modes/interactive/components/prime-onboarding-splash.d.ts.map +0 -1
  270. package/dist/modes/interactive/components/prime-onboarding-splash.js +0 -286
  271. package/dist/modes/interactive/components/prime-onboarding-splash.js.map +0 -1
  272. package/dist/modes/interactive/components/prime-team-selector.d.ts +0 -30
  273. package/dist/modes/interactive/components/prime-team-selector.d.ts.map +0 -1
  274. package/dist/modes/interactive/components/prime-team-selector.js +0 -166
  275. package/dist/modes/interactive/components/prime-team-selector.js.map +0 -1
  276. package/dist/skills/prime-intellect/SKILL.md +0 -87
  277. package/dist/skills/prime-intellect/references/compute.md +0 -45
  278. package/dist/skills/prime-intellect/references/environments.md +0 -73
  279. package/dist/skills/prime-intellect/references/inference.md +0 -47
  280. package/dist/skills/prime-intellect/references/sandboxes.md +0 -75
  281. package/skills/prime-intellect/SKILL.md +0 -87
  282. package/skills/prime-intellect/references/compute.md +0 -45
  283. package/skills/prime-intellect/references/environments.md +0 -73
  284. package/skills/prime-intellect/references/inference.md +0 -47
  285. package/skills/prime-intellect/references/sandboxes.md +0 -75
package/docs/settings.md CHANGED
@@ -19,12 +19,14 @@ The sections below cover user-facing settings. Onboarding flags, recent-model hi
19
19
 
20
20
  ### Model & Thinking
21
21
 
22
+ There is no built-in default provider or model. The `defaultProvider` and `defaultModel` keys store your explicit selection for later launches. Authentication is stored separately. A missing or unavailable choice does not select a fallback.
23
+
22
24
  | Setting | Type | Default | Description |
23
25
  |---------|------|---------|-------------|
24
- | `defaultProvider` | string | - | Default provider (e.g., `"anthropic"`, `"openai"`) |
25
- | `defaultModel` | string | - | Default model ID |
26
+ | `defaultProvider` | string | - | Saved explicitly selected provider (e.g., `"anthropic"`, `"openai"`) |
27
+ | `defaultModel` | string | - | Saved explicitly selected model ID |
26
28
  | `defaultServiceTier` | string or null | `"default"` | Provider service-tier preference: `"auto"`, `"default"`, `"flex"`, `"scale"`, `"priority"`, or null; actual support and billing depend on the provider |
27
- | `defaultThinkingLevel` | string | `"xhigh"` | `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"` |
29
+ | `defaultThinkingLevel` | string | `"medium"` | `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"` |
28
30
  | `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output |
29
31
  | `thinkingBudgets` | object | - | Custom token budgets per thinking level |
30
32
 
@@ -71,44 +73,6 @@ The stable `latest.json` and beta `beta.json` manifests use the same JSON shape:
71
73
 
72
74
  Private download manifests require `version` and `package` (or `packageName`) set to the active package name. Version-only manifests and manifests naming another product are refused. `tarball` is optional; when present, Base Context installs that tarball instead of the package name. Relative tarball paths resolve against `BASE_CONTEXT_DOWNLOAD_BASE_URL`. The default owned npm registry lookup is unchanged.
73
75
 
74
- ### Pseudonymous usage analytics
75
-
76
- Analytics are off by default. Remote analytics require explicit opt-in, `BASE_CONTEXT_TELEMETRY_ENDPOINT`, and a dedicated `BASE_CONTEXT_TELEMETRY_API_KEY`. There is no inherited endpoint or inference-key fallback. When enabled, events include aggregate usage and performance data such as execution mode, token usage, tool counts, retries, and compactions.
77
-
78
- Base Context does not send prompts, responses, thinking, tool arguments or results, command text, filenames, paths, repository information, environment variables, credentials, raw error messages, hostnames, usernames, emails, or hardware identifiers. A random installation ID is stored as `telemetry.json` in the configured agent directory (normally `~/.base-context/`).
79
-
80
- Telemetry can be disabled globally or for an individual project. Project settings can only further restrict telemetry: they cannot re-enable a global opt-out or suppress the global one-time disclosure.
81
-
82
- | Setting | Type | Default | Description |
83
- |---------|------|---------|-------------|
84
- | `telemetry.enabled` | boolean | `false` | Opt in to aggregate events at an explicitly configured destination |
85
-
86
- Disable analytics with any of:
87
-
88
- ```json
89
- {
90
- "telemetry": {
91
- "enabled": false
92
- }
93
- }
94
- ```
95
-
96
- ```bash
97
- BASE_CONTEXT_TELEMETRY=0 base-context
98
- DO_NOT_TRACK=1 base-context
99
- base-context --offline
100
- ```
101
-
102
- Opt-in alone does not send events without both the explicit endpoint and dedicated key.
103
-
104
- ### Trace sharing
105
-
106
- | Setting | Type | Default | Description |
107
- | --- | --- | --- | --- |
108
- | `agentTraces.enabled` | boolean | `false` | Opt in to automatic trace sharing at an explicitly configured destination |
109
-
110
- Trace sharing is separate from aggregate telemetry. It requires an explicit `BASE_CONTEXT_TRACES_BASE_URL` and a dedicated credential such as `BASE_CONTEXT_TRACES_API_KEY`; `PRIME_API_KEY` is not a trace credential. Use `/traces status` and `/traces preview` before opting in. Trace exports can contain conversation and tool content. Do not assume that turning on aggregate analytics also enables traces, or that retained work is safe to upload without review.
111
-
112
76
  ### Warnings
113
77
 
114
78
  | Setting | Type | Default | Description |
@@ -315,8 +279,11 @@ Normally the package manager's global modules location is queried using `root -g
315
279
  | Setting | Type | Default | Description |
316
280
  | --- | --- | --- | --- |
317
281
  | `rlmMaxDepth` | number | Falls through to `BASE_CONTEXT_RLM_MAX_DEPTH`, then `2` | Global default recursion-depth limit for new sessions |
282
+ | `rlmMaxSubagents` | number | `4` | Maximum live subagents in a root-agent family; non-negative safe integer |
283
+
284
+ These are global settings, not project overrides. Session-local and inherited depth limits can differ from the creation default. See [RLM programming](rlm.md) and [long-running agents](long-running-agents.md).
318
285
 
319
- This is a global setting, not a project override. Session-local and inherited limits can differ from the creation default. See [RLM programming](rlm.md) and [long-running agents](long-running-agents.md).
286
+ Use [`/agents N`](usage.md#limit-concurrent-subagents) to apply and save `rlmMaxSubagents`; `/agents` reports the current family limit. This preference survives restarts. Running and idle descendants count, but the main agent and inactive saved sessions do not. `0` disables new spawns. Lowering the limit only blocks new admissions; it never kills or passivates existing agents.
320
287
 
321
288
  ### Daemon
322
289
 
package/docs/skills.md CHANGED
@@ -48,7 +48,6 @@ Disable discovery with `--no-skills` (explicit `--skill` paths still load).
48
48
 
49
49
  Base Context ships with built-in skills that load by default:
50
50
 
51
- - `prime-intellect` - Prime Intellect products and workflows via the prime CLI: verifiers environments and the Environments Hub, evaluations (local and hosted), Hosted Training and prime-rl, sandboxes, tunnels, Prime Inference, GPU compute, and storage. Reference docs for each area load on demand from the skill's `references/` directory.
52
51
  - `skill-creator` - teaches the agent to create new skills: markdown skill layout, frontmatter rules, placement and precedence, and the full Python-backed skill contract (package layout, `run()` convention, optional CLI, kernel venv behavior) with a working template in `references/python-skills.md`.
53
52
  - `websearch` - a Python-backed Google search skill using the [Serper](https://serper.dev) API.
54
53
 
@@ -0,0 +1,171 @@
1
+ # Prime Agent 0.9.5 selection for base-context 1.0.1
2
+
3
+ Base-context forked Prime Agent 0.9.3 at
4
+ `915c78f42c248b08238dd27fcd4bcab32c60beab`. The selection range below is separate
5
+ from that fork ancestor. We reviewed all **113 commits** between
6
+ upstream `v0.9.4` (`f771dfcedd684d1afff84ca2c6fa95c7a21efbc2`) and
7
+ `v0.9.5` (`a7d791bc1be09793ed5f3ec05bf4cccbc60679ea`). This is a selective
8
+ adaptation, not a merge of the release's 500-file net change.
9
+
10
+ The fork keeps explicit provider, authentication and model selection; its native
11
+ context, request-budget and source-ownership rules; family-wide `/agents` limits;
12
+ and the Node/npm installer with managed Python 3.13. See [fork philosophy](fork-philosophy.md).
13
+
14
+ ## Selected fixes
15
+
16
+ Thirteen focused groups come from fourteen upstream commits. Some mixed commits
17
+ supply only a few generic lines:
18
+
19
+ - Kernel pipe errors do not crash the worker.
20
+ - LiteLLM context-overflow messages are recognized, without mistaking rate limits for overflow.
21
+ - The packaged Node CLI has the missing lazy Bedrock entry and keeps native request-attempt accounting.
22
+ - Responses streams track interleaved output slots, use final tool arguments, and retain incomplete-response usage.
23
+ - Blocked input-pump idle waits yield instead of starving I/O.
24
+ - Draft disposal handles teardown failures and respects pending attaches.
25
+ - Daemon discovery stays inside the current state root, including owned custom sockets.
26
+ - Python child deletion accepts the returned spawn handle; `await rlm(...)` remains the spawn API.
27
+ - Required CLI values and invalid mode/thinking selections produce errors.
28
+ - Provider projection drops orphan tool results without rewriting canonical history or granting an invalid native projection.
29
+ - Anthropic budget-based thinking respects its minimum while keeping the previously computed total token cap. Impossible totals fail before transport.
30
+ - Startup navigation observes initialization and connection lifetime without replacing provider-first onboarding.
31
+ - A terminal model error gets a factual `needs_input` recap without another model request or a new wire enum.
32
+
33
+ The separate candidate review also led to an effective-authentication-header
34
+ attribution fix: rejecting an overriding credential must not invalidate an unused
35
+ stored API key. Provider and RPC documentation follow the retained interfaces.
36
+
37
+ ## What stays out
38
+
39
+ Prime Inference/account/team/credits integrations, telemetry and sharing do not
40
+ return. Neither do implicit default/backup models, the compiled Bun installer,
41
+ new `rlm.spawn`/`collect` APIs, broad retry-policy changes, or unrelated UI and
42
+ performance redesigns. The native protocol remains 13/schema49. Lawful upstream
43
+ copyright and MIT attribution remain intact.
44
+
45
+ Optional WebSocket retry policy is deferred rather than bypassing native attempt
46
+ admission and captured-context accounting. SSE remains the default. The other
47
+ explicit deferrals below are not claims that those upstream ideas are useless;
48
+ they are outside this release's selected adaptations.
49
+
50
+ ## Complete decision table
51
+
52
+ “Select subset” does not mean cherry-pick the whole commit. “Already” identifies
53
+ local behavior that needed no transplant. “Defer” is outside 1.0.1; “Skip” is
54
+ irrelevant or incompatible with the retained product. Upstream commits can be
55
+ viewed at `https://github.com/PrimeIntellect-ai/prime-agent/commit/<commit>`.
56
+
57
+ | # | Full upstream commit | Decision | Reason |
58
+ |---:|---|---|---|
59
+ | 1 | `363eb61920621c53bd4313deb91360fa3d188e5c` | Defer | Agents-view visual redesign; no correctness dependency. |
60
+ | 2 | `e6b79144c23e2d73df41cbba5335a8db65ddffe3` | Skip | Upstream contribution-ticket CI only. |
61
+ | 3 | `71766abb2c1e427382871c77d2dc3896164a3456` | Defer | Harness/system-prompt architecture change; do not replace native TaskFrame/epoch/source authority. |
62
+ | 4 | `31ebd50c791d6e016b059c9bc35e9ba4d845672d` | Defer | OpenCode session/app headers are attribution/affinity, not a demonstrated failing request; upstream user-agent identity is wrong for fork. |
63
+ | 5 | `24519c30856965a832551206b3e9ed87e814d602` | Already | Single-pass substituteArgs is already local; retain current prompt-template tests. |
64
+ | 6 | `a6625e17a042716fca4527c9297b972da3eb2142` | Select subset | Reject incomplete/orphan tool exchanges without mutating canonical source or granting invalid native projection. |
65
+ | 7 | `55ade48b73f636d992855b7cab797d71dc1f6f1c` | Select | LiteLLM maximum-context recognition; keep native overflow policy and rate-limit exclusion. |
66
+ | 8 | `bcdcd6e65e10959c9904ec4467747528303493b0` | Defer | Manual-compaction goal resume is relevant but local captured-owner compaction is substantially rewritten; not a direct transplant. |
67
+ | 9 | `427ea4c72cc606ac061a14287c0c15c471eff002` | Skip | Test adjustment for upstream harness-digest architecture. |
68
+ | 10 | `f9c7e06b58b9760944abb33b81f758ce5df1a1b3` | Skip | Upstream performance CI/harness, not product fixes; historical benchmark stays immutable. |
69
+ | 11 | `d4bc773d85348220b9134a646bcaa03b3bba74d2` | Skip | Upstream PR benchmark comment UI. |
70
+ | 12 | `163dd5798cd40e86b540aa9ecb68201920a83a1b` | Defer | Inactive-session model display, optional roster UI. |
71
+ | 13 | `e8b7168cc9972694232a9ed9c0a6a51655e44abf` | Select subset | Packaged Node Bedrock entry is missing; drop unrelated Linear-ticket/CI changes. |
72
+ | 14 | `81cd5390dbc871afb87be0d2012d205dd633bae3` | Select subset | Keep preexisting computed output cap/default arithmetic. Reject impossible budget before transport; floor thinking within same computed cap. |
73
+ | 15 | `ea7dbd1f103ffc9457e3f4a06ed1845ca5aa0a21` | Skip | Breaks retained callable await rlm(...) contract by switching to rlm.spawn. |
74
+ | 16 | `5a843023781b461b8cd7c7b45718f133b61e16b0` | Defer | Synthetic-message grammar churn changes prompt semantics and source classification. |
75
+ | 17 | `4f4d51c5b5e1d29a95ac0391556898910e1e01c7` | Defer | Roster API consolidation is not required by current family-wide /agents admission. |
76
+ | 18 | `27daf22125a188067768244bee04aba3ef74bafb` | Defer | New /autonomous budget-command UX; not necessary for selected correctness fixes. |
77
+ | 19 | `55c611fbf6300366c4edf204a79e4f7e8b99f5cf` | Skip | Removed /traces feature. |
78
+ | 20 | `1eee2938b4eeb7a4d72e17035adda669a89b63de` | Skip | Upstream stacked-PR benchmark CI. |
79
+ | 21 | `656747485307acb5f24e9a75ebac321dfe27acf7` | Defer | Provider-path display polish; preserve exact selector semantics. |
80
+ | 22 | `fb2db8ee1b61c69d53a84404e617bc74d6f205c9` | Skip | Subtree UI counts are not family-wide pending/running/idle admission; keep fork /agents implementation. |
81
+ | 23 | `ecd60e3cd4313643dede3cb2fcf13b4e2e9a2a75` | Defer | Consumed Bash notice withdrawal changes Python/host event delivery and native action/source state; not needed for selected fixes. |
82
+ | 24 | `48d7fca76b1f7a6f6cf7326c9e0392b82a245634` | Defer | Side-question tool declarations change cache/replay behavior; blocked execution must not become native replay permission. |
83
+ | 25 | `5255a6b88002d60ff810e27b33d104eb424c5e07` | Defer | /btw cache-effort change depends on side-question redesign; no core runtime dependency. |
84
+ | 26 | `b53c2e9d0666d62ac72b94b754fd649f1f9ff366` | Skip | GitHub discussion investigation automation. |
85
+ | 27 | `2aab9d466739f4f9c280c8800c40a32977197ce5` | Skip | Benchmark infrastructure for compiled upstream installs. |
86
+ | 28 | `fc782b8a431838c4376578664cf9218d4a5710b1` | Defer | Compaction/refinement visual redesign. |
87
+ | 29 | `a38ad5d538a06a42d845d52024db0b631b788b9b` | Defer | Inline configuration picker redesign conflicts with qualified provider-first flow. |
88
+ | 30 | `126b43c0ce4f51c49cbb3036a3507ece06bfcc8e` | Defer | Prompt-bar UX redesign. |
89
+ | 31 | `5c14169759736add628d7fce28eb97b3baa85ce9` | Defer | Agents/chat UI redesign; preserve local /agents controls. |
90
+ | 32 | `370c56235dce1f52b89c4e20b25f714b6f4e8dc1` | Defer | Large verbosity/UI/theme redesign, not needed for selected correctness fixes. |
91
+ | 33 | `46c60b7500923be9e77022cb85c731241f35e2ac` | Skip | Upstream PR contribution gate. |
92
+ | 34 | `ca67580b524800a971f92420d4c665bd83df1dfa` | Select subset | Generic Responses interleaving/final arguments/incomplete parsing only; reject Grok subscription and auth/model expansion. |
93
+ | 35 | `a329e744b23fcf7efee2a43661e996a82b5ee5a3` | Skip | Upstream benchmark startup/cleanup harness. |
94
+ | 36 | `0badc0f52edf3e6b56981ad8b2ac6178d1fe392d` | Defer | Subagent expand-affordance UI polish. |
95
+ | 37 | `dd760d310886d9ae771c3f563b148fa1caf23125` | Skip | Compiled Bun distribution/installer migration; fork uses Node/npm + owned Python3.13 runtime. |
96
+ | 38 | `5a3b922a9d1cb8ae416d96de9e99e4b39817b882` | Skip | Migrates npm users to upstream compiled distribution and paths. |
97
+ | 39 | `df6c709a06d85805cebb1c2a1001e144754cab9e` | Skip | Prime CLI/profile/Inference config isolation; these integrations are removed locally. |
98
+ | 40 | `19f959ba2311abdafc297337fe5846494494daa5` | Skip | Discussion notification state automation. |
99
+ | 41 | `011574e4111baa466f30079136853f230a152d33` | Skip | Compiled-install update/rollback architecture; not fork installer. |
100
+ | 42 | `c872594b758882ed7f814815f1e002e85033a657` | Skip | Upstream benchmark readiness/reporting harness. |
101
+ | 43 | `66658d2cf73153340f23133fbc16eafac31423e8` | Defer | Maintenance identity for OpenCode; native request coordinator already owns identity/capture, inspect through native path if needed. |
102
+ | 44 | `a487fa4191357334f669691d06ea694efa1678df` | Defer | Spawn-ledger stat cache is performance-only and touches admission/recovery authority. |
103
+ | 45 | `878410b3981f20c6d685faa210ad0e43426cf483` | Defer | Roster rebuild optimization; optional UI performance change. |
104
+ | 46 | `9661a1151525d5c967ec43774736eb14a39dc12a` | Defer | Large snapshot/transport/recovery change; not a minimal patch and interacts with protocol13/schema49. |
105
+ | 47 | `748ed685911cfeca9d9049aec2c91e1a0114b507` | Defer | Independent startup/list deadline policy overlaps fork-specific global admission and owned recovery; selected changes do not require adopting new scheduling. |
106
+ | 48 | `dbd29e948af43fcf2a64992ed31025faa19e7598` | Defer | Optional extension-render fallback changes UI restoration policy and does not change retained canonical history. Not required by selected startup fix. |
107
+ | 49 | `64a3d5205d3aebe69538034ef9e96810feda4485` | Defer | New timed orphan-worker shutdown policy; lifecycle/capacity/recovery changes exceed minimal fixes. |
108
+ | 50 | `b043f6ef125ff4bcd6664c96aef5f0c3a642a0d1` | Select subset | Normal back-navigation during initialization must not crash or update a disposed UI. Keep provider-first onboarding. |
109
+ | 51 | `90ca4a457e0fef0bd925215d4c973abd6264f067` | Select | Missing kernel pipe error listeners can crash worker; tiny isolated fix. |
110
+ | 52 | `9150cc28a3e1989b44ce5070910d1c7cfb052340` | Skip | New rlm.collect feature and fan-in protocol; retained workflow is admission + messages/files. |
111
+ | 53 | `766ddce2ce03b61636e8c10223542e9b3a534802` | Defer | Broad shell environment override changes user commands; upstream GIT_TERMINAL_PROMPTS is also misspelled (Git uses singular PROMPT). |
112
+ | 54 | `292ae3028e1b0ad376059bee65d5cf8c5e8d29fd` | Defer | Slash usage UX changes include commands removed/repurposed locally. |
113
+ | 55 | `59a074f511f583e68d0cea5ca6e37fecd0ffa7df` | Skip | Global daemon formatting-only churn. |
114
+ | 56 | `bb772f361084c793174cfe1d9bb5d6526dc7e3a5` | Select subset | Best-effort draft disposal and pending-attach check only; retain fork roster and family-capacity authority, without optional roster merge. |
115
+ | 57 | `2e43ebca3753c82ed0ef2064cb14081588ebd4d3` | Skip | Blanket autonomous pause while children run conflicts with continue-independent-work directive and fork lifecycle semantics. |
116
+ | 58 | `1316d187b7d73484265edd5276d7a21f1342f6b8` | Already | Provider-first runStartupOnboarding marks shown only after successful configured-model completion. |
117
+ | 59 | `5362cf3005126f9413a610eb9b610eb34a07c258` | Skip | Wait/backup model retry overhaul duplicates/conflicts with native coordinator recovery and explicit selected model. |
118
+ | 60 | `4535c87e5112f1c976b6f6dabb831f0d2ef93edf` | Defer | Reject 250ms stdin truncation because slow producers could lose input. Non-TTY/cross-project confirmation changes are independent of selected strict parser and qualified installer. |
119
+ | 61 | `9fcda876f501a66d19e5d45e78981c2595386697` | Defer | New model-cycling keybinding UX; preserve explicit persistence and configurable keys if later added. |
120
+ | 62 | `1fc1adb6e8062bf871a9b59705c1d15468e589f0` | Defer | Slash typo heuristic is not a demonstrated runtime bug and may reject intentional prompt text. |
121
+ | 63 | `7a049d50c61ed5a93e65060255e54d752f56dbf2` | Skip | Upstream UI interaction benchmark CI. |
122
+ | 64 | `215e7a7409b2931b49d3aad62d29fa8e94345973` | Defer | Goal monotonicity idea relevant, but local native _reloadBranchRuntimeState replaces patched helper; adapt only with source-boundary evidence. |
123
+ | 65 | `6be5d4f297f112df8b57b6807de9405c5444fc0f` | Defer | Head-plus-tail summary serialization changes prompt content rather than repairing canonical source. Keep native selection/budget behavior stable for regression comparison. |
124
+ | 66 | `f31c440c788499a4ba49736acbf1504a25e3ad4c` | Defer | Kernel diff path extraction plus silent 200-file cap changes summary semantics; not required and must not promote tool data to authority. |
125
+ | 67 | `2f5cd0cc163149ad8345f2c7eefe9ce08278cb0a` | Skip | Refinement backup model plus Prime catalog default change; no implicit model or Prime dependency. |
126
+ | 68 | `292a3eb35a338e6a7b204eb094a7b722246b0a7b` | Select subset | Hard-error invalid --thinking in args.ts; stale --no-daemon text cleanup only if still present. Combine with strict flag parsing. |
127
+ | 69 | `ad4df9364daa7efbb98226dfe1ecb8565635d0e6` | Defer | Harness relevance ranking/search is a new architecture/API, not native public retrieval. |
128
+ | 70 | `5c42f8549f3b21f99140a01d5f287e20ee99e19b` | Defer | Session append/fork optimization touches durable native source lifecycle; not blindly compatible. |
129
+ | 71 | `13f52b01cdf0332c02005602268332c77353326a` | Defer | New autonomous/subagent model settings; optional feature, preserve explicit supported provider/auth selection. |
130
+ | 72 | `6af0e1a4efdfa4f20b1e680f57797397f38090db` | Skip | Upstream swarm capability eval harness, not requested benchmark/control protocol. |
131
+ | 73 | `9e7ac5d5e36a9bd3aae6ee10b7cd61ffa002a573` | Select subset | Accept RLMSpawnHandle for delete_subagent only; do not require rlm.spawn or collect. |
132
+ | 74 | `284eebc2d09b67264b52b757f43a579286afc380` | Defer | Helpful selector errors coupled to implicit short-form fallback and Prime example; retain exact explicit selectors. |
133
+ | 75 | `c0de2eb29e72e44036574ca226ac1452f85c6bea` | Already | Dead ./hooks subpath is already absent from local coding-agent exports. |
134
+ | 76 | `b6b9b0b4220db54dd067efd956208318550d804f` | Skip | Private Prime Inference template route and enable_thinking fix; route removed locally. |
135
+ | 77 | `7d1913969d33fa5a31366321e3f5db0070dcf2bf` | Defer | SSE remains default. Upstream automatic WebSocket replay/retry is not a direct fit for native physical-attempt admission and captured-prefix budgets; keep current explicit transport failure rather than bypass coordinator. |
136
+ | 78 | `8f52777f2d3778c28c97fb438ca9f6bf07310b75` | Select subset | Latest terminal assistant error produces factual needs_input recap without classifier/model request; preserve async owned persistence and protocol13/schema49; later successful turn uses normal classification. |
137
+ | 79 | `598eebe8cd6645983c43d50647af9e36e9b07bc0` | Skip | Compiled macOS signing/notarization distribution. |
138
+ | 80 | `fb90167a9a061c4585522a973f809a8c0b2c55ab` | Skip | Generic MCP/service catalog merge reverted by commit99; no net feature to transplant. |
139
+ | 81 | `5d66ab91326db0b93a8f0044e0fff51834b19929` | Defer | Host-owned extension timer API/lifecycle expansion; not prerequisite. |
140
+ | 82 | `8eaa00c37385c6b25fed9d46584b50ffc2e87f9c` | Select | Park waitForIdle on actual blocked-pump predicate; preserve native owner/current/settlement guards. |
141
+ | 83 | `807ff048eb53506be7add0116c17f3047292ac8f` | Skip | Readiness wait is for removed Prime private catalog and fallback-model route. |
142
+ | 84 | `9bd6a9bc0fbc1e4072cfd7162f855a305c39efb0` | Defer | Queued goal mutation at delivery must respect fork persisted action/source pins; do not mutate frozen records directly. |
143
+ | 85 | `2a461b0de77c47d780639c680ca898939a9afda2` | Skip | Remote trace upload test fix; feature removed. |
144
+ | 86 | `f87021bf2de379855e0bedc55ac67bad57d825a0` | Defer | Persistent cross-worker link is a transport redesign; preserve worker claim/family capacity behavior. |
145
+ | 87 | `ec56defc1c9d121c571988b1ad0f2027283825ff` | Skip | 431-line Git-command policy guard plus813-line suite is an unrequested guardrail; not minimal correctness transplant. |
146
+ | 88 | `849ab8599809d1f48fd11f32f58fc4b7de0434ae` | Skip | Upstream benchmark terminal wait CI. |
147
+ | 89 | `4a4a2305eb58ab1081f72b3a85da4c962acf70bd` | Skip | Upstream benchmark marker CI. |
148
+ | 90 | `5d25a44bd22e1c1fe8321e141cd6c3932563d14c` | Skip | Upstream benchmark UI navigation harness. |
149
+ | 91 | `cfb2d3fe35c7c23cf6678ec235412b0d3f286fae` | Defer | Catalog read/write queue performance; depends on ledger changes and fork capacity/source authority. |
150
+ | 92 | `327139f7a153388fbe9f90366d8ab944a73d867c` | Defer | Further transcript download optimization depends on46 snapshot redesign. |
151
+ | 93 | `6ceb046fe059502c7c617ba8f97ad530df639a41` | Skip | Upstream compiled-release CI download paths. |
152
+ | 94 | `27bd07994e3acb96dcde82a1cd8b91e9168a6bc0` | Defer | Catalog canonicalization memo/performance; depends on44/91 and native session file rules. |
153
+ | 95 | `c8a631e5de703ab9f50efcf3503359489a2e9be8` | Skip | Upstream cold-worker/catalog benchmark fixtures. |
154
+ | 96 | `1a891aa1b765cc351db03547857f4ba9370eb3d5` | Skip | Upstream resident-chat UI benchmark fixtures. |
155
+ | 97 | `0cf1314d196d5db4d0918b4fb3d991c9a5b7a2da` | Defer | Model picker effort arrows depend on inline-picker UI; not needed. |
156
+ | 98 | `683a2d431bd0600048c0bc83c7429c83ed0df2b5` | Defer | Markdown heading/error icon polish, low impact. |
157
+ | 99 | `350ddbaa467d1e1fb8e11a97a1545d251a70afb6` | Skip | Reverts80; keep existing MCP architecture. |
158
+ | 100 | `e334a8b460f0ff8791810fac0c9e66a1b4757298` | Select subset | Strict required-value/invalid-mode parsing; keep fork --rpc-protocol-version, prime_context, exports and unknown extension flags. |
159
+ | 101 | `a5cc2371ec597560990393b7801a559eda2af0fd` | Defer | Sticky chat name/top bar UI feature. |
160
+ | 102 | `dca77ecfe450adbd61c2555c3f1e8c5b70f6cf3f` | Defer | Human-priority scheduling is useful but rewrites action ordering/admission; needs native source ordering adaptation. |
161
+ | 103 | `4bf122bb6fd3455b09511791a16a00022098f8a7` | Skip | Upstream nightly channel/release infrastructure. |
162
+ | 104 | `f5859162cc5c983090a753222b554a880207dd68` | Defer | Inline provider login UI refactor; provider-first fork is already qualified and removes upstream Prime flows. |
163
+ | 105 | `ad426c672327696c42d641379840212ca5a8b85b` | Defer | Depends on deferred catalog scheduling change; do not silently add new timeout semantics. |
164
+ | 106 | `83b2d32545d6ad37d4be460031db6df3ae8058e6` | Skip | Prime-specific first-run onboarding rewrite reintroduces unwanted product integration. |
165
+ | 107 | `ebe530d28c9f24fb03353085c05a68dcd29e7fbc` | Defer | New child progress/snapshot API and prompt messages; unnecessary wire/runtime feature expansion. |
166
+ | 108 | `ebe299ee599d2e7444c034d1b38af38808036e74` | Skip | Compiled Linux musl/baseline artifact matrix, not fork Node runtime installer. |
167
+ | 109 | `9c59b394cac2bad490a38d7eee55efe8b0d03d86` | Select subset | State-root-scoped daemon discovery only; reject telemetry/platform fidelity, compiled installer and unrelated CLI refactor. |
168
+ | 110 | `ca82b869b5a20fd385bb9c8585b0bd07d47da186` | Skip | Trace-onboarding prompt; remote traces removed. |
169
+ | 111 | `8d64d3e1cd1b16dc77a736126f8757673138d0b7` | Skip | Embed native clipboard addon in compiled Bun binaries; not current Node distribution. |
170
+ | 112 | `e9d68d92c58492fe554c843ba90f894bcff3fe66` | Skip | Upstream compiled-release manifest forward compatibility; fork installer format differs. |
171
+ | 113 | `a7d791bc1be09793ed5f3ec05bf4cccbc60679ea` | Skip | Upstream release0.9.5 version/changelog assembly; keep fork1.0.1 and lawful attribution. |
package/docs/usage.md CHANGED
@@ -42,13 +42,13 @@ Type `/` in the editor to open command completion. Extensions can register custo
42
42
  | `/login`, `/logout` | Manage OAuth or API-key credentials |
43
43
  | `/model` | Switch models |
44
44
  | `/effort` | Set the reasoning/thinking level |
45
+ | `/agents [N]` | Show or save the maximum live subagents (default: 4) |
45
46
  | `/scoped-models` | Enable/disable models for Ctrl+P cycling |
46
47
  | `/settings` | Thinking level, theme, message delivery, transport |
47
48
  | `/resume [id\|path]` | Open the agents view, or resume a session directly |
48
49
  | `/new` | Start a new session |
49
50
  | `/name <name>` | Set session display name |
50
51
  | `/session` | Show session file, ID, and message counts |
51
- | `/traces [status\|on\|off\|preview\|upload-current\|upload-all\|login]` | Preview, upload, or manage opt-in trace sharing |
52
52
  | `/usage`, `/context` | Show the parent and subagent context, token, and cost breakdown |
53
53
  | `/tree` | Jump to any point in the session and continue from there |
54
54
  | `/fork` | Create a new session from a previous user message |
@@ -58,12 +58,21 @@ Type `/` in the editor to open command completion. Extensions can register custo
58
58
  | `/copy` | Copy last assistant message to clipboard |
59
59
  | `/btw <question>`, `/side <question>` | Ask an inline side question without adding it to the session; replies continue the side conversation, esc returns |
60
60
  | `/export [file]` | Export session to HTML |
61
- | `/share` | Upload as private GitHub gist with shareable HTML link |
62
61
  | `/reload` | Reload keybindings, extensions, skills, prompts, and context files |
63
62
  | `/hotkeys` | Show all keyboard shortcuts |
64
63
  | `/changelog` | Display version history |
65
64
  | `/quit` | Quit Base Context |
66
65
 
66
+ ### Limit concurrent subagents
67
+
68
+ `/agents` reads the current configured limit from the active root-agent family and displays exactly `The current maximum number of concurrent subagents is {N}.`, with `{N}` replaced by that current value, not a hardcoded default.
69
+
70
+ `/agents 6` sets the limit to 6 for the current root agent and all its descendants. The main agent does not count. Running and idle subagents count; inactive saved sessions do not. A fresh installation defaults to **4** live subagents.
71
+
72
+ The value is saved as the global `rlmMaxSubagents` preference, so it survives restarts and supplies the limit for later sessions. Use a non-negative safe integer. `/agents 0` disables new subagent spawns. Malformed, fractional, negative, and unsafe integer values are rejected without changing the setting.
73
+
74
+ Lowering the limit never kills or passivates existing agents. They keep running or remain idle. Only new spawns/admissions are blocked until the live count is below the limit. This slash command is separate from `base-context agents`, which lists agents.
75
+
67
76
  ## Message Queue
68
77
 
69
78
  You can submit messages while the agent is still working:
@@ -165,11 +174,9 @@ Replace the default system prompt with:
165
174
 
166
175
  Append to the default prompt without replacing it with `APPEND_SYSTEM.md` in either location.
167
176
 
168
- ## Exporting and Sharing Sessions
169
-
170
- Use `/export [file]` to write a session to HTML.
177
+ ## Exporting Sessions
171
178
 
172
- Use `/share` to upload a private GitHub gist with a shareable HTML link.
179
+ Use `/export [file]` to write a session to a local HTML file.
173
180
 
174
181
  ## CLI Reference
175
182
 
@@ -373,15 +380,12 @@ base-context --tools ipython -p "Review the code"
373
380
  | `BASE_CONTEXT_SKIP_VERSION_CHECK` | Skip the Base Context version lookup at startup |
374
381
  | `BASE_CONTEXT_DOWNLOAD_BASE_URL` | Explicit custom static update-manifest base; leave unset for owned npm updates. A GitHub repository URL is not that manifest endpoint; see [update settings](settings.md#update-checks) |
375
382
  | `BASE_CONTEXT_CACHE_RETENTION` | Set to `long` for extended prompt cache where the provider supports it |
376
- | `PRIME_API_KEY` | Prime Inference API key; not a trace-sharing credential |
377
- | `BASE_CONTEXT_TRACES_API_KEY` | Dedicated key for explicitly configured, opt-in Base Context trace sharing |
378
- | `BASE_CONTEXT_TRACES_BASE_URL` | Explicit trace upload API base; no upstream destination is selected by default |
379
383
  | `BASE_CONTEXT_KERNEL_PYTHON` | Explicit Python executable with a current `base-context-runtime` for a manual runtime route |
380
384
  | `BASE_CONTEXT_KERNEL_VENV` | Explicit manual kernel environment directory; owned preparation still creates its own fresh release-local environment |
381
385
  | `BASE_CONTEXT_INSTALL_UV` | Set to `1` to allow uv installation for kernel bootstrap |
382
386
  | `VISUAL`, `EDITOR` | External editor for Ctrl+G |
383
387
 
384
- `BASE_CONTEXT_HOME`, `BASE_CONTEXT_SESSION_DIR` and kernel path overrides require non-empty absolute paths (`~/` is supported). The project configuration directory is `.base-context/`; changing the product HOME does not rename it. These product settings do not redirect into old Prime state. `PRIME_API_KEY` remains a provider name, not a product-prefix alias. The Python import stays `rlm`, but an upstream `prime-agent-runtime` environment is not the Base Context runtime. Non-owned default bootstrap uses `~/.base-context/runtime`; the owned installer prepares each release's runtime before activation.
388
+ `BASE_CONTEXT_HOME`, `BASE_CONTEXT_SESSION_DIR` and kernel path overrides require non-empty absolute paths (`~/` is supported). The project configuration directory is `.base-context/`; changing the product HOME does not rename it. These product settings do not redirect into old Prime state. Provider variables such as `OPENAI_API_KEY` keep their provider names; they are not product-prefix aliases. The Python import stays `rlm`, but an upstream `prime-agent-runtime` environment is not the Base Context runtime. Non-owned default bootstrap uses `~/.base-context/runtime`; the owned installer prepares each release's runtime before activation.
385
389
 
386
390
  ## Design Principles
387
391
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ponythewhite/base-context",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Synerise base-context: a coding and research agent with durable context and a persistent Python REPL",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,9 +42,9 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@agentclientprotocol/sdk": "^1.3.0",
45
- "@ponythewhite/base-context-agent": "1.0.0",
46
- "@ponythewhite/base-context-ai": "1.0.0",
47
- "@ponythewhite/base-context-tui": "1.0.0",
45
+ "@ponythewhite/base-context-agent": "1.0.1",
46
+ "@ponythewhite/base-context-ai": "1.0.1",
47
+ "@ponythewhite/base-context-tui": "1.0.1",
48
48
  "@silvia-odwyer/photon-node": "^0.3.4",
49
49
  "chalk": "^5.5.0",
50
50
  "cli-highlight": "^2.1.11",
@@ -1,149 +0,0 @@
1
- import type { AuthStorage } from "./auth-storage.js";
2
- import { type SessionHeader, type SessionManager } from "./session-manager.js";
3
- import type { SettingsManager } from "./settings-manager.js";
4
- export type AgentTraceCredentialSource = "environment" | "stored";
5
- export interface AgentTraceCredential {
6
- apiKey: string;
7
- source: AgentTraceCredentialSource;
8
- label: string;
9
- }
10
- export type AgentTraceUploadResult = {
11
- status: "uploaded";
12
- sessionId: string;
13
- traceId: string;
14
- bytesStored: number;
15
- key?: string;
16
- } | {
17
- status: "disabled";
18
- } | {
19
- status: "unchanged";
20
- } | {
21
- status: "missing_credentials";
22
- } | {
23
- status: "no_session_file";
24
- } | {
25
- status: "empty_session";
26
- } | {
27
- status: "invalid_session";
28
- message: string;
29
- } | {
30
- status: "too_large";
31
- size: number;
32
- maxBytes: number;
33
- } | {
34
- status: "failed";
35
- statusCode?: number;
36
- message: string;
37
- retryAfterMs?: number;
38
- };
39
- export interface AgentTraceUploadOptions {
40
- sessionFile: string | undefined;
41
- authStorage: AuthStorage;
42
- settingsManager: SettingsManager;
43
- /** Require the global automatic-sharing opt-in. Set false only for an explicit one-shot upload command. */
44
- requireEnabled?: boolean;
45
- baseUrl?: string;
46
- configPath?: string;
47
- fetchFn?: typeof fetch;
48
- reloadConfig?: boolean;
49
- requestTimeoutMs?: number;
50
- signal?: AbortSignal;
51
- }
52
- export interface AgentTraceSessionUploadOptions extends Omit<AgentTraceUploadOptions, "sessionFile"> {
53
- sessionManager: SessionManager;
54
- }
55
- export interface AgentTraceUploadInstallOptions {
56
- authStorage: AuthStorage;
57
- settingsManager: SettingsManager;
58
- baseUrl?: string;
59
- configPath?: string;
60
- fetchFn?: typeof fetch;
61
- requestTimeoutMs?: number;
62
- /** The session's semantic-edge ledger; registered with the outbox as its own delivery kind. */
63
- semanticEdgesLedgerPath?: string;
64
- }
65
- export type AgentTracePreviewResult = {
66
- status: "ready";
67
- sessionFile: string;
68
- sessionId: string;
69
- traceId: string;
70
- parentSessionId?: string;
71
- cwd: string;
72
- size: number;
73
- maxBytes: number;
74
- uploadable: boolean;
75
- endpoint?: string;
76
- gitRepo?: string;
77
- gitCommit?: string;
78
- contentPreview: string;
79
- truncated: boolean;
80
- } | {
81
- status: "no_session_file";
82
- } | {
83
- status: "empty_session";
84
- } | {
85
- status: "invalid_session";
86
- message: string;
87
- } | {
88
- status: "failed";
89
- message: string;
90
- };
91
- export interface AgentTracePreviewOptions {
92
- sessionFile: string | undefined;
93
- baseUrl?: string;
94
- maxContentChars?: number;
95
- }
96
- export interface AgentTraceUploadAllProgress {
97
- completed: number;
98
- total: number;
99
- sessionFile?: string;
100
- result?: AgentTraceUploadResult;
101
- }
102
- export interface AgentTraceUploadAllOptions extends Omit<AgentTraceUploadOptions, "sessionFile"> {
103
- sessionDir?: string;
104
- concurrency?: number;
105
- onProgress?: (progress: AgentTraceUploadAllProgress) => void;
106
- }
107
- export interface AgentTraceUploadAllResult {
108
- total: number;
109
- uploaded: number;
110
- failed: number;
111
- skipped: number;
112
- bytesStored: number;
113
- results: Array<{
114
- sessionFile: string;
115
- result: AgentTraceUploadResult;
116
- }>;
117
- }
118
- /** Active-branch git for the indexing headers: walk leaf to root, not the last git_state in
119
- * file order (which may belong to a sibling branch). */
120
- export declare function activeGitContext(body: string, header: SessionHeader): {
121
- repoUrl?: string;
122
- commit?: string;
123
- } | undefined;
124
- export declare function previewAgentTraceFile(options: AgentTracePreviewOptions): Promise<AgentTracePreviewResult>;
125
- export declare function findAgentTraceFiles(sessionDir?: string): Promise<string[]>;
126
- export declare function uploadAllAgentTraces(options: AgentTraceUploadAllOptions): Promise<AgentTraceUploadAllResult>;
127
- export declare const SEMANTIC_EDGES_OUTBOX_KIND = "semantic-edges";
128
- export interface AgentTraceCatchUpResult {
129
- pruned: number;
130
- /** Registered semantic-edge ledgers with bytes beyond their cursor; no delivery endpoint exists yet. */
131
- semanticEdgeLedgersPending: number;
132
- results: Array<{
133
- sessionFile: string;
134
- result: AgentTraceUploadResult;
135
- }>;
136
- }
137
- /**
138
- * Explicit catch-up for the Base Context export outbox. This is never run at
139
- * startup and does not read inherited agent-traces-outbox entries.
140
- */
141
- export declare function catchUpAgentTraceUploads(options: Omit<AgentTraceUploadOptions, "sessionFile">): Promise<AgentTraceCatchUpResult>;
142
- export declare function getPrimeAgentTraceCredential(authStorage: AuthStorage, options?: {
143
- reloadAuth?: boolean;
144
- configPath?: string;
145
- }): Promise<AgentTraceCredential | undefined>;
146
- export declare function uploadAgentTraceFile(options: AgentTraceUploadOptions): Promise<AgentTraceUploadResult>;
147
- export declare function uploadAgentTraceSession(options: AgentTraceSessionUploadOptions): Promise<AgentTraceUploadResult>;
148
- export declare function installAgentTraceUpload(sessionManager: SessionManager, options: AgentTraceUploadInstallOptions): void;
149
- //# sourceMappingURL=agent-traces.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"agent-traces.d.ts","sourceRoot":"","sources":["../../src/core/agent-traces.ts"],"names":[],"mappings":"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","sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { type Dirent, existsSync, mkdirSync, renameSync, writeFileSync } from \"node:fs\";\nimport { mkdir, readdir, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { appendRotatingLog, getAgentDir, getAgentTracesLogPath, getSessionsDir, VERSION } from \"../config.js\";\nimport { assertProductStatePath } from \"../runtime-paths.js\";\nimport type { AuthStorage } from \"./auth-storage.js\";\nimport { BASE_CONTEXT_TRACES_PROVIDER_ID, resolvePrimeAgentTracesBaseUrl } from \"./prime-inference-auth.js\";\nimport { readSessionJournal, readSessionJournalHeader } from \"./session-journal-reader.js\";\nimport { getSessionArtifactsRoot, type SessionHeader, type SessionManager } from \"./session-manager.js\";\nimport type { SettingsManager } from \"./settings-manager.js\";\n\nconst MAX_TRACE_BYTES = 20 * 1024 * 1024;\nconst DEFAULT_REQUEST_TIMEOUT_MS = 15_000;\nconst TRACE_UPLOAD_DEBOUNCE_MS = 1_000;\nconst TRACE_UPLOAD_MIN_INTERVAL_MS = 60_000;\nconst TRACE_UPLOAD_RETRY_BASE_DELAY_MS = 500;\nconst TRACE_UPLOAD_RETRY_MAX_DELAY_MS = 10_000;\nconst TRACE_UPLOAD_MAX_RETRIES = 3;\nconst TRACE_UPLOAD_RETRY_JITTER = 0.2;\nconst TRACE_PREVIEW_MAX_CHARS = 8_000;\nconst TRACE_UPLOAD_ALL_CONCURRENCY = 4;\nconst TRACE_UPLOAD_RATE_LIMIT_REQUESTS = 5;\nconst TRACE_UPLOAD_RATE_LIMIT_WINDOW_MS = 60_000;\nconst TRACE_UPLOAD_RATE_LIMIT_SAFETY_MS = 100;\nconst MAX_TIMER_DELAY_MS = 2 ** 31 - 1;\nconst TRACE_UPLOAD_ALL_MIN_REQUEST_INTERVAL_MS =\n\tMath.ceil(TRACE_UPLOAD_RATE_LIMIT_WINDOW_MS / TRACE_UPLOAD_RATE_LIMIT_REQUESTS) + TRACE_UPLOAD_RATE_LIMIT_SAFETY_MS;\n\nexport type AgentTraceCredentialSource = \"environment\" | \"stored\";\n\nexport interface AgentTraceCredential {\n\tapiKey: string;\n\tsource: AgentTraceCredentialSource;\n\tlabel: string;\n}\n\nexport type AgentTraceUploadResult =\n\t| {\n\t\t\tstatus: \"uploaded\";\n\t\t\tsessionId: string;\n\t\t\ttraceId: string;\n\t\t\tbytesStored: number;\n\t\t\tkey?: string;\n\t }\n\t| { status: \"disabled\" }\n\t| { status: \"unchanged\" }\n\t| { status: \"missing_credentials\" }\n\t| { status: \"no_session_file\" }\n\t| { status: \"empty_session\" }\n\t| { status: \"invalid_session\"; message: string }\n\t| { status: \"too_large\"; size: number; maxBytes: number }\n\t| { status: \"failed\"; statusCode?: number; message: string; retryAfterMs?: number };\n\nexport interface AgentTraceUploadOptions {\n\tsessionFile: string | undefined;\n\tauthStorage: AuthStorage;\n\tsettingsManager: SettingsManager;\n\t/** Require the global automatic-sharing opt-in. Set false only for an explicit one-shot upload command. */\n\trequireEnabled?: boolean;\n\tbaseUrl?: string;\n\tconfigPath?: string;\n\tfetchFn?: typeof fetch;\n\treloadConfig?: boolean;\n\trequestTimeoutMs?: number;\n\tsignal?: AbortSignal;\n}\n\nexport interface AgentTraceSessionUploadOptions extends Omit<AgentTraceUploadOptions, \"sessionFile\"> {\n\tsessionManager: SessionManager;\n}\n\nexport interface AgentTraceUploadInstallOptions {\n\tauthStorage: AuthStorage;\n\tsettingsManager: SettingsManager;\n\tbaseUrl?: string;\n\tconfigPath?: string;\n\tfetchFn?: typeof fetch;\n\trequestTimeoutMs?: number;\n\t/** The session's semantic-edge ledger; registered with the outbox as its own delivery kind. */\n\tsemanticEdgesLedgerPath?: string;\n}\n\nexport type AgentTracePreviewResult =\n\t| {\n\t\t\tstatus: \"ready\";\n\t\t\tsessionFile: string;\n\t\t\tsessionId: string;\n\t\t\ttraceId: string;\n\t\t\tparentSessionId?: string;\n\t\t\tcwd: string;\n\t\t\tsize: number;\n\t\t\tmaxBytes: number;\n\t\t\tuploadable: boolean;\n\t\t\tendpoint?: string;\n\t\t\tgitRepo?: string;\n\t\t\tgitCommit?: string;\n\t\t\tcontentPreview: string;\n\t\t\ttruncated: boolean;\n\t }\n\t| { status: \"no_session_file\" }\n\t| { status: \"empty_session\" }\n\t| { status: \"invalid_session\"; message: string }\n\t| { status: \"failed\"; message: string };\n\nexport interface AgentTracePreviewOptions {\n\tsessionFile: string | undefined;\n\tbaseUrl?: string;\n\tmaxContentChars?: number;\n}\n\nexport interface AgentTraceUploadAllProgress {\n\tcompleted: number;\n\ttotal: number;\n\tsessionFile?: string;\n\tresult?: AgentTraceUploadResult;\n}\n\nexport interface AgentTraceUploadAllOptions extends Omit<AgentTraceUploadOptions, \"sessionFile\"> {\n\tsessionDir?: string;\n\tconcurrency?: number;\n\tonProgress?: (progress: AgentTraceUploadAllProgress) => void;\n}\n\nexport interface AgentTraceUploadAllResult {\n\ttotal: number;\n\tuploaded: number;\n\tfailed: number;\n\tskipped: number;\n\tbytesStored: number;\n\tresults: Array<{ sessionFile: string; result: AgentTraceUploadResult }>;\n}\n\nfunction stringEnv(name: string): string | undefined {\n\tconst value = process.env[name];\n\treturn typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction describeError(error: unknown): string {\n\tif (!(error instanceof Error)) {\n\t\treturn String(error);\n\t}\n\tconst cause = (error as { cause?: unknown }).cause;\n\tif (isRecord(cause)) {\n\t\tconst code = typeof cause.code === \"string\" ? cause.code : undefined;\n\t\tconst causeMessage = typeof cause.message === \"string\" ? cause.message : undefined;\n\t\tconst detail = code ?? causeMessage;\n\t\tif (detail && detail !== error.message) {\n\t\t\treturn `${error.message} (${detail})`;\n\t\t}\n\t}\n\treturn error.message;\n}\n\nclass TraceUploadTimeoutError extends Error {\n\tconstructor(timeoutMs: number) {\n\t\tsuper(`Trace upload timed out after ${timeoutMs}ms`);\n\t\tthis.name = \"TraceUploadTimeoutError\";\n\t}\n}\n\nconst RETRIABLE_NETWORK_CODES = new Set([\n\t\"ECONNRESET\",\n\t\"ECONNREFUSED\",\n\t\"ECONNABORTED\",\n\t\"EPIPE\",\n\t\"ETIMEDOUT\",\n\t\"ENETUNREACH\",\n\t\"ENETDOWN\",\n\t\"EAI_AGAIN\",\n\t\"UND_ERR_SOCKET\",\n\t\"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\n// 429 is deliberately absent: a rate-limited upload is rescheduled by its caller instead of sleeping in-request.\nconst RETRIABLE_HTTP_STATUSES = new Set([408, 425, 500, 502, 503, 504]);\n\nfunction isRetriableNetworkError(error: unknown): boolean {\n\tif (error instanceof TraceUploadTimeoutError) {\n\t\treturn true;\n\t}\n\tif (!(error instanceof Error) || error.name === \"AbortError\") {\n\t\treturn false;\n\t}\n\tconst cause = (error as { cause?: unknown }).cause;\n\treturn isRecord(cause) && typeof cause.code === \"string\" && RETRIABLE_NETWORK_CODES.has(cause.code);\n}\n\nfunction stringField(data: Record<string, unknown>, key: string): string | undefined {\n\tconst value = data[key];\n\treturn typeof value === \"string\" && value.trim() ? value.trim() : undefined;\n}\n\nfunction numberField(data: Record<string, unknown>, key: string): number | undefined {\n\tconst value = data[key];\n\treturn typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction isSessionHeader(value: unknown): value is SessionHeader {\n\treturn (\n\t\tisRecord(value) &&\n\t\tvalue.type === \"session\" &&\n\t\ttypeof value.id === \"string\" &&\n\t\ttypeof value.timestamp === \"string\" &&\n\t\ttypeof value.cwd === \"string\" &&\n\t\t(value.parentSession === undefined || typeof value.parentSession === \"string\")\n\t);\n}\n\nfunction readSessionHeader(sessionFile: string): SessionHeader | undefined {\n\ttry {\n\t\tconst parsed = readSessionJournalHeader(sessionFile);\n\t\treturn isSessionHeader(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nasync function readSessionTraceBody(sessionFile: string): Promise<string> {\n\tconst lines: string[] = [];\n\tfor await (const { json } of readSessionJournal(sessionFile)) {\n\t\tlines.push(`${json}\\n`);\n\t}\n\treturn lines.join(\"\");\n}\n\n/** Active-branch git for the indexing headers: walk leaf to root, not the last git_state in\n * file order (which may belong to a sibling branch). */\nexport function activeGitContext(\n\tbody: string,\n\theader: SessionHeader,\n): { repoUrl?: string; commit?: string } | undefined {\n\tconst byId = new Map<string, { parentId: string | null; type: string; git?: unknown }>();\n\tlet leafId: string | null = null;\n\tfor (const line of body.split(\"\\n\")) {\n\t\tif (!line.trim()) continue;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!isRecord(parsed) || parsed.type === \"session\" || typeof parsed.id !== \"string\") continue;\n\t\tbyId.set(parsed.id, {\n\t\t\tparentId: typeof parsed.parentId === \"string\" ? parsed.parentId : null,\n\t\t\ttype: typeof parsed.type === \"string\" ? parsed.type : \"\",\n\t\t\tgit: parsed.git,\n\t\t});\n\t\tleafId = parsed.id;\n\t}\n\n\tlet current = leafId ? byId.get(leafId) : undefined;\n\tfor (let depth = 0; current && depth < byId.size + 1; depth += 1) {\n\t\tif (current.type === \"git_state\" && isRecord(current.git)) {\n\t\t\treturn current.git as { repoUrl?: string; commit?: string };\n\t\t}\n\t\tcurrent = current.parentId ? byId.get(current.parentId) : undefined;\n\t}\n\treturn header.git;\n}\n\nfunction resolveParentSessionPath(sessionFile: string, parentSession: string): string {\n\treturn isAbsolute(parentSession) ? parentSession : resolve(dirname(sessionFile), parentSession);\n}\n\nfunction resolveTraceContext(\n\tsessionFile: string,\n\theader: SessionHeader,\n): { traceId: string; parentSessionId?: string } {\n\tlet traceId = header.id;\n\tlet parentSessionId: string | undefined;\n\tlet currentFile = sessionFile;\n\tlet currentHeader = header;\n\n\tfor (let depth = 0; depth < 32; depth += 1) {\n\t\tif (!currentHeader.parentSession) {\n\t\t\tbreak;\n\t\t}\n\n\t\tconst parentPath = resolveParentSessionPath(currentFile, currentHeader.parentSession);\n\t\tconst parentHeader = readSessionHeader(parentPath);\n\t\tif (!parentHeader) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (depth === 0) {\n\t\t\tparentSessionId = parentHeader.id;\n\t\t}\n\t\ttraceId = parentHeader.id;\n\t\tcurrentFile = parentPath;\n\t\tcurrentHeader = parentHeader;\n\t}\n\n\treturn { traceId, parentSessionId };\n}\n\nfunction parseResponseObject(text: string): Record<string, unknown> | undefined {\n\tif (!text.trim()) {\n\t\treturn undefined;\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\treturn isRecord(parsed) ? parsed : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nasync function readResponseMessage(response: Response): Promise<string> {\n\tconst text = await response.text().catch(() => \"\");\n\tif (!text.trim()) {\n\t\treturn response.statusText || \"Unknown error\";\n\t}\n\n\tconst parsed = parseResponseObject(text);\n\tif (parsed) {\n\t\tconst error = parsed.error;\n\t\tif (isRecord(error)) {\n\t\t\tconst message = stringField(error, \"message\");\n\t\t\tif (message) return message;\n\t\t}\n\t\tconst detail = stringField(parsed, \"detail\");\n\t\tif (detail) return detail;\n\t\tconst message = stringField(parsed, \"message\");\n\t\tif (message) return message;\n\t}\n\n\treturn text.trim();\n}\n\nasync function fetchWithTimeout(\n\tfetchFn: typeof fetch,\n\turl: string,\n\tinit: RequestInit,\n\ttimeoutMs: number,\n\tsignal?: AbortSignal,\n): Promise<Response> {\n\tconst controller = new AbortController();\n\tconst timeoutError = new TraceUploadTimeoutError(timeoutMs);\n\tlet timedOut = false;\n\tconst timeout = setTimeout(() => {\n\t\ttimedOut = true;\n\t\tcontroller.abort(timeoutError);\n\t}, timeoutMs);\n\ttimeout.unref();\n\tconst onAbort = () => controller.abort(signal?.reason);\n\tif (signal?.aborted) {\n\t\tonAbort();\n\t} else {\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t}\n\n\ttry {\n\t\treturn await fetchFn(url, { ...init, signal: controller.signal });\n\t} catch (error) {\n\t\tif (timedOut) {\n\t\t\tthrow timeoutError;\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tclearTimeout(timeout);\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t}\n}\n\nfunction delay(ms: number, signal?: AbortSignal): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tif (signal?.aborted) {\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\t\tconst timeout = setTimeout(finish, ms);\n\t\ttimeout.unref();\n\t\tconst onAbort = () => finish();\n\t\tfunction finish() {\n\t\t\tclearTimeout(timeout);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve();\n\t\t}\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\nfunction traceUploadRetryDelay(retryIndex: number): number {\n\tconst exponentialDelay = Math.min(\n\t\tTRACE_UPLOAD_RETRY_MAX_DELAY_MS,\n\t\tTRACE_UPLOAD_RETRY_BASE_DELAY_MS * 2 ** retryIndex,\n\t);\n\tconst jitterMultiplier = 1 - TRACE_UPLOAD_RETRY_JITTER + Math.random() * TRACE_UPLOAD_RETRY_JITTER * 2;\n\treturn Math.max(0, Math.round(exponentialDelay * jitterMultiplier));\n}\n\nfunction retryAfterDelay(response: Response, capMs: number = TRACE_UPLOAD_RATE_LIMIT_WINDOW_MS): number | undefined {\n\tconst value = response.headers.get(\"retry-after\")?.trim();\n\tif (!value) {\n\t\treturn undefined;\n\t}\n\tconst seconds = Number(value);\n\tif (Number.isFinite(seconds) && seconds >= 0) {\n\t\treturn Math.min(Math.ceil(seconds * 1_000), capMs);\n\t}\n\tconst retryAt = Date.parse(value);\n\treturn Number.isFinite(retryAt) ? Math.min(Math.max(0, retryAt - Date.now()), capMs) : undefined;\n}\n\ntype BeforeTraceUploadRequest = () => Promise<void>;\n\nasync function fetchWithRetry(\n\tfetchFn: typeof fetch,\n\turl: string,\n\tinit: RequestInit,\n\ttimeoutMs: number,\n\tsignal?: AbortSignal,\n\tbeforeRequest?: BeforeTraceUploadRequest,\n): Promise<Response> {\n\tfor (let attempt = 0; ; attempt += 1) {\n\t\tlet retryDelayMs: number | undefined;\n\t\ttry {\n\t\t\tawait beforeRequest?.();\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow signal.reason ?? new Error(\"Trace upload cancelled\");\n\t\t\t}\n\t\t\tconst response = await fetchWithTimeout(fetchFn, url, init, timeoutMs, signal);\n\t\t\tif (attempt >= TRACE_UPLOAD_MAX_RETRIES || !RETRIABLE_HTTP_STATUSES.has(response.status)) {\n\t\t\t\treturn response;\n\t\t\t}\n\t\t\tif (response.status === 503) {\n\t\t\t\tretryDelayMs = retryAfterDelay(response);\n\t\t\t}\n\t\t\tawait response.body?.cancel().catch(() => undefined);\n\t\t} catch (error) {\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow signal.reason ?? error;\n\t\t\t}\n\t\t\tif (attempt >= TRACE_UPLOAD_MAX_RETRIES || !isRetriableNetworkError(error)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\n\t\tawait delay(retryDelayMs ?? traceUploadRetryDelay(attempt), signal);\n\t\tif (signal?.aborted) {\n\t\t\tthrow signal.reason ?? new Error(\"Trace upload cancelled\");\n\t\t}\n\t}\n}\n\nfunction traceContentPreview(body: string, maxChars: number): { content: string; truncated: boolean } {\n\tif (body.length <= maxChars) {\n\t\treturn { content: body.trimEnd(), truncated: false };\n\t}\n\tconst marker = \"\\n... middle of trace omitted ...\\n\";\n\tconst available = Math.max(0, maxChars - marker.length);\n\tconst headChars = Math.ceil(available / 2);\n\tconst tailChars = Math.floor(available / 2);\n\treturn {\n\t\tcontent: `${body.slice(0, headChars).trimEnd()}${marker}${body.slice(body.length - tailChars).trimStart()}`,\n\t\ttruncated: true,\n\t};\n}\n\nexport async function previewAgentTraceFile(options: AgentTracePreviewOptions): Promise<AgentTracePreviewResult> {\n\tif (!options.sessionFile) {\n\t\treturn { status: \"no_session_file\" };\n\t}\n\n\tlet fileSize: number;\n\ttry {\n\t\tconst stats = await stat(options.sessionFile);\n\t\tif (!stats.isFile()) {\n\t\t\treturn { status: \"no_session_file\" };\n\t\t}\n\t\tfileSize = stats.size;\n\t} catch {\n\t\treturn { status: \"no_session_file\" };\n\t}\n\tif (fileSize === 0) {\n\t\treturn { status: \"empty_session\" };\n\t}\n\n\tconst header = readSessionHeader(options.sessionFile);\n\tif (!header) {\n\t\treturn { status: \"invalid_session\", message: \"Session file is missing a valid session header\" };\n\t}\n\n\tlet body = \"\";\n\tif (fileSize <= MAX_TRACE_BYTES) {\n\t\ttry {\n\t\t\tbody = await readSessionTraceBody(options.sessionFile);\n\t\t} catch (error) {\n\t\t\treturn { status: \"failed\", message: describeError(error) };\n\t\t}\n\t\tif (!body.trim()) {\n\t\t\treturn { status: \"empty_session\" };\n\t\t}\n\t}\n\n\tconst traceContext = resolveTraceContext(options.sessionFile, header);\n\tconst baseUrl = resolvePrimeAgentTracesBaseUrl(options.baseUrl);\n\tconst git = body ? activeGitContext(body, header) : header.git;\n\tconst preview = body\n\t\t? traceContentPreview(body, Math.max(256, options.maxContentChars ?? TRACE_PREVIEW_MAX_CHARS))\n\t\t: { content: \"\", truncated: true };\n\treturn {\n\t\tstatus: \"ready\",\n\t\tsessionFile: options.sessionFile,\n\t\tsessionId: header.id,\n\t\ttraceId: traceContext.traceId,\n\t\tparentSessionId: traceContext.parentSessionId,\n\t\tcwd: header.cwd,\n\t\tsize: fileSize,\n\t\tmaxBytes: MAX_TRACE_BYTES,\n\t\tuploadable: fileSize <= MAX_TRACE_BYTES && baseUrl !== undefined,\n\t\tendpoint: baseUrl ? `${baseUrl}/api/v1/agent-traces/sessions/${encodeURIComponent(header.id)}` : undefined,\n\t\tgitRepo: git?.repoUrl,\n\t\tgitCommit: git?.commit,\n\t\tcontentPreview: preview.content,\n\t\ttruncated: preview.truncated,\n\t};\n}\n\nasync function findSessionFilesUnder(root: string, files: Set<string>): Promise<void> {\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = await readdir(root, { withFileTypes: true });\n\t} catch {\n\t\treturn;\n\t}\n\n\tfor (const entry of entries) {\n\t\tconst entryPath = join(root, entry.name);\n\t\tif (entry.isDirectory()) {\n\t\t\tawait findSessionFilesUnder(entryPath, files);\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.isFile() && entry.name.endsWith(\".jsonl\") && readSessionHeader(entryPath)) {\n\t\t\tfiles.add(entryPath);\n\t\t}\n\t}\n}\n\nexport async function findAgentTraceFiles(sessionDir: string = getSessionsDir()): Promise<string[]> {\n\tconst files = new Set<string>();\n\tconst roots = new Set([resolve(sessionDir), resolve(getSessionArtifactsRoot(sessionDir))]);\n\tawait Promise.all([...roots].map((root) => findSessionFilesUnder(root, files)));\n\treturn [...files].sort();\n}\n\nfunction createTraceUploadAllRequestGate(signal?: AbortSignal): BeforeTraceUploadRequest {\n\tlet nextRequestAt = 0;\n\tlet queue = Promise.resolve();\n\treturn () => {\n\t\tconst slot = queue.then(async () => {\n\t\t\tconst waitMs = Math.max(0, nextRequestAt - Date.now());\n\t\t\tif (waitMs > 0) {\n\t\t\t\tawait delay(waitMs, signal);\n\t\t\t}\n\t\t\tif (!signal?.aborted) {\n\t\t\t\tnextRequestAt = Date.now() + TRACE_UPLOAD_ALL_MIN_REQUEST_INTERVAL_MS;\n\t\t\t}\n\t\t});\n\t\tqueue = slot.catch(() => undefined);\n\t\treturn slot;\n\t};\n}\n\nexport async function uploadAllAgentTraces(options: AgentTraceUploadAllOptions): Promise<AgentTraceUploadAllResult> {\n\tconst { sessionDir, concurrency, onProgress, ...uploadOptions } = options;\n\tconst sessionFiles = await findAgentTraceFiles(sessionDir);\n\ttype UploadResultItem = AgentTraceUploadAllResult[\"results\"][number];\n\tconst results: Array<UploadResultItem | undefined> = new Array(sessionFiles.length);\n\tlet cursor = 0;\n\tlet completed = 0;\n\tconst beforeRequest = createTraceUploadAllRequestGate(uploadOptions.signal);\n\tonProgress?.({ completed, total: sessionFiles.length });\n\n\tconst worker = async () => {\n\t\twhile (true) {\n\t\t\tif (uploadOptions.signal?.aborted) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst index = cursor;\n\t\t\tcursor += 1;\n\t\t\tconst sessionFile = sessionFiles[index];\n\t\t\tif (!sessionFile) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst result = await uploadAgentTraceFileWithRequestGate(\n\t\t\t\t{\n\t\t\t\t\t...uploadOptions,\n\t\t\t\t\tsessionFile,\n\t\t\t\t\treloadConfig: false,\n\t\t\t\t},\n\t\t\t\tbeforeRequest,\n\t\t\t);\n\t\t\tif (uploadOptions.signal?.aborted && result.status === \"failed\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tresults[index] = { sessionFile, result };\n\t\t\tcompleted += 1;\n\t\t\tonProgress?.({ completed, total: sessionFiles.length, sessionFile, result });\n\t\t}\n\t};\n\n\tconst requestedConcurrency = concurrency ?? TRACE_UPLOAD_ALL_CONCURRENCY;\n\tconst normalizedConcurrency =\n\t\tNumber.isFinite(requestedConcurrency) && requestedConcurrency > 0\n\t\t\t? Math.max(1, Math.floor(requestedConcurrency))\n\t\t\t: TRACE_UPLOAD_ALL_CONCURRENCY;\n\tconst workerCount = Math.min(sessionFiles.length, normalizedConcurrency);\n\tawait Promise.all(Array.from({ length: workerCount }, worker));\n\n\tconst completedResults = results.filter((item): item is UploadResultItem => item !== undefined);\n\tlet uploaded = 0;\n\tlet failed = 0;\n\tlet bytesStored = 0;\n\tfor (const item of completedResults) {\n\t\tif (item.result.status === \"uploaded\") {\n\t\t\tuploaded += 1;\n\t\t\tbytesStored += item.result.bytesStored;\n\t\t} else if (item.result.status === \"failed\") {\n\t\t\tfailed += 1;\n\t\t}\n\t}\n\treturn {\n\t\ttotal: sessionFiles.length,\n\t\tuploaded,\n\t\tfailed,\n\t\tskipped: sessionFiles.length - uploaded - failed,\n\t\tbytesStored,\n\t\tresults: completedResults,\n\t};\n}\n\ninterface AgentTraceUploadedSignature {\n\tsize: number;\n\tmtimeMs: number;\n}\n\nexport const SEMANTIC_EDGES_OUTBOX_KIND = \"semantic-edges\";\n\nexport interface AgentTraceCatchUpResult {\n\tpruned: number;\n\t/** Registered semantic-edge ledgers with bytes beyond their cursor; no delivery endpoint exists yet. */\n\tsemanticEdgeLedgersPending: number;\n\tresults: Array<{ sessionFile: string; result: AgentTraceUploadResult }>;\n}\n\nfunction getAgentTraceOutboxDir(): string {\n\treturn assertProductStatePath(join(getAgentDir(), \"trace-export-outbox\"));\n}\n\n// One entry file per session file (keyed by path hash): concurrent writers cannot lose each other's cursors, and a bad read costs only its own entry.\nfunction agentTraceOutboxEntryPath(sessionFile: string): string {\n\tconst key = createHash(\"sha256\").update(sessionFile).digest(\"hex\").slice(0, 32);\n\treturn join(getAgentTraceOutboxDir(), `${key}.json`);\n}\n\nfunction parseOutboxEntry(raw: string):\n\t| {\n\t\t\tsessionFile: string;\n\t\t\tkind?: string;\n\t\t\tuploaded: AgentTraceUploadedSignature | null;\n\t\t\tuploadedBytes?: number;\n\t }\n\t| undefined {\n\tconst parsed = parseResponseObject(raw);\n\tif (!parsed || typeof parsed.sessionFile !== \"string\") {\n\t\treturn undefined;\n\t}\n\tconst uploaded =\n\t\ttypeof parsed.size === \"number\" && typeof parsed.mtimeMs === \"number\"\n\t\t\t? { size: parsed.size, mtimeMs: parsed.mtimeMs }\n\t\t\t: null;\n\treturn {\n\t\tsessionFile: parsed.sessionFile,\n\t\tkind: typeof parsed.kind === \"string\" ? parsed.kind : undefined,\n\t\tuploaded,\n\t\tuploadedBytes: typeof parsed.uploadedBytes === \"number\" ? parsed.uploadedBytes : undefined,\n\t};\n}\n\n/** `undefined` = no usable cursor; `null` = scheduled but never uploaded. */\nasync function readAgentTraceOutboxEntry(sessionFile: string): Promise<AgentTraceUploadedSignature | null | undefined> {\n\tlet raw: string;\n\ttry {\n\t\traw = await readFile(agentTraceOutboxEntryPath(sessionFile), \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\tconst entry = parseOutboxEntry(raw);\n\treturn entry && entry.sessionFile === sessionFile ? entry.uploaded : undefined;\n}\n\nfunction signatureEquals(a: AgentTraceUploadedSignature | null | undefined, b: AgentTraceUploadedSignature): boolean {\n\treturn a != null && a.size === b.size && a.mtimeMs === b.mtimeMs;\n}\n\n/** Session files with a live upload controller in this process; catch-up leaves them to their controller. */\nconst locallyManagedSessionFiles = new Set<string>();\n\n/** Best-effort and synchronous: upload intent must be on disk the moment the transcript persist returns. */\nfunction markAgentTraceOutboxPendingSync(sessionFile: string, kind?: string): boolean {\n\ttry {\n\t\tconst entryPath = agentTraceOutboxEntryPath(sessionFile);\n\t\tif (existsSync(entryPath)) {\n\t\t\treturn true;\n\t\t}\n\t\tmkdirSync(getAgentTraceOutboxDir(), { recursive: true });\n\t\tconst tempPath = `${entryPath}.${process.pid}.${randomUUID()}.tmp`;\n\t\twriteFileSync(\n\t\t\ttempPath,\n\t\t\t`${JSON.stringify(kind === undefined ? { sessionFile } : { sessionFile, kind })}\\n`,\n\t\t\t\"utf8\",\n\t\t);\n\t\trenameSync(tempPath, entryPath);\n\t\treturn true;\n\t} catch {\n\t\t// A broken agent dir must not break session persists.\n\t\treturn false;\n\t}\n}\n\nasync function recordAgentTraceOutboxUpload(\n\tsessionFile: string,\n\tsignature: AgentTraceUploadedSignature,\n): Promise<void> {\n\tconst entryPath = agentTraceOutboxEntryPath(sessionFile);\n\tawait mkdir(getAgentTraceOutboxDir(), { recursive: true });\n\tconst tempPath = `${entryPath}.${process.pid}.${randomUUID()}.tmp`;\n\tawait writeFile(tempPath, `${JSON.stringify({ sessionFile, ...signature })}\\n`, \"utf8\");\n\tawait rename(tempPath, entryPath);\n}\n\n/**\n * Explicit catch-up for the Base Context export outbox. This is never run at\n * startup and does not read inherited agent-traces-outbox entries.\n */\nexport async function catchUpAgentTraceUploads(\n\toptions: Omit<AgentTraceUploadOptions, \"sessionFile\">,\n): Promise<AgentTraceCatchUpResult> {\n\tconst catchUp: AgentTraceCatchUpResult = { pruned: 0, semanticEdgeLedgersPending: 0, results: [] };\n\tif (options.requireEnabled !== false && !(await getAgentTracesEnabled(options))) {\n\t\treturn catchUp;\n\t}\n\tlet entryNames: string[];\n\ttry {\n\t\tentryNames = await readdir(getAgentTraceOutboxDir());\n\t} catch {\n\t\treturn catchUp;\n\t}\n\tconst beforeRequest = createTraceUploadAllRequestGate(options.signal);\n\tfor (const entryName of entryNames) {\n\t\tif (options.signal?.aborted) {\n\t\t\tbreak;\n\t\t}\n\t\tif (!entryName.endsWith(\".json\")) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst entryPath = join(getAgentTraceOutboxDir(), entryName);\n\t\tlet raw: string;\n\t\ttry {\n\t\t\traw = await readFile(entryPath, \"utf8\");\n\t\t} catch {\n\t\t\t// Transient read error: keep the entry and retry at the next startup.\n\t\t\tcontinue;\n\t\t}\n\t\tconst entry = parseOutboxEntry(raw);\n\t\tif (!entry) {\n\t\t\tawait unlink(entryPath).catch(() => undefined);\n\t\t\tcatchUp.pruned += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.kind === SEMANTIC_EDGES_OUTBOX_KIND) {\n\t\t\tlet ledgerStats: Awaited<ReturnType<typeof stat>>;\n\t\t\ttry {\n\t\t\t\tledgerStats = await stat(entry.sessionFile);\n\t\t\t} catch (error) {\n\t\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\t\tawait unlink(entryPath).catch(() => undefined);\n\t\t\t\t\tcatchUp.pruned += 1;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!ledgerStats.isFile()) {\n\t\t\t\tawait unlink(entryPath).catch(() => undefined);\n\t\t\t\tcatchUp.pruned += 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Append-only byte cursor: a ledger whose size equals its delivered offset has nothing new.\n\t\t\tif (ledgerStats.size === entry.uploadedBytes) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// No delivery endpoint exists yet (verifiers#2449 consumes edges in-band over ACP\n\t\t\t// metadata; the trace server has no semantic-edges route). The delta and cursor stay\n\t\t\t// untouched so the first real sender delivers the whole backlog.\n\t\t\tcatchUp.semanticEdgeLedgersPending += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (entry.kind !== undefined) {\n\t\t\t// A newer build may register kinds this one cannot deliver; leave their cursors alone.\n\t\t\tcontinue;\n\t\t}\n\t\tif (locallyManagedSessionFiles.has(entry.sessionFile)) {\n\t\t\tcontinue;\n\t\t}\n\t\tlet stats: Awaited<ReturnType<typeof stat>>;\n\t\ttry {\n\t\t\tstats = await stat(entry.sessionFile);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\tawait unlink(entryPath).catch(() => undefined);\n\t\t\t\tcatchUp.pruned += 1;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (!stats.isFile()) {\n\t\t\tawait unlink(entryPath).catch(() => undefined);\n\t\t\tcatchUp.pruned += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (signatureEquals(entry.uploaded, { size: stats.size, mtimeMs: stats.mtimeMs })) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst result = await uploadAgentTraceFileWithRequestGate(\n\t\t\t{ ...options, sessionFile: entry.sessionFile, reloadConfig: false },\n\t\t\tbeforeRequest,\n\t\t);\n\t\tcatchUp.results.push({ sessionFile: entry.sessionFile, result });\n\t}\n\treturn catchUp;\n}\n\nexport async function getPrimeAgentTraceCredential(\n\tauthStorage: AuthStorage,\n\toptions: { reloadAuth?: boolean; configPath?: string } = {},\n): Promise<AgentTraceCredential | undefined> {\n\tconst traceEnvKey = stringEnv(\"BASE_CONTEXT_TRACES_API_KEY\");\n\tif (traceEnvKey) {\n\t\treturn { apiKey: traceEnvKey, source: \"environment\", label: \"BASE_CONTEXT_TRACES_API_KEY\" };\n\t}\n\n\tif (options.reloadAuth !== false) {\n\t\tauthStorage.reload();\n\t}\n\n\tconst traceKey = await authStorage.getApiKey(BASE_CONTEXT_TRACES_PROVIDER_ID, { includeFallback: false });\n\tif (traceKey) {\n\t\treturn { apiKey: traceKey, source: \"stored\", label: \"Base Context Traces credential\" };\n\t}\n\n\treturn undefined;\n}\n\nasync function getAgentTracesEnabled(\n\toptions: Pick<AgentTraceUploadOptions, \"reloadConfig\" | \"settingsManager\">,\n): Promise<boolean> {\n\tif (options.reloadConfig !== false) {\n\t\tawait options.settingsManager.reload().catch(() => undefined);\n\t}\n\treturn options.settingsManager.getAgentTracesEnabled();\n}\n\nasync function uploadAgentTraceFileWithRequestGate(\n\toptions: AgentTraceUploadOptions,\n\tbeforeRequest?: BeforeTraceUploadRequest,\n): Promise<AgentTraceUploadResult> {\n\tconst result = await performAgentTraceUpload(options, beforeRequest);\n\tlogAgentTraceOutcome(options.sessionFile, result);\n\treturn result;\n}\n\nexport function uploadAgentTraceFile(options: AgentTraceUploadOptions): Promise<AgentTraceUploadResult> {\n\treturn uploadAgentTraceFileWithRequestGate(options);\n}\n\nfunction logAgentTraceOutcome(sessionFile: string | undefined, result: AgentTraceUploadResult): void {\n\tlet line: string | undefined;\n\tswitch (result.status) {\n\t\tcase \"uploaded\":\n\t\t\tline = `uploaded session ${result.sessionId} (${result.bytesStored} bytes)`;\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tline = `upload failed${result.statusCode ? ` (HTTP ${result.statusCode})` : \"\"}: ${result.message}`;\n\t\t\tbreak;\n\t\tcase \"too_large\":\n\t\t\tline = `upload skipped: session is ${result.size} bytes (limit ${result.maxBytes})`;\n\t\t\tbreak;\n\t\tcase \"invalid_session\":\n\t\t\tline = `upload skipped: ${result.message}`;\n\t\t\tbreak;\n\t\tcase \"missing_credentials\":\n\t\t\tline = \"upload skipped: configure a dedicated BASE_CONTEXT_TRACES_API_KEY or Base Context Traces credential\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn;\n\t}\n\tconst suffix = sessionFile ? ` [${sessionFile}]` : \"\";\n\tappendRotatingLog(getAgentTracesLogPath(), `[${new Date().toISOString()}] ${line}${suffix}`);\n}\n\nasync function performAgentTraceUpload(\n\toptions: AgentTraceUploadOptions,\n\tbeforeRequest?: BeforeTraceUploadRequest,\n): Promise<AgentTraceUploadResult> {\n\tconst requireEnabled = options.requireEnabled !== false;\n\tif (requireEnabled && !(await getAgentTracesEnabled(options))) {\n\t\treturn { status: \"disabled\" };\n\t}\n\tconst baseUrl = resolvePrimeAgentTracesBaseUrl(options.baseUrl);\n\tif (!baseUrl) {\n\t\treturn {\n\t\t\tstatus: \"failed\",\n\t\t\tmessage:\n\t\t\t\t\"Trace export requires an explicit BASE_CONTEXT_TRACES_BASE_URL and dedicated BASE_CONTEXT_TRACES_API_KEY.\",\n\t\t};\n\t}\n\tif (!options.sessionFile) {\n\t\treturn { status: \"no_session_file\" };\n\t}\n\n\tlet signature: AgentTraceUploadedSignature;\n\ttry {\n\t\tconst stats = await stat(options.sessionFile);\n\t\tif (!stats.isFile()) {\n\t\t\treturn { status: \"no_session_file\" };\n\t\t}\n\t\tsignature = { size: stats.size, mtimeMs: stats.mtimeMs };\n\t} catch {\n\t\treturn { status: \"no_session_file\" };\n\t}\n\tconst fileSize = signature.size;\n\tif (fileSize === 0) {\n\t\treturn { status: \"empty_session\" };\n\t}\n\tif (fileSize > MAX_TRACE_BYTES) {\n\t\treturn { status: \"too_large\", size: fileSize, maxBytes: MAX_TRACE_BYTES };\n\t}\n\t// Cursor invariant: an automatic upload never re-sends a file whose content already matches its uploaded cursor.\n\tif (requireEnabled && signatureEquals(await readAgentTraceOutboxEntry(options.sessionFile), signature)) {\n\t\treturn { status: \"unchanged\" };\n\t}\n\n\tconst header = readSessionHeader(options.sessionFile);\n\tif (!header) {\n\t\treturn { status: \"invalid_session\", message: \"Session file is missing a valid session header\" };\n\t}\n\n\tconst credential = await getPrimeAgentTraceCredential(options.authStorage, {\n\t\tconfigPath: options.configPath,\n\t\treloadAuth: options.reloadConfig !== false,\n\t});\n\tif (!credential) {\n\t\treturn { status: \"missing_credentials\" };\n\t}\n\n\tif (requireEnabled && !(await getAgentTracesEnabled(options))) {\n\t\treturn { status: \"disabled\" };\n\t}\n\n\tlet body: string;\n\ttry {\n\t\tbody = await readSessionTraceBody(options.sessionFile);\n\t} catch (error) {\n\t\treturn { status: \"failed\", message: describeError(error) };\n\t}\n\tif (!body.trim()) {\n\t\treturn { status: \"empty_session\" };\n\t}\n\n\tconst traceContext = resolveTraceContext(options.sessionFile, header);\n\tconst bodyBytes = Buffer.byteLength(body, \"utf8\");\n\tconst headers: Record<string, string> = {\n\t\tAuthorization: `Bearer ${credential.apiKey}`,\n\t\t\"Content-Type\": \"application/x-ndjson\",\n\t\tAccept: \"application/json\",\n\t\t\"X-Trace-Id\": traceContext.traceId,\n\t\t\"X-Cwd\": header.cwd,\n\t\t\"X-Agent-Version\": VERSION,\n\t};\n\tif (traceContext.parentSessionId) {\n\t\theaders[\"X-Parent-Session\"] = traceContext.parentSessionId;\n\t}\n\tconst git = activeGitContext(body, header);\n\tif (git?.repoUrl) {\n\t\theaders[\"X-Git-Repo\"] = git.repoUrl;\n\t}\n\tif (git?.commit) {\n\t\theaders[\"X-Git-Commit\"] = git.commit;\n\t}\n\n\tif (requireEnabled && !(await getAgentTracesEnabled(options))) {\n\t\treturn { status: \"disabled\" };\n\t}\n\n\tconst url = `${baseUrl}/api/v1/agent-traces/sessions/${encodeURIComponent(header.id)}`;\n\tconst fetchFn = options.fetchFn ?? fetch;\n\n\tlet response: Response;\n\ttry {\n\t\tresponse = await fetchWithRetry(\n\t\t\tfetchFn,\n\t\t\turl,\n\t\t\t{\n\t\t\t\tmethod: \"PUT\",\n\t\t\t\tredirect: \"error\",\n\t\t\t\theaders,\n\t\t\t\tbody,\n\t\t\t},\n\t\t\toptions.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS,\n\t\t\toptions.signal,\n\t\t\tbeforeRequest,\n\t\t);\n\t} catch (error) {\n\t\treturn { status: \"failed\", message: describeError(error) };\n\t}\n\n\tif (!response.ok) {\n\t\treturn {\n\t\t\tstatus: \"failed\",\n\t\t\tstatusCode: response.status,\n\t\t\tmessage: await readResponseMessage(response),\n\t\t\tretryAfterMs: retryAfterDelay(response, MAX_TIMER_DELAY_MS),\n\t\t};\n\t}\n\n\tconst responseText = await response.text().catch(() => \"\");\n\tconst responseData = parseResponseObject(responseText);\n\ttry {\n\t\tawait recordAgentTraceOutboxUpload(options.sessionFile, signature);\n\t} catch (error) {\n\t\treturn { status: \"failed\", message: `stored, but recording the upload cursor failed: ${describeError(error)}` };\n\t}\n\treturn {\n\t\tstatus: \"uploaded\",\n\t\tsessionId: responseData ? (stringField(responseData, \"session_id\") ?? header.id) : header.id,\n\t\ttraceId: responseData ? (stringField(responseData, \"trace_id\") ?? traceContext.traceId) : traceContext.traceId,\n\t\tbytesStored: responseData ? (numberField(responseData, \"bytes_stored\") ?? bodyBytes) : bodyBytes,\n\t\tkey: responseData ? stringField(responseData, \"key\") : undefined,\n\t};\n}\n\nexport function uploadAgentTraceSession(options: AgentTraceSessionUploadOptions): Promise<AgentTraceUploadResult> {\n\treturn uploadAgentTraceFile({\n\t\t...options,\n\t\tsessionFile: options.sessionManager.getSessionFile(),\n\t});\n}\n\nclass AgentTraceUploadController {\n\tprivate timeout: NodeJS.Timeout | undefined;\n\tprivate pending = false;\n\tprivate inFlight: Promise<void> | undefined;\n\tprivate lastUploadStartedAt: number | undefined;\n\tprivate notBeforeAt = 0;\n\n\tconstructor(\n\t\tprivate readonly sessionManager: SessionManager,\n\t\tprivate options: AgentTraceUploadInstallOptions,\n\t) {}\n\n\tupdate(options: AgentTraceUploadInstallOptions): void {\n\t\tthis.options = options;\n\t}\n\n\tschedule = (): void => {\n\t\tif (\n\t\t\t!this.options.settingsManager.getAgentTracesEnabled() ||\n\t\t\t!resolvePrimeAgentTracesBaseUrl(this.options.baseUrl)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tthis.pending = true;\n\t\t// Intent is consent-gated at persist time: an entry created while sharing\n\t\t// is off would turn a later enable into retroactive collection of\n\t\t// opted-out sessions. Marking re-runs every persist (existsSync-cheap),\n\t\t// so an entry pruned by a racing catch-up is re-registered.\n\t\tif (this.options.settingsManager.getAgentTracesEnabled()) {\n\t\t\tconst sessionFile = this.sessionManager.getSessionFile();\n\t\t\tif (\n\t\t\t\tsessionFile &&\n\t\t\t\t!locallyManagedSessionFiles.has(sessionFile) &&\n\t\t\t\tmarkAgentTraceOutboxPendingSync(sessionFile)\n\t\t\t) {\n\t\t\t\tlocallyManagedSessionFiles.add(sessionFile);\n\t\t\t}\n\t\t\tconst ledgerPath = this.options.semanticEdgesLedgerPath;\n\t\t\tif (ledgerPath) {\n\t\t\t\tmarkAgentTraceOutboxPendingSync(ledgerPath, SEMANTIC_EDGES_OUTBOX_KIND);\n\t\t\t}\n\t\t}\n\t\tthis.arm();\n\t};\n\n\tprivate arm(): void {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t}\n\t\tconst elapsed = this.lastUploadStartedAt === undefined ? undefined : Date.now() - this.lastUploadStartedAt;\n\t\tconst throttleDelay = elapsed === undefined ? 0 : Math.max(0, TRACE_UPLOAD_MIN_INTERVAL_MS - elapsed);\n\t\tconst notBeforeDelay = Math.max(0, this.notBeforeAt - Date.now());\n\t\tthis.timeout = setTimeout(\n\t\t\t() => {\n\t\t\t\tthis.timeout = undefined;\n\t\t\t\tvoid this.runScheduledUpload();\n\t\t\t},\n\t\t\tMath.max(TRACE_UPLOAD_DEBOUNCE_MS, throttleDelay, notBeforeDelay),\n\t\t);\n\t\tthis.timeout.unref();\n\t}\n\n\tprivate async runScheduledUpload(): Promise<void> {\n\t\tif (this.inFlight) {\n\t\t\treturn;\n\t\t}\n\t\tthis.pending = false;\n\t\tthis.lastUploadStartedAt = Date.now();\n\t\tthis.inFlight = uploadAgentTraceSession({\n\t\t\t...this.options,\n\t\t\tsessionManager: this.sessionManager,\n\t\t}).then(\n\t\t\t(result) => {\n\t\t\t\tif (result.status === \"failed\" && isRescheduledUploadFailure(result.statusCode)) {\n\t\t\t\t\tthis.pending = true;\n\t\t\t\t\tif (result.retryAfterMs !== undefined) {\n\t\t\t\t\t\tthis.notBeforeAt = Date.now() + result.retryAfterMs;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => undefined,\n\t\t);\n\t\tawait this.inFlight;\n\t\tthis.inFlight = undefined;\n\t\tif (this.pending) {\n\t\t\tthis.arm();\n\t\t}\n\t}\n}\n\nfunction isRescheduledUploadFailure(statusCode: number | undefined): boolean {\n\treturn statusCode === undefined || statusCode === 429 || RETRIABLE_HTTP_STATUSES.has(statusCode);\n}\n\nconst traceUploadControllers = new WeakMap<SessionManager, AgentTraceUploadController>();\nexport function installAgentTraceUpload(sessionManager: SessionManager, options: AgentTraceUploadInstallOptions): void {\n\t// Enabling export applies to live sessions, not an inherited outbox backlog.\n\tlet controller = traceUploadControllers.get(sessionManager);\n\tif (controller) {\n\t\tcontroller.update(options);\n\t\treturn;\n\t}\n\n\tcontroller = new AgentTraceUploadController(sessionManager, options);\n\ttraceUploadControllers.set(sessionManager, controller);\n\tsessionManager.onPersist(controller.schedule);\n}\n"]}