@pellux/goodvibes-sdk 0.18.43 → 0.18.47
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.
- package/dist/_internal/platform/config/manager.d.ts +12 -0
- package/dist/_internal/platform/config/manager.d.ts.map +1 -1
- package/dist/_internal/platform/config/manager.js +30 -0
- package/dist/_internal/platform/core/orchestrator-turn-helpers.js +1 -1
- package/dist/_internal/platform/core/orchestrator-turn-loop.js +1 -1
- package/dist/_internal/platform/daemon/facade.d.ts +19 -0
- package/dist/_internal/platform/daemon/facade.d.ts.map +1 -1
- package/dist/_internal/platform/daemon/facade.js +90 -1
- package/dist/_internal/platform/daemon/host-mode-watcher.d.ts +49 -0
- package/dist/_internal/platform/daemon/host-mode-watcher.d.ts.map +1 -0
- package/dist/_internal/platform/daemon/host-mode-watcher.js +41 -0
- package/dist/_internal/platform/daemon/http-listener.d.ts +17 -0
- package/dist/_internal/platform/daemon/http-listener.d.ts.map +1 -1
- package/dist/_internal/platform/daemon/http-listener.js +87 -1
- package/dist/_internal/platform/providers/anthropic-compat.d.ts.map +1 -1
- package/dist/_internal/platform/providers/anthropic-compat.js +9 -6
- package/dist/_internal/platform/providers/anthropic-sdk-provider.d.ts.map +1 -1
- package/dist/_internal/platform/providers/anthropic-sdk-provider.js +9 -6
- package/dist/_internal/platform/providers/anthropic.d.ts.map +1 -1
- package/dist/_internal/platform/providers/anthropic.js +9 -6
- package/dist/_internal/platform/providers/auto-register.d.ts +2 -3
- package/dist/_internal/platform/providers/auto-register.d.ts.map +1 -1
- package/dist/_internal/platform/providers/auto-register.js +1 -8
- package/dist/_internal/platform/providers/gemini.d.ts.map +1 -1
- package/dist/_internal/platform/providers/gemini.js +5 -5
- package/dist/_internal/platform/providers/index.d.ts +1 -0
- package/dist/_internal/platform/providers/index.d.ts.map +1 -1
- package/dist/_internal/platform/providers/index.js +1 -0
- package/dist/_internal/platform/providers/interface.d.ts +12 -1
- package/dist/_internal/platform/providers/interface.d.ts.map +1 -1
- package/dist/_internal/platform/providers/llama-cpp.d.ts.map +1 -1
- package/dist/_internal/platform/providers/llama-cpp.js +4 -3
- package/dist/_internal/platform/providers/lm-studio-helpers.d.ts +2 -3
- package/dist/_internal/platform/providers/lm-studio-helpers.d.ts.map +1 -1
- package/dist/_internal/platform/providers/lm-studio-helpers.js +0 -9
- package/dist/_internal/platform/providers/lm-studio.d.ts.map +1 -1
- package/dist/_internal/platform/providers/lm-studio.js +7 -3
- package/dist/_internal/platform/providers/ollama.d.ts.map +1 -1
- package/dist/_internal/platform/providers/ollama.js +3 -3
- package/dist/_internal/platform/providers/openai-codex.d.ts.map +1 -1
- package/dist/_internal/platform/providers/openai-codex.js +3 -10
- package/dist/_internal/platform/providers/openai-compat.d.ts.map +1 -1
- package/dist/_internal/platform/providers/openai-compat.js +11 -7
- package/dist/_internal/platform/providers/openai.d.ts.map +1 -1
- package/dist/_internal/platform/providers/openai.js +11 -7
- package/dist/_internal/platform/providers/provider-api.d.ts +4 -0
- package/dist/_internal/platform/providers/provider-api.d.ts.map +1 -1
- package/dist/_internal/platform/providers/provider-not-found-error.d.ts +16 -0
- package/dist/_internal/platform/providers/provider-not-found-error.d.ts.map +1 -0
- package/dist/_internal/platform/providers/provider-not-found-error.js +24 -0
- package/dist/_internal/platform/providers/registry.d.ts +44 -2
- package/dist/_internal/platform/providers/registry.d.ts.map +1 -1
- package/dist/_internal/platform/providers/registry.js +73 -13
- package/dist/_internal/platform/providers/stop-reason-maps.d.ts +30 -0
- package/dist/_internal/platform/providers/stop-reason-maps.d.ts.map +1 -0
- package/dist/_internal/platform/providers/stop-reason-maps.js +89 -0
- package/dist/_internal/platform/runtime/contracts/migrations/event-envelope.d.ts +1 -1
- package/dist/_internal/platform/runtime/contracts/version.d.ts +1 -1
- package/dist/_internal/platform/runtime/contracts/version.js +1 -1
- package/dist/_internal/platform/runtime/events/domain-map.d.ts +10 -0
- package/dist/_internal/platform/runtime/events/domain-map.d.ts.map +1 -1
- package/dist/_internal/platform/runtime/events/index.d.ts +1 -1
- package/dist/_internal/platform/runtime/events/index.d.ts.map +1 -1
- package/dist/_internal/platform/runtime/forensics/classifier.d.ts.map +1 -1
- package/dist/_internal/platform/runtime/forensics/classifier.js +1 -4
- package/dist/_internal/platform/runtime/index.d.ts +1 -1
- package/dist/_internal/platform/runtime/index.d.ts.map +1 -1
- package/dist/_internal/platform/version.js +1 -1
- package/dist/_internal/transport-http/auth.d.ts +17 -0
- package/dist/_internal/transport-http/auth.d.ts.map +1 -1
- package/dist/_internal/transport-http/auth.js +22 -0
- package/dist/_internal/transport-http/http-core.d.ts.map +1 -1
- package/dist/_internal/transport-http/http-core.js +5 -3
- package/dist/_internal/transport-http/index.d.ts +2 -2
- package/dist/_internal/transport-http/index.d.ts.map +1 -1
- package/dist/_internal/transport-http/index.js +1 -1
- package/dist/_internal/transport-realtime/runtime-events.d.ts +3 -2
- package/dist/_internal/transport-realtime/runtime-events.d.ts.map +1 -1
- package/dist/_internal/transport-realtime/runtime-events.js +4 -10
- package/dist/client.d.ts +19 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -25,6 +25,10 @@ export type ConfigOverrides = ConfigCliOverrides & ({
|
|
|
25
25
|
export interface ConfigSetOptions {
|
|
26
26
|
bypassManagedLock?: boolean;
|
|
27
27
|
}
|
|
28
|
+
/** Callback invoked when a watched config key changes. */
|
|
29
|
+
export type ConfigChangeCallback<K extends ConfigKey> = (newValue: ConfigValue<K>, oldValue: ConfigValue<K>) => void;
|
|
30
|
+
/** Unsubscribe handle returned by ConfigManager.subscribe(). */
|
|
31
|
+
export type ConfigUnsubscribe = () => void;
|
|
28
32
|
/**
|
|
29
33
|
* ConfigManager — Layered, mutable, persistent config system.
|
|
30
34
|
*
|
|
@@ -39,6 +43,7 @@ export declare class ConfigManager {
|
|
|
39
43
|
private readonly workingDirectory;
|
|
40
44
|
private readonly homeDirectory;
|
|
41
45
|
private hookDispatcher;
|
|
46
|
+
private readonly _listeners;
|
|
42
47
|
constructor(overrides: ConfigOverrides);
|
|
43
48
|
getControlPlaneConfigDir(): string;
|
|
44
49
|
getWorkingDirectory(): string | null;
|
|
@@ -49,6 +54,13 @@ export declare class ConfigManager {
|
|
|
49
54
|
get<K extends ConfigKey>(key: K): ConfigValue<K>;
|
|
50
55
|
/** Set a config value by dot-path key and auto-save to disk. */
|
|
51
56
|
set<K extends ConfigKey>(key: K, value: ConfigValue<K>, options?: ConfigSetOptions): void;
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to changes on a specific config key.
|
|
59
|
+
* Returns an unsubscribe function. Safe to call multiple times.
|
|
60
|
+
*/
|
|
61
|
+
subscribe<K extends ConfigKey>(key: K, cb: ConfigChangeCallback<K>): ConfigUnsubscribe;
|
|
62
|
+
/** Notify synchronous subscribers of a key change. */
|
|
63
|
+
private notifyListeners;
|
|
52
64
|
/**
|
|
53
65
|
* Fire the Change:config hook for a config key change.
|
|
54
66
|
* Best-effort: the hook dispatcher may not be initialised during startup.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/config/manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAI3H,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMxD,wFAAwF;AACxF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,QAAQ,EAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACzE,CAAC;AAEF,yEAAyE;AACzE,UAAU,kBAAkB;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,eAAe,GAAG,kBAAkB,GAAG,CAC/C;IACA,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,GACC;IACA,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CACF,CAAC;AASF,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/config/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,6 +65,7 @@ export class ConfigManager {
|
|
|
65
65
|
workingDirectory;
|
|
66
66
|
homeDirectory;
|
|
67
67
|
hookDispatcher = null;
|
|
68
|
+
_listeners = new Map();
|
|
68
69
|
constructor(overrides) {
|
|
69
70
|
const roots = overrides;
|
|
70
71
|
const configDir = requireAbsoluteOwnedPath(roots.configDir, 'configDir');
|
|
@@ -157,8 +158,37 @@ export class ConfigManager {
|
|
|
157
158
|
const previousValue = parent[field];
|
|
158
159
|
parent[field] = value;
|
|
159
160
|
this.save();
|
|
161
|
+
this.notifyListeners(key, previousValue, value);
|
|
160
162
|
this.emitConfigHook(key, previousValue, value);
|
|
161
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Subscribe to changes on a specific config key.
|
|
166
|
+
* Returns an unsubscribe function. Safe to call multiple times.
|
|
167
|
+
*/
|
|
168
|
+
subscribe(key, cb) {
|
|
169
|
+
if (!this._listeners.has(key)) {
|
|
170
|
+
this._listeners.set(key, new Set());
|
|
171
|
+
}
|
|
172
|
+
// Cast via unknown -> (n: unknown, o: unknown) => void to avoid deeply-recursive ConfigValue<K> comparison
|
|
173
|
+
// that exceeds TypeScript's stack depth limit on the 100-entry conditional type.
|
|
174
|
+
const wrapped = (newVal, oldVal) => cb(newVal, oldVal);
|
|
175
|
+
this._listeners.get(key).add(wrapped);
|
|
176
|
+
return () => {
|
|
177
|
+
this._listeners.get(key)?.delete(wrapped);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** Notify synchronous subscribers of a key change. */
|
|
181
|
+
notifyListeners(key, oldValue, newValue) {
|
|
182
|
+
const set = this._listeners.get(key);
|
|
183
|
+
if (!set)
|
|
184
|
+
return;
|
|
185
|
+
for (const cb of set) {
|
|
186
|
+
try {
|
|
187
|
+
cb(newValue, oldValue);
|
|
188
|
+
}
|
|
189
|
+
catch { /* listener errors must not abort set() */ }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
162
192
|
/**
|
|
163
193
|
* Fire the Change:config hook for a config key change.
|
|
164
194
|
* Best-effort: the hook dispatcher may not be initialised during startup.
|
|
@@ -206,7 +206,7 @@ export function handleFinalResponseOutcome(args) {
|
|
|
206
206
|
export function emitMalformedToolUseWarning(args) {
|
|
207
207
|
logger.warn('Orchestrator: provider reported stopReason=tool_use but returned no tool calls (malformed response)', {
|
|
208
208
|
model: args.providerRegistry.getCurrentModel().id,
|
|
209
|
-
stopReason: '
|
|
209
|
+
stopReason: 'tool_call',
|
|
210
210
|
});
|
|
211
211
|
if (args.isReconciliationEnabled) {
|
|
212
212
|
args.conversation.addSystemMessage('[Tool Reconciliation] Provider returned stop_reason=tool_use but no tool calls were included in the response. ' +
|
|
@@ -231,7 +231,7 @@ export async function executeOrchestratorTurnLoop(context) {
|
|
|
231
231
|
}
|
|
232
232
|
const reasoningForMsg = reasoningAccumulated || undefined;
|
|
233
233
|
const reasoningSummaryForMsg = response.reasoningSummary || undefined;
|
|
234
|
-
if (response.stopReason === '
|
|
234
|
+
if (response.stopReason === 'tool_call' && response.toolCalls.length === 0) {
|
|
235
235
|
emitMalformedToolUseWarning({
|
|
236
236
|
conversation: context.conversation,
|
|
237
237
|
providerRegistry: context.providerRegistry,
|
|
@@ -61,6 +61,14 @@ export declare class DaemonServer {
|
|
|
61
61
|
private agentTaskAdapterUnsub;
|
|
62
62
|
private tlsState;
|
|
63
63
|
private approvalBrokerUnsubscribe;
|
|
64
|
+
/** Unsubscribe from controlPlane config key watchers; cleared on stop(). */
|
|
65
|
+
private _configWatchUnsub;
|
|
66
|
+
/** True while a config-driven restart is in progress — prevents re-entrancy. */
|
|
67
|
+
private _restarting;
|
|
68
|
+
/** Awaitable promise for the active restart cycle; null when idle. */
|
|
69
|
+
private _restartingPromise;
|
|
70
|
+
/** True if a config change arrived while _restarting was set; triggers a second cycle. */
|
|
71
|
+
private _restartDirty;
|
|
64
72
|
constructor(config?: DaemonConfig, _configManager?: ConfigManager);
|
|
65
73
|
listRecentControlPlaneEvents(limit?: number): readonly import('../control-plane/gateway.js').ControlPlaneRecentEvent[];
|
|
66
74
|
/**
|
|
@@ -80,7 +88,18 @@ export declare class DaemonServer {
|
|
|
80
88
|
* against a 10-second timeout so a hung service cannot block the full
|
|
81
89
|
* shutdown sequence (C1 fix).
|
|
82
90
|
*/
|
|
91
|
+
/**
|
|
92
|
+
* Wait for any in-progress config-driven restart to settle.
|
|
93
|
+
* Callers that change config mid-flight and need to know when the server
|
|
94
|
+
* has rebounded should await this before inspecting state.
|
|
95
|
+
*/
|
|
96
|
+
waitForRestart(): Promise<void>;
|
|
83
97
|
stop(): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Subscribe to controlPlane binding keys and restart the server on change.
|
|
100
|
+
* Called once from start() after the server is up. Clears itself on stop().
|
|
101
|
+
*/
|
|
102
|
+
private _attachControlPlaneConfigWatcher;
|
|
84
103
|
/**
|
|
85
104
|
* Returns true if the server is currently running.
|
|
86
105
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"facade.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/daemon/facade.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAiD1D,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAuB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"facade.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/daemon/facade.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,8 @@ import { GlobalNetworkTransportInstaller, resolveInboundTlsContext, } from '../r
|
|
|
10
10
|
import { createRuntimeServices } from '../runtime/services.js';
|
|
11
11
|
import { readAutomationReasoningEffort, readAutomationWakeMode, readExternalContentSource, readStringList, } from './helpers.js';
|
|
12
12
|
import { requirePortAvailable } from './port-check.js';
|
|
13
|
+
import { resolveHostBinding } from './host-resolver.js';
|
|
14
|
+
import { createHostModeRestartWatcher } from './host-mode-watcher.js';
|
|
13
15
|
// ---------------------------------------------------------------------------
|
|
14
16
|
// DaemonServer
|
|
15
17
|
// ---------------------------------------------------------------------------
|
|
@@ -74,6 +76,14 @@ export class DaemonServer {
|
|
|
74
76
|
agentTaskAdapterUnsub = null;
|
|
75
77
|
tlsState = null;
|
|
76
78
|
approvalBrokerUnsubscribe = null;
|
|
79
|
+
/** Unsubscribe from controlPlane config key watchers; cleared on stop(). */
|
|
80
|
+
_configWatchUnsub = null;
|
|
81
|
+
/** True while a config-driven restart is in progress — prevents re-entrancy. */
|
|
82
|
+
_restarting = false;
|
|
83
|
+
/** Awaitable promise for the active restart cycle; null when idle. */
|
|
84
|
+
_restartingPromise = null;
|
|
85
|
+
/** True if a config change arrived while _restarting was set; triggers a second cycle. */
|
|
86
|
+
_restartDirty = false;
|
|
77
87
|
constructor(config = {}, _configManager) {
|
|
78
88
|
this.config = config;
|
|
79
89
|
const resolved = resolveDaemonFacadeRuntime(config, _configManager);
|
|
@@ -218,7 +228,10 @@ export class DaemonServer {
|
|
|
218
228
|
runtimeStore: this.runtimeStore,
|
|
219
229
|
});
|
|
220
230
|
const self = this;
|
|
221
|
-
|
|
231
|
+
// Skip real OS port check when a mock serveFactory is injected (test-only path).
|
|
232
|
+
if (this.serveFactory === Bun.serve) {
|
|
233
|
+
await requirePortAvailable(this.port, this.host, 'daemon');
|
|
234
|
+
}
|
|
222
235
|
this.transportEventsHelper.emitTransportInitializing();
|
|
223
236
|
try {
|
|
224
237
|
this.tlsState = resolveInboundTlsContext(this.configManager, 'controlPlane');
|
|
@@ -279,6 +292,7 @@ export class DaemonServer {
|
|
|
279
292
|
this.watcherRegistry.startWatcher('daemon-heartbeat');
|
|
280
293
|
}
|
|
281
294
|
this.controlPlaneGateway.setServerState({ enabled: true, host: this.host, port: this.port });
|
|
295
|
+
this._attachControlPlaneConfigWatcher();
|
|
282
296
|
this.transportEventsHelper.emitTransportConnected();
|
|
283
297
|
logger.info('DaemonServer started', {
|
|
284
298
|
port: this.port,
|
|
@@ -317,9 +331,27 @@ export class DaemonServer {
|
|
|
317
331
|
* against a 10-second timeout so a hung service cannot block the full
|
|
318
332
|
* shutdown sequence (C1 fix).
|
|
319
333
|
*/
|
|
334
|
+
/**
|
|
335
|
+
* Wait for any in-progress config-driven restart to settle.
|
|
336
|
+
* Callers that change config mid-flight and need to know when the server
|
|
337
|
+
* has rebounded should await this before inspecting state.
|
|
338
|
+
*/
|
|
339
|
+
async waitForRestart() {
|
|
340
|
+
// Loop to handle dirty-flag chained restarts: each cycle may spawn another.
|
|
341
|
+
while (this._restartingPromise)
|
|
342
|
+
await this._restartingPromise;
|
|
343
|
+
}
|
|
320
344
|
async stop() {
|
|
321
345
|
if (this.server === null)
|
|
322
346
|
return;
|
|
347
|
+
// Tear down config watcher only on intentional stop, not mid-restart.
|
|
348
|
+
// During a restart cycle (_restarting=true) the watcher must stay active so
|
|
349
|
+
// config changes that arrive between stop() and the subsequent start() can be
|
|
350
|
+
// captured by the dirty flag.
|
|
351
|
+
if (!this._restarting) {
|
|
352
|
+
this._configWatchUnsub?.();
|
|
353
|
+
this._configWatchUnsub = null;
|
|
354
|
+
}
|
|
323
355
|
// Synchronous pre-stop teardown
|
|
324
356
|
this.watcherRegistry.stopWatcher('daemon-heartbeat', 'daemon-stopped');
|
|
325
357
|
if (this.replyPoller !== null) {
|
|
@@ -346,6 +378,63 @@ export class DaemonServer {
|
|
|
346
378
|
this.transportEventsHelper.emitTransportDisconnected('Daemon server stopped', false);
|
|
347
379
|
logger.info('DaemonServer stopped');
|
|
348
380
|
}
|
|
381
|
+
/**
|
|
382
|
+
* Subscribe to controlPlane binding keys and restart the server on change.
|
|
383
|
+
* Called once from start() after the server is up. Clears itself on stop().
|
|
384
|
+
*/
|
|
385
|
+
_attachControlPlaneConfigWatcher() {
|
|
386
|
+
if (this._configWatchUnsub)
|
|
387
|
+
return; // idempotent
|
|
388
|
+
const restart = () => {
|
|
389
|
+
if (this._restarting) {
|
|
390
|
+
// A change arrived mid-restart — queue a second cycle via dirty flag.
|
|
391
|
+
// Check _restarting BEFORE isRunning: stop() runs synchronously inside the
|
|
392
|
+
// restart IIFE, so isRunning may be false even while a restart is in progress.
|
|
393
|
+
this._restartDirty = true;
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (!this.isRunning)
|
|
397
|
+
return;
|
|
398
|
+
this._restarting = true;
|
|
399
|
+
this._restartingPromise = (async () => {
|
|
400
|
+
try {
|
|
401
|
+
logger.info('DaemonServer: controlPlane binding changed, restarting daemon server…');
|
|
402
|
+
await this.stop();
|
|
403
|
+
// Re-resolve host/port from updated config
|
|
404
|
+
const newBinding = resolveHostBinding(this.configManager.get('controlPlane.hostMode') ?? 'local', String(this.configManager.get('controlPlane.host') ?? '127.0.0.1'), Number(this.configManager.get('controlPlane.port') ?? 3421), 'controlPlane');
|
|
405
|
+
this.host = newBinding.host;
|
|
406
|
+
this.port = newBinding.port;
|
|
407
|
+
await this.start();
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
logger.error('DaemonServer: restart after config change failed', { error: summarizeError(err) });
|
|
411
|
+
}
|
|
412
|
+
finally {
|
|
413
|
+
this._restarting = false;
|
|
414
|
+
// If a config change arrived while we were restarting, kick off a second
|
|
415
|
+
// cycle BEFORE nulling _restartingPromise so waitForRestart() chains correctly.
|
|
416
|
+
if (this._restartDirty) {
|
|
417
|
+
this._restartDirty = false;
|
|
418
|
+
restart(); // sets this._restartingPromise to the new cycle
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
this._restartingPromise = null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
})();
|
|
425
|
+
};
|
|
426
|
+
// getIsRunning must also return true while a restart cycle is in progress
|
|
427
|
+
// (_restarting=true) so that config changes arriving mid-restart reach the
|
|
428
|
+
// dirty-flag path inside `restart`. When the server is intentionally stopped
|
|
429
|
+
// (not mid-restart) isRunning and _restarting are both false.
|
|
430
|
+
const watcher = createHostModeRestartWatcher({
|
|
431
|
+
configManager: this.configManager,
|
|
432
|
+
keys: ['controlPlane.hostMode', 'controlPlane.host', 'controlPlane.port'],
|
|
433
|
+
onRestart: restart,
|
|
434
|
+
getIsRunning: () => this.isRunning || this._restarting,
|
|
435
|
+
});
|
|
436
|
+
this._configWatchUnsub = () => watcher.unsubscribe();
|
|
437
|
+
}
|
|
349
438
|
/**
|
|
350
439
|
* Returns true if the server is currently running.
|
|
351
440
|
*/
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host-mode-watcher.ts
|
|
3
|
+
*
|
|
4
|
+
* Shared helper that creates a config-key watcher for host-mode restart logic.
|
|
5
|
+
* Extracted so the restart guard + dirty-flag loop-back pattern can be tested
|
|
6
|
+
* independently of DaemonServer or HttpListener.
|
|
7
|
+
*
|
|
8
|
+
* Both facade.ts and http-listener.ts delegate to this helper, passing their
|
|
9
|
+
* own `onRestart` closure and `getIsRunning` accessor.
|
|
10
|
+
*/
|
|
11
|
+
import type { ConfigManager } from '../config/manager.js';
|
|
12
|
+
import type { ConfigKey } from '@pellux/goodvibes-sdk/platform/config/schema';
|
|
13
|
+
export interface HostModeWatcherOptions {
|
|
14
|
+
/** The ConfigManager to subscribe on. */
|
|
15
|
+
configManager: ConfigManager;
|
|
16
|
+
/** Config keys that should trigger a restart when changed. */
|
|
17
|
+
keys: ConfigKey[];
|
|
18
|
+
/**
|
|
19
|
+
* Called when a key change is detected and the server is running.
|
|
20
|
+
* The implementation is responsible for performing stop + restart.
|
|
21
|
+
* The caller is responsible for re-entrancy / dirty-flag semantics: if a
|
|
22
|
+
* new change arrives while a prior restart is in progress, `onRestart`
|
|
23
|
+
* will be invoked again. The caller must either guard against overlapping
|
|
24
|
+
* cycles or queue the second cycle via its own dirty-flag mechanism.
|
|
25
|
+
*/
|
|
26
|
+
onRestart: () => void;
|
|
27
|
+
/** Returns true when the server/listener is currently running. */
|
|
28
|
+
getIsRunning: () => boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface HostModeWatcherHandle {
|
|
31
|
+
/** Detach all subscriptions — safe to call multiple times. */
|
|
32
|
+
unsubscribe: () => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a host-mode restart watcher.
|
|
36
|
+
*
|
|
37
|
+
* Behaviour:
|
|
38
|
+
* - Subscribes to each key in `keys`.
|
|
39
|
+
* - When a key changes and `getIsRunning()` is true, calls `onRestart()`.
|
|
40
|
+
* - The caller's `onRestart` implementation handles re-entrancy / dirty-flag
|
|
41
|
+
* logic internally (the watcher does NOT duplicate that logic).
|
|
42
|
+
* - `unsubscribe()` removes all subscriptions; subsequent key changes are
|
|
43
|
+
* silently ignored.
|
|
44
|
+
*
|
|
45
|
+
* @param opts - Watcher configuration.
|
|
46
|
+
* @returns A handle with an `unsubscribe` method.
|
|
47
|
+
*/
|
|
48
|
+
export declare function createHostModeRestartWatcher(opts: HostModeWatcherOptions): HostModeWatcherHandle;
|
|
49
|
+
//# sourceMappingURL=host-mode-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-mode-watcher.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/daemon/host-mode-watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8CAA8C,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,yCAAyC;IACzC,aAAa,EAAE,aAAa,CAAC;IAC7B,8DAA8D;IAC9D,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kEAAkE;IAClE,YAAY,EAAE,MAAM,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,8DAA8D;IAC9D,WAAW,EAAE,MAAM,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,sBAAsB,GAAG,qBAAqB,CAiBhG"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host-mode-watcher.ts
|
|
3
|
+
*
|
|
4
|
+
* Shared helper that creates a config-key watcher for host-mode restart logic.
|
|
5
|
+
* Extracted so the restart guard + dirty-flag loop-back pattern can be tested
|
|
6
|
+
* independently of DaemonServer or HttpListener.
|
|
7
|
+
*
|
|
8
|
+
* Both facade.ts and http-listener.ts delegate to this helper, passing their
|
|
9
|
+
* own `onRestart` closure and `getIsRunning` accessor.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Create a host-mode restart watcher.
|
|
13
|
+
*
|
|
14
|
+
* Behaviour:
|
|
15
|
+
* - Subscribes to each key in `keys`.
|
|
16
|
+
* - When a key changes and `getIsRunning()` is true, calls `onRestart()`.
|
|
17
|
+
* - The caller's `onRestart` implementation handles re-entrancy / dirty-flag
|
|
18
|
+
* logic internally (the watcher does NOT duplicate that logic).
|
|
19
|
+
* - `unsubscribe()` removes all subscriptions; subsequent key changes are
|
|
20
|
+
* silently ignored.
|
|
21
|
+
*
|
|
22
|
+
* @param opts - Watcher configuration.
|
|
23
|
+
* @returns A handle with an `unsubscribe` method.
|
|
24
|
+
*/
|
|
25
|
+
export function createHostModeRestartWatcher(opts) {
|
|
26
|
+
const { configManager, keys, onRestart, getIsRunning } = opts;
|
|
27
|
+
const listener = () => {
|
|
28
|
+
if (!getIsRunning())
|
|
29
|
+
return;
|
|
30
|
+
onRestart();
|
|
31
|
+
};
|
|
32
|
+
const unsubs = keys.map((key) => configManager.subscribe(key, listener));
|
|
33
|
+
return {
|
|
34
|
+
unsubscribe: () => {
|
|
35
|
+
for (const u of unsubs)
|
|
36
|
+
u();
|
|
37
|
+
// Clear the array so subsequent calls are no-ops.
|
|
38
|
+
unsubs.length = 0;
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -39,6 +39,14 @@ export declare class HttpListener {
|
|
|
39
39
|
private readonly configManager;
|
|
40
40
|
private readonly serveFactory;
|
|
41
41
|
private tlsState;
|
|
42
|
+
/** Unsubscribe from httpListener config key watchers; cleared on stop(). */
|
|
43
|
+
private _configWatchUnsub;
|
|
44
|
+
/** True while a config-driven restart is in progress — prevents re-entrancy. */
|
|
45
|
+
private _restarting;
|
|
46
|
+
/** Awaitable promise for the active restart cycle; null when idle. */
|
|
47
|
+
private _restartingPromise;
|
|
48
|
+
/** True if a config change arrived while _restarting was set; triggers a second cycle. */
|
|
49
|
+
private _restartDirty;
|
|
42
50
|
constructor(config: HttpListenerConfig);
|
|
43
51
|
/**
|
|
44
52
|
* Enable the listener. Requires danger.httpListener = true in config.
|
|
@@ -50,10 +58,19 @@ export declare class HttpListener {
|
|
|
50
58
|
* Start listening. Refuses to start if not enabled.
|
|
51
59
|
*/
|
|
52
60
|
start(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Wait for any in-progress config-driven restart to settle.
|
|
63
|
+
*/
|
|
64
|
+
waitForRestart(): Promise<void>;
|
|
53
65
|
/**
|
|
54
66
|
* Stop the listener.
|
|
55
67
|
*/
|
|
56
68
|
stop(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Subscribe to httpListener binding keys and restart the server on change.
|
|
71
|
+
* Called once from start() after the server is up. Clears itself on stop().
|
|
72
|
+
*/
|
|
73
|
+
private _attachHttpListenerConfigWatcher;
|
|
57
74
|
/**
|
|
58
75
|
* Returns true if the listener is currently running.
|
|
59
76
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-listener.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/daemon/http-listener.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAMxD,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"http-listener.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/daemon/http-listener.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAMxD,OAAO,EAAE,eAAe,EAAE,MAAM,mDAAmD,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAWrD,UAAU,kBAAkB;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,aAAa,EAAE,aAAa,CAAC;IAC7B,YAAY,CAAC,EAAE,OAAO,GAAG,CAAC,KAAK,CAAC;IAChC,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,UAAU,gBAAgB;IACxB,YAAY,EAAE,OAAO,CAAC;CACvB;AA0ED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAsBX,OAAO,CAAC,MAAM;IArB1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,cAAc,CAAW;IACjC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmB;IAChD,OAAO,CAAC,QAAQ,CAA0C;IAC1D,4EAA4E;IAC5E,OAAO,CAAC,iBAAiB,CAA6B;IACtD,gFAAgF;IAChF,OAAO,CAAC,WAAW,CAAS;IAC5B,sEAAsE;IACtE,OAAO,CAAC,kBAAkB,CAA8B;IACxD,0FAA0F;IAC1F,OAAO,CAAC,aAAa,CAAS;gBAEV,MAAM,EAAE,kBAAkB;IAiB9C;;;;OAIG;IACH,MAAM,CAAC,YAAY,EAAE,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO;IAU/D;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC5B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAKrC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;;OAGG;IACH,OAAO,CAAC,gCAAgC;IAwDxC;;OAEG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAMD,OAAO,CAAC,SAAS;YAOH,aAAa;YAYb,aAAa;YAsCb,WAAW;YA6BX,aAAa;CAgD5B"}
|
|
@@ -7,6 +7,7 @@ import { extractForwardedClientIp, resolveInboundTlsContext } from '../runtime/n
|
|
|
7
7
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
|
|
8
8
|
import { requirePortAvailable } from './port-check.js';
|
|
9
9
|
import { resolveHostBinding } from './host-resolver.js';
|
|
10
|
+
import { createHostModeRestartWatcher } from './host-mode-watcher.js';
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
11
12
|
// Rate limiter (sliding window per IP, in-memory)
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
@@ -95,6 +96,14 @@ export class HttpListener {
|
|
|
95
96
|
configManager;
|
|
96
97
|
serveFactory;
|
|
97
98
|
tlsState = null;
|
|
99
|
+
/** Unsubscribe from httpListener config key watchers; cleared on stop(). */
|
|
100
|
+
_configWatchUnsub = null;
|
|
101
|
+
/** True while a config-driven restart is in progress — prevents re-entrancy. */
|
|
102
|
+
_restarting = false;
|
|
103
|
+
/** Awaitable promise for the active restart cycle; null when idle. */
|
|
104
|
+
_restartingPromise = null;
|
|
105
|
+
/** True if a config change arrived while _restarting was set; triggers a second cycle. */
|
|
106
|
+
_restartDirty = false;
|
|
98
107
|
constructor(config) {
|
|
99
108
|
this.config = config;
|
|
100
109
|
this.configManager = config.configManager;
|
|
@@ -136,7 +145,10 @@ export class HttpListener {
|
|
|
136
145
|
logger.info('HttpListener: already running');
|
|
137
146
|
return;
|
|
138
147
|
}
|
|
139
|
-
|
|
148
|
+
// Skip real OS port check when a mock serveFactory is injected (test-only path).
|
|
149
|
+
if (this.serveFactory === Bun.serve) {
|
|
150
|
+
await requirePortAvailable(this.port, this.host, 'HTTP listener');
|
|
151
|
+
}
|
|
140
152
|
const self = this;
|
|
141
153
|
this.tlsState = resolveInboundTlsContext(this.configManager, 'httpListener');
|
|
142
154
|
this.server = this.serveFactory({
|
|
@@ -147,6 +159,7 @@ export class HttpListener {
|
|
|
147
159
|
return self.handleRequest(req);
|
|
148
160
|
},
|
|
149
161
|
});
|
|
162
|
+
this._attachHttpListenerConfigWatcher();
|
|
150
163
|
logger.info('HttpListener started', {
|
|
151
164
|
port: this.port,
|
|
152
165
|
host: this.host,
|
|
@@ -155,12 +168,28 @@ export class HttpListener {
|
|
|
155
168
|
trustProxy: this.tlsState.trustProxy,
|
|
156
169
|
});
|
|
157
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Wait for any in-progress config-driven restart to settle.
|
|
173
|
+
*/
|
|
174
|
+
async waitForRestart() {
|
|
175
|
+
// Loop to handle dirty-flag chained restarts: each cycle may spawn another.
|
|
176
|
+
while (this._restartingPromise)
|
|
177
|
+
await this._restartingPromise;
|
|
178
|
+
}
|
|
158
179
|
/**
|
|
159
180
|
* Stop the listener.
|
|
160
181
|
*/
|
|
161
182
|
async stop() {
|
|
162
183
|
if (this.server === null)
|
|
163
184
|
return;
|
|
185
|
+
// Tear down config watcher only on intentional stop, not mid-restart.
|
|
186
|
+
// During a restart cycle (_restarting=true) the watcher must stay active so
|
|
187
|
+
// config changes that arrive between stop() and the subsequent start() can be
|
|
188
|
+
// captured by the dirty flag.
|
|
189
|
+
if (!this._restarting) {
|
|
190
|
+
this._configWatchUnsub?.();
|
|
191
|
+
this._configWatchUnsub = null;
|
|
192
|
+
}
|
|
164
193
|
// Stop rate limiter sweep interval before tearing down (C5 fix)
|
|
165
194
|
this.rateLimiter.stop();
|
|
166
195
|
this.server.stop(true);
|
|
@@ -168,6 +197,63 @@ export class HttpListener {
|
|
|
168
197
|
this.tlsState = null;
|
|
169
198
|
logger.info('HttpListener stopped');
|
|
170
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* Subscribe to httpListener binding keys and restart the server on change.
|
|
202
|
+
* Called once from start() after the server is up. Clears itself on stop().
|
|
203
|
+
*/
|
|
204
|
+
_attachHttpListenerConfigWatcher() {
|
|
205
|
+
if (this._configWatchUnsub)
|
|
206
|
+
return; // idempotent
|
|
207
|
+
const restart = () => {
|
|
208
|
+
if (this._restarting) {
|
|
209
|
+
// A change arrived mid-restart — queue a second cycle via dirty flag.
|
|
210
|
+
// Check _restarting BEFORE isRunning: stop() runs synchronously inside the
|
|
211
|
+
// restart IIFE, so isRunning may be false even while a restart is in progress.
|
|
212
|
+
this._restartDirty = true;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (!this.isRunning)
|
|
216
|
+
return;
|
|
217
|
+
this._restarting = true;
|
|
218
|
+
this._restartingPromise = (async () => {
|
|
219
|
+
try {
|
|
220
|
+
logger.info('HttpListener: httpListener binding changed, restarting HTTP listener…');
|
|
221
|
+
await this.stop();
|
|
222
|
+
// Re-resolve host/port from updated config
|
|
223
|
+
const newBinding = resolveHostBinding(this.configManager.get('httpListener.hostMode') ?? 'local', String(this.configManager.get('httpListener.host') ?? '127.0.0.1'), Number(this.configManager.get('httpListener.port') ?? 3422), 'httpListener');
|
|
224
|
+
this.host = newBinding.host;
|
|
225
|
+
this.port = newBinding.port;
|
|
226
|
+
await this.start();
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
logger.error('HttpListener: restart after config change failed', { error: summarizeError(err) });
|
|
230
|
+
}
|
|
231
|
+
finally {
|
|
232
|
+
this._restarting = false;
|
|
233
|
+
// If a config change arrived while we were restarting, kick off a second
|
|
234
|
+
// cycle BEFORE nulling _restartingPromise so waitForRestart() chains correctly.
|
|
235
|
+
if (this._restartDirty) {
|
|
236
|
+
this._restartDirty = false;
|
|
237
|
+
restart(); // sets this._restartingPromise to the new cycle
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
this._restartingPromise = null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
})();
|
|
244
|
+
};
|
|
245
|
+
// getIsRunning must also return true while a restart cycle is in progress
|
|
246
|
+
// (_restarting=true) so that config changes arriving mid-restart reach the
|
|
247
|
+
// dirty-flag path inside `restart`. When the server is intentionally stopped
|
|
248
|
+
// (not mid-restart) isRunning and _restarting are both false.
|
|
249
|
+
const watcher = createHostModeRestartWatcher({
|
|
250
|
+
configManager: this.configManager,
|
|
251
|
+
keys: ['httpListener.hostMode', 'httpListener.host', 'httpListener.port'],
|
|
252
|
+
onRestart: restart,
|
|
253
|
+
getIsRunning: () => this.isRunning || this._restarting,
|
|
254
|
+
});
|
|
255
|
+
this._configWatchUnsub = () => watcher.unsubscribe();
|
|
256
|
+
}
|
|
171
257
|
/**
|
|
172
258
|
* Returns true if the listener is currently running.
|
|
173
259
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-compat.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/providers/anthropic-compat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,YAAY,
|
|
1
|
+
{"version":3,"file":"anthropic-compat.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/providers/anthropic-compat.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,YAAY,EAEZ,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AA8CxB,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,gFAAgF;IAChF,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,gFAAgF;IAChF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,iGAAiG;IACjG,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qDAAqD;IACrD,cAAc,CAAC,EAAE,WAAW,GAAG,QAAQ,CAAC;IACxC,+CAA+C;IAC/C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;GAQG;AACH,qBAAa,uBAAwB,YAAW,WAAW;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAE1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAS;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAU;IAC9C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAS;gBAE9B,IAAI,EAAE,sBAAsB;IAkBlC,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA4MhD,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;CA6C3F"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { REASONING_BUDGET_MAP } from './interface.js';
|
|
2
2
|
import { ProviderError } from '@pellux/goodvibes-sdk/platform/types/errors';
|
|
3
|
+
import { mapAnthropicStopReason } from './stop-reason-maps.js';
|
|
3
4
|
import { withRetry } from '@pellux/goodvibes-sdk/platform/utils/retry';
|
|
4
5
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils/logger';
|
|
5
6
|
import { toAnthropicTools, toAnthropicMessages, fromAnthropicContent, } from './tool-formats.js';
|
|
@@ -120,7 +121,8 @@ export class AnthropicCompatProvider {
|
|
|
120
121
|
let outputTokens = 0;
|
|
121
122
|
let cacheReadTokens = 0;
|
|
122
123
|
let cacheWriteTokens = 0;
|
|
123
|
-
let
|
|
124
|
+
let rawStopReason;
|
|
125
|
+
let stopReason = 'unknown';
|
|
124
126
|
// Accumulate tool use blocks by index
|
|
125
127
|
const toolBlocks = new Map();
|
|
126
128
|
const reader = res.body?.getReader();
|
|
@@ -188,10 +190,10 @@ export class AnthropicCompatProvider {
|
|
|
188
190
|
}
|
|
189
191
|
}
|
|
190
192
|
else if (event.type === 'message_delta') {
|
|
191
|
-
if (event.delta?.stop_reason
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
if (event.delta?.stop_reason) {
|
|
194
|
+
rawStopReason = event.delta.stop_reason;
|
|
195
|
+
stopReason = mapAnthropicStopReason(rawStopReason);
|
|
196
|
+
}
|
|
195
197
|
if (event.usage?.output_tokens)
|
|
196
198
|
outputTokens = event.usage.output_tokens;
|
|
197
199
|
if (event.usage?.cache_read_input_tokens != null)
|
|
@@ -239,7 +241,8 @@ export class AnthropicCompatProvider {
|
|
|
239
241
|
content: text,
|
|
240
242
|
toolCalls,
|
|
241
243
|
usage: { inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens },
|
|
242
|
-
stopReason,
|
|
244
|
+
stopReason: stopReason === 'unknown' && text ? 'completed' : stopReason,
|
|
245
|
+
...(rawStopReason !== undefined ? { providerStopReason: rawStopReason } : {}),
|
|
243
246
|
};
|
|
244
247
|
});
|
|
245
248
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic-sdk-provider.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/providers/anthropic-sdk-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,
|
|
1
|
+
{"version":3,"file":"anthropic-sdk-provider.d.ts","sourceRoot":"","sources":["../../../../src/_internal/platform/providers/anthropic-sdk-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAEZ,WAAW,EACX,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AAgBxB,KAAK,4BAA4B,GAAG;IAClC,QAAQ,EAAE;QACR,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;CACH,CAAC;AAoBF,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,WAAW,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,4BAA4B,CAAC;IAC1D,QAAQ,CAAC,IAAI,EAAE,8BAA8B,CAAC;IAC9C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,qBAAa,oBAAqB,YAAW,WAAW;IAI1C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;gBAEG,OAAO,EAAE,2BAA2B;IAK3D,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAsHhD,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;CAqC3F"}
|