@bastani/atomic 0.9.4-alpha.6 → 0.9.4-alpha.7
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/CHANGELOG.md +20 -0
- package/dist/builtin/cursor/package.json +3 -3
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +2 -2
- package/dist/builtin/subagents/CHANGELOG.md +14 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/runs/background/async-execution-chain.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +11 -1
- package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +2 -0
- package/dist/builtin/subagents/src/runs/background/subagent-runner-step.ts +27 -4
- package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +25 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +1 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +3 -1
- package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +22 -0
- package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +15 -5
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +5 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +4 -1
- package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +4 -1
- package/dist/builtin/subagents/src/runs/shared/attempt-watchdog.ts +126 -0
- package/dist/builtin/subagents/src/runs/shared/model-candidate-filter.ts +53 -0
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +52 -53
- package/dist/builtin/subagents/src/runs/shared/parallel-utils.ts +2 -0
- package/dist/builtin/subagents/src/shared/model-info.ts +16 -0
- package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +6 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/runs/shared/model-fallback-failures.ts +34 -4
- package/dist/core/agent-session-extension-bindings.d.ts +2 -0
- package/dist/core/agent-session-extension-bindings.d.ts.map +1 -1
- package/dist/core/agent-session-extension-bindings.js +10 -2
- package/dist/core/agent-session-extension-bindings.js.map +1 -1
- package/dist/core/agent-session-methods.d.ts +2 -1
- package/dist/core/agent-session-methods.d.ts.map +1 -1
- package/dist/core/agent-session-methods.js.map +1 -1
- package/dist/core/copilot-model-catalog.d.ts +25 -74
- package/dist/core/copilot-model-catalog.d.ts.map +1 -1
- package/dist/core/copilot-model-catalog.js +118 -68
- package/dist/core/copilot-model-catalog.js.map +1 -1
- package/dist/core/copilot-model-synthesis.d.ts +10 -0
- package/dist/core/copilot-model-synthesis.d.ts.map +1 -0
- package/dist/core/copilot-model-synthesis.js +89 -0
- package/dist/core/copilot-model-synthesis.js.map +1 -0
- package/dist/core/model-registry-builtins.d.ts.map +1 -1
- package/dist/core/model-registry-builtins.js +21 -3
- package/dist/core/model-registry-builtins.js.map +1 -1
- package/dist/core/tools/read-selectors.d.ts.map +1 -1
- package/dist/core/tools/read-selectors.js +19 -1
- package/dist/core/tools/read-selectors.js.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-model-routing.js +1 -0
- package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
- package/docs/models.md +3 -3
- package/docs/providers.md +1 -1
- package/docs/rpc.md +2 -2
- package/docs/sdk.md +1 -1
- package/docs/settings.md +2 -1
- package/docs/subagents.md +6 -0
- package/docs/workflows.md +2 -0
- package/npm-shrinkwrap.json +27 -27
- package/package.json +6 -6
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { APP_NAME } from "@bastani/atomic";
|
|
3
3
|
import { ChainClarifyComponent, type ChainClarifyResult } from "./chain-clarify.ts";
|
|
4
4
|
import { currentModelFullId, resolveModelCandidate } from "../shared/model-fallback.ts";
|
|
5
|
-
import { toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
5
|
+
import { collectKnownModelProviders, toModelInfo, type ModelInfo } from "../../shared/model-info.ts";
|
|
6
6
|
import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.ts";
|
|
7
7
|
import { recordRun } from "../shared/run-history.ts";
|
|
8
8
|
import { getSingleResultOutput, compactForegroundDetails } from "../../shared/utils.ts";
|
|
@@ -76,6 +76,7 @@ export async function runSinglePath(data: ExecutionContextData, deps: ResolvedEx
|
|
|
76
76
|
|
|
77
77
|
const currentProvider = ctx.model?.provider;
|
|
78
78
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
79
|
+
const knownModelProviders = collectKnownModelProviders(ctx.modelRegistry);
|
|
79
80
|
let task = params.task ?? "";
|
|
80
81
|
let modelOverride: string | undefined = resolveModelCandidate(
|
|
81
82
|
(params.model as string | undefined) ?? agentConfig.model,
|
|
@@ -145,6 +146,7 @@ export async function runSinglePath(data: ExecutionContextData, deps: ResolvedEx
|
|
|
145
146
|
agentConfig,
|
|
146
147
|
ctx: asyncCtx,
|
|
147
148
|
availableModels,
|
|
149
|
+
knownModelProviders,
|
|
148
150
|
cwd: effectiveCwd,
|
|
149
151
|
maxOutput: params.maxOutput,
|
|
150
152
|
artifactsDir: artifactConfig.enabled ? artifactsDir : undefined,
|
|
@@ -246,6 +248,7 @@ export async function runSinglePath(data: ExecutionContextData, deps: ResolvedEx
|
|
|
246
248
|
index: 0,
|
|
247
249
|
modelOverride,
|
|
248
250
|
availableModels,
|
|
251
|
+
knownModelProviders,
|
|
249
252
|
preferredModelProvider: currentProvider,
|
|
250
253
|
currentModel: currentModelFullId(ctx.model),
|
|
251
254
|
skills: effectiveSkills,
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { ChildProcess } from "node:child_process";
|
|
2
|
+
import { trySignalChild } from "../../shared/post-exit-stdio-guard.ts";
|
|
3
|
+
|
|
4
|
+
const DEFAULT_IDLE_MS = 5 * 60_000;
|
|
5
|
+
const DEFAULT_WALL_MS = 60 * 60_000;
|
|
6
|
+
const DEFAULT_KILL_GRACE_MS = 3_000;
|
|
7
|
+
|
|
8
|
+
export interface AttemptTimeoutConfig {
|
|
9
|
+
idleMs: number;
|
|
10
|
+
wallMs: number;
|
|
11
|
+
killGraceMs: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AttemptWatchdog {
|
|
15
|
+
activity(): void;
|
|
16
|
+
clear(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Parse a millisecond override from the environment. Non-numeric values are
|
|
20
|
+
* silently ignored (the default applies). Zero or negative values are clamped
|
|
21
|
+
* to `0`, which callers treat as "timeout disabled". */
|
|
22
|
+
function envMs(name: string): number | undefined {
|
|
23
|
+
const raw = process.env[name];
|
|
24
|
+
if (!raw) return undefined;
|
|
25
|
+
const value = Number(raw);
|
|
26
|
+
if (!Number.isFinite(value)) return undefined;
|
|
27
|
+
return value > 0 ? Math.floor(value) : 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function positiveEnvMs(name: string): number | undefined {
|
|
31
|
+
const value = envMs(name);
|
|
32
|
+
return value !== undefined && value > 0 ? value : undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function resolveAttemptTimeoutConfig(): AttemptTimeoutConfig {
|
|
36
|
+
return {
|
|
37
|
+
// `0` (or a negative value) disables the corresponding timeout entirely.
|
|
38
|
+
idleMs: envMs("ATOMIC_SUBAGENT_ATTEMPT_IDLE_TIMEOUT_MS") ?? DEFAULT_IDLE_MS,
|
|
39
|
+
wallMs: envMs("ATOMIC_SUBAGENT_ATTEMPT_TIMEOUT_MS") ?? DEFAULT_WALL_MS,
|
|
40
|
+
// The SIGTERM→SIGKILL grace period intentionally cannot be disabled: once a
|
|
41
|
+
// watchdog trips, escalation must always be bounded. `0`, negative, or
|
|
42
|
+
// non-numeric values fall back to the default.
|
|
43
|
+
killGraceMs: positiveEnvMs("ATOMIC_SUBAGENT_ATTEMPT_KILL_GRACE_MS") ?? DEFAULT_KILL_GRACE_MS,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function idleTimeoutMessage(idleMs: number): string {
|
|
48
|
+
return `Subagent model attempt timed out after ${idleMs}ms without child activity.`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function wallTimeoutMessage(wallMs: number): string {
|
|
52
|
+
return `Subagent model attempt timed out after ${wallMs}ms.`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createAttemptWatchdog(params: {
|
|
56
|
+
child: ChildProcess;
|
|
57
|
+
config?: Partial<AttemptTimeoutConfig>;
|
|
58
|
+
onTimeout: (message: string) => void;
|
|
59
|
+
isSettled: () => boolean;
|
|
60
|
+
/** Reports whether a tool call is currently executing in the child. A slow tool
|
|
61
|
+
* (long build, large test suite) can legitimately stay silent past the idle
|
|
62
|
+
* window, so an in-flight tool execution counts as activity and defers the idle
|
|
63
|
+
* trip. The wall-clock cap still bounds the whole attempt. */
|
|
64
|
+
isToolActive?: () => boolean;
|
|
65
|
+
}): AttemptWatchdog {
|
|
66
|
+
const config = { ...resolveAttemptTimeoutConfig(), ...(params.config ?? {}) };
|
|
67
|
+
let idleTimer: NodeJS.Timeout | undefined;
|
|
68
|
+
let wallTimer: NodeJS.Timeout | undefined;
|
|
69
|
+
let killTimer: NodeJS.Timeout | undefined;
|
|
70
|
+
let tripped = false;
|
|
71
|
+
|
|
72
|
+
const clearIdle = () => {
|
|
73
|
+
if (!idleTimer) return;
|
|
74
|
+
clearTimeout(idleTimer);
|
|
75
|
+
idleTimer = undefined;
|
|
76
|
+
};
|
|
77
|
+
const clearAll = () => {
|
|
78
|
+
clearIdle();
|
|
79
|
+
if (wallTimer) {
|
|
80
|
+
clearTimeout(wallTimer);
|
|
81
|
+
wallTimer = undefined;
|
|
82
|
+
}
|
|
83
|
+
if (killTimer) {
|
|
84
|
+
clearTimeout(killTimer);
|
|
85
|
+
killTimer = undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const trip = (message: string) => {
|
|
89
|
+
if (tripped || params.isSettled()) return;
|
|
90
|
+
tripped = true;
|
|
91
|
+
clearIdle();
|
|
92
|
+
params.onTimeout(message);
|
|
93
|
+
trySignalChild(params.child, "SIGTERM");
|
|
94
|
+
killTimer = setTimeout(() => {
|
|
95
|
+
if (!params.isSettled()) trySignalChild(params.child, "SIGKILL");
|
|
96
|
+
}, config.killGraceMs);
|
|
97
|
+
killTimer.unref?.();
|
|
98
|
+
};
|
|
99
|
+
const scheduleIdle = () => {
|
|
100
|
+
clearIdle();
|
|
101
|
+
if (config.idleMs <= 0) return; // idle watchdog disabled
|
|
102
|
+
idleTimer = setTimeout(() => {
|
|
103
|
+
if (params.isToolActive?.()) {
|
|
104
|
+
// Do not kill a healthy attempt that is busy inside a slow tool call;
|
|
105
|
+
// re-arm the idle window and let the wall-clock cap bound the attempt.
|
|
106
|
+
scheduleIdle();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
trip(idleTimeoutMessage(config.idleMs));
|
|
110
|
+
}, config.idleMs);
|
|
111
|
+
idleTimer.unref?.();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
scheduleIdle();
|
|
115
|
+
if (config.wallMs > 0) {
|
|
116
|
+
wallTimer = setTimeout(() => trip(wallTimeoutMessage(config.wallMs)), config.wallMs);
|
|
117
|
+
wallTimer.unref?.();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
activity() {
|
|
122
|
+
if (!tripped && !params.isSettled()) scheduleIdle();
|
|
123
|
+
},
|
|
124
|
+
clear: clearAll,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { splitKnownThinkingSuffix } from "../../shared/model-info.ts";
|
|
2
|
+
import type { ModelAttempt } from "../../shared/types.ts";
|
|
3
|
+
import type { AvailableModelInfo } from "./model-fallback.ts";
|
|
4
|
+
|
|
5
|
+
export interface FilteredModelCandidates {
|
|
6
|
+
candidates: string[];
|
|
7
|
+
skippedAttempts: ModelAttempt[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function providerFromModel(model: string | undefined): string | undefined {
|
|
11
|
+
if (!model) return undefined;
|
|
12
|
+
const { baseModel } = splitKnownThinkingSuffix(model);
|
|
13
|
+
const slash = baseModel.indexOf("/");
|
|
14
|
+
return slash > 0 ? baseModel.slice(0, slash) : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function skippedModelAttempt(model: string, reason: string): ModelAttempt {
|
|
18
|
+
return {
|
|
19
|
+
model,
|
|
20
|
+
success: false,
|
|
21
|
+
exitCode: null,
|
|
22
|
+
error: reason,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function filterSpawnableModelCandidates(params: {
|
|
27
|
+
candidates: string[];
|
|
28
|
+
availableModels?: AvailableModelInfo[];
|
|
29
|
+
knownModelProviders?: string[];
|
|
30
|
+
currentModel?: string;
|
|
31
|
+
}): FilteredModelCandidates {
|
|
32
|
+
const providersWithAvailableAuth = new Set((params.availableModels ?? []).map((model) => model.provider));
|
|
33
|
+
const knownProviders = new Set(params.knownModelProviders ?? []);
|
|
34
|
+
const currentModel = params.currentModel;
|
|
35
|
+
const filtered: string[] = [];
|
|
36
|
+
const skippedAttempts: ModelAttempt[] = [];
|
|
37
|
+
for (const candidate of params.candidates) {
|
|
38
|
+
if (currentModel && candidate === currentModel) {
|
|
39
|
+
filtered.push(candidate);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const provider = providerFromModel(candidate);
|
|
43
|
+
const shouldSkip = provider !== undefined
|
|
44
|
+
&& knownProviders.has(provider)
|
|
45
|
+
&& !providersWithAvailableAuth.has(provider);
|
|
46
|
+
if (!shouldSkip) {
|
|
47
|
+
filtered.push(candidate);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
skippedAttempts.push(skippedModelAttempt(candidate, `Skipped ${candidate}: provider '${provider}' has no configured API key/auth in the current session.`));
|
|
51
|
+
}
|
|
52
|
+
return { candidates: filtered, skippedAttempts };
|
|
53
|
+
}
|
|
@@ -117,6 +117,7 @@ export type ModelFallbackFailureKind =
|
|
|
117
117
|
| "provider_unavailable"
|
|
118
118
|
| "network_timeout"
|
|
119
119
|
| "model_unavailable"
|
|
120
|
+
| "request_incompatible"
|
|
120
121
|
| "cancelled"
|
|
121
122
|
| "task_failure"
|
|
122
123
|
| "unknown";
|
|
@@ -144,6 +145,7 @@ const FALLBACKABLE_FAILURE_KINDS: ReadonlySet<ModelFallbackFailureKind> = new Se
|
|
|
144
145
|
"provider_unavailable",
|
|
145
146
|
"network_timeout",
|
|
146
147
|
"model_unavailable",
|
|
148
|
+
"request_incompatible",
|
|
147
149
|
]);
|
|
148
150
|
|
|
149
151
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
@@ -222,6 +224,9 @@ function normalizeCode(value: string | number | undefined): string | undefined {
|
|
|
222
224
|
|
|
223
225
|
function kindFromStatus(status: number | undefined): ModelFallbackFailureKind | undefined {
|
|
224
226
|
switch (status) {
|
|
227
|
+
case 400:
|
|
228
|
+
case 413: case 422:
|
|
229
|
+
return "request_incompatible";
|
|
225
230
|
case 401:
|
|
226
231
|
case 403:
|
|
227
232
|
return "auth_on_candidate_provider";
|
|
@@ -237,6 +242,15 @@ function kindFromStatus(status: number | undefined): ModelFallbackFailureKind |
|
|
|
237
242
|
}
|
|
238
243
|
}
|
|
239
244
|
|
|
245
|
+
const REQUEST_INCOMPATIBLE_CODES: ReadonlySet<string> = new Set([
|
|
246
|
+
"invalid_request", "invalid_request_error", "bad_request", "context_length_exceeded",
|
|
247
|
+
"request_too_large", "too_large", "request_entity_too_large", "max_tokens",
|
|
248
|
+
"max_context_length", "context_window_exceeded",
|
|
249
|
+
]);
|
|
250
|
+
function requestIncompatibleKindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
|
|
251
|
+
const normalizedCode = normalizeCode(code);
|
|
252
|
+
return normalizedCode !== undefined && REQUEST_INCOMPATIBLE_CODES.has(normalizedCode) ? "request_incompatible" : undefined;
|
|
253
|
+
}
|
|
240
254
|
function refusalKindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
|
|
241
255
|
const normalizedCode = normalizeCode(code);
|
|
242
256
|
if (normalizedCode === undefined) return undefined;
|
|
@@ -258,6 +272,14 @@ function refusalKindFromCode(code: string | number | undefined): ModelFallbackFa
|
|
|
258
272
|
}
|
|
259
273
|
}
|
|
260
274
|
|
|
275
|
+
const CODE_KINDS_BY_KIND: ReadonlyArray<readonly [ModelFallbackFailureKind, ReadonlySet<string>]> = [
|
|
276
|
+
["auth_on_candidate_provider", new Set(["auth", "auth_required", "authentication_required", "unauthorized", "forbidden", "invalid_api_key", "missing_api_key", "invalid_key"])],
|
|
277
|
+
["network_timeout", new Set(["etimedout", "econnreset", "econnrefused", "enotfound", "eai_again", "fetch_failed", "network_error", "timeout", "timeout_error", "und_err_connect_timeout"])],
|
|
278
|
+
["rate_limit", new Set(["rate_limit", "rate_limit_exceeded", "too_many_requests", "quota_exceeded"])],
|
|
279
|
+
["cancelled", new Set(["aborterror", "aborted", "cancelled", "canceled"])],
|
|
280
|
+
["model_unavailable", new Set(["model_not_found", "model_unavailable", "model_disabled", "unknown_model"])],
|
|
281
|
+
["provider_unavailable", new Set(["provider_error", "api_error", "service_unavailable", "temporarily_unavailable", "overloaded"])],
|
|
282
|
+
];
|
|
261
283
|
function kindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
|
|
262
284
|
const normalizedCode = normalizeCode(code);
|
|
263
285
|
if (normalizedCode === undefined) return undefined;
|
|
@@ -265,54 +287,19 @@ function kindFromCode(code: string | number | undefined): ModelFallbackFailureKi
|
|
|
265
287
|
if (refusalKind !== undefined) return refusalKind;
|
|
266
288
|
const httpStatusKind = kindFromStatus(integerFrom(code));
|
|
267
289
|
if (httpStatusKind !== undefined) return httpStatusKind;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
case "econnrefused":
|
|
282
|
-
case "enotfound":
|
|
283
|
-
case "eai_again":
|
|
284
|
-
case "fetch_failed":
|
|
285
|
-
case "network_error":
|
|
286
|
-
case "timeout":
|
|
287
|
-
case "timeout_error":
|
|
288
|
-
case "und_err_connect_timeout":
|
|
289
|
-
return "network_timeout";
|
|
290
|
-
case "rate_limit":
|
|
291
|
-
case "rate_limit_exceeded":
|
|
292
|
-
case "too_many_requests":
|
|
293
|
-
case "quota_exceeded":
|
|
294
|
-
return "rate_limit";
|
|
295
|
-
case "aborterror":
|
|
296
|
-
case "aborted":
|
|
297
|
-
case "cancelled":
|
|
298
|
-
case "canceled":
|
|
299
|
-
return "cancelled";
|
|
300
|
-
case "model_not_found":
|
|
301
|
-
case "model_unavailable":
|
|
302
|
-
case "model_disabled":
|
|
303
|
-
case "unknown_model":
|
|
304
|
-
return "model_unavailable";
|
|
305
|
-
case "provider_error":
|
|
306
|
-
case "api_error":
|
|
307
|
-
case "service_unavailable":
|
|
308
|
-
case "temporarily_unavailable":
|
|
309
|
-
case "overloaded":
|
|
310
|
-
return "provider_unavailable";
|
|
311
|
-
default:
|
|
312
|
-
return undefined;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
290
|
+
const requestIncompatibleKind = requestIncompatibleKindFromCode(code);
|
|
291
|
+
if (requestIncompatibleKind !== undefined) return requestIncompatibleKind;
|
|
292
|
+
return CODE_KINDS_BY_KIND.find(([_, codes]) => codes.has(normalizedCode))?.[0];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const REQUEST_INCOMPATIBLE_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
296
|
+
/\bcontext[_\s-]?(?:length|window)(?:[_\s-]?exceeded)?\b/i,
|
|
297
|
+
/\bmax[_\s-]?(?:context|tokens?)\b/i,
|
|
298
|
+
/\b(?:request(?:[_\s-]?entity)?[_\s-]?too|too)[_\s-]?large\b/i,
|
|
299
|
+
/\b(?:unsupported|unknown|invalid)\s+(?:tool|parameter|function)\b/i,
|
|
300
|
+
/\b(?:tool|parameter|function)\s+(?:not\s+(?:supported|found|allowed)|unknown|invalid)\b/i,
|
|
301
|
+
/\b(?:invalid[_\s-]?request(?:[_\s-]?error)?|bad[_\s-]?request)\b/i,
|
|
302
|
+
];
|
|
316
303
|
const PROVIDER_REFUSAL_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
317
304
|
/\bfinish[_\s-]?reason\b[^\n]*\bcontent[_\s-]?filter\b/i,
|
|
318
305
|
/\bcontent[_\s-]?filter(?:ed|ing)?\b/i,
|
|
@@ -335,6 +322,7 @@ function refusalKindFromMessage(message: string): ModelFallbackFailureKind | und
|
|
|
335
322
|
function fallbackKindFromMessage(message: string, name: string | undefined): ModelFallbackFailureKind | undefined {
|
|
336
323
|
const refusalKind = refusalKindFromMessage(message);
|
|
337
324
|
if (refusalKind !== undefined) return refusalKind;
|
|
325
|
+
if (REQUEST_INCOMPATIBLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "request_incompatible";
|
|
338
326
|
const nameKind = kindFromCode(name);
|
|
339
327
|
if (nameKind !== undefined) return nameKind;
|
|
340
328
|
if (!RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return undefined;
|
|
@@ -372,15 +360,21 @@ function makeSignal(
|
|
|
372
360
|
};
|
|
373
361
|
}
|
|
374
362
|
|
|
375
|
-
function
|
|
363
|
+
function fallbackSignalForMessage(
|
|
364
|
+
message: string | undefined,
|
|
376
365
|
value: unknown,
|
|
377
366
|
source: ModelFallbackFailureSource | undefined,
|
|
378
367
|
): ModelFallbackFailureSignal | undefined {
|
|
379
|
-
|
|
380
|
-
if (!message.trim()) return undefined;
|
|
368
|
+
if (message === undefined || message.trim().length === 0) return undefined;
|
|
381
369
|
const kind = fallbackKindFromMessage(message, errorName(value));
|
|
382
370
|
return kind === undefined ? undefined : makeSignal(kind, value, source);
|
|
383
371
|
}
|
|
372
|
+
function fallbackSignalFromMessage(value: unknown, source: ModelFallbackFailureSource | undefined): ModelFallbackFailureSignal | undefined {
|
|
373
|
+
return fallbackSignalForMessage(modelFailureMessage(value), value, source);
|
|
374
|
+
}
|
|
375
|
+
function fallbackSignalFromDirectMessage(value: unknown, source: ModelFallbackFailureSource | undefined): ModelFallbackFailureSignal | undefined {
|
|
376
|
+
return fallbackSignalForMessage(directMessageFrom(value), value, source);
|
|
377
|
+
}
|
|
384
378
|
|
|
385
379
|
function classifyAssistantRefusalSignal(
|
|
386
380
|
value: unknown,
|
|
@@ -433,6 +427,12 @@ function structuredSignal(
|
|
|
433
427
|
if (isRefusalSignal(causeSignal)) return causeSignal;
|
|
434
428
|
firstNestedFallbackSignal ??= causeSignal;
|
|
435
429
|
}
|
|
430
|
+
// Direct-message classification runs after nested traversal so a generic wrapper
|
|
431
|
+
// ("invalid request"/"400 bad request") cannot mask a non-retryable nested signal.
|
|
432
|
+
// Kept in the same position as the workflows classifier so the two copies stay
|
|
433
|
+
// behaviorally parallel (see test/unit/model-fallback-classifier-conformance.test.ts).
|
|
434
|
+
const directMessageSignal = fallbackSignalFromDirectMessage(value, source);
|
|
435
|
+
if (directMessageSignal !== undefined) return directMessageSignal;
|
|
436
436
|
const statusKind = kindFromStatus(statusFrom(value));
|
|
437
437
|
if (statusKind !== undefined) return makeSignal(statusKind, value, source);
|
|
438
438
|
if (codeKind !== undefined) return makeSignal(codeKind, value, source);
|
|
@@ -463,8 +463,7 @@ function messageFromUnknown(value: unknown, seen: Set<unknown>): string | undefi
|
|
|
463
463
|
const status = statusFrom(value);
|
|
464
464
|
if (status !== undefined) return `Model request failed with status ${status}`;
|
|
465
465
|
const code = codeFrom(value);
|
|
466
|
-
|
|
467
|
-
return undefined;
|
|
466
|
+
return code !== undefined ? `Model request failed with code ${String(code)}` : undefined;
|
|
468
467
|
}
|
|
469
468
|
export function modelFailureMessage(error: unknown): string {
|
|
470
469
|
const structuredMessage = messageFromUnknown(error, new Set());
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CodexFastModeResolvedSettings, CodexFastModeScope } from "@bastani/atomic";
|
|
2
|
+
import type { ModelAttempt } from "../../shared/types.ts";
|
|
2
3
|
|
|
3
4
|
export interface RunnerSubagentStep {
|
|
4
5
|
agent: string;
|
|
@@ -13,6 +14,7 @@ export interface RunnerSubagentStep {
|
|
|
13
14
|
fastMode?: boolean;
|
|
14
15
|
modelFastModes?: Record<string, boolean>;
|
|
15
16
|
modelCandidates?: string[];
|
|
17
|
+
modelAttempts?: ModelAttempt[];
|
|
16
18
|
codexFastModeSettings?: CodexFastModeResolvedSettings;
|
|
17
19
|
codexFastModeScope?: CodexFastModeScope;
|
|
18
20
|
tools?: string[];
|
|
@@ -27,6 +27,22 @@ export function toModelInfo(model: RegistryModelLike): ModelInfo {
|
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
interface KnownProviderRegistryLike {
|
|
31
|
+
getAvailable(): ReadonlyArray<{ provider: string }>;
|
|
32
|
+
/** Older hosts may not expose `getAll()`; callers probe at runtime and fall back
|
|
33
|
+
* to `getAvailable()`. */
|
|
34
|
+
getAll?: () => ReadonlyArray<{ provider: string }>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Collect the distinct providers of every model the registry knows about — including
|
|
38
|
+
* providers without configured auth — falling back to `getAvailable()` when the host
|
|
39
|
+
* registry does not expose `getAll()`. Used to pre-filter spawn candidates whose
|
|
40
|
+
* provider is known but keyless. */
|
|
41
|
+
export function collectKnownModelProviders(registry: KnownProviderRegistryLike): string[] {
|
|
42
|
+
const models = typeof registry.getAll === "function" ? registry.getAll() : registry.getAvailable();
|
|
43
|
+
return [...new Set(models.map((model) => model.provider))];
|
|
44
|
+
}
|
|
45
|
+
|
|
30
46
|
/** Resolve the effective thinking level from a model string (which may contain a known suffix like `:high`)
|
|
31
47
|
* and an explicit thinking config value. Returns `undefined` when no thinking is applicable
|
|
32
48
|
* (e.g. no model was specified, or the model has no suffix and no config was provided). */
|
|
@@ -80,6 +80,8 @@ export interface RunSyncOptions {
|
|
|
80
80
|
modelOverride?: string;
|
|
81
81
|
/** Registry models available for heuristic bare-model resolution */
|
|
82
82
|
availableModels?: Array<{ provider: string; id: string; fullId: string }>;
|
|
83
|
+
/** Providers known to the registry before auth filtering */
|
|
84
|
+
knownModelProviders?: string[];
|
|
83
85
|
/** Current parent-session provider to prefer for ambiguous bare model ids */
|
|
84
86
|
preferredModelProvider?: string;
|
|
85
87
|
/** Current parent-session model to try after configured fallback models */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/web-access",
|
|
3
|
-
"version": "0.9.4-alpha.
|
|
3
|
+
"version": "0.9.4-alpha.7",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
|
|
6
6
|
"contributors": [
|
|
@@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.9.4-alpha.7] - 2026-07-02
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed workflow stage model fallback so request/context incompatibility failures (HTTP 400/413/422 bad/unprocessable/payload-too-large request, unsupported tool/parameter, context-length/context-window overflow, `invalid_request`/`bad_request`/`too_large` errors) advance the chain to the next candidate instead of stopping. When none of the configured candidates can serve the request, the stage now falls back to the current user-selected model. Refusals, content-filter/safety blocks, cancellations, and task failures still stop the chain ([#1580](https://github.com/bastani-inc/atomic/issues/1580)).
|
|
14
|
+
|
|
9
15
|
## [0.9.4-alpha.6] - 2026-07-01
|
|
10
16
|
|
|
11
17
|
### Changed
|
|
@@ -51,6 +51,7 @@ export type ModelFallbackFailureKind =
|
|
|
51
51
|
| "network_timeout"
|
|
52
52
|
| "transport_error"
|
|
53
53
|
| "model_unavailable"
|
|
54
|
+
| "request_incompatible"
|
|
54
55
|
| "cancelled"
|
|
55
56
|
| "task_failure"
|
|
56
57
|
| "unknown";
|
|
@@ -79,6 +80,7 @@ const FALLBACKABLE_FAILURE_KINDS: ReadonlySet<ModelFallbackFailureKind> = new Se
|
|
|
79
80
|
"network_timeout",
|
|
80
81
|
"transport_error",
|
|
81
82
|
"model_unavailable",
|
|
83
|
+
"request_incompatible",
|
|
82
84
|
]);
|
|
83
85
|
|
|
84
86
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
@@ -157,6 +159,9 @@ function normalizeCode(value: string | number | undefined): string | undefined {
|
|
|
157
159
|
|
|
158
160
|
function kindFromStatus(status: number | undefined): ModelFallbackFailureKind | undefined {
|
|
159
161
|
switch (status) {
|
|
162
|
+
case 400:
|
|
163
|
+
case 413: case 422:
|
|
164
|
+
return "request_incompatible";
|
|
160
165
|
case 401:
|
|
161
166
|
case 403:
|
|
162
167
|
return "auth_on_candidate_provider";
|
|
@@ -193,6 +198,15 @@ function refusalKindFromCode(code: string | number | undefined): ModelFallbackFa
|
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
200
|
|
|
201
|
+
const REQUEST_INCOMPATIBLE_CODES: ReadonlySet<string> = new Set([
|
|
202
|
+
"invalid_request", "invalid_request_error", "bad_request", "context_length_exceeded",
|
|
203
|
+
"request_too_large", "too_large", "request_entity_too_large", "max_tokens",
|
|
204
|
+
"max_context_length", "context_window_exceeded",
|
|
205
|
+
]);
|
|
206
|
+
function requestIncompatibleKindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
|
|
207
|
+
const normalizedCode = normalizeCode(code);
|
|
208
|
+
return normalizedCode !== undefined && REQUEST_INCOMPATIBLE_CODES.has(normalizedCode) ? "request_incompatible" : undefined;
|
|
209
|
+
}
|
|
196
210
|
function kindFromCode(code: string | number | undefined): ModelFallbackFailureKind | undefined {
|
|
197
211
|
const normalizedCode = normalizeCode(code);
|
|
198
212
|
if (normalizedCode === undefined) return undefined;
|
|
@@ -200,6 +214,8 @@ function kindFromCode(code: string | number | undefined): ModelFallbackFailureKi
|
|
|
200
214
|
if (refusalKind !== undefined) return refusalKind;
|
|
201
215
|
const httpStatusKind = kindFromStatus(integerFrom(code));
|
|
202
216
|
if (httpStatusKind !== undefined) return httpStatusKind;
|
|
217
|
+
const requestIncompatibleKind = requestIncompatibleKindFromCode(code);
|
|
218
|
+
if (requestIncompatibleKind !== undefined) return requestIncompatibleKind;
|
|
203
219
|
|
|
204
220
|
switch (normalizedCode) {
|
|
205
221
|
case "auth":
|
|
@@ -248,6 +264,18 @@ function kindFromCode(code: string | number | undefined): ModelFallbackFailureKi
|
|
|
248
264
|
}
|
|
249
265
|
}
|
|
250
266
|
|
|
267
|
+
const REQUEST_INCOMPATIBLE_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
268
|
+
/\bcontext[_\s-]?length(?:[_\s-]?exceeded)?\b/i,
|
|
269
|
+
/\bcontext[_\s-]?window(?:[_\s-]?exceeded)?\b/i,
|
|
270
|
+
/\bmax[_\s-]?context\b/i,
|
|
271
|
+
/\bmax[_\s-]?tokens?\b/i,
|
|
272
|
+
/\brequest(?:[_\s-]?entity)?[_\s-]?too[_\s-]?large\b/i,
|
|
273
|
+
/\btoo[_\s-]?large\b/i,
|
|
274
|
+
/\b(?:unsupported|unknown|invalid)\s+(?:tool|parameter|function)\b/i,
|
|
275
|
+
/\b(?:tool|parameter|function)\s+(?:not\s+(?:supported|found|allowed)|unknown|invalid)\b/i,
|
|
276
|
+
/\binvalid[_\s-]?request(?:[_\s-]?error)?\b/i,
|
|
277
|
+
/\bbad[_\s-]?request\b/i,
|
|
278
|
+
];
|
|
251
279
|
const PROVIDER_REFUSAL_FAILURE_PATTERNS: readonly RegExp[] = [
|
|
252
280
|
/\bfinish[_\s-]?reason\b[^\n]*\bcontent[_\s-]?filter\b/i,
|
|
253
281
|
/\bcontent[_\s-]?filter(?:ed|ing)?\b/i,
|
|
@@ -283,6 +311,7 @@ function fallbackKindFromMessage(message: string, name: string | undefined): Mod
|
|
|
283
311
|
if (refusalKind !== undefined) return refusalKind;
|
|
284
312
|
const transportOutageKind = transportOutageKindFromMessage(message);
|
|
285
313
|
if (transportOutageKind !== undefined) return transportOutageKind;
|
|
314
|
+
if (REQUEST_INCOMPATIBLE_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return "request_incompatible";
|
|
286
315
|
const nameKind = kindFromCode(name);
|
|
287
316
|
if (nameKind !== undefined) return nameKind;
|
|
288
317
|
if (!RETRYABLE_MODEL_FAILURE_PATTERNS.some((pattern) => pattern.test(message))) return undefined;
|
|
@@ -371,9 +400,6 @@ function structuredSignal(
|
|
|
371
400
|
const directRefusalSignal = classifyAssistantRefusalSignal(value, source);
|
|
372
401
|
if (directRefusalSignal !== undefined) return directRefusalSignal;
|
|
373
402
|
|
|
374
|
-
const directMessageSignal = fallbackSignalFromDirectMessage(value, source);
|
|
375
|
-
if (directMessageSignal !== undefined) return directMessageSignal;
|
|
376
|
-
|
|
377
403
|
const codeKind = kindFromCode(codeFrom(value));
|
|
378
404
|
const nameKind = kindFromCode(errorName(value));
|
|
379
405
|
if (codeKind === "cancelled" || nameKind === "cancelled") return makeSignal("cancelled", value, source);
|
|
@@ -387,7 +413,6 @@ function structuredSignal(
|
|
|
387
413
|
if (isRefusalSignal(diagnosticSignal)) return diagnosticSignal;
|
|
388
414
|
firstNestedFallbackSignal ??= diagnosticSignal;
|
|
389
415
|
}
|
|
390
|
-
|
|
391
416
|
const cause = causeOf(value);
|
|
392
417
|
const causeSignal = structuredSignal(cause, nestedSeen, source)
|
|
393
418
|
?? fallbackSignalFromMessage(cause, source);
|
|
@@ -396,6 +421,11 @@ function structuredSignal(
|
|
|
396
421
|
firstNestedFallbackSignal ??= causeSignal;
|
|
397
422
|
}
|
|
398
423
|
|
|
424
|
+
// Direct-message classification runs after nested traversal so a generic wrapper
|
|
425
|
+
// ("invalid request"/"400 bad request") cannot mask a non-retryable nested signal.
|
|
426
|
+
const directMessageSignal = fallbackSignalFromDirectMessage(value, source);
|
|
427
|
+
if (directMessageSignal !== undefined) return directMessageSignal;
|
|
428
|
+
|
|
399
429
|
const statusKind = kindFromStatus(statusFrom(value));
|
|
400
430
|
if (statusKind !== undefined) return makeSignal(statusKind, value, source);
|
|
401
431
|
if (codeKind !== undefined) return makeSignal(codeKind, value, source);
|
|
@@ -17,6 +17,7 @@ export declare function buildExtensionResourcePaths(this: AgentSession, entries:
|
|
|
17
17
|
}>;
|
|
18
18
|
export declare function getExtensionSourceLabel(this: AgentSession, extensionPath: string): string;
|
|
19
19
|
export declare function _applyExtensionBindings(this: AgentSession, runner: ExtensionRunner): void;
|
|
20
|
+
export declare function refreshCurrentModelFromRegistry(this: AgentSession): void;
|
|
20
21
|
export declare function _refreshCurrentModelFromRegistry(this: AgentSession): void;
|
|
21
22
|
export declare function _bindExtensionCore(this: AgentSession, runner: ExtensionRunner): void;
|
|
22
23
|
export declare function reload(this: AgentSession): Promise<void>;
|
|
@@ -30,6 +31,7 @@ export declare const agentSessionExtensionBindingsMethods: {
|
|
|
30
31
|
buildExtensionResourcePaths: typeof buildExtensionResourcePaths;
|
|
31
32
|
getExtensionSourceLabel: typeof getExtensionSourceLabel;
|
|
32
33
|
_applyExtensionBindings: typeof _applyExtensionBindings;
|
|
34
|
+
refreshCurrentModelFromRegistry: typeof refreshCurrentModelFromRegistry;
|
|
33
35
|
_refreshCurrentModelFromRegistry: typeof _refreshCurrentModelFromRegistry;
|
|
34
36
|
_bindExtensionCore: typeof _bindExtensionCore;
|
|
35
37
|
reload: typeof reload;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-session-extension-bindings.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-extension-bindings.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAK7D,wBAAsB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBnG;AAGD,wBAAsB,6BAA6B,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBnH;AAGD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,KAAK,CAAC;IAC/H,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,WAAW,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxF,CAAC,CAcD;AAGD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAOzF;AAGD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAQzF;AAGD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAiBzE;AAGD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA+GpF;AAGD,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB9D;AAMD;;;GAGG;AAEH,eAAO,MAAM,oCAAoC;;;;;;;;;CAShD,CAAC","sourcesContent":["import { basename, dirname } from \"node:path\";\nimport { resetApiProviders } from \"@earendil-works/pi-ai/compat\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { ExtensionBindings } from \"./agent-session-types.ts\";\nimport type { ExtensionRunner } from \"./extensions/index.ts\";\nimport type { ResourceExtensionPaths } from \"./resource-loader.ts\";\nimport { emitSessionShutdownEvent } from \"./extensions/runner.ts\";\nimport type { SlashCommandInfo } from \"./slash-commands.ts\";\n\nexport async function bindExtensions(this: AgentSession, bindings: ExtensionBindings): Promise<void> {\n\tif (bindings.uiContext !== undefined) {\n\t\tthis._extensionUIContext = bindings.uiContext;\n\t}\n\tif (bindings.mode !== undefined) {\n\t\tthis._extensionMode = bindings.mode;\n\t}\n\tif (bindings.commandContextActions !== undefined) {\n\t\tthis._extensionCommandContextActions = bindings.commandContextActions;\n\t}\n\tif (bindings.shutdownHandler !== undefined) {\n\t\tthis._extensionShutdownHandler = bindings.shutdownHandler;\n\t}\n\tif (bindings.onError !== undefined) {\n\t\tthis._extensionErrorListener = bindings.onError;\n\t}\n\n\tthis._applyExtensionBindings(this._extensionRunner);\n\tawait this._extensionRunner.emit(this._sessionStartEvent);\n\tawait this.extendResourcesFromExtensions(this._sessionStartEvent.reason === \"reload\" ? \"reload\" : \"startup\");\n}\n\n\nexport async function extendResourcesFromExtensions(this: AgentSession, reason: \"startup\" | \"reload\"): Promise<void> {\n\tif (!this._extensionRunner.hasHandlers(\"resources_discover\")) {\n\t\treturn;\n\t}\n\n\tconst { skillPaths, promptPaths, themePaths } = await this._extensionRunner.emitResourcesDiscover(\n\t\tthis._cwd,\n\t\treason,\n\t);\n\n\tif (skillPaths.length === 0 && promptPaths.length === 0 && themePaths.length === 0) {\n\t\treturn;\n\t}\n\n\tconst extensionPaths: ResourceExtensionPaths = {\n\t\tskillPaths: this.buildExtensionResourcePaths(skillPaths),\n\t\tpromptPaths: this.buildExtensionResourcePaths(promptPaths),\n\t\tthemePaths: this.buildExtensionResourcePaths(themePaths),\n\t};\n\n\tthis._resourceLoader.extendResources(extensionPaths);\n\tthis._baseSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames());\n\tthis.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;\n}\n\n\nexport function buildExtensionResourcePaths(this: AgentSession, entries: Array<{ path: string; extensionPath: string }>): Array<{\n\tpath: string;\n\tmetadata: { source: string; scope: \"temporary\"; origin: \"top-level\"; baseDir?: string };\n}> {\n\treturn entries.map((entry) => {\n\t\tconst source = this.getExtensionSourceLabel(entry.extensionPath);\n\t\tconst baseDir = entry.extensionPath.startsWith(\"<\") ? undefined : dirname(entry.extensionPath);\n\t\treturn {\n\t\t\tpath: entry.path,\n\t\t\tmetadata: {\n\t\t\t\tsource,\n\t\t\t\tscope: \"temporary\",\n\t\t\t\torigin: \"top-level\",\n\t\t\t\tbaseDir,\n\t\t\t},\n\t\t};\n\t});\n}\n\n\nexport function getExtensionSourceLabel(this: AgentSession, extensionPath: string): string {\n\tif (extensionPath.startsWith(\"<\")) {\n\t\treturn `extension:${extensionPath.replace(/[<>]/g, \"\")}`;\n\t}\n\tconst base = basename(extensionPath);\n\tconst name = base.replace(/\\.(ts|js)$/, \"\");\n\treturn `extension:${name}`;\n}\n\n\nexport function _applyExtensionBindings(this: AgentSession, runner: ExtensionRunner): void {\n\trunner.setUIContext(this._extensionUIContext, this._extensionMode);\n\trunner.bindCommandContext(this._extensionCommandContextActions);\n\n\tthis._extensionErrorUnsubscriber?.();\n\tthis._extensionErrorUnsubscriber = this._extensionErrorListener\n\t\t? runner.onError(this._extensionErrorListener)\n\t\t: undefined;\n}\n\n\nexport function _refreshCurrentModelFromRegistry(this: AgentSession): void {\n\tconst currentModel = this.model;\n\tif (!currentModel) {\n\t\treturn;\n\t}\n\n\tconst refreshedModel = this._modelRegistry.find(currentModel.provider, currentModel.id);\n\tif (!refreshedModel || refreshedModel === currentModel) {\n\t\treturn;\n\t}\n\n\tconst replay = this._getResumeContextWindowReplayForModel(refreshedModel);\n\tthis.agent.state.model = replay.model;\n\tif (currentModel.contextWindow !== replay.contextWindow) {\n\t\tthis._emit({ type: \"context_window_changed\", contextWindow: replay.contextWindow });\n\t}\n\tthis._refreshBaseSystemPromptFromActiveTools();\n}\n\n\nexport function _bindExtensionCore(this: AgentSession, runner: ExtensionRunner): void {\n\tconst getCommands = (): SlashCommandInfo[] => {\n\t\tconst extensionCommands: SlashCommandInfo[] = runner.getRegisteredCommands().map((command) => ({\n\t\t\tname: command.invocationName,\n\t\t\tdescription: command.description,\n\t\t\tsource: \"extension\",\n\t\t\tsourceInfo: command.sourceInfo,\n\t\t}));\n\n\t\tconst templates: SlashCommandInfo[] = this.promptTemplates.map((template) => ({\n\t\t\tname: template.name,\n\t\t\tdescription: template.description,\n\t\t\tsource: \"prompt\",\n\t\t\tsourceInfo: template.sourceInfo,\n\t\t}));\n\n\t\tconst skills: SlashCommandInfo[] = this._resourceLoader.getSkills().skills.map((skill) => ({\n\t\t\tname: `skill:${skill.name}`,\n\t\t\tdescription: skill.description,\n\t\t\tsource: \"skill\",\n\t\t\tsourceInfo: skill.sourceInfo,\n\t\t}));\n\n\t\treturn [...extensionCommands, ...templates, ...skills];\n\t};\n\n\trunner.bindCore(\n\t\t{\n\t\t\tsendMessage: (message, options) => {\n\t\t\t\tthis.sendCustomMessage(message, options).catch((err) => {\n\t\t\t\t\trunner.emitError({\n\t\t\t\t\t\textensionPath: \"<runtime>\",\n\t\t\t\t\t\tevent: \"send_message\",\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tsendUserMessage: (content, options) => {\n\t\t\t\tthis.sendUserMessage(content, options).catch((err) => {\n\t\t\t\t\trunner.emitError({\n\t\t\t\t\t\textensionPath: \"<runtime>\",\n\t\t\t\t\t\tevent: \"send_user_message\",\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tappendEntry: (customType, data) => {\n\t\t\t\tthis.sessionManager.appendCustomEntry(customType, data);\n\t\t\t},\n\t\t\tsetSessionName: (name) => {\n\t\t\t\tthis.setSessionName(name);\n\t\t\t},\n\t\t\tgetSessionName: () => {\n\t\t\t\treturn this.sessionManager.getSessionName();\n\t\t\t},\n\t\t\tsetLabel: (entryId, label) => {\n\t\t\t\tthis.sessionManager.appendLabelChange(entryId, label);\n\t\t\t},\n\t\t\tgetActiveTools: () => this.getActiveToolNames(),\n\t\t\tgetAllTools: () => this.getAllTools(),\n\t\t\tsetActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),\n\t\t\trefreshTools: () => this._refreshToolRegistry(),\n\t\t\tgetCommands,\n\t\t\tsetModel: async (model) => {\n\t\t\t\tif (!this.modelRegistry.hasConfiguredAuth(model)) return false;\n\t\t\t\tawait this.setModel(model);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tgetThinkingLevel: () => this.thinkingLevel,\n\t\t\tsetThinkingLevel: (level) => this.setThinkingLevel(level),\n\t\t},\n\t\t{\n\t\t\tgetModel: () => this.model,\n\t\t\tisIdle: () => !this.isStreaming,\n\t\t\tisProjectTrusted: () => this.settingsManager.isProjectTrusted(),\n\t\t\tgetSignal: () => this.agent.signal,\n\t\t\tabort: () => this.abort(),\n\t\t\thasPendingMessages: () => this.pendingMessageCount > 0,\n\t\t\tshutdown: () => {\n\t\t\t\tthis._extensionShutdownHandler?.();\n\t\t\t},\n\t\t\tgetContextUsage: () => this.getContextUsage(),\n\t\t\tcompact: (options) => {\n\t\t\t\tvoid (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.compact({\n\t\t\t\t\t\t\t...(options?.compression_ratio === undefined ? {} : { compression_ratio: options.compression_ratio }),\n\t\t\t\t\t\t\t...(options?.preserve_recent === undefined ? {} : { preserve_recent: options.preserve_recent }),\n\t\t\t\t\t\t\t...(options?.query === undefined ? {} : { query: options.query }),\n\t\t\t\t\t\t});\n\t\t\t\t\t\toptions?.onComplete?.(result);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\t\t\toptions?.onError?.(err);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t},\n\t\t\tgetSystemPrompt: () => this.systemPrompt,\n\t\t\tgetSystemPromptOptions: () => this._baseSystemPromptOptions,\n\t\t},\n\t\t{\n\t\t\tregisterProvider: (name, config) => {\n\t\t\t\tthis._modelRegistry.registerProvider(name, config);\n\t\t\t\tthis._refreshCurrentModelFromRegistry();\n\t\t\t},\n\t\t\tunregisterProvider: (name) => {\n\t\t\t\tthis._modelRegistry.unregisterProvider(name);\n\t\t\t\tthis._refreshCurrentModelFromRegistry();\n\t\t\t},\n\t\t},\n\t);\n}\n\n\nexport async function reload(this: AgentSession): Promise<void> {\n\tconst previousFlagValues = this._extensionRunner.getFlagValues();\n\tawait emitSessionShutdownEvent(this._extensionRunner, { type: \"session_shutdown\", reason: \"reload\" });\n\tawait this.settingsManager.reload();\n\tresetApiProviders();\n\tawait this._resourceLoader.reload();\n\tthis._buildRuntime({\n\t\tactiveToolNames: this.getActiveToolNames(),\n\t\tflagValues: previousFlagValues,\n\t\tincludeAllExtensionTools: true,\n\t});\n\n\tconst hasBindings =\n\t\tthis._extensionUIContext ||\n\t\tthis._extensionCommandContextActions ||\n\t\tthis._extensionShutdownHandler ||\n\t\tthis._extensionErrorListener;\n\tif (hasBindings) {\n\t\tawait this._extensionRunner.emit({ type: \"session_start\", reason: \"reload\" });\n\t\tawait this.extendResourcesFromExtensions(\"reload\");\n\t}\n}\n\n// =========================================================================\n// Auto-Retry\n// =========================================================================\n\n/**\n * Check if an error is retryable (overloaded, rate limit, server errors).\n * Context overflow errors are NOT retryable (handled by compaction instead).\n */\n\nexport const agentSessionExtensionBindingsMethods = {\n\tbindExtensions,\n\textendResourcesFromExtensions,\n\tbuildExtensionResourcePaths,\n\tgetExtensionSourceLabel,\n\t_applyExtensionBindings,\n\t_refreshCurrentModelFromRegistry,\n\t_bindExtensionCore,\n\treload,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"agent-session-extension-bindings.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-extension-bindings.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAK7D,wBAAsB,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBnG;AAGD,wBAAsB,6BAA6B,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBnH;AAGD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,KAAK,CAAC;IAC/H,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,WAAW,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxF,CAAC,CAcD;AAGD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAOzF;AAGD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAQzF;AAGD,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAExE;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAqBzE;AAGD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA+GpF;AAGD,wBAAsB,MAAM,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB9D;AAMD;;;GAGG;AAEH,eAAO,MAAM,oCAAoC;;;;;;;;;;CAUhD,CAAC","sourcesContent":["import { basename, dirname } from \"node:path\";\nimport { resetApiProviders } from \"@earendil-works/pi-ai/compat\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\nimport type { ExtensionBindings } from \"./agent-session-types.ts\";\nimport type { ExtensionRunner } from \"./extensions/index.ts\";\nimport type { ResourceExtensionPaths } from \"./resource-loader.ts\";\nimport { emitSessionShutdownEvent } from \"./extensions/runner.ts\";\nimport type { SlashCommandInfo } from \"./slash-commands.ts\";\n\nexport async function bindExtensions(this: AgentSession, bindings: ExtensionBindings): Promise<void> {\n\tif (bindings.uiContext !== undefined) {\n\t\tthis._extensionUIContext = bindings.uiContext;\n\t}\n\tif (bindings.mode !== undefined) {\n\t\tthis._extensionMode = bindings.mode;\n\t}\n\tif (bindings.commandContextActions !== undefined) {\n\t\tthis._extensionCommandContextActions = bindings.commandContextActions;\n\t}\n\tif (bindings.shutdownHandler !== undefined) {\n\t\tthis._extensionShutdownHandler = bindings.shutdownHandler;\n\t}\n\tif (bindings.onError !== undefined) {\n\t\tthis._extensionErrorListener = bindings.onError;\n\t}\n\n\tthis._applyExtensionBindings(this._extensionRunner);\n\tawait this._extensionRunner.emit(this._sessionStartEvent);\n\tawait this.extendResourcesFromExtensions(this._sessionStartEvent.reason === \"reload\" ? \"reload\" : \"startup\");\n}\n\n\nexport async function extendResourcesFromExtensions(this: AgentSession, reason: \"startup\" | \"reload\"): Promise<void> {\n\tif (!this._extensionRunner.hasHandlers(\"resources_discover\")) {\n\t\treturn;\n\t}\n\n\tconst { skillPaths, promptPaths, themePaths } = await this._extensionRunner.emitResourcesDiscover(\n\t\tthis._cwd,\n\t\treason,\n\t);\n\n\tif (skillPaths.length === 0 && promptPaths.length === 0 && themePaths.length === 0) {\n\t\treturn;\n\t}\n\n\tconst extensionPaths: ResourceExtensionPaths = {\n\t\tskillPaths: this.buildExtensionResourcePaths(skillPaths),\n\t\tpromptPaths: this.buildExtensionResourcePaths(promptPaths),\n\t\tthemePaths: this.buildExtensionResourcePaths(themePaths),\n\t};\n\n\tthis._resourceLoader.extendResources(extensionPaths);\n\tthis._baseSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames());\n\tthis.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;\n}\n\n\nexport function buildExtensionResourcePaths(this: AgentSession, entries: Array<{ path: string; extensionPath: string }>): Array<{\n\tpath: string;\n\tmetadata: { source: string; scope: \"temporary\"; origin: \"top-level\"; baseDir?: string };\n}> {\n\treturn entries.map((entry) => {\n\t\tconst source = this.getExtensionSourceLabel(entry.extensionPath);\n\t\tconst baseDir = entry.extensionPath.startsWith(\"<\") ? undefined : dirname(entry.extensionPath);\n\t\treturn {\n\t\t\tpath: entry.path,\n\t\t\tmetadata: {\n\t\t\t\tsource,\n\t\t\t\tscope: \"temporary\",\n\t\t\t\torigin: \"top-level\",\n\t\t\t\tbaseDir,\n\t\t\t},\n\t\t};\n\t});\n}\n\n\nexport function getExtensionSourceLabel(this: AgentSession, extensionPath: string): string {\n\tif (extensionPath.startsWith(\"<\")) {\n\t\treturn `extension:${extensionPath.replace(/[<>]/g, \"\")}`;\n\t}\n\tconst base = basename(extensionPath);\n\tconst name = base.replace(/\\.(ts|js)$/, \"\");\n\treturn `extension:${name}`;\n}\n\n\nexport function _applyExtensionBindings(this: AgentSession, runner: ExtensionRunner): void {\n\trunner.setUIContext(this._extensionUIContext, this._extensionMode);\n\trunner.bindCommandContext(this._extensionCommandContextActions);\n\n\tthis._extensionErrorUnsubscriber?.();\n\tthis._extensionErrorUnsubscriber = this._extensionErrorListener\n\t\t? runner.onError(this._extensionErrorListener)\n\t\t: undefined;\n}\n\n\nexport function refreshCurrentModelFromRegistry(this: AgentSession): void {\n\tthis._refreshCurrentModelFromRegistry();\n}\n\nexport function _refreshCurrentModelFromRegistry(this: AgentSession): void {\n\tconst currentModel = this.model;\n\tif (!currentModel) {\n\t\treturn;\n\t}\n\n\tconst refreshedModel = this._modelRegistry.find(currentModel.provider, currentModel.id);\n\tif (!refreshedModel || refreshedModel === currentModel) {\n\t\treturn;\n\t}\n\n\tconst previousModel = currentModel;\n\tconst previousThinkingLevel = this.thinkingLevel;\n\tconst replay = this._getResumeContextWindowReplayForModel(refreshedModel);\n\tthis.agent.state.model = replay.model;\n\tif (currentModel.contextWindow !== replay.contextWindow) {\n\t\tthis._emit({ type: \"context_window_changed\", contextWindow: replay.contextWindow });\n\t}\n\tthis.setThinkingLevel(previousThinkingLevel);\n\tthis._refreshBaseSystemPromptFromActiveTools();\n\tthis._emit({ type: \"model_changed\", model: replay.model, previousModel, source: \"restore\" });\n}\n\n\nexport function _bindExtensionCore(this: AgentSession, runner: ExtensionRunner): void {\n\tconst getCommands = (): SlashCommandInfo[] => {\n\t\tconst extensionCommands: SlashCommandInfo[] = runner.getRegisteredCommands().map((command) => ({\n\t\t\tname: command.invocationName,\n\t\t\tdescription: command.description,\n\t\t\tsource: \"extension\",\n\t\t\tsourceInfo: command.sourceInfo,\n\t\t}));\n\n\t\tconst templates: SlashCommandInfo[] = this.promptTemplates.map((template) => ({\n\t\t\tname: template.name,\n\t\t\tdescription: template.description,\n\t\t\tsource: \"prompt\",\n\t\t\tsourceInfo: template.sourceInfo,\n\t\t}));\n\n\t\tconst skills: SlashCommandInfo[] = this._resourceLoader.getSkills().skills.map((skill) => ({\n\t\t\tname: `skill:${skill.name}`,\n\t\t\tdescription: skill.description,\n\t\t\tsource: \"skill\",\n\t\t\tsourceInfo: skill.sourceInfo,\n\t\t}));\n\n\t\treturn [...extensionCommands, ...templates, ...skills];\n\t};\n\n\trunner.bindCore(\n\t\t{\n\t\t\tsendMessage: (message, options) => {\n\t\t\t\tthis.sendCustomMessage(message, options).catch((err) => {\n\t\t\t\t\trunner.emitError({\n\t\t\t\t\t\textensionPath: \"<runtime>\",\n\t\t\t\t\t\tevent: \"send_message\",\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tsendUserMessage: (content, options) => {\n\t\t\t\tthis.sendUserMessage(content, options).catch((err) => {\n\t\t\t\t\trunner.emitError({\n\t\t\t\t\t\textensionPath: \"<runtime>\",\n\t\t\t\t\t\tevent: \"send_user_message\",\n\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tappendEntry: (customType, data) => {\n\t\t\t\tthis.sessionManager.appendCustomEntry(customType, data);\n\t\t\t},\n\t\t\tsetSessionName: (name) => {\n\t\t\t\tthis.setSessionName(name);\n\t\t\t},\n\t\t\tgetSessionName: () => {\n\t\t\t\treturn this.sessionManager.getSessionName();\n\t\t\t},\n\t\t\tsetLabel: (entryId, label) => {\n\t\t\t\tthis.sessionManager.appendLabelChange(entryId, label);\n\t\t\t},\n\t\t\tgetActiveTools: () => this.getActiveToolNames(),\n\t\t\tgetAllTools: () => this.getAllTools(),\n\t\t\tsetActiveTools: (toolNames) => this.setActiveToolsByName(toolNames),\n\t\t\trefreshTools: () => this._refreshToolRegistry(),\n\t\t\tgetCommands,\n\t\t\tsetModel: async (model) => {\n\t\t\t\tif (!this.modelRegistry.hasConfiguredAuth(model)) return false;\n\t\t\t\tawait this.setModel(model);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tgetThinkingLevel: () => this.thinkingLevel,\n\t\t\tsetThinkingLevel: (level) => this.setThinkingLevel(level),\n\t\t},\n\t\t{\n\t\t\tgetModel: () => this.model,\n\t\t\tisIdle: () => !this.isStreaming,\n\t\t\tisProjectTrusted: () => this.settingsManager.isProjectTrusted(),\n\t\t\tgetSignal: () => this.agent.signal,\n\t\t\tabort: () => this.abort(),\n\t\t\thasPendingMessages: () => this.pendingMessageCount > 0,\n\t\t\tshutdown: () => {\n\t\t\t\tthis._extensionShutdownHandler?.();\n\t\t\t},\n\t\t\tgetContextUsage: () => this.getContextUsage(),\n\t\t\tcompact: (options) => {\n\t\t\t\tvoid (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await this.compact({\n\t\t\t\t\t\t\t...(options?.compression_ratio === undefined ? {} : { compression_ratio: options.compression_ratio }),\n\t\t\t\t\t\t\t...(options?.preserve_recent === undefined ? {} : { preserve_recent: options.preserve_recent }),\n\t\t\t\t\t\t\t...(options?.query === undefined ? {} : { query: options.query }),\n\t\t\t\t\t\t});\n\t\t\t\t\t\toptions?.onComplete?.(result);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\t\t\toptions?.onError?.(err);\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t},\n\t\t\tgetSystemPrompt: () => this.systemPrompt,\n\t\t\tgetSystemPromptOptions: () => this._baseSystemPromptOptions,\n\t\t},\n\t\t{\n\t\t\tregisterProvider: (name, config) => {\n\t\t\t\tthis._modelRegistry.registerProvider(name, config);\n\t\t\t\tthis.refreshCurrentModelFromRegistry();\n\t\t\t},\n\t\t\tunregisterProvider: (name) => {\n\t\t\t\tthis._modelRegistry.unregisterProvider(name);\n\t\t\t\tthis.refreshCurrentModelFromRegistry();\n\t\t\t},\n\t\t},\n\t);\n}\n\n\nexport async function reload(this: AgentSession): Promise<void> {\n\tconst previousFlagValues = this._extensionRunner.getFlagValues();\n\tawait emitSessionShutdownEvent(this._extensionRunner, { type: \"session_shutdown\", reason: \"reload\" });\n\tawait this.settingsManager.reload();\n\tresetApiProviders();\n\tawait this._resourceLoader.reload();\n\tthis._buildRuntime({\n\t\tactiveToolNames: this.getActiveToolNames(),\n\t\tflagValues: previousFlagValues,\n\t\tincludeAllExtensionTools: true,\n\t});\n\n\tconst hasBindings =\n\t\tthis._extensionUIContext ||\n\t\tthis._extensionCommandContextActions ||\n\t\tthis._extensionShutdownHandler ||\n\t\tthis._extensionErrorListener;\n\tif (hasBindings) {\n\t\tawait this._extensionRunner.emit({ type: \"session_start\", reason: \"reload\" });\n\t\tawait this.extendResourcesFromExtensions(\"reload\");\n\t}\n}\n\n// =========================================================================\n// Auto-Retry\n// =========================================================================\n\n/**\n * Check if an error is retryable (overloaded, rate limit, server errors).\n * Context overflow errors are NOT retryable (handled by compaction instead).\n */\n\nexport const agentSessionExtensionBindingsMethods = {\n\tbindExtensions,\n\textendResourcesFromExtensions,\n\tbuildExtensionResourcePaths,\n\tgetExtensionSourceLabel,\n\t_applyExtensionBindings,\n\trefreshCurrentModelFromRegistry,\n\t_refreshCurrentModelFromRegistry,\n\t_bindExtensionCore,\n\treload,\n};\n"]}
|