@oh-my-pi/pi-coding-agent 17.0.6 → 17.0.8
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 +65 -0
- package/dist/cli.js +4967 -4948
- package/dist/types/config/model-registry.d.ts +1 -1
- package/dist/types/config/model-resolver.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +16 -0
- package/dist/types/dap/client.d.ts +19 -0
- package/dist/types/extensibility/extensions/runner.d.ts +4 -2
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/extensibility/typebox.d.ts +4 -0
- package/dist/types/hindsight/client.d.ts +10 -0
- package/dist/types/hindsight/config.d.ts +8 -0
- package/dist/types/mcp/tool-bridge.d.ts +9 -0
- package/dist/types/mnemopi/state.d.ts +2 -0
- package/dist/types/modes/components/user-message.d.ts +25 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +8 -0
- package/dist/types/modes/interactive-mode.d.ts +7 -1
- package/dist/types/modes/types.d.ts +14 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +12 -8
- package/dist/types/sdk.d.ts +1 -0
- package/dist/types/session/agent-session-error-log.test.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +78 -2
- package/dist/types/task/executor.d.ts +10 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/prewalk.d.ts +3 -0
- package/dist/types/tools/ask.d.ts +10 -0
- package/dist/types/tools/tool-timeouts.d.ts +8 -2
- package/dist/types/utils/changelog.d.ts +4 -6
- package/package.json +12 -13
- package/src/cli/models-cli.ts +37 -17
- package/src/cli/update-cli.ts +14 -1
- package/src/config/model-discovery.ts +3 -2
- package/src/config/model-registry.ts +53 -26
- package/src/config/model-resolver.ts +56 -31
- package/src/config/settings-schema.ts +5 -0
- package/src/config/settings.ts +68 -5
- package/src/dap/client.ts +86 -7
- package/src/edit/diff.ts +4 -4
- package/src/eval/py/prelude.py +5 -5
- package/src/export/html/template.js +23 -9
- package/src/extensibility/extensions/runner.ts +9 -4
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/extensibility/typebox.ts +22 -9
- package/src/hindsight/client.test.ts +42 -0
- package/src/hindsight/client.ts +40 -3
- package/src/hindsight/config.ts +18 -0
- package/src/launch/broker.ts +31 -5
- package/src/lsp/index.ts +1 -1
- package/src/main.ts +15 -5
- package/src/mcp/tool-bridge.ts +9 -0
- package/src/mnemopi/state.ts +70 -3
- package/src/modes/components/agent-dashboard.ts +6 -2
- package/src/modes/components/chat-transcript-builder.ts +13 -2
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/plan-review-overlay.ts +20 -4
- package/src/modes/components/user-message.ts +100 -1
- package/src/modes/controllers/command-controller.ts +22 -5
- package/src/modes/controllers/event-controller.ts +4 -1
- package/src/modes/controllers/extension-ui-controller.ts +18 -0
- package/src/modes/controllers/input-controller.ts +47 -12
- package/src/modes/controllers/selector-controller.ts +82 -6
- package/src/modes/interactive-mode.ts +36 -4
- package/src/modes/types.ts +16 -3
- package/src/modes/utils/ui-helpers.ts +40 -20
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/sdk.ts +134 -48
- package/src/session/agent-session-error-log.test.ts +59 -0
- package/src/session/agent-session.ts +300 -26
- package/src/system-prompt.ts +1 -2
- package/src/task/executor.ts +41 -27
- package/src/task/index.ts +11 -0
- package/src/task/prewalk.ts +6 -0
- package/src/task/worktree.ts +25 -11
- package/src/tools/ask.ts +15 -0
- package/src/tools/bash.ts +14 -6
- package/src/tools/browser.ts +1 -1
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval.ts +4 -5
- package/src/tools/fetch.ts +1 -1
- package/src/tools/hub/launch.ts +7 -0
- package/src/tools/tool-timeouts.ts +10 -3
- package/src/tools/write.ts +31 -0
- package/src/utils/changelog.ts +54 -58
- package/src/utils/git.ts +57 -49
package/src/sdk.ts
CHANGED
|
@@ -119,7 +119,7 @@ import {
|
|
|
119
119
|
obfuscateProviderContext,
|
|
120
120
|
SecretObfuscator,
|
|
121
121
|
} from "./secrets";
|
|
122
|
-
import { AgentSession, type PlanYolo, type Prewalk } from "./session/agent-session";
|
|
122
|
+
import { AgentSession, type InitialRetryFallbackState, type PlanYolo, type Prewalk } from "./session/agent-session";
|
|
123
123
|
import { discoverAuthStorage as discoverAuthStorageFromConfig } from "./session/auth-broker-config";
|
|
124
124
|
import type { AuthStorage } from "./session/auth-storage";
|
|
125
125
|
import { createInterruptedTurnAbortMessage } from "./session/exit-diagnostics";
|
|
@@ -907,7 +907,7 @@ function registerEvalCleanup(): void {
|
|
|
907
907
|
postmortem.register("julia-cleanup", disposeAllJuliaKernelSessions);
|
|
908
908
|
}
|
|
909
909
|
|
|
910
|
-
function customToolToDefinition(tool: CustomTool): ToolDefinition {
|
|
910
|
+
export function customToolToDefinition(tool: CustomTool): ToolDefinition {
|
|
911
911
|
const definition: ToolDefinition & { [TOOL_DEFINITION_MARKER]: true } = {
|
|
912
912
|
name: tool.name,
|
|
913
913
|
label: tool.label,
|
|
@@ -917,6 +917,9 @@ function customToolToDefinition(tool: CustomTool): ToolDefinition {
|
|
|
917
917
|
loadMode: defaultLoadModeForToolName(tool.name, tool.loadMode),
|
|
918
918
|
deferrable: tool.deferrable,
|
|
919
919
|
approval: typeof tool.approval === "function" ? tool.approval.bind(tool) : tool.approval,
|
|
920
|
+
// Preserved through RegisteredToolAdapter so MCP-backed tools' explicit
|
|
921
|
+
// `strict: false` (#4336/#4340) survives the custom-tool → definition bridge.
|
|
922
|
+
strict: tool.strict,
|
|
920
923
|
mcpServerName: tool.mcpServerName,
|
|
921
924
|
mcpToolName: tool.mcpToolName,
|
|
922
925
|
execute: (toolCallId, params, signal, onUpdate, ctx) =>
|
|
@@ -1387,6 +1390,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1387
1390
|
);
|
|
1388
1391
|
let model = options.model;
|
|
1389
1392
|
let modelFallbackMessage: string | undefined;
|
|
1393
|
+
let initialRetryFallback: InitialRetryFallbackState | undefined;
|
|
1390
1394
|
// Identify session model strings to restore in fallback order. We do an
|
|
1391
1395
|
// initial pass here so model-dependent setup (thinking-level resolution,
|
|
1392
1396
|
// host preconnect) can use the restored model; extension-registered
|
|
@@ -2085,26 +2089,70 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2085
2089
|
preferences: matchPreferences,
|
|
2086
2090
|
});
|
|
2087
2091
|
if (resolved.configuredPatterns && resolved.configuredPatterns.length > 0) {
|
|
2088
|
-
|
|
2092
|
+
const primaryPatterns: Array<{
|
|
2093
|
+
pattern: string;
|
|
2094
|
+
retryFallback: InitialRetryFallbackState | undefined;
|
|
2095
|
+
}> = resolved.configuredPatterns.map(pattern => ({
|
|
2096
|
+
pattern,
|
|
2097
|
+
retryFallback: undefined,
|
|
2098
|
+
}));
|
|
2099
|
+
if (!resolved.configuredRole || !settings.get("retry.modelFallback")) {
|
|
2100
|
+
return primaryPatterns;
|
|
2101
|
+
}
|
|
2102
|
+
const fallbackChains = settings.get("retry.fallbackChains");
|
|
2103
|
+
const roleFallbacks =
|
|
2104
|
+
fallbackChains[resolved.configuredRole] ??
|
|
2105
|
+
(resolved.configuredRole === "default" ? undefined : fallbackChains.default);
|
|
2106
|
+
if (!Array.isArray(roleFallbacks)) return primaryPatterns;
|
|
2107
|
+
const originalSelector = resolved.configuredPatterns[0];
|
|
2108
|
+
const parsedOriginal = parseModelString(originalSelector, {
|
|
2109
|
+
allowMaxSuffix: true,
|
|
2110
|
+
allowAutoAlias: true,
|
|
2111
|
+
isLiteralModelId: (provider, id) => modelRegistry.find(provider, id) !== undefined,
|
|
2112
|
+
});
|
|
2113
|
+
const retryFallback: InitialRetryFallbackState = {
|
|
2114
|
+
role: resolved.configuredRole,
|
|
2115
|
+
originalSelector,
|
|
2116
|
+
originalThinkingLevel: parsedOriginal?.thinkingLevel,
|
|
2117
|
+
};
|
|
2118
|
+
return [
|
|
2119
|
+
...primaryPatterns,
|
|
2120
|
+
...roleFallbacks
|
|
2121
|
+
.filter((pattern): pattern is string => typeof pattern === "string")
|
|
2122
|
+
.map(pattern => ({ pattern, retryFallback })),
|
|
2123
|
+
];
|
|
2089
2124
|
}
|
|
2090
2125
|
if (resolved.model) {
|
|
2091
2126
|
return [
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2127
|
+
{
|
|
2128
|
+
pattern: formatModelSelectorValue(
|
|
2129
|
+
resolved.selector ?? formatModelStringWithRouting(resolved.model),
|
|
2130
|
+
resolved.thinkingLevel,
|
|
2131
|
+
),
|
|
2132
|
+
retryFallback: undefined,
|
|
2133
|
+
},
|
|
2096
2134
|
];
|
|
2097
2135
|
}
|
|
2098
|
-
return resolveConfiguredModelPatterns([trimmedSelector], settings)
|
|
2136
|
+
return resolveConfiguredModelPatterns([trimmedSelector], settings).map(pattern => ({
|
|
2137
|
+
pattern,
|
|
2138
|
+
retryFallback: undefined,
|
|
2139
|
+
}));
|
|
2099
2140
|
}),
|
|
2100
2141
|
);
|
|
2101
2142
|
for (let patternIndex = 0; patternIndex < expandedModelPatterns.length; patternIndex += 1) {
|
|
2102
|
-
const pattern = expandedModelPatterns[patternIndex];
|
|
2143
|
+
const { pattern, retryFallback } = expandedModelPatterns[patternIndex];
|
|
2103
2144
|
const primary = parseModelPattern(pattern, availableModels, matchPreferences);
|
|
2104
|
-
if (!primary.model) continue;
|
|
2145
|
+
if (!primary.model || (retryFallback && !hasModelAuth(primary.model))) continue;
|
|
2105
2146
|
let selectedModel = primary.model;
|
|
2106
2147
|
let selectedThinkingLevel = primary.thinkingLevel;
|
|
2107
2148
|
let selectedExplicitThinkingLevel = primary.explicitThinkingLevel;
|
|
2149
|
+
// A chain entry without its own `:level` suffix inherits the
|
|
2150
|
+
// unavailable primary's configured thinking level, matching
|
|
2151
|
+
// runtime fallback-chain semantics.
|
|
2152
|
+
if (retryFallback && !selectedExplicitThinkingLevel && retryFallback.originalThinkingLevel !== undefined) {
|
|
2153
|
+
selectedThinkingLevel = retryFallback.originalThinkingLevel;
|
|
2154
|
+
selectedExplicitThinkingLevel = true;
|
|
2155
|
+
}
|
|
2108
2156
|
let authFallbackUsed = false;
|
|
2109
2157
|
if (options.modelPatternAuthFallback) {
|
|
2110
2158
|
const primaryKey = await modelRegistry.getApiKey(primary.model);
|
|
@@ -2132,8 +2180,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2132
2180
|
);
|
|
2133
2181
|
const seenSelectors = new Set<string>([primarySelector]);
|
|
2134
2182
|
const fallbackSelectors: string[] = [];
|
|
2135
|
-
for (const
|
|
2136
|
-
const fallback = parseModelPattern(
|
|
2183
|
+
for (const fallbackEntry of expandedModelPatterns.slice(patternIndex + 1)) {
|
|
2184
|
+
const fallback = parseModelPattern(fallbackEntry.pattern, availableModels, matchPreferences);
|
|
2137
2185
|
if (!fallback.model) continue;
|
|
2138
2186
|
const fallbackSelector = formatModelSelectorValue(
|
|
2139
2187
|
formatModelStringWithRouting(fallback.model),
|
|
@@ -2174,6 +2222,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2174
2222
|
}
|
|
2175
2223
|
}
|
|
2176
2224
|
model = selectedModel;
|
|
2225
|
+
initialRetryFallback = retryFallback;
|
|
2177
2226
|
modelFallbackMessage = undefined;
|
|
2178
2227
|
if (selectedExplicitThinkingLevel) {
|
|
2179
2228
|
restoredSessionThinkingLevel = selectedThinkingLevel;
|
|
@@ -2202,48 +2251,84 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2202
2251
|
// path-scoped `enabledModels` allow-list when configured. Skip when the
|
|
2203
2252
|
// user explicitly requested a model via --model that wasn't found.
|
|
2204
2253
|
if (!model && deferredModelPatterns.length === 0) {
|
|
2205
|
-
//
|
|
2206
|
-
//
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
//
|
|
2210
|
-
//
|
|
2211
|
-
// `
|
|
2212
|
-
//
|
|
2213
|
-
//
|
|
2214
|
-
// `
|
|
2215
|
-
//
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2254
|
+
// Retry the configured default role against the current catalog,
|
|
2255
|
+
// setting `model` (+ thinking level) when it resolves. Extension
|
|
2256
|
+
// factories register providers AFTER the early `defaultRoleSpec`
|
|
2257
|
+
// resolution, and configured discovery providers may still be
|
|
2258
|
+
// mid-discovery, so a role pointing at such a model (an openai-compat
|
|
2259
|
+
// plugin's `posthog/claude-opus-4-8`, a models.yml `openai-models-list`
|
|
2260
|
+
// endpoint) returned `undefined` there. Without this retry the
|
|
2261
|
+
// `pickDefaultAvailableModel` fallback below happily replaces the
|
|
2262
|
+
// user's configured default with a bundled provider's default whenever
|
|
2263
|
+
// a stray `OPENAI_API_KEY`/`ANTHROPIC_API_KEY` is in the environment.
|
|
2264
|
+
// (issues #3569, #6162)
|
|
2265
|
+
const tryResolveDefaultRole = async (): Promise<boolean> => {
|
|
2266
|
+
if (hasExplicitModel) return false;
|
|
2267
|
+
// Re-resolve the allowed set: extension factories and discovery
|
|
2268
|
+
// refreshes above may have registered models not visible earlier.
|
|
2269
|
+
const fallbackCandidates = await resolveAllowedModels(modelRegistry, settings, modelMatchPreferences);
|
|
2219
2270
|
const reResolvedRoleSpec = resolveModelRoleValue(settings.getModelRole("default"), fallbackCandidates, {
|
|
2220
2271
|
settings,
|
|
2221
2272
|
matchPreferences: modelMatchPreferences,
|
|
2222
2273
|
});
|
|
2223
|
-
if (reResolvedRoleSpec.model)
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
}
|
|
2274
|
+
if (!reResolvedRoleSpec.model) return false;
|
|
2275
|
+
defaultRoleSpec = reResolvedRoleSpec;
|
|
2276
|
+
const resolvedDefaultModel = reResolvedRoleSpec.model;
|
|
2277
|
+
model = resolvedDefaultModel;
|
|
2278
|
+
modelFallbackMessage = undefined;
|
|
2279
|
+
// Recompute the thinking level against the now-real model.
|
|
2280
|
+
// `pickInitialThinkingLevel` closes over `defaultRoleSpec`,
|
|
2281
|
+
// so the role's explicit selector (e.g. `:max`) now applies.
|
|
2282
|
+
thinkingLevel = pickInitialThinkingLevel(resolvedDefaultModel);
|
|
2283
|
+
autoThinking = thinkingLevel === AUTO_THINKING;
|
|
2284
|
+
effectiveThinkingLevel = concreteThinkingLevel(thinkingLevel);
|
|
2285
|
+
effectiveThinkingLevel = logger.time("resolveThinkingLevelForModel", () =>
|
|
2286
|
+
autoThinking
|
|
2287
|
+
? resolveProvisionalAutoLevel(resolvedDefaultModel)
|
|
2288
|
+
: resolveThinkingLevelForModel(resolvedDefaultModel, effectiveThinkingLevel),
|
|
2289
|
+
);
|
|
2290
|
+
preconnectModelHost(resolvedDefaultModel.baseUrl);
|
|
2291
|
+
return true;
|
|
2292
|
+
};
|
|
2293
|
+
|
|
2294
|
+
await tryResolveDefaultRole();
|
|
2242
2295
|
|
|
2243
2296
|
if (!model) {
|
|
2244
|
-
const
|
|
2245
|
-
|
|
2246
|
-
|
|
2297
|
+
const fallbackCandidates = await resolveAllowedModels(modelRegistry, settings, modelMatchPreferences);
|
|
2298
|
+
let pick = pickDefaultAvailableModel(fallbackCandidates.filter(hasModelAuth));
|
|
2299
|
+
|
|
2300
|
+
// Cold-cache discovery race (issues #6114, #6162): a discovery
|
|
2301
|
+
// provider (models.yml `openai-models-list`, LM Studio/Ollama/
|
|
2302
|
+
// llama.cpp, or an openai-compat proxy) ships no static models, so
|
|
2303
|
+
// the static+cached catalog resolved nothing above. Background
|
|
2304
|
+
// discovery in main.ts fires only AFTER createAgentSession returns,
|
|
2305
|
+
// so on a cache-cold boot the configured default stays unresolved
|
|
2306
|
+
// and `pick` silently degrades to an unrelated authed provider's
|
|
2307
|
+
// default (#6162) or "No models available" (#6114) — even though
|
|
2308
|
+
// `omp models` (which awaits discovery) lists the model. Await one
|
|
2309
|
+
// cache-aware discovery pass and retry when a default role is
|
|
2310
|
+
// configured (must win over `pick`) or nothing resolved at all.
|
|
2311
|
+
// The common path — role already resolved, or a `pick` with no
|
|
2312
|
+
// configured default — never pays for it.
|
|
2313
|
+
const defaultRoleConfigured = Boolean(settings.getModelRole("default"));
|
|
2314
|
+
if (
|
|
2315
|
+
!hasExplicitModel &&
|
|
2316
|
+
(defaultRoleConfigured || !pick) &&
|
|
2317
|
+
modelRegistry.getDiscoverableProviders().length > 0
|
|
2318
|
+
) {
|
|
2319
|
+
await logger.time("resolveModelDiscoveryFallback", () => modelRegistry.refresh("online-if-uncached"));
|
|
2320
|
+
if (!(await tryResolveDefaultRole()) && !model) {
|
|
2321
|
+
const refreshedCandidates = await resolveAllowedModels(
|
|
2322
|
+
modelRegistry,
|
|
2323
|
+
settings,
|
|
2324
|
+
modelMatchPreferences,
|
|
2325
|
+
);
|
|
2326
|
+
pick = pickDefaultAvailableModel(refreshedCandidates.filter(hasModelAuth));
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
if (!model && pick) {
|
|
2331
|
+
model = pick;
|
|
2247
2332
|
}
|
|
2248
2333
|
}
|
|
2249
2334
|
if (model) {
|
|
@@ -2918,6 +3003,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
2918
3003
|
agent,
|
|
2919
3004
|
pruneToolDescriptions: inlineToolDescriptors,
|
|
2920
3005
|
thinkingLevel: autoThinking ? AUTO_THINKING : effectiveThinkingLevel,
|
|
3006
|
+
initialRetryFallback,
|
|
2921
3007
|
prewalk: options.prewalk,
|
|
2922
3008
|
planYolo: options.planYolo,
|
|
2923
3009
|
serviceTierByFamily: initialServiceTierByFamily,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract: a turn ending in a provider error surfaces one warn-level log
|
|
3
|
+
* carrying `provider`/`model`/`errorMessage`/`errorStatus`/`errorId`, so
|
|
4
|
+
* recurring provider stream failures are diagnosable from the main log alone
|
|
5
|
+
* (issue #6177). Non-error stops must not emit it.
|
|
6
|
+
*/
|
|
7
|
+
import { afterAll, afterEach, describe, expect, it } from "bun:test";
|
|
8
|
+
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
|
9
|
+
import { logger } from "@oh-my-pi/pi-utils";
|
|
10
|
+
import { logProviderTurnError } from "./agent-session";
|
|
11
|
+
|
|
12
|
+
function makeMessage(overrides: Partial<AssistantMessage>): AssistantMessage {
|
|
13
|
+
return {
|
|
14
|
+
role: "assistant",
|
|
15
|
+
content: [],
|
|
16
|
+
api: "cursor",
|
|
17
|
+
provider: "cursor",
|
|
18
|
+
model: "composer-2.5",
|
|
19
|
+
usage: {} as AssistantMessage["usage"],
|
|
20
|
+
stopReason: "error",
|
|
21
|
+
timestamp: 1,
|
|
22
|
+
...overrides,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
describe("logProviderTurnError", () => {
|
|
27
|
+
const events: logger.LogEvent[] = [];
|
|
28
|
+
const dispose = logger.registerLogSink(event => events.push(event));
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
events.length = 0;
|
|
32
|
+
});
|
|
33
|
+
afterAll(() => dispose());
|
|
34
|
+
|
|
35
|
+
it("emits a warn log with the provider error fields for stopReason:error", () => {
|
|
36
|
+
logProviderTurnError(
|
|
37
|
+
makeMessage({
|
|
38
|
+
errorMessage: "stream stall: idle watchdog fired",
|
|
39
|
+
errorStatus: 500,
|
|
40
|
+
errorId: 42,
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const warn = events.find(e => e.level === "warn" && e.message === "agent turn ended with provider error");
|
|
45
|
+
expect(warn).toBeDefined();
|
|
46
|
+
expect(warn?.context).toMatchObject({
|
|
47
|
+
provider: "cursor",
|
|
48
|
+
model: "composer-2.5",
|
|
49
|
+
errorMessage: "stream stall: idle watchdog fired",
|
|
50
|
+
errorStatus: 500,
|
|
51
|
+
errorId: 42,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("does not emit for a successful stop", () => {
|
|
56
|
+
logProviderTurnError(makeMessage({ stopReason: "stop", errorMessage: undefined }));
|
|
57
|
+
expect(events.some(e => e.message === "agent turn ended with provider error")).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|