@vybestack/llxprt-code-core 0.4.8 → 0.5.0-nightly.251102.e5b51aa3

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 (213) hide show
  1. package/dist/prompt-config/defaults/default-prompts.json +4 -17
  2. package/dist/src/auth/precedence.d.ts +69 -9
  3. package/dist/src/auth/precedence.js +467 -69
  4. package/dist/src/auth/precedence.js.map +1 -1
  5. package/dist/src/auth/types.d.ts +2 -2
  6. package/dist/src/config/config.d.ts +15 -1
  7. package/dist/src/config/config.js +118 -6
  8. package/dist/src/config/config.js.map +1 -1
  9. package/dist/src/config/index.d.ts +6 -0
  10. package/dist/src/config/index.js +5 -0
  11. package/dist/src/config/index.js.map +1 -1
  12. package/dist/src/config/profileManager.d.ts +23 -3
  13. package/dist/src/config/profileManager.js +54 -7
  14. package/dist/src/config/profileManager.js.map +1 -1
  15. package/dist/src/config/subagentManager.d.ts +96 -0
  16. package/dist/src/config/subagentManager.js +371 -0
  17. package/dist/src/config/subagentManager.js.map +1 -0
  18. package/dist/src/config/types.d.ts +18 -0
  19. package/dist/src/config/types.js +3 -0
  20. package/dist/src/config/types.js.map +1 -0
  21. package/dist/src/core/client.d.ts +27 -7
  22. package/dist/src/core/client.js +231 -55
  23. package/dist/src/core/client.js.map +1 -1
  24. package/dist/src/core/contentGenerator.d.ts +3 -1
  25. package/dist/src/core/contentGenerator.js +3 -0
  26. package/dist/src/core/contentGenerator.js.map +1 -1
  27. package/dist/src/core/coreToolScheduler.d.ts +1 -5
  28. package/dist/src/core/coreToolScheduler.js +95 -23
  29. package/dist/src/core/coreToolScheduler.js.map +1 -1
  30. package/dist/src/core/geminiChat.d.ts +42 -12
  31. package/dist/src/core/geminiChat.js +405 -205
  32. package/dist/src/core/geminiChat.js.map +1 -1
  33. package/dist/src/core/nonInteractiveToolExecutor.d.ts +3 -2
  34. package/dist/src/core/nonInteractiveToolExecutor.js +94 -10
  35. package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
  36. package/dist/src/core/subagent.d.ts +86 -7
  37. package/dist/src/core/subagent.js +809 -79
  38. package/dist/src/core/subagent.js.map +1 -1
  39. package/dist/src/core/subagentOrchestrator.d.ts +73 -0
  40. package/dist/src/core/subagentOrchestrator.js +387 -0
  41. package/dist/src/core/subagentOrchestrator.js.map +1 -0
  42. package/dist/src/core/subagentScheduler.d.ts +16 -0
  43. package/dist/src/core/subagentScheduler.js +7 -0
  44. package/dist/src/core/subagentScheduler.js.map +1 -0
  45. package/dist/src/core/turn.d.ts +5 -1
  46. package/dist/src/core/turn.js +5 -1
  47. package/dist/src/core/turn.js.map +1 -1
  48. package/dist/src/hooks/tool-render-suppression-hook.js +6 -1
  49. package/dist/src/hooks/tool-render-suppression-hook.js.map +1 -1
  50. package/dist/src/ide/ideContext.d.ts +32 -32
  51. package/dist/src/index.d.ts +19 -1
  52. package/dist/src/index.js +15 -2
  53. package/dist/src/index.js.map +1 -1
  54. package/dist/src/interfaces/index.d.ts +1 -0
  55. package/dist/src/interfaces/index.js +4 -0
  56. package/dist/src/interfaces/index.js.map +1 -0
  57. package/dist/src/interfaces/nodejs-error.interface.d.ts +4 -0
  58. package/dist/src/interfaces/nodejs-error.interface.js +2 -0
  59. package/dist/src/interfaces/nodejs-error.interface.js.map +1 -0
  60. package/dist/src/parsers/TextToolCallParser.d.ts +17 -1
  61. package/dist/src/parsers/TextToolCallParser.js +542 -148
  62. package/dist/src/parsers/TextToolCallParser.js.map +1 -1
  63. package/dist/src/prompt-config/defaults/core.md +15 -0
  64. package/dist/src/prompt-config/defaults/providers/gemini/core.md +203 -119
  65. package/dist/src/prompt-config/defaults/tool-defaults.js +2 -0
  66. package/dist/src/prompt-config/defaults/tool-defaults.js.map +1 -1
  67. package/dist/src/prompt-config/defaults/tools/list-subagents.md +7 -0
  68. package/dist/src/prompt-config/defaults/tools/task.md +8 -0
  69. package/dist/src/providers/BaseProvider.d.ts +115 -30
  70. package/dist/src/providers/BaseProvider.js +445 -109
  71. package/dist/src/providers/BaseProvider.js.map +1 -1
  72. package/dist/src/providers/IProvider.d.ts +50 -18
  73. package/dist/src/providers/LoggingProviderWrapper.d.ts +60 -16
  74. package/dist/src/providers/LoggingProviderWrapper.js +213 -60
  75. package/dist/src/providers/LoggingProviderWrapper.js.map +1 -1
  76. package/dist/src/providers/ProviderManager.d.ts +73 -2
  77. package/dist/src/providers/ProviderManager.js +492 -40
  78. package/dist/src/providers/ProviderManager.js.map +1 -1
  79. package/dist/src/providers/anthropic/AnthropicProvider.d.ts +35 -38
  80. package/dist/src/providers/anthropic/AnthropicProvider.js +222 -227
  81. package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
  82. package/dist/src/providers/errors.d.ts +86 -0
  83. package/dist/src/providers/errors.js +89 -0
  84. package/dist/src/providers/errors.js.map +1 -1
  85. package/dist/src/providers/gemini/GeminiProvider.d.ts +101 -41
  86. package/dist/src/providers/gemini/GeminiProvider.js +386 -311
  87. package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
  88. package/dist/src/providers/openai/ConversationCache.d.ts +5 -3
  89. package/dist/src/providers/openai/ConversationCache.js +93 -32
  90. package/dist/src/providers/openai/ConversationCache.js.map +1 -1
  91. package/dist/src/providers/openai/OpenAIProvider.d.ts +82 -42
  92. package/dist/src/providers/openai/OpenAIProvider.js +391 -536
  93. package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
  94. package/dist/src/providers/openai/getOpenAIProviderInfo.d.ts +1 -1
  95. package/dist/src/providers/openai/getOpenAIProviderInfo.js +52 -22
  96. package/dist/src/providers/openai/getOpenAIProviderInfo.js.map +1 -1
  97. package/dist/src/providers/openai/openaiRequestParams.d.ts +7 -0
  98. package/dist/src/providers/openai/openaiRequestParams.js +66 -0
  99. package/dist/src/providers/openai/openaiRequestParams.js.map +1 -0
  100. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.d.ts +6 -33
  101. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js +84 -183
  102. package/dist/src/providers/openai-responses/OpenAIResponsesProvider.js.map +1 -1
  103. package/dist/src/providers/types/providerRuntime.d.ts +17 -0
  104. package/dist/src/providers/types/providerRuntime.js +7 -0
  105. package/dist/src/providers/types/providerRuntime.js.map +1 -0
  106. package/dist/src/providers/utils/authToken.d.ts +12 -0
  107. package/dist/src/providers/utils/authToken.js +17 -0
  108. package/dist/src/providers/utils/authToken.js.map +1 -0
  109. package/dist/src/providers/utils/userMemory.d.ts +8 -0
  110. package/dist/src/providers/utils/userMemory.js +34 -0
  111. package/dist/src/providers/utils/userMemory.js.map +1 -0
  112. package/dist/src/runtime/AgentRuntimeContext.d.ts +213 -0
  113. package/dist/src/runtime/AgentRuntimeContext.js +17 -0
  114. package/dist/src/runtime/AgentRuntimeContext.js.map +1 -0
  115. package/dist/src/runtime/AgentRuntimeLoader.d.ts +47 -0
  116. package/dist/src/runtime/AgentRuntimeLoader.js +122 -0
  117. package/dist/src/runtime/AgentRuntimeLoader.js.map +1 -0
  118. package/dist/src/runtime/AgentRuntimeState.d.ts +232 -0
  119. package/dist/src/runtime/AgentRuntimeState.js +439 -0
  120. package/dist/src/runtime/AgentRuntimeState.js.map +1 -0
  121. package/dist/src/runtime/RuntimeInvocationContext.d.ts +51 -0
  122. package/dist/src/runtime/RuntimeInvocationContext.js +52 -0
  123. package/dist/src/runtime/RuntimeInvocationContext.js.map +1 -0
  124. package/dist/src/runtime/createAgentRuntimeContext.d.ts +7 -0
  125. package/dist/src/runtime/createAgentRuntimeContext.js +65 -0
  126. package/dist/src/runtime/createAgentRuntimeContext.js.map +1 -0
  127. package/dist/src/runtime/index.d.ts +13 -0
  128. package/dist/src/runtime/index.js +14 -0
  129. package/dist/src/runtime/index.js.map +1 -0
  130. package/dist/src/runtime/providerRuntimeContext.d.ts +30 -0
  131. package/dist/src/runtime/providerRuntimeContext.js +70 -0
  132. package/dist/src/runtime/providerRuntimeContext.js.map +1 -0
  133. package/dist/src/runtime/runtimeAdapters.d.ts +22 -0
  134. package/dist/src/runtime/runtimeAdapters.js +81 -0
  135. package/dist/src/runtime/runtimeAdapters.js.map +1 -0
  136. package/dist/src/runtime/runtimeStateFactory.d.ts +21 -0
  137. package/dist/src/runtime/runtimeStateFactory.js +104 -0
  138. package/dist/src/runtime/runtimeStateFactory.js.map +1 -0
  139. package/dist/src/services/todo-context-tracker.d.ts +10 -8
  140. package/dist/src/services/todo-context-tracker.js +26 -10
  141. package/dist/src/services/todo-context-tracker.js.map +1 -1
  142. package/dist/src/services/tool-call-tracker-service.d.ts +11 -7
  143. package/dist/src/services/tool-call-tracker-service.js +89 -29
  144. package/dist/src/services/tool-call-tracker-service.js.map +1 -1
  145. package/dist/src/settings/SettingsService.d.ts +4 -0
  146. package/dist/src/settings/SettingsService.js +65 -2
  147. package/dist/src/settings/SettingsService.js.map +1 -1
  148. package/dist/src/settings/settingsServiceInstance.d.ts +6 -1
  149. package/dist/src/settings/settingsServiceInstance.js +28 -8
  150. package/dist/src/settings/settingsServiceInstance.js.map +1 -1
  151. package/dist/src/telemetry/loggers.d.ts +5 -1
  152. package/dist/src/telemetry/loggers.js.map +1 -1
  153. package/dist/src/telemetry/loggers.test.circular.js +4 -0
  154. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  155. package/dist/src/telemetry/metrics.d.ts +3 -1
  156. package/dist/src/telemetry/metrics.js.map +1 -1
  157. package/dist/src/telemetry/types.d.ts +1 -0
  158. package/dist/src/telemetry/types.js +3 -0
  159. package/dist/src/telemetry/types.js.map +1 -1
  160. package/dist/src/test-utils/index.d.ts +2 -0
  161. package/dist/src/test-utils/index.js +2 -0
  162. package/dist/src/test-utils/index.js.map +1 -1
  163. package/dist/src/test-utils/mockWorkspaceContext.d.ts +0 -3
  164. package/dist/src/test-utils/mockWorkspaceContext.js +3 -4
  165. package/dist/src/test-utils/mockWorkspaceContext.js.map +1 -1
  166. package/dist/src/test-utils/providerCallOptions.d.ts +43 -0
  167. package/dist/src/test-utils/providerCallOptions.js +137 -0
  168. package/dist/src/test-utils/providerCallOptions.js.map +1 -0
  169. package/dist/src/test-utils/runtime.d.ts +92 -0
  170. package/dist/src/test-utils/runtime.js +226 -0
  171. package/dist/src/test-utils/runtime.js.map +1 -0
  172. package/dist/src/test-utils/tools.d.ts +4 -4
  173. package/dist/src/test-utils/tools.js +20 -10
  174. package/dist/src/test-utils/tools.js.map +1 -1
  175. package/dist/src/tools/list-subagents.d.ts +31 -0
  176. package/dist/src/tools/list-subagents.js +109 -0
  177. package/dist/src/tools/list-subagents.js.map +1 -0
  178. package/dist/src/tools/task.d.ts +87 -0
  179. package/dist/src/tools/task.js +427 -0
  180. package/dist/src/tools/task.js.map +1 -0
  181. package/dist/src/tools/todo-read.js +1 -1
  182. package/dist/src/tools/todo-read.js.map +1 -1
  183. package/dist/src/tools/todo-store.js +4 -2
  184. package/dist/src/tools/todo-store.js.map +1 -1
  185. package/dist/src/tools/todo-write.js +4 -2
  186. package/dist/src/tools/todo-write.js.map +1 -1
  187. package/dist/src/tools/tool-error.d.ts +1 -0
  188. package/dist/src/tools/tool-error.js +1 -0
  189. package/dist/src/tools/tool-error.js.map +1 -1
  190. package/dist/src/tools/tool-registry.d.ts +2 -0
  191. package/dist/src/tools/tool-registry.js +46 -21
  192. package/dist/src/tools/tool-registry.js.map +1 -1
  193. package/dist/src/types/modelParams.d.ts +4 -0
  194. package/dist/src/utils/editor.js +10 -8
  195. package/dist/src/utils/editor.js.map +1 -1
  196. package/dist/src/utils/gitIgnoreParser.js +15 -3
  197. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  198. package/package.json +1 -1
  199. package/dist/src/prompt-config/defaults/providers/anthropic/core.md +0 -97
  200. package/dist/src/prompt-config/defaults/providers/anthropic/tools/glob.md +0 -34
  201. package/dist/src/prompt-config/defaults/providers/anthropic/tools/list-directory.md +0 -11
  202. package/dist/src/prompt-config/defaults/providers/anthropic/tools/read-file.md +0 -14
  203. package/dist/src/prompt-config/defaults/providers/anthropic/tools/read-many-files.md +0 -31
  204. package/dist/src/prompt-config/defaults/providers/anthropic/tools/replace.md +0 -41
  205. package/dist/src/prompt-config/defaults/providers/anthropic/tools/run-shell-command.md +0 -32
  206. package/dist/src/prompt-config/defaults/providers/anthropic/tools/save-memory.md +0 -35
  207. package/dist/src/prompt-config/defaults/providers/anthropic/tools/search-file-content.md +0 -44
  208. package/dist/src/prompt-config/defaults/providers/anthropic/tools/todo-write.md +0 -45
  209. package/dist/src/prompt-config/defaults/providers/anthropic/tools/write-file.md +0 -11
  210. package/dist/src/prompt-config/defaults/providers/openai/core.md +0 -97
  211. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-pause.md +0 -28
  212. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-read.md +0 -5
  213. package/dist/src/prompt-config/defaults/providers/openai/tools/todo-write.md +0 -45
@@ -3,9 +3,18 @@
3
3
  * Copyright 2025 Vybestack LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ /**
7
+ * Base provider class with authentication precedence logic
8
+ */
9
+ import { AsyncLocalStorage } from 'node:async_hooks';
6
10
  import { DebugLogger } from '../debug/index.js';
7
11
  import { AuthPrecedenceResolver, } from '../auth/precedence.js';
12
+ import { peekActiveProviderRuntimeContext, setActiveProviderRuntimeContext, } from '../runtime/providerRuntimeContext.js';
13
+ import { createRuntimeInvocationContext, } from '../runtime/RuntimeInvocationContext.js';
14
+ import { SettingsService } from '../settings/SettingsService.js';
8
15
  import { getSettingsService } from '../settings/settingsServiceInstance.js';
16
+ import { MissingProviderRuntimeError } from './errors.js';
17
+ import { resolveRuntimeAuthToken } from './utils/authToken.js';
9
18
  /**
10
19
  * Abstract base provider class that implements authentication precedence logic
11
20
  * This class provides lazy OAuth triggering and proper authentication precedence
@@ -15,32 +24,87 @@ export class BaseProvider {
15
24
  authResolver;
16
25
  baseProviderConfig;
17
26
  providerConfig;
18
- globalConfig;
19
- cachedAuthToken;
20
- authCacheTimestamp;
21
- AUTH_CACHE_DURATION = 60000; // 1 minute in milliseconds
27
+ /**
28
+ * @plan PLAN-20250218-STATELESSPROVIDER.P05
29
+ * @requirement REQ-SP-001
30
+ * @pseudocode provider-invocation.md lines 8-15
31
+ */
32
+ defaultSettingsService;
33
+ defaultConfig;
34
+ activeCallContext = new AsyncLocalStorage();
22
35
  // Callback for tracking throttle wait times (set by LoggingProviderWrapper)
23
36
  throttleTracker;
24
- constructor(config, providerConfig, globalConfig) {
37
+ get globalConfig() {
38
+ return this.defaultConfig;
39
+ }
40
+ set globalConfig(config) {
41
+ this.defaultConfig = config;
42
+ }
43
+ constructor(config, providerConfig, globalConfig, settingsService) {
25
44
  this.name = config.name;
26
45
  this.baseProviderConfig = config;
27
46
  this.providerConfig = providerConfig;
28
- this.globalConfig = globalConfig;
29
- // If an initial apiKey is provided, store it in SettingsService
30
- // Only store non-empty API keys to ensure proper precedence fallback
31
- if (config.apiKey && config.apiKey.trim() !== '') {
32
- const settingsService = getSettingsService();
33
- settingsService.set('auth-key', config.apiKey);
34
- }
35
- // Initialize auth precedence resolver
36
- // OAuth enablement will be checked dynamically through the manager
47
+ this.defaultConfig = globalConfig;
48
+ let fallbackSettingsService;
49
+ if (settingsService) {
50
+ fallbackSettingsService = settingsService;
51
+ }
52
+ else {
53
+ try {
54
+ fallbackSettingsService = getSettingsService();
55
+ }
56
+ catch {
57
+ fallbackSettingsService = new SettingsService();
58
+ }
59
+ }
60
+ this.defaultSettingsService = fallbackSettingsService;
37
61
  const precedenceConfig = {
62
+ apiKey: config.apiKey,
38
63
  envKeyNames: config.envKeyNames || [],
39
- isOAuthEnabled: config.isOAuthEnabled ?? false, // Use the config value, which can be updated
64
+ isOAuthEnabled: config.isOAuthEnabled ?? false,
40
65
  supportsOAuth: this.supportsOAuth(),
41
66
  oauthProvider: config.oauthProvider,
67
+ providerId: this.name,
42
68
  };
43
- this.authResolver = new AuthPrecedenceResolver(precedenceConfig, config.oauthManager);
69
+ this.authResolver = new AuthPrecedenceResolver(precedenceConfig, config.oauthManager, fallbackSettingsService);
70
+ }
71
+ /**
72
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
73
+ * @plan:PLAN-20251018-STATELESSPROVIDER2.P06
74
+ * @requirement REQ-SP2-001
75
+ * @pseudocode base-provider-call-contract.md lines 1-2
76
+ */
77
+ setRuntimeSettingsService(settingsService) {
78
+ if (!settingsService) {
79
+ return;
80
+ }
81
+ this.defaultSettingsService = settingsService;
82
+ this.authResolver.setSettingsService(settingsService);
83
+ }
84
+ /**
85
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
86
+ * @plan:PLAN-20251023-STATELESS-HARDENING.P05
87
+ * @requirement REQ-SP2-001
88
+ * @requirement:REQ-SP4-001
89
+ * @pseudocode base-provider-call-contract.md lines 1-3
90
+ */
91
+ resolveSettingsService() {
92
+ const activeOptions = this.activeCallContext.getStore();
93
+ if (activeOptions?.settings) {
94
+ return activeOptions.settings;
95
+ }
96
+ if (this.defaultSettingsService) {
97
+ return this.defaultSettingsService;
98
+ }
99
+ throw new MissingProviderRuntimeError({
100
+ providerKey: `BaseProvider.${this.name}`,
101
+ missingFields: ['settings'],
102
+ stage: 'resolveSettingsService',
103
+ metadata: {
104
+ requirement: 'REQ-SP4-001',
105
+ hint: 'Provider runtime guard expects ProviderManager to set runtime settings.',
106
+ },
107
+ });
44
108
  }
45
109
  /**
46
110
  * Set throttle tracking callback (used by LoggingProviderWrapper)
@@ -52,86 +116,95 @@ export class BaseProvider {
52
116
  logger.debug(() => `Throttle tracker set for provider`);
53
117
  }
54
118
  /**
119
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
120
+ * @requirement REQ-SP2-001
121
+ * @pseudocode base-provider-call-contract.md lines 2-3
55
122
  * Gets the base URL with proper precedence:
56
123
  * 1. Ephemeral settings (highest priority - from /baseurl or profile)
57
- * 2. Provider config (from IProviderConfig)
58
- * 3. Base provider config (initial constructor value)
59
- * 4. undefined (use provider default)
124
+ * 2. Provider-specific settings in SettingsService
125
+ * 3. Provider config (from IProviderConfig)
126
+ * 4. Base provider config (initial constructor value)
127
+ * 5. undefined (use provider default)
60
128
  */
61
129
  getBaseURL() {
62
- const settingsService = getSettingsService();
63
- // 1. Check ephemeral settings first (from /baseurl command or profile)
130
+ const activeOptions = this.activeCallContext.getStore();
131
+ if (activeOptions) {
132
+ return activeOptions.resolved.baseURL;
133
+ }
134
+ const settingsService = this.resolveSettingsService();
135
+ return this.computeBaseURL(settingsService);
136
+ }
137
+ /**
138
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
139
+ * @requirement REQ-SP2-001
140
+ * @pseudocode base-provider-call-contract.md lines 2-3
141
+ * Gets the current model with proper precedence:
142
+ * 1. Ephemeral settings (highest priority)
143
+ * 2. Provider-specific settings in SettingsService
144
+ * 3. Provider config
145
+ * 4. Default model
146
+ */
147
+ getModel() {
148
+ const activeOptions = this.activeCallContext.getStore();
149
+ if (activeOptions) {
150
+ return activeOptions.resolved.model;
151
+ }
152
+ const settingsService = this.resolveSettingsService();
153
+ return this.computeModel(settingsService);
154
+ }
155
+ computeBaseURL(settingsService) {
64
156
  const ephemeralBaseUrl = settingsService.get('base-url');
65
157
  if (ephemeralBaseUrl && ephemeralBaseUrl !== 'none') {
66
158
  return ephemeralBaseUrl;
67
159
  }
68
- // 2. Check provider config (from IProviderConfig)
160
+ const providerSettings = settingsService.getProviderSettings(this.name);
161
+ const providerBaseUrl = providerSettings?.baseUrl;
162
+ if (providerBaseUrl && providerBaseUrl !== 'none') {
163
+ return providerBaseUrl;
164
+ }
69
165
  if (this.providerConfig?.baseUrl) {
70
166
  return this.providerConfig.baseUrl;
71
167
  }
72
- // 3. Check base provider config (constructor value)
73
168
  if (this.baseProviderConfig.baseURL) {
74
169
  return this.baseProviderConfig.baseURL;
75
170
  }
76
- // 4. Return undefined to use provider's default
77
171
  return undefined;
78
172
  }
79
- /**
80
- * Gets the current model with proper precedence:
81
- * 1. Ephemeral settings (highest priority)
82
- * 2. Provider-specific settings in SettingsService
83
- * 3. Provider config
84
- * 4. Default model
85
- */
86
- getModel() {
87
- const settingsService = getSettingsService();
88
- // 1. Check ephemeral settings first
173
+ computeModel(settingsService) {
89
174
  const ephemeralModel = settingsService.get('model');
90
175
  if (ephemeralModel) {
91
176
  return ephemeralModel;
92
177
  }
93
- // 2. Check provider-specific settings
94
178
  const providerSettings = settingsService.getProviderSettings(this.name);
95
179
  const providerModel = providerSettings?.model;
96
180
  if (providerModel) {
97
181
  return providerModel;
98
182
  }
99
- // 3. Check provider config
100
183
  if (this.providerConfig?.defaultModel) {
101
184
  return this.providerConfig.defaultModel;
102
185
  }
103
- // 4. Return default
104
186
  return this.getDefaultModel();
105
187
  }
106
188
  /**
189
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
190
+ * @requirement REQ-SP2-001
191
+ * @pseudocode base-provider-call-contract.md lines 1-3
107
192
  * Gets authentication token using the precedence chain
108
193
  * This method implements lazy OAuth triggering - only triggers OAuth when actually making API calls
109
194
  * Returns empty string if no auth is available (for local/self-hosted endpoints)
110
195
  */
111
196
  async getAuthToken() {
112
- // Check cache first (short-lived cache to avoid repeated OAuth calls)
113
- if (this.cachedAuthToken &&
114
- this.authCacheTimestamp &&
115
- Date.now() - this.authCacheTimestamp < this.AUTH_CACHE_DURATION) {
116
- return this.cachedAuthToken;
117
- }
118
- // Clear stale cache
119
- this.cachedAuthToken = undefined;
120
- this.authCacheTimestamp = undefined;
121
- // Resolve authentication using precedence chain
122
- const token = await this.authResolver.resolveAuthentication();
123
- if (!token) {
124
- // Return empty string for local/self-hosted endpoints that don't require auth
125
- // Individual providers can decide how to handle this
126
- return '';
127
- }
128
- // Cache the token briefly
129
- this.cachedAuthToken = token;
130
- this.authCacheTimestamp = Date.now();
131
- if (process.env.DEBUG) {
132
- const authMethod = await this.authResolver.getAuthMethodName();
133
- console.log(`[${this.name}] Authentication resolved using: ${authMethod}`);
197
+ const activeOptions = this.activeCallContext.getStore();
198
+ if (activeOptions) {
199
+ const runtimeToken = await resolveRuntimeAuthToken(activeOptions.resolved.authToken);
200
+ if (runtimeToken) {
201
+ return runtimeToken;
202
+ }
134
203
  }
204
+ const settingsService = this.resolveSettingsService();
205
+ const token = (await this.authResolver.resolveAuthentication({
206
+ settingsService,
207
+ })) ?? '';
135
208
  return token;
136
209
  }
137
210
  /**
@@ -153,63 +226,47 @@ export class BaseProvider {
153
226
  return false;
154
227
  }
155
228
  /**
229
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
230
+ * @requirement REQ-SP2-001
231
+ * @pseudocode base-provider-call-contract.md lines 1-3
156
232
  * Checks if authentication is available without triggering OAuth
157
233
  */
158
234
  async hasNonOAuthAuthentication() {
159
- return this.authResolver.hasNonOAuthAuthentication();
235
+ return this.authResolver.hasNonOAuthAuthentication({
236
+ settingsService: this.resolveSettingsService(),
237
+ });
160
238
  }
161
239
  /**
240
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
241
+ * @requirement REQ-SP2-001
242
+ * @pseudocode base-provider-call-contract.md lines 1-3
162
243
  * Checks if OAuth is the only available authentication method
163
244
  */
164
245
  async isOAuthOnlyAvailable() {
165
- return this.authResolver.isOAuthOnlyAvailable();
246
+ return this.authResolver.isOAuthOnlyAvailable({
247
+ settingsService: this.resolveSettingsService(),
248
+ });
166
249
  }
167
250
  /**
251
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
252
+ * @requirement REQ-SP2-001
253
+ * @pseudocode base-provider-call-contract.md lines 1-3
168
254
  * Gets the current authentication method name for debugging
169
255
  */
170
256
  async getAuthMethodName() {
171
- return this.authResolver.getAuthMethodName();
172
- }
173
- /**
174
- * Updates the API key (used for CLI --key argument and other sources)
175
- */
176
- setApiKey(apiKey) {
177
- const settingsService = getSettingsService();
178
- // CRITICAL FIX: When clearing the key, set to undefined instead of empty string
179
- // This ensures the precedence chain properly skips this level
180
- if (!apiKey || apiKey.trim() === '') {
181
- settingsService.set('auth-key', undefined);
182
- }
183
- else {
184
- settingsService.set('auth-key', apiKey);
185
- }
186
- this.clearAuthCache();
257
+ return this.authResolver.getAuthMethodName({
258
+ settingsService: this.resolveSettingsService(),
259
+ });
187
260
  }
188
261
  /**
189
262
  * Clears authentication (used when removing keys/keyfiles)
190
263
  */
191
264
  clearAuth() {
192
- const settingsService = getSettingsService();
265
+ const settingsService = this.resolveSettingsService();
193
266
  settingsService.set('auth-key', undefined);
194
267
  settingsService.set('auth-keyfile', undefined);
195
268
  this.clearAuthCache();
196
269
  }
197
- /**
198
- * Updates the base URL in ephemeral settings
199
- */
200
- setBaseUrl(baseUrl) {
201
- const settingsService = getSettingsService();
202
- // Store in ephemeral settings as the highest priority source
203
- if (!baseUrl || baseUrl.trim() === '' || baseUrl === 'none') {
204
- // Clear the ephemeral setting
205
- settingsService.set('base-url', undefined);
206
- }
207
- else {
208
- settingsService.set('base-url', baseUrl);
209
- }
210
- // Clear auth cache as base URL change might affect auth
211
- this.clearAuthCache();
212
- }
213
270
  /**
214
271
  * Updates OAuth configuration
215
272
  */
@@ -231,23 +288,300 @@ export class BaseProvider {
231
288
  * Clears the authentication token cache
232
289
  */
233
290
  clearAuthCache() {
234
- this.cachedAuthToken = undefined;
235
- this.authCacheTimestamp = undefined;
291
+ // Legacy no-op retained for compatibility with existing logout flows.
236
292
  }
237
293
  /**
238
294
  * Checks if the provider is authenticated using any available method
239
295
  */
240
296
  async isAuthenticated() {
241
297
  try {
242
- const token = await this.authResolver.resolveAuthentication();
243
- return token !== null;
298
+ const token = (await this.authResolver.resolveAuthentication({
299
+ settingsService: this.resolveSettingsService(),
300
+ })) ?? '';
301
+ return token !== '';
244
302
  }
245
303
  catch {
246
304
  return false;
247
305
  }
248
306
  }
307
+ /**
308
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
309
+ * @requirement REQ-SP2-001
310
+ * @pseudocode base-provider-call-contract.md lines 1-5
311
+ */
312
+ generateChatCompletion(contentsOrOptions, maybeTools) {
313
+ const normalizedPromise = this.normalizeGenerateChatOptions(contentsOrOptions, maybeTools);
314
+ const previousRuntimeContext = peekActiveProviderRuntimeContext();
315
+ let preparedIteratorPromise = null;
316
+ let normalizedOptions;
317
+ let underlyingIterator;
318
+ const prepareIterator = async () => {
319
+ if (!preparedIteratorPromise) {
320
+ preparedIteratorPromise = (async () => {
321
+ normalizedOptions = await normalizedPromise;
322
+ underlyingIterator = this.invokeWithNormalizedOptions(normalizedOptions, previousRuntimeContext ?? null);
323
+ })();
324
+ }
325
+ await preparedIteratorPromise;
326
+ };
327
+ const withContext = (operation) => {
328
+ if (!normalizedOptions) {
329
+ throw new Error('Normalized options are not prepared');
330
+ }
331
+ return this.activeCallContext.run(normalizedOptions, operation);
332
+ };
333
+ const adapter = {
334
+ next: async (...args) => {
335
+ await prepareIterator();
336
+ const iterator = underlyingIterator;
337
+ if (!iterator) {
338
+ throw new Error('Provider iterator not initialised');
339
+ }
340
+ return withContext(() => iterator.next(...args));
341
+ },
342
+ return: async (value) => {
343
+ await prepareIterator();
344
+ const iterator = underlyingIterator;
345
+ if (!iterator) {
346
+ throw new Error('Provider iterator not initialised');
347
+ }
348
+ if (iterator.return) {
349
+ return withContext(() => iterator.return(value));
350
+ }
351
+ return { done: true, value: undefined };
352
+ },
353
+ throw: async (error) => {
354
+ await prepareIterator();
355
+ const iterator = underlyingIterator;
356
+ if (!iterator) {
357
+ throw new Error('Provider iterator not initialised');
358
+ }
359
+ if (iterator.throw) {
360
+ return withContext(() => iterator.throw(error));
361
+ }
362
+ throw error;
363
+ },
364
+ [Symbol.asyncIterator]() {
365
+ return this;
366
+ },
367
+ };
368
+ return adapter;
369
+ }
370
+ /**
371
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
372
+ * @requirement REQ-SP2-001
373
+ * @pseudocode base-provider-call-contract.md lines 3-5
374
+ */
375
+ invokeWithNormalizedOptions(normalized, previousContext) {
376
+ const needsContextSwap = !previousContext ||
377
+ previousContext.settingsService !== normalized.settings ||
378
+ (normalized.config && previousContext.config !== normalized.config);
379
+ const mergedMetadata = normalized.runtime
380
+ ? {
381
+ ...(normalized.runtime.metadata ?? {}),
382
+ ...normalized.metadata,
383
+ }
384
+ : {
385
+ ...(previousContext?.metadata ?? {}),
386
+ ...normalized.metadata,
387
+ };
388
+ if (!('source' in mergedMetadata)) {
389
+ mergedMetadata.source = 'BaseProvider.generateChatCompletion';
390
+ }
391
+ const runtimeContext = normalized.runtime
392
+ ? {
393
+ ...normalized.runtime,
394
+ settingsService: normalized.settings,
395
+ config: normalized.config ?? normalized.runtime.config,
396
+ metadata: mergedMetadata,
397
+ }
398
+ : {
399
+ settingsService: normalized.settings,
400
+ config: normalized.config ?? previousContext?.config,
401
+ runtimeId: previousContext?.runtimeId ?? 'base-provider.normalized-call',
402
+ metadata: mergedMetadata,
403
+ };
404
+ return async function* () {
405
+ if (needsContextSwap) {
406
+ setActiveProviderRuntimeContext(runtimeContext);
407
+ }
408
+ try {
409
+ const iterator = this.generateChatCompletionWithOptions(normalized);
410
+ for await (const chunk of iterator) {
411
+ yield chunk;
412
+ }
413
+ }
414
+ finally {
415
+ if (needsContextSwap) {
416
+ setActiveProviderRuntimeContext(previousContext ?? null);
417
+ }
418
+ normalized.resolved.authToken = '';
419
+ this.authResolver.setSettingsService(this.defaultSettingsService);
420
+ }
421
+ }.call(this);
422
+ }
423
+ /**
424
+ * @plan PLAN-20251018-STATELESSPROVIDER2.P06
425
+ * @plan:PLAN-20251023-STATELESS-HARDENING.P05
426
+ * @requirement REQ-SP2-001
427
+ * @requirement:REQ-SP4-001
428
+ * @pseudocode base-provider-call-contract.md lines 1-3
429
+ */
430
+ async normalizeGenerateChatOptions(contentsOrOptions, maybeTools) {
431
+ const providedOptions = Array.isArray(contentsOrOptions)
432
+ ? { contents: contentsOrOptions, tools: maybeTools }
433
+ : contentsOrOptions;
434
+ const settings = providedOptions.settings ?? this.defaultSettingsService ?? null;
435
+ if (!settings) {
436
+ throw new MissingProviderRuntimeError({
437
+ providerKey: `BaseProvider.${this.name}`,
438
+ missingFields: ['settings'],
439
+ stage: 'normalizeGenerateChatOptions',
440
+ metadata: {
441
+ hint: 'ProviderManager must supply settings via GenerateChatOptions or setRuntimeSettingsService.',
442
+ requirement: 'REQ-SP4-001',
443
+ },
444
+ });
445
+ }
446
+ const runtimeConfig = providedOptions.runtime?.config ?? null;
447
+ const configCandidate = providedOptions.config ?? runtimeConfig ?? this.defaultConfig ?? null;
448
+ const runtimeMetadata = providedOptions.runtime?.metadata ?? {};
449
+ const metadataFromOptions = providedOptions.metadata ?? {};
450
+ const metadata = {
451
+ ...runtimeMetadata,
452
+ ...metadataFromOptions,
453
+ };
454
+ const resolvedModel = this.computeModel(settings);
455
+ const resolvedBaseURL = this.computeBaseURL(settings);
456
+ const resolvedAuth = (await this.authResolver.resolveAuthentication({
457
+ settingsService: settings,
458
+ })) ?? '';
459
+ const resolved = {
460
+ model: resolvedModel,
461
+ baseURL: resolvedBaseURL,
462
+ authToken: resolvedAuth,
463
+ telemetry: providedOptions.resolved?.telemetry,
464
+ };
465
+ const guard = this.assertRuntimeContext({
466
+ providerKey: `BaseProvider.${this.name}`,
467
+ settings,
468
+ config: configCandidate,
469
+ runtime: providedOptions.runtime,
470
+ metadata,
471
+ resolved,
472
+ stage: 'normalizeGenerateChatOptions',
473
+ });
474
+ const finalConfig = guard.runtime.config ?? configCandidate ?? undefined;
475
+ const normalizedRuntime = {
476
+ ...guard.runtime,
477
+ metadata: guard.metadata,
478
+ config: finalConfig,
479
+ };
480
+ const invocation = providedOptions.invocation ??
481
+ createRuntimeInvocationContext({
482
+ runtime: normalizedRuntime,
483
+ settings,
484
+ providerName: this.name,
485
+ ephemeralsSnapshot: this.buildEphemeralsSnapshot(settings),
486
+ telemetry: resolved.telemetry,
487
+ metadata: guard.metadata,
488
+ userMemory: typeof providedOptions.userMemory === 'string'
489
+ ? providedOptions.userMemory
490
+ : undefined,
491
+ fallbackRuntimeId: `${this.name}:normalizeGenerateChatOptions`,
492
+ });
493
+ return {
494
+ ...providedOptions,
495
+ contents: providedOptions.contents,
496
+ tools: providedOptions.tools ?? maybeTools,
497
+ settings,
498
+ config: finalConfig,
499
+ runtime: normalizedRuntime,
500
+ metadata: guard.metadata,
501
+ resolved,
502
+ invocation,
503
+ };
504
+ }
505
+ buildEphemeralsSnapshot(settings) {
506
+ const snapshot = {
507
+ ...settings.getAllGlobalSettings(),
508
+ };
509
+ const providerEphemerals = settings.getProviderSettings(this.name);
510
+ snapshot[this.name] = { ...providerEphemerals };
511
+ return snapshot;
512
+ }
513
+ /**
514
+ * @plan:PLAN-20251023-STATELESS-HARDENING.P05
515
+ * @requirement:REQ-SP4-001
516
+ * @pseudocode base-provider-fallback-removal.md lines 11-14
517
+ */
518
+ assertRuntimeContext(input) {
519
+ const missing = [];
520
+ if (!input.settings) {
521
+ missing.push('settings');
522
+ }
523
+ if (!input.config) {
524
+ missing.push('config');
525
+ }
526
+ const resolvedMissing = [];
527
+ if (!input.resolved) {
528
+ resolvedMissing.push('resolved');
529
+ }
530
+ else {
531
+ if (typeof input.resolved.model !== 'string' ||
532
+ input.resolved.model.trim() === '') {
533
+ resolvedMissing.push('resolved.model');
534
+ }
535
+ if (input.resolved.baseURL !== undefined &&
536
+ input.resolved.baseURL !== null &&
537
+ typeof input.resolved.baseURL !== 'string') {
538
+ resolvedMissing.push('resolved.baseURL');
539
+ }
540
+ if (input.resolved.authToken === undefined ||
541
+ input.resolved.authToken === null) {
542
+ resolvedMissing.push('resolved.authToken');
543
+ }
544
+ }
545
+ const missingFields = [...missing, ...resolvedMissing];
546
+ if (missingFields.length > 0) {
547
+ throw new MissingProviderRuntimeError({
548
+ providerKey: input.providerKey,
549
+ missingFields,
550
+ stage: input.stage,
551
+ metadata: {
552
+ ...(input.metadata ?? {}),
553
+ requirement: 'REQ-SP4-001',
554
+ },
555
+ });
556
+ }
557
+ const metadata = {
558
+ ...(input.runtime?.metadata ?? {}),
559
+ ...(input.metadata ?? {}),
560
+ requirement: 'REQ-SP4-001',
561
+ stage: input.stage,
562
+ };
563
+ const runtimeMetadata = metadata;
564
+ const currentRuntimeId = typeof runtimeMetadata.runtimeId === 'string'
565
+ ? runtimeMetadata.runtimeId
566
+ : undefined;
567
+ const runtime = input.runtime
568
+ ? {
569
+ ...input.runtime,
570
+ settingsService: input.settings,
571
+ config: input.runtime.config ?? input.config ?? undefined,
572
+ metadata,
573
+ }
574
+ : {
575
+ settingsService: input.settings,
576
+ config: input.config ?? undefined,
577
+ runtimeId: currentRuntimeId && currentRuntimeId.trim()
578
+ ? currentRuntimeId
579
+ : `${input.providerKey}:${input.stage}`,
580
+ metadata,
581
+ };
582
+ return { runtime, metadata };
583
+ }
249
584
  // Optional methods with default implementations
250
- setModel(_modelId) { }
251
585
  getCurrentModel() {
252
586
  // Use the same logic as getModel() to check ephemeral settings
253
587
  return this.getModel();
@@ -255,7 +589,6 @@ export class BaseProvider {
255
589
  getToolFormat() {
256
590
  return 'default';
257
591
  }
258
- setToolFormatOverride(_format) { }
259
592
  isPaidMode() {
260
593
  return false;
261
594
  }
@@ -269,7 +602,7 @@ export class BaseProvider {
269
602
  const maybeConfig = config;
270
603
  if (typeof maybeConfig.getUserMemory === 'function' &&
271
604
  typeof maybeConfig.getModel === 'function') {
272
- this.globalConfig = config;
605
+ this.defaultConfig = config;
273
606
  return;
274
607
  }
275
608
  this.providerConfig = config;
@@ -280,7 +613,6 @@ export class BaseProvider {
280
613
  async invokeServerTool(toolName, _params, _config, _signal) {
281
614
  throw new Error(`Server tool '${toolName}' not supported by ${this.name} provider`);
282
615
  }
283
- setModelParams(_params) { }
284
616
  getModelParams() {
285
617
  return undefined;
286
618
  }
@@ -288,7 +620,7 @@ export class BaseProvider {
288
620
  * Get setting value from SettingsService
289
621
  */
290
622
  async getProviderSetting(key, fallback) {
291
- const settingsService = getSettingsService();
623
+ const settingsService = this.resolveSettingsService();
292
624
  try {
293
625
  const settings = await settingsService.getSettings(this.name);
294
626
  return settings[key] || fallback;
@@ -304,11 +636,15 @@ export class BaseProvider {
304
636
  * Set setting value in SettingsService
305
637
  */
306
638
  async setProviderSetting(key, value) {
307
- const settingsService = getSettingsService();
639
+ const settingsService = this.resolveSettingsService();
308
640
  try {
309
641
  await settingsService.updateSettings(this.name, {
310
642
  [key]: value,
311
643
  });
644
+ const updatedSettings = await settingsService.getSettings(this.name);
645
+ if (updatedSettings[key] !== value) {
646
+ settingsService.set(`providers.${this.name}.${String(key)}`, value);
647
+ }
312
648
  }
313
649
  catch (error) {
314
650
  if (process.env.DEBUG) {
@@ -356,11 +692,11 @@ export class BaseProvider {
356
692
  * Get model parameters from SettingsService
357
693
  */
358
694
  async getModelParamsFromSettings() {
359
- const settingsService = getSettingsService();
695
+ const settingsService = this.resolveSettingsService();
360
696
  try {
361
697
  const settings = await settingsService.getSettings(this.name);
362
698
  // Extract model parameters from settings, excluding standard fields
363
- const { enabled: _enabled, apiKey: _apiKey, baseUrl: _baseUrl, model: _model, maxTokens, temperature, ...modelParams } = settings;
699
+ const { enabled: _enabled, apiKey: _apiKey, baseUrl: _baseUrl, model: _model, maxTokens, temperature, ...additionalSettings } = settings;
364
700
  // Include temperature and maxTokens as model params if they exist
365
701
  const params = {};
366
702
  if (temperature !== undefined)
@@ -368,8 +704,8 @@ export class BaseProvider {
368
704
  if (maxTokens !== undefined)
369
705
  params.max_tokens = maxTokens;
370
706
  return Object.keys(params).length > 0 ||
371
- Object.keys(modelParams).length > 0
372
- ? { ...params, ...modelParams }
707
+ Object.keys(additionalSettings).length > 0
708
+ ? { ...params, ...additionalSettings }
373
709
  : undefined;
374
710
  }
375
711
  catch (error) {
@@ -383,7 +719,7 @@ export class BaseProvider {
383
719
  * Set model parameters in SettingsService
384
720
  */
385
721
  async setModelParamsInSettings(params) {
386
- const settingsService = getSettingsService();
722
+ const settingsService = this.resolveSettingsService();
387
723
  try {
388
724
  if (params === undefined) {
389
725
  // Clear model parameters by setting them to undefined