@caupulican/pi-adaptative 0.81.26 → 0.81.27

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 (229) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +3 -2
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/core/agent-session-services.d.ts +4 -0
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +19 -2
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +21 -10
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +71 -33
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  14. package/dist/core/autonomy/bounded-completion.js +59 -19
  15. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  16. package/dist/core/autonomy/lane-tool-surface.d.ts +31 -0
  17. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -0
  18. package/dist/core/autonomy/lane-tool-surface.js +96 -0
  19. package/dist/core/autonomy/lane-tool-surface.js.map +1 -0
  20. package/dist/core/background-lane-controller.d.ts +6 -3
  21. package/dist/core/background-lane-controller.d.ts.map +1 -1
  22. package/dist/core/background-lane-controller.js +134 -25
  23. package/dist/core/background-lane-controller.js.map +1 -1
  24. package/dist/core/billing-failover-controller.d.ts.map +1 -1
  25. package/dist/core/billing-failover-controller.js +1 -1
  26. package/dist/core/billing-failover-controller.js.map +1 -1
  27. package/dist/core/context/current-work-memory.d.ts +9 -0
  28. package/dist/core/context/current-work-memory.d.ts.map +1 -0
  29. package/dist/core/context/current-work-memory.js +61 -0
  30. package/dist/core/context/current-work-memory.js.map +1 -0
  31. package/dist/core/context/file-store-memory-provider.d.ts +9 -0
  32. package/dist/core/context/file-store-memory-provider.d.ts.map +1 -0
  33. package/dist/core/context/file-store-memory-provider.js +106 -0
  34. package/dist/core/context/file-store-memory-provider.js.map +1 -0
  35. package/dist/core/context/long-term-memory-trigger.d.ts +15 -0
  36. package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -0
  37. package/dist/core/context/long-term-memory-trigger.js +39 -0
  38. package/dist/core/context/long-term-memory-trigger.js.map +1 -0
  39. package/dist/core/context/memory-prompt-block.d.ts +3 -1
  40. package/dist/core/context/memory-prompt-block.d.ts.map +1 -1
  41. package/dist/core/context/memory-prompt-block.js +20 -3
  42. package/dist/core/context/memory-prompt-block.js.map +1 -1
  43. package/dist/core/context/memory-prompt-budget.d.ts +17 -0
  44. package/dist/core/context/memory-prompt-budget.d.ts.map +1 -0
  45. package/dist/core/context/memory-prompt-budget.js +64 -0
  46. package/dist/core/context/memory-prompt-budget.js.map +1 -0
  47. package/dist/core/context/memory-provider-contract.d.ts.map +1 -1
  48. package/dist/core/context/memory-provider-contract.js +2 -5
  49. package/dist/core/context/memory-provider-contract.js.map +1 -1
  50. package/dist/core/context/memory-tier-composer.d.ts +27 -0
  51. package/dist/core/context/memory-tier-composer.d.ts.map +1 -0
  52. package/dist/core/context/memory-tier-composer.js +79 -0
  53. package/dist/core/context/memory-tier-composer.js.map +1 -0
  54. package/dist/core/context-gc.d.ts.map +1 -1
  55. package/dist/core/context-gc.js +1 -0
  56. package/dist/core/context-gc.js.map +1 -1
  57. package/dist/core/cost/cost-summary.d.ts +3 -1
  58. package/dist/core/cost/cost-summary.d.ts.map +1 -1
  59. package/dist/core/cost/cost-summary.js +3 -3
  60. package/dist/core/cost/cost-summary.js.map +1 -1
  61. package/dist/core/cost-guard.d.ts +17 -7
  62. package/dist/core/cost-guard.d.ts.map +1 -1
  63. package/dist/core/cost-guard.js +33 -10
  64. package/dist/core/cost-guard.js.map +1 -1
  65. package/dist/core/default-tool-surface.d.ts +9 -0
  66. package/dist/core/default-tool-surface.d.ts.map +1 -0
  67. package/dist/core/default-tool-surface.js +18 -0
  68. package/dist/core/default-tool-surface.js.map +1 -0
  69. package/dist/core/delegation/worker-runner.d.ts +11 -5
  70. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  71. package/dist/core/delegation/worker-runner.js +24 -12
  72. package/dist/core/delegation/worker-runner.js.map +1 -1
  73. package/dist/core/extensions/loader.d.ts.map +1 -1
  74. package/dist/core/extensions/loader.js +15 -0
  75. package/dist/core/extensions/loader.js.map +1 -1
  76. package/dist/core/extensions/runner.d.ts +2 -0
  77. package/dist/core/extensions/runner.d.ts.map +1 -1
  78. package/dist/core/extensions/runner.js +7 -0
  79. package/dist/core/extensions/runner.js.map +1 -1
  80. package/dist/core/extensions/types.d.ts +9 -0
  81. package/dist/core/extensions/types.d.ts.map +1 -1
  82. package/dist/core/extensions/types.js.map +1 -1
  83. package/dist/core/failure-corpus.d.ts.map +1 -1
  84. package/dist/core/failure-corpus.js +2 -4
  85. package/dist/core/failure-corpus.js.map +1 -1
  86. package/dist/core/footer-data-provider.d.ts.map +1 -1
  87. package/dist/core/footer-data-provider.js +9 -1
  88. package/dist/core/footer-data-provider.js.map +1 -1
  89. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  90. package/dist/core/goals/goal-continuation-prompt.js +4 -3
  91. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  92. package/dist/core/http-dispatcher.d.ts +12 -0
  93. package/dist/core/http-dispatcher.d.ts.map +1 -1
  94. package/dist/core/http-dispatcher.js +42 -4
  95. package/dist/core/http-dispatcher.js.map +1 -1
  96. package/dist/core/memory/memory-manager.d.ts +8 -3
  97. package/dist/core/memory/memory-manager.d.ts.map +1 -1
  98. package/dist/core/memory/memory-manager.js +27 -11
  99. package/dist/core/memory/memory-manager.js.map +1 -1
  100. package/dist/core/memory/memory-provider.d.ts +5 -1
  101. package/dist/core/memory/memory-provider.d.ts.map +1 -1
  102. package/dist/core/memory/memory-provider.js.map +1 -1
  103. package/dist/core/memory/providers/file-store.d.ts +3 -1
  104. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  105. package/dist/core/memory/providers/file-store.js +7 -2
  106. package/dist/core/memory/providers/file-store.js.map +1 -1
  107. package/dist/core/memory/providers/transcript-recall.d.ts +1 -0
  108. package/dist/core/memory/providers/transcript-recall.d.ts.map +1 -1
  109. package/dist/core/memory/providers/transcript-recall.js +4 -7
  110. package/dist/core/memory/providers/transcript-recall.js.map +1 -1
  111. package/dist/core/memory-controller.d.ts +36 -10
  112. package/dist/core/memory-controller.d.ts.map +1 -1
  113. package/dist/core/memory-controller.js +137 -21
  114. package/dist/core/memory-controller.js.map +1 -1
  115. package/dist/core/model-registry.d.ts +23 -0
  116. package/dist/core/model-registry.d.ts.map +1 -1
  117. package/dist/core/model-registry.js +87 -8
  118. package/dist/core/model-registry.js.map +1 -1
  119. package/dist/core/model-resolver.d.ts.map +1 -1
  120. package/dist/core/model-resolver.js +30 -11
  121. package/dist/core/model-resolver.js.map +1 -1
  122. package/dist/core/model-router-controller.d.ts.map +1 -1
  123. package/dist/core/model-router-controller.js +33 -29
  124. package/dist/core/model-router-controller.js.map +1 -1
  125. package/dist/core/model-selection-controller.d.ts +3 -0
  126. package/dist/core/model-selection-controller.d.ts.map +1 -1
  127. package/dist/core/model-selection-controller.js +8 -1
  128. package/dist/core/model-selection-controller.js.map +1 -1
  129. package/dist/core/models/perf-profile.d.ts.map +1 -1
  130. package/dist/core/models/perf-profile.js +95 -33
  131. package/dist/core/models/perf-profile.js.map +1 -1
  132. package/dist/core/profile-filter-controller.d.ts +11 -0
  133. package/dist/core/profile-filter-controller.d.ts.map +1 -1
  134. package/dist/core/profile-filter-controller.js +56 -11
  135. package/dist/core/profile-filter-controller.js.map +1 -1
  136. package/dist/core/profile-registry.d.ts +2 -2
  137. package/dist/core/profile-registry.d.ts.map +1 -1
  138. package/dist/core/profile-registry.js +58 -7
  139. package/dist/core/profile-registry.js.map +1 -1
  140. package/dist/core/reflection-controller.d.ts +7 -7
  141. package/dist/core/reflection-controller.d.ts.map +1 -1
  142. package/dist/core/reflection-controller.js +78 -12
  143. package/dist/core/reflection-controller.js.map +1 -1
  144. package/dist/core/research/research-runner.d.ts +2 -2
  145. package/dist/core/research/research-runner.d.ts.map +1 -1
  146. package/dist/core/research/research-runner.js +8 -6
  147. package/dist/core/research/research-runner.js.map +1 -1
  148. package/dist/core/resource-loader.d.ts +4 -2
  149. package/dist/core/resource-loader.d.ts.map +1 -1
  150. package/dist/core/resource-loader.js +11 -1
  151. package/dist/core/resource-loader.js.map +1 -1
  152. package/dist/core/runtime-builder.d.ts +13 -1
  153. package/dist/core/runtime-builder.d.ts.map +1 -1
  154. package/dist/core/runtime-builder.js +127 -35
  155. package/dist/core/runtime-builder.js.map +1 -1
  156. package/dist/core/sdk.d.ts +8 -7
  157. package/dist/core/sdk.d.ts.map +1 -1
  158. package/dist/core/sdk.js +35 -5
  159. package/dist/core/sdk.js.map +1 -1
  160. package/dist/core/security/secret-text.d.ts +3 -0
  161. package/dist/core/security/secret-text.d.ts.map +1 -0
  162. package/dist/core/security/secret-text.js +26 -0
  163. package/dist/core/security/secret-text.js.map +1 -0
  164. package/dist/core/settings-manager.d.ts +59 -17
  165. package/dist/core/settings-manager.d.ts.map +1 -1
  166. package/dist/core/settings-manager.js +300 -51
  167. package/dist/core/settings-manager.js.map +1 -1
  168. package/dist/core/skills.d.ts.map +1 -1
  169. package/dist/core/skills.js +1 -1
  170. package/dist/core/skills.js.map +1 -1
  171. package/dist/core/system-prompt-builder.d.ts +7 -0
  172. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  173. package/dist/core/system-prompt-builder.js +23 -1
  174. package/dist/core/system-prompt-builder.js.map +1 -1
  175. package/dist/core/tools/delegate.d.ts.map +1 -1
  176. package/dist/core/tools/delegate.js +15 -11
  177. package/dist/core/tools/delegate.js.map +1 -1
  178. package/dist/main.d.ts.map +1 -1
  179. package/dist/main.js +2 -0
  180. package/dist/main.js.map +1 -1
  181. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  182. package/dist/modes/interactive/components/footer.js +8 -1
  183. package/dist/modes/interactive/components/footer.js.map +1 -1
  184. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  185. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  186. package/dist/modes/interactive/components/settings-selector.js +69 -29
  187. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  188. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  189. package/dist/modes/interactive/components/thinking-selector.js +2 -0
  190. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  191. package/dist/modes/interactive/config-backup.d.ts +7 -1
  192. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  193. package/dist/modes/interactive/config-backup.js +196 -32
  194. package/dist/modes/interactive/config-backup.js.map +1 -1
  195. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  196. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  197. package/dist/modes/interactive/interactive-mode.js +10 -28
  198. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  199. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  200. package/dist/modes/interactive/local-model-commands.js +2 -0
  201. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  202. package/dist/modes/interactive/profile-menu-controller.d.ts +6 -4
  203. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  204. package/dist/modes/interactive/profile-menu-controller.js +248 -65
  205. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  206. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  207. package/dist/modes/interactive/report-commands.js +8 -2
  208. package/dist/modes/interactive/report-commands.js.map +1 -1
  209. package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
  210. package/dist/modes/interactive/settings-selector-flow.js +8 -0
  211. package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
  212. package/dist/modes/interactive/theme/theme-schema.json +1 -1
  213. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  214. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  215. package/dist/modes/interactive/theme/theme.js +3 -1
  216. package/dist/modes/interactive/theme/theme.js.map +1 -1
  217. package/docs/extensions.md +32 -0
  218. package/docs/resources.md +15 -7
  219. package/docs/settings.md +54 -5
  220. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  221. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  222. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  223. package/examples/extensions/preset.ts +2 -2
  224. package/examples/extensions/sandbox/package-lock.json +2 -2
  225. package/examples/extensions/sandbox/package.json +1 -1
  226. package/examples/extensions/with-deps/package-lock.json +2 -2
  227. package/examples/extensions/with-deps/package.json +1 -1
  228. package/npm-shrinkwrap.json +44 -35
  229. package/package.json +4 -4
@@ -34,7 +34,7 @@
34
34
  */
35
35
  import { existsSync, readFileSync } from "node:fs";
36
36
  import { isAbsolute, join } from "node:path";
37
- import { resetApiProviders } from "@caupulican/pi-ai";
37
+ import { DEFAULT_ACTIVE_TOOL_NAMES } from "./default-tool-surface.js";
38
38
  import { createCoreDiagnosticsToolDefinitions } from "./extensions/builtin.js";
39
39
  import { ExtensionRunner, wrapRegisteredTools, } from "./extensions/index.js";
40
40
  import { disposeExtensionEventSubscriptions } from "./extensions/loader.js";
@@ -68,6 +68,8 @@ export class RuntimeBuilder {
68
68
  _toolPromptSnippets = new Map();
69
69
  _toolPromptGuidelines = new Map();
70
70
  _baseToolDefinitions = new Map();
71
+ _reloadPromise;
72
+ _reloadRequested = false;
71
73
  deps;
72
74
  constructor(deps) {
73
75
  this.deps = deps;
@@ -201,26 +203,26 @@ export class RuntimeBuilder {
201
203
  this._toolRegistry = toolRegistry;
202
204
  const requestedBase = options?.activeToolNames ? [...options.activeToolNames] : [...previousActiveToolNames];
203
205
  const nextActiveToolNames = requestedBase.filter((name) => isAllowedTool(name));
204
- const autoActivated = [];
206
+ const persistentAutoActivated = [];
205
207
  if (allowedToolNames) {
206
208
  for (const toolName of this._toolRegistry.keys()) {
207
209
  if (allowedToolNames.has(toolName)) {
208
210
  nextActiveToolNames.push(toolName);
209
- autoActivated.push(toolName);
211
+ persistentAutoActivated.push(toolName);
210
212
  }
211
213
  }
212
214
  }
213
215
  else if (options?.includeAllExtensionTools) {
214
216
  for (const tool of wrappedExtensionTools) {
215
217
  nextActiveToolNames.push(tool.name);
216
- autoActivated.push(tool.name);
218
+ persistentAutoActivated.push(tool.name);
217
219
  }
218
220
  }
219
221
  else if (!options?.activeToolNames) {
220
222
  for (const toolName of this._toolRegistry.keys()) {
221
223
  if (!previousRegistryNames.has(toolName)) {
222
224
  nextActiveToolNames.push(toolName);
223
- autoActivated.push(toolName);
225
+ persistentAutoActivated.push(toolName);
224
226
  }
225
227
  }
226
228
  }
@@ -242,7 +244,6 @@ export class RuntimeBuilder {
242
244
  }
243
245
  if (matchesResourceProfilePattern(toolName, explicitAllowPatterns)) {
244
246
  nextActiveToolNames.push(toolName);
245
- autoActivated.push(toolName);
246
247
  }
247
248
  }
248
249
  // G13: an explicit grant that binds to NO registered tool is a silent no-op — typo'd
@@ -260,14 +261,29 @@ export class RuntimeBuilder {
260
261
  // guarantee, not just this settings/profile refresh flow.
261
262
  this.deps.setActiveToolsByName([...new Set(nextActiveToolNames)]);
262
263
  // setActiveToolsByName just stored the profile-filtered ACTIVE set as the request; restore
263
- // the true pre-filter request (plus this refresh's auto-activations) so an internal refresh
264
- // can never permanently narrow it.
265
- this.deps.setRequestedActiveToolNames([...new Set([...requestedBase, ...autoActivated])]);
264
+ // the true pre-filter request (plus non-profile auto-activations) so an internal refresh can
265
+ // never permanently narrow it. Explicit profile grants are generation-local activations: if
266
+ // profile A names grep and profile B later grants "*", A must not pin grep into B's request.
267
+ this.deps.setRequestedActiveToolNames([...new Set([...requestedBase, ...persistentAutoActivated])]);
266
268
  }
267
269
  _createReloadRuntimeSnapshot() {
268
270
  const agent = this.deps.getAgent();
271
+ const toolProfileFilter = this.deps.getToolProfileFilter();
272
+ const requestedActiveToolNames = this.deps.getRequestedActiveToolNames();
269
273
  return {
270
274
  extensionRunner: this.deps.getExtensionRunner(),
275
+ settings: this.deps.getSettingsManager().createReloadSnapshot(),
276
+ modelRegistry: this.deps.getModelRegistry().createReloadSnapshot(),
277
+ model: agent.state.model,
278
+ thinkingLevel: agent.state.thinkingLevel,
279
+ sessionLeafId: this.deps.getSessionManager().getLeafId(),
280
+ toolProfileFilter: toolProfileFilter
281
+ ? { allow: [...toolProfileFilter.allow], block: [...toolProfileFilter.block] }
282
+ : undefined,
283
+ requestedActiveToolNames: requestedActiveToolNames ? [...requestedActiveToolNames] : undefined,
284
+ memory: this.deps.createMemoryReloadSnapshot(),
285
+ profileFilter: this.deps.createProfileFilterReloadSnapshot(),
286
+ unboundToolGrantWarnings: [...this.deps.getUnboundToolGrantWarnings()],
271
287
  baseToolDefinitions: this._baseToolDefinitions,
272
288
  toolRegistry: this._toolRegistry,
273
289
  toolDefinitions: this._toolDefinitions,
@@ -279,6 +295,15 @@ export class RuntimeBuilder {
279
295
  };
280
296
  }
281
297
  _restoreReloadRuntimeSnapshot(snapshot) {
298
+ this.deps.getSettingsManager().restoreReloadSnapshot(snapshot.settings);
299
+ this.deps.getModelRegistry().restoreReloadSnapshot(snapshot.modelRegistry);
300
+ this.deps.setToolProfileFilter(snapshot.toolProfileFilter
301
+ ? { allow: [...snapshot.toolProfileFilter.allow], block: [...snapshot.toolProfileFilter.block] }
302
+ : undefined);
303
+ this.deps.setRequestedActiveToolNames(snapshot.requestedActiveToolNames ? [...snapshot.requestedActiveToolNames] : undefined);
304
+ this.deps.restoreMemoryReloadSnapshot(snapshot.memory);
305
+ this.deps.restoreProfileFilterReloadSnapshot(snapshot.profileFilter);
306
+ this.deps.setUnboundToolGrantWarnings([...snapshot.unboundToolGrantWarnings]);
282
307
  // setExtensionRunner restores both _extensionRunner and _extensionRunnerRef.current together
283
308
  // (the same field-then-ref pair the host wrote at every assignment site); nothing reads the
284
309
  // ref between the two writes, so folding them is unobservable.
@@ -289,9 +314,18 @@ export class RuntimeBuilder {
289
314
  this._toolPromptSnippets = snapshot.toolPromptSnippets;
290
315
  this._toolPromptGuidelines = snapshot.toolPromptGuidelines;
291
316
  const agent = this.deps.getAgent();
317
+ agent.state.model = snapshot.model;
318
+ agent.state.thinkingLevel = snapshot.thinkingLevel;
292
319
  agent.state.tools = snapshot.agentTools;
293
320
  agent.state.systemPrompt = snapshot.agentSystemPrompt;
294
321
  this.deps.setBaseSystemPrompt(snapshot.baseSystemPrompt);
322
+ const sessionManager = this.deps.getSessionManager();
323
+ if (snapshot.sessionLeafId === null) {
324
+ sessionManager.resetLeaf();
325
+ }
326
+ else {
327
+ sessionManager.branch(snapshot.sessionLeafId);
328
+ }
295
329
  this.deps.applyExtensionBindings(snapshot.extensionRunner);
296
330
  }
297
331
  _doctorReloadRuntime() {
@@ -408,29 +442,60 @@ export class RuntimeBuilder {
408
442
  extensionsResult.runtime.pendingProviderRegistrations =
409
443
  extensionsResult.runtime.pendingProviderRegistrations.filter((registration) => runtimeExtensionPaths.has(registration.extensionPath));
410
444
  const runner = new ExtensionRunner(extensions, extensionsResult.runtime, this.deps.getCwd(), this.deps.getSessionManager(), this.deps.getModelRegistry());
445
+ const offBuildErrors = options.onError ? runner.onError(options.onError) : undefined;
411
446
  this.deps.setExtensionRunner(runner);
412
- this.deps.bindExtensionCore(runner);
447
+ try {
448
+ this.deps.bindExtensionCore(runner);
449
+ }
450
+ catch (error) {
451
+ offBuildErrors?.();
452
+ throw error;
453
+ }
413
454
  this.deps.applyExtensionBindings(runner);
414
455
  const defaultActiveToolNames = baseToolsOverride
415
456
  ? Object.keys(baseToolsOverride)
416
- : [
417
- "read",
418
- "bash",
419
- "edit",
420
- "write",
421
- "context_audit",
422
- "goal",
423
- "delegate",
424
- "run_toolkit_script",
425
- ...(settingsManager.getScoutSettings().enabled ? ["context_scout"] : []),
426
- ];
457
+ : [...DEFAULT_ACTIVE_TOOL_NAMES, ...(settingsManager.getScoutSettings().enabled ? ["context_scout"] : [])];
427
458
  const baseActiveToolNames = options.activeToolNames ?? defaultActiveToolNames;
428
459
  this.refreshToolRegistry({
429
460
  activeToolNames: baseActiveToolNames,
430
461
  includeAllExtensionTools: options.includeAllExtensionTools,
431
462
  });
463
+ return offBuildErrors;
464
+ }
465
+ reload() {
466
+ if (this._reloadPromise) {
467
+ // State can change while a generation is being validated. Coalesce any number of
468
+ // overlapping requests into one follow-up generation instead of silently treating a
469
+ // later profile/settings mutation as part of the already-snapshotted generation.
470
+ this._reloadRequested = true;
471
+ return this._reloadPromise;
472
+ }
473
+ const reloadPromise = this._drainReloadRequests();
474
+ this._reloadPromise = reloadPromise;
475
+ return reloadPromise;
476
+ }
477
+ async _drainReloadRequests() {
478
+ let finalError;
479
+ try {
480
+ do {
481
+ this._reloadRequested = false;
482
+ try {
483
+ await this._reloadOnce();
484
+ finalError = undefined;
485
+ }
486
+ catch (error) {
487
+ finalError = error;
488
+ }
489
+ } while (this._reloadRequested);
490
+ if (finalError !== undefined)
491
+ throw finalError;
492
+ }
493
+ finally {
494
+ this._reloadPromise = undefined;
495
+ this._reloadRequested = false;
496
+ }
432
497
  }
433
- async reload() {
498
+ async _reloadOnce() {
434
499
  if (this.deps.isStreaming()) {
435
500
  throw new Error("Cannot reload while the agent is streaming or a tool call is active");
436
501
  }
@@ -443,8 +508,10 @@ export class RuntimeBuilder {
443
508
  // active set — otherwise a reload under a small model permanently shrinks the restorable set.
444
509
  const activeToolNames = this.deps.getRequestedActiveToolNames() ?? this.deps.getActiveToolNames();
445
510
  const previousFlagValues = previousRunner.getFlagValues();
511
+ const previousExtensionProviderNames = previousRunner.getRegisteredProviderNames();
446
512
  const reloadErrors = [];
447
513
  let newRunner;
514
+ let offReloadErrors;
448
515
  try {
449
516
  await this.deps.getSettingsManager().reload();
450
517
  // Re-derive the resource-profile tool filter from the freshly reloaded settings.
@@ -453,21 +520,30 @@ export class RuntimeBuilder {
453
520
  // active profile's tools allow/block — or switching the active profile — would not
454
521
  // apply on /reload and allowed tools would stay missing.
455
522
  this.deps.setToolProfileFilter(this.deps.deriveToolProfileFilter());
456
- // Re-apply the active profile's model/thinking from the freshly reloaded settings, so a live
457
- // profile edit (or switch) takes effect on /reload. Skipped when the launch used an explicit
458
- // --model/--thinking flag, which must win over the profile across reloads.
459
- await this.deps.reapplyActiveProfileModelSettings();
460
- await this.deps.getResourceLoader().reload({ failOnExtensionErrors: true, deferExtensionDispose: true });
461
- resetApiProviders();
462
- this.buildRuntime({
523
+ // Resource discovery must consume this exact settings generation. A second settings read here
524
+ // could combine profile state from one generation with resources from the next.
525
+ await this.deps.getResourceLoader().reload({
526
+ failOnExtensionErrors: true,
527
+ deferExtensionDispose: true,
528
+ skipSettingsReload: true,
529
+ });
530
+ // Replace the previous extension-owned provider generation before binding the new one. The
531
+ // bulk refresh also preserves API/OAuth streams for surviving non-extension providers.
532
+ this.deps.getModelRegistry().unregisterProviders(previousExtensionProviderNames);
533
+ offReloadErrors = this.buildRuntime({
463
534
  activeToolNames,
464
535
  flagValues: previousFlagValues,
465
536
  includeAllExtensionTools: true,
537
+ onError: (error) => {
538
+ reloadErrors.push(`${error.extensionPath} ${error.event}: ${error.error}`);
539
+ },
466
540
  });
467
541
  newRunner = this.deps.getExtensionRunner();
468
- const offDoctorErrors = newRunner.onError((error) => {
469
- reloadErrors.push(`${error.extensionPath} ${error.event}: ${error.error}`);
470
- });
542
+ // Extensions are now bound and their queued providers/models are registered, so a profile may
543
+ // safely select a model contributed by an extension granted in that same profile generation.
544
+ await this.deps.reapplyActiveProfileModelSettings();
545
+ // Model capability and system-prompt/tool exposure must reflect the newly selected model.
546
+ this.refreshToolRegistry({ activeToolNames, includeAllExtensionTools: true });
471
547
  try {
472
548
  this._doctorReloadRuntime();
473
549
  // Reload starts memory providers fresh; loaded extensions re-register below.
@@ -483,22 +559,24 @@ export class RuntimeBuilder {
483
559
  }
484
560
  }
485
561
  finally {
486
- offDoctorErrors();
562
+ offReloadErrors?.();
563
+ offReloadErrors = undefined;
487
564
  }
488
565
  if (reloadErrors.length > 0) {
489
566
  throw new Error(`Extension reload failed doctor: ${reloadErrors.slice(0, 6).join("; ")}`);
490
567
  }
491
568
  await emitSessionShutdownEvent(previousRunner, { type: "session_shutdown", reason: "reload" });
492
569
  previousRunner.invalidate();
493
- this.deps.getResourceLoader().commitReload?.();
570
+ await this.deps.getResourceLoader().commitReload?.();
494
571
  // Re-derive the memory subsystem from the reloaded settings/providers.
495
572
  await this.deps.initializeMemory();
496
573
  }
497
574
  catch (error) {
575
+ offReloadErrors?.();
498
576
  if (newRunner && newRunner !== previousRunner) {
499
577
  newRunner.invalidate("This extension ctx was discarded because reload failed and Pi restored the previous valid runtime.");
500
578
  }
501
- this.deps.getResourceLoader().rollbackReload?.();
579
+ await this.deps.getResourceLoader().rollbackReload?.();
502
580
  this._restoreReloadRuntimeSnapshot(snapshot);
503
581
  throw error;
504
582
  }
@@ -526,6 +604,8 @@ export class RuntimeBuilder {
526
604
  await this.deps.getExtensionRunner().emitToExtension(ext, { type: "session_shutdown", reason: "unload" });
527
605
  // Unregister its providers (keyed by the extension's own path, as registered)
528
606
  const runtime = this.deps.getResourceLoader().getExtensions().runtime;
607
+ const ownedMemoryProviders = runtime.memoryProvidersByExtension.get(ext.path) ?? new Set();
608
+ const ownedContextMemoryProviders = runtime.contextMemoryProvidersByExtension.get(ext.path) ?? new Set();
529
609
  for (const name of runtime.getProvidersForExtension(ext.path)) {
530
610
  runtime.unregisterProvider(name, ext.path);
531
611
  }
@@ -542,6 +622,15 @@ export class RuntimeBuilder {
542
622
  includeAllExtensionTools: true,
543
623
  });
544
624
  previousRunner.invalidate();
625
+ const memorySnapshot = this.deps.createMemoryReloadSnapshot();
626
+ this.deps.restoreMemoryReloadSnapshot({
627
+ ...memorySnapshot,
628
+ pendingMemoryProviders: memorySnapshot.pendingMemoryProviders.filter((provider) => !ownedMemoryProviders.has(provider)),
629
+ pendingContextMemoryProviders: memorySnapshot.pendingContextMemoryProviders.filter((provider) => !ownedContextMemoryProviders.has(provider)),
630
+ });
631
+ runtime.memoryProvidersByExtension.delete(ext.path);
632
+ runtime.contextMemoryProvidersByExtension.delete(ext.path);
633
+ await this.deps.initializeMemory();
545
634
  // Notify extensions-changed listeners
546
635
  this.deps.notifyExtensionsChanged();
547
636
  }
@@ -586,6 +675,9 @@ export class RuntimeBuilder {
586
675
  });
587
676
  // Run session_start lifecycle for the new extension only
588
677
  await this.deps.getExtensionRunner().emitToExtension(extension, { type: "session_start", reason: "load" });
678
+ // Activate newly registered legacy/context providers immediately. Reinitialization also
679
+ // refreshes provider tools and preserves all providers owned by existing extensions.
680
+ await this.deps.initializeMemory();
589
681
  // Notify extensions-changed listeners
590
682
  this.deps.notifyExtensionsChanged();
591
683
  }