create-walle 0.9.13 → 0.9.15
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/README.md +8 -3
- package/bin/create-walle.js +232 -32
- package/bin/mcp-inject.js +18 -53
- package/package.json +3 -1
- package/template/claude-task-manager/api-prompts.js +11 -2
- package/template/claude-task-manager/approval-agent.js +7 -0
- package/template/claude-task-manager/db.js +94 -75
- package/template/claude-task-manager/docs/session-standup-command-center-design.md +242 -0
- package/template/claude-task-manager/docs/session-tooltip-freshness-design.md +224 -0
- package/template/claude-task-manager/docs/session-ux-issue-review-2026-05-01.md +369 -0
- package/template/claude-task-manager/fuzzy-utils.js +10 -2
- package/template/claude-task-manager/git-utils.js +140 -10
- package/template/claude-task-manager/lib/agent-capabilities.js +1 -1
- package/template/claude-task-manager/lib/agent-presets.js +38 -5
- package/template/claude-task-manager/lib/codex-terminal-final.js +53 -0
- package/template/claude-task-manager/lib/ctm-session-context-api.js +222 -0
- package/template/claude-task-manager/lib/session-diagnostics.js +56 -0
- package/template/claude-task-manager/lib/session-history.js +309 -16
- package/template/claude-task-manager/lib/session-standup.js +409 -0
- package/template/claude-task-manager/lib/session-stream.js +253 -20
- package/template/claude-task-manager/lib/standup-attention.js +200 -0
- package/template/claude-task-manager/lib/status-hooks.js +8 -2
- package/template/claude-task-manager/lib/update-telemetry.js +114 -0
- package/template/claude-task-manager/lib/walle-ctm-history.js +49 -6
- package/template/claude-task-manager/lib/walle-default-model.js +55 -0
- package/template/claude-task-manager/lib/walle-mcp-auto-config.js +66 -0
- package/template/claude-task-manager/lib/walle-supervisor.js +86 -19
- package/template/claude-task-manager/lib/walle-transcript.js +1 -3
- package/template/claude-task-manager/lib/worktree-cwd.js +82 -0
- package/template/claude-task-manager/package.json +1 -0
- package/template/claude-task-manager/providers/codex-mcp.js +104 -0
- package/template/claude-task-manager/providers/index.js +2 -0
- package/template/claude-task-manager/public/css/setup.css +2 -1
- package/template/claude-task-manager/public/css/walle.css +71 -0
- package/template/claude-task-manager/public/index.html +2388 -429
- package/template/claude-task-manager/public/js/message-renderer.js +314 -35
- package/template/claude-task-manager/public/js/session-search-utils.js +185 -3
- package/template/claude-task-manager/public/js/session-status-precedence.js +125 -0
- package/template/claude-task-manager/public/js/setup.js +62 -19
- package/template/claude-task-manager/public/js/stream-view.js +396 -55
- package/template/claude-task-manager/public/js/terminal-restore-state.js +57 -0
- package/template/claude-task-manager/public/js/walle-session.js +234 -26
- package/template/claude-task-manager/public/js/walle.js +143 -2
- package/template/claude-task-manager/server.js +1402 -433
- package/template/claude-task-manager/session-integrity.js +77 -28
- package/template/claude-task-manager/workers/approval-widget-validator.js +15 -5
- package/template/claude-task-manager/workers/scrollback-worker.js +5 -6
- package/template/claude-task-manager/workers/state-detectors/codex.js +6 -0
- package/template/package.json +1 -1
- package/template/wall-e/agent-runners/claude-code.js +2 -0
- package/template/wall-e/agent.js +63 -8
- package/template/wall-e/api-walle.js +330 -52
- package/template/wall-e/brain.js +291 -42
- package/template/wall-e/chat.js +172 -15
- package/template/wall-e/coding/compaction-service.js +19 -5
- package/template/wall-e/coding/stream-processor.js +22 -2
- package/template/wall-e/coding/workspace-replay.js +1 -4
- package/template/wall-e/coding-orchestrator.js +250 -80
- package/template/wall-e/compat.js +0 -28
- package/template/wall-e/context/context-builder.js +3 -1
- package/template/wall-e/embeddings.js +2 -7
- package/template/wall-e/eval/agent-runner.js +30 -9
- package/template/wall-e/eval/benchmark-generator.js +21 -1
- package/template/wall-e/eval/benchmarks/chat-eval.json +66 -6
- package/template/wall-e/eval/benchmarks/coding-agent.json +0 -596
- package/template/wall-e/eval/cc-replay.js +1 -0
- package/template/wall-e/eval/codex-cli-baseline.js +633 -0
- package/template/wall-e/eval/debug-agent003.js +1 -0
- package/template/wall-e/eval/eval-orchestrator.js +3 -3
- package/template/wall-e/eval/run-agent-benchmarks.js +11 -3
- package/template/wall-e/eval/run-codex-cli-baseline.js +177 -0
- package/template/wall-e/eval/run-model-comparison.js +1 -0
- package/template/wall-e/eval/swebench-adapter.js +1 -0
- package/template/wall-e/evaluation/quorum-evaluator.js +0 -1
- package/template/wall-e/extraction/knowledge-extractor.js +1 -2
- package/template/wall-e/lib/mcp-integration.js +336 -0
- package/template/wall-e/llm/ollama.js +47 -8
- package/template/wall-e/llm/ollama.plugin.json +1 -1
- package/template/wall-e/llm/tool-adapter.js +1 -0
- package/template/wall-e/loops/ingest.js +42 -8
- package/template/wall-e/loops/initiative.js +87 -2
- package/template/wall-e/mcp-server.js +872 -19
- package/template/wall-e/memory/ctm-context-client.js +230 -0
- package/template/wall-e/memory/ctm-session-context.js +1376 -0
- package/template/wall-e/prompts/coding/memory-protocol.md +6 -0
- package/template/wall-e/server.js +30 -1
- package/template/wall-e/skills/_bundled/memory-search/SKILL.md +8 -0
- package/template/wall-e/skills/_bundled/scan-ctm-sessions/SKILL.md +20 -0
- package/template/wall-e/skills/_bundled/scan-ctm-sessions/run.js +43 -0
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +471 -188
- package/template/wall-e/skills/skill-planner.js +86 -4
- package/template/wall-e/slack/socket-mode-listener.js +276 -0
- package/template/wall-e/telemetry.js +70 -2
- package/template/wall-e/tools/builtin-middleware.js +55 -2
- package/template/wall-e/tools/shell-policy.js +1 -1
- package/template/wall-e/tools/slack-owner.js +104 -0
- package/template/website/index.html +4 -4
- package/template/builder-journal.md +0 -17
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
const { toOpenAI, messagesToOpenAI, responseFromOpenAI } = require('./tool-adapter');
|
|
4
4
|
|
|
5
|
+
function isGemma4Model(model) {
|
|
6
|
+
return /^gemma4:/i.test(String(model || ''));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function resolveThinkValue({ model, thinking, reasoningEffort } = {}) {
|
|
10
|
+
if (thinking === false || thinking === 'disabled' || thinking === 'off') return false;
|
|
11
|
+
if (thinking === true || thinking === 'enabled' || thinking === 'on') return true;
|
|
12
|
+
if (['low', 'medium', 'high'].includes(thinking)) return thinking;
|
|
13
|
+
|
|
14
|
+
// Ollama's GPT-OSS models accept effort levels. Gemma4's thinking renderer
|
|
15
|
+
// accepts the boolean form, so keep Gemma4 on true when callers ask for any
|
|
16
|
+
// non-disabled reasoning effort.
|
|
17
|
+
if (reasoningEffort && reasoningEffort !== 'disabled') {
|
|
18
|
+
if (/^gpt-oss/i.test(String(model || '')) && ['low', 'medium', 'high'].includes(reasoningEffort)) {
|
|
19
|
+
return reasoningEffort;
|
|
20
|
+
}
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildOllamaOptions({ model, maxTokens, temperature, options } = {}) {
|
|
27
|
+
const out = { ...(options || {}) };
|
|
28
|
+
if (maxTokens != null) out.num_predict = maxTokens;
|
|
29
|
+
if (temperature != null) out.temperature = temperature;
|
|
30
|
+
if (isGemma4Model(model) && out.num_ctx == null) {
|
|
31
|
+
out.num_ctx = 16384;
|
|
32
|
+
}
|
|
33
|
+
return Object.keys(out).length ? out : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
5
36
|
/**
|
|
6
37
|
* Create an Ollama local-model LLM provider.
|
|
7
38
|
* Uses Ollama's OpenAI-compatible API — no npm dependency needed.
|
|
@@ -9,11 +40,13 @@ const { toOpenAI, messagesToOpenAI, responseFromOpenAI } = require('./tool-adapt
|
|
|
9
40
|
*/
|
|
10
41
|
function createOllamaProvider(config = {}) {
|
|
11
42
|
const baseUrl = (config.baseUrl || 'http://localhost:11434').replace(/\/+$/, '');
|
|
43
|
+
const defaultThink = config.think ?? false;
|
|
12
44
|
|
|
13
45
|
return {
|
|
14
46
|
type: 'ollama',
|
|
15
47
|
|
|
16
|
-
async chat({ model, messages, tools, system, maxTokens, temperature, signal } = {}) {
|
|
48
|
+
async chat({ model, messages, tools, system, maxTokens, temperature, signal, thinking, reasoningEffort, options } = {}) {
|
|
49
|
+
const modelId = model || 'gemma4:e4b';
|
|
17
50
|
const openaiMessages = [];
|
|
18
51
|
|
|
19
52
|
// System prompt as first message
|
|
@@ -30,13 +63,15 @@ function createOllamaProvider(config = {}) {
|
|
|
30
63
|
const useNativeApi = !tools || tools.length === 0;
|
|
31
64
|
|
|
32
65
|
if (useNativeApi) {
|
|
66
|
+
const resolvedThink = resolveThinkValue({ model: modelId, thinking, reasoningEffort });
|
|
33
67
|
const nativeBody = {
|
|
34
|
-
model:
|
|
68
|
+
model: modelId,
|
|
35
69
|
messages: openaiMessages,
|
|
36
70
|
stream: false,
|
|
71
|
+
think: resolvedThink !== undefined ? resolvedThink : defaultThink,
|
|
37
72
|
};
|
|
38
|
-
|
|
39
|
-
if (
|
|
73
|
+
const nativeOptions = buildOllamaOptions({ model: modelId, maxTokens, temperature, options });
|
|
74
|
+
if (nativeOptions) nativeBody.options = nativeOptions;
|
|
40
75
|
|
|
41
76
|
const start = Date.now();
|
|
42
77
|
const resp = await fetch(`${baseUrl}/api/chat`, {
|
|
@@ -54,6 +89,7 @@ function createOllamaProvider(config = {}) {
|
|
|
54
89
|
const raw = await resp.json();
|
|
55
90
|
const latencyMs = Date.now() - start;
|
|
56
91
|
const msg = raw.message || {};
|
|
92
|
+
const reasoningContent = msg.thinking || msg.reasoning || msg.reasoning_content || null;
|
|
57
93
|
|
|
58
94
|
// Extract accurate timing from native API (durations are in nanoseconds)
|
|
59
95
|
const evalDurationNs = raw.eval_duration || 0;
|
|
@@ -65,6 +101,7 @@ function createOllamaProvider(config = {}) {
|
|
|
65
101
|
|
|
66
102
|
return {
|
|
67
103
|
content: msg.content || null,
|
|
104
|
+
reasoningContent,
|
|
68
105
|
toolCalls: [],
|
|
69
106
|
stopReason: 'end_turn',
|
|
70
107
|
usage: {
|
|
@@ -76,17 +113,19 @@ function createOllamaProvider(config = {}) {
|
|
|
76
113
|
promptEvalDurationMs: promptEvalDurationNs / 1e6,
|
|
77
114
|
},
|
|
78
115
|
latencyMs,
|
|
79
|
-
model:
|
|
116
|
+
model: modelId,
|
|
80
117
|
provider: 'ollama',
|
|
81
118
|
raw,
|
|
82
119
|
};
|
|
83
120
|
}
|
|
84
121
|
|
|
85
122
|
// Fall back to OpenAI-compat API for tool use
|
|
123
|
+
const resolvedThink = resolveThinkValue({ model: modelId, thinking, reasoningEffort });
|
|
86
124
|
const body = {
|
|
87
|
-
model:
|
|
125
|
+
model: modelId,
|
|
88
126
|
messages: openaiMessages,
|
|
89
127
|
stream: false,
|
|
128
|
+
think: resolvedThink !== undefined ? resolvedThink : defaultThink,
|
|
90
129
|
};
|
|
91
130
|
|
|
92
131
|
const openaiTools = toOpenAI(tools);
|
|
@@ -132,7 +171,7 @@ function createOllamaProvider(config = {}) {
|
|
|
132
171
|
if (normalized.stopReason === 'tool_calls') stopReason = 'tool_use';
|
|
133
172
|
else if (normalized.stopReason === 'stop' || !normalized.stopReason) stopReason = 'end_turn';
|
|
134
173
|
|
|
135
|
-
return { ...normalized, stopReason, latencyMs: Date.now() - start, model:
|
|
174
|
+
return { ...normalized, stopReason, latencyMs: Date.now() - start, model: modelId, provider: 'ollama', raw };
|
|
136
175
|
},
|
|
137
176
|
|
|
138
177
|
async listModels() {
|
|
@@ -169,4 +208,4 @@ function createOllamaProvider(config = {}) {
|
|
|
169
208
|
};
|
|
170
209
|
}
|
|
171
210
|
|
|
172
|
-
module.exports = { createOllamaProvider };
|
|
211
|
+
module.exports = { createOllamaProvider, buildOllamaOptions, resolveThinkValue };
|
|
@@ -205,6 +205,7 @@ function responseFromOpenAI(resp) {
|
|
|
205
205
|
|
|
206
206
|
return {
|
|
207
207
|
content: msg.content || null,
|
|
208
|
+
reasoningContent: msg.reasoning_content || msg.reasoning || msg.thinking || null,
|
|
208
209
|
toolCalls: (msg.tool_calls || []).map((tc, index) => ({
|
|
209
210
|
id: tc.id || synthesizeOpenAIToolCallId(index),
|
|
210
211
|
name: tc.function.name,
|
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
const brain = require('../brain');
|
|
2
2
|
const eventBus = require('../events/event-bus');
|
|
3
3
|
|
|
4
|
+
function checkpointName(adapter) {
|
|
5
|
+
const name = adapter?.name || adapter?.constructor?.name || 'unknown';
|
|
6
|
+
return `ingest:${name}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function newerTimestamp(a, b) {
|
|
10
|
+
if (!a) return b || null;
|
|
11
|
+
if (!b) return a;
|
|
12
|
+
return b > a ? b : a;
|
|
13
|
+
}
|
|
14
|
+
|
|
4
15
|
async function runOnce(adapters) {
|
|
5
|
-
const
|
|
6
|
-
const since = checkpoint?.last_memory_at || null;
|
|
16
|
+
const legacyCheckpoint = brain.getCheckpoint('ingest');
|
|
7
17
|
let memoriesIngested = 0;
|
|
8
|
-
let latestTimestamp =
|
|
18
|
+
let latestTimestamp = legacyCheckpoint?.last_memory_at || null;
|
|
19
|
+
const adapterResults = {};
|
|
9
20
|
|
|
10
21
|
for (const adapter of adapters) {
|
|
22
|
+
const adapterCheckpointName = checkpointName(adapter);
|
|
23
|
+
const adapterCheckpoint = brain.getCheckpoint(adapterCheckpointName);
|
|
24
|
+
const since = adapterCheckpoint?.last_memory_at || null;
|
|
25
|
+
let adapterIngested = 0;
|
|
26
|
+
let adapterLatestTimestamp = since;
|
|
27
|
+
|
|
11
28
|
try {
|
|
12
29
|
const memories = await adapter.poll(since);
|
|
13
30
|
for (const mem of memories) {
|
|
31
|
+
adapterLatestTimestamp = newerTimestamp(adapterLatestTimestamp, mem.timestamp);
|
|
32
|
+
latestTimestamp = newerTimestamp(latestTimestamp, mem.timestamp);
|
|
14
33
|
const result = brain.insertMemory(mem);
|
|
15
34
|
if (result) {
|
|
16
35
|
memoriesIngested++;
|
|
17
|
-
|
|
18
|
-
latestTimestamp = mem.timestamp;
|
|
19
|
-
}
|
|
36
|
+
adapterIngested++;
|
|
20
37
|
if (mem.importance >= 0.8 || mem.memory_type === 'message_received') {
|
|
21
38
|
eventBus.emitHighImportance(result?.id, (mem.content || '').slice(0, 200));
|
|
22
39
|
}
|
|
@@ -59,17 +76,34 @@ async function runOnce(adapters) {
|
|
|
59
76
|
console.error('[ingest] Exchange chunking failed:', e.message);
|
|
60
77
|
}
|
|
61
78
|
}
|
|
79
|
+
brain.upsertCheckpoint(adapterCheckpointName, {
|
|
80
|
+
last_memory_at: adapterLatestTimestamp,
|
|
81
|
+
metadata: JSON.stringify({
|
|
82
|
+
adapter: adapter.name || adapter.constructor?.name || 'unknown',
|
|
83
|
+
memories_ingested: adapterIngested,
|
|
84
|
+
memories_seen: memories.length,
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
adapterResults[adapter.name || adapter.constructor?.name || 'unknown'] = {
|
|
88
|
+
memoriesIngested: adapterIngested,
|
|
89
|
+
memoriesSeen: memories.length,
|
|
90
|
+
lastMemoryAt: adapterLatestTimestamp,
|
|
91
|
+
};
|
|
62
92
|
} catch (err) {
|
|
63
93
|
console.error(`[ingest] Adapter ${adapter.name} failed:`, err.message);
|
|
94
|
+
adapterResults[adapter.name || adapter.constructor?.name || 'unknown'] = {
|
|
95
|
+
error: err.message,
|
|
96
|
+
lastMemoryAt: adapterLatestTimestamp,
|
|
97
|
+
};
|
|
64
98
|
}
|
|
65
99
|
}
|
|
66
100
|
|
|
67
101
|
brain.upsertCheckpoint('ingest', {
|
|
68
102
|
last_memory_at: latestTimestamp,
|
|
69
|
-
metadata: JSON.stringify({ memories_ingested: memoriesIngested }),
|
|
103
|
+
metadata: JSON.stringify({ memories_ingested: memoriesIngested, adapters: adapterResults }),
|
|
70
104
|
});
|
|
71
105
|
|
|
72
106
|
return { memoriesIngested };
|
|
73
107
|
}
|
|
74
108
|
|
|
75
|
-
module.exports = { runOnce };
|
|
109
|
+
module.exports = { checkpointName, runOnce };
|
|
@@ -3,12 +3,24 @@
|
|
|
3
3
|
const brain = require('../brain');
|
|
4
4
|
const { buildStateSnapshot, isStateEmpty, formatSnapshot } = require('../context/state-snapshot');
|
|
5
5
|
const { canActAutonomously, getDomainConfidence } = require('../decision/confidence');
|
|
6
|
-
const { getDefaultClient, getDefaultModel, resolveCompatibleModel } = require('../llm/client');
|
|
6
|
+
const { getDefaultClient, getDefaultModel, getDefaultProviderType, resolveCompatibleModel } = require('../llm/client');
|
|
7
7
|
const { v4: uuidv4 } = require('uuid');
|
|
8
8
|
const { buildDisciplineHeader } = require('./loop-prompt-discipline');
|
|
9
9
|
const { augmentSystemPrompt } = require('./loop-directives');
|
|
10
|
+
const telemetry = require('../telemetry');
|
|
11
|
+
const { classifyProviderError } = require('../llm/provider-error');
|
|
10
12
|
|
|
11
13
|
const COOLDOWN_MS = 30 * 60 * 1000; // 30 minutes between same decisions
|
|
14
|
+
const PROVIDER_COOLDOWN_MS = {
|
|
15
|
+
auth_error: 60 * 60 * 1000,
|
|
16
|
+
quota_exceeded: 60 * 60 * 1000,
|
|
17
|
+
rate_limited: 15 * 60 * 1000,
|
|
18
|
+
network: 10 * 60 * 1000,
|
|
19
|
+
provider_unavailable: 10 * 60 * 1000,
|
|
20
|
+
timeout: 10 * 60 * 1000,
|
|
21
|
+
};
|
|
22
|
+
const PROVIDER_COOLDOWN_TYPES = new Set(Object.keys(PROVIDER_COOLDOWN_MS));
|
|
23
|
+
let providerCooldown = null;
|
|
12
24
|
|
|
13
25
|
/**
|
|
14
26
|
* Read ~/.walle/HEARTBEAT.md (or opts.heartbeatPath for tests). Returns
|
|
@@ -239,11 +251,60 @@ function isOnCooldown(decision, recentDecisions) {
|
|
|
239
251
|
});
|
|
240
252
|
}
|
|
241
253
|
|
|
254
|
+
function getActiveProviderCooldown(now = Date.now()) {
|
|
255
|
+
if (!providerCooldown) return null;
|
|
256
|
+
if (providerCooldown.until <= now) {
|
|
257
|
+
providerCooldown = null;
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
return providerCooldown;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function setProviderCooldown(providerError, now = Date.now()) {
|
|
264
|
+
if (!providerError || !PROVIDER_COOLDOWN_TYPES.has(providerError.type)) return null;
|
|
265
|
+
const cooldownMs = PROVIDER_COOLDOWN_MS[providerError.type] || 10 * 60 * 1000;
|
|
266
|
+
providerCooldown = {
|
|
267
|
+
type: providerError.type,
|
|
268
|
+
provider: providerError.provider || null,
|
|
269
|
+
model: providerError.model || null,
|
|
270
|
+
message: providerError.userMessage || providerError.message || '',
|
|
271
|
+
until: now + cooldownMs,
|
|
272
|
+
cooldownMs,
|
|
273
|
+
};
|
|
274
|
+
try {
|
|
275
|
+
telemetry.track('initiative_provider_cooldown', {
|
|
276
|
+
type: providerCooldown.type,
|
|
277
|
+
provider: providerCooldown.provider,
|
|
278
|
+
model: providerCooldown.model,
|
|
279
|
+
cooldown_seconds: Math.round(cooldownMs / 1000),
|
|
280
|
+
});
|
|
281
|
+
} catch {}
|
|
282
|
+
return providerCooldown;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function skippedProviderCooldown(cooldown, reason = 'provider-cooldown') {
|
|
286
|
+
return {
|
|
287
|
+
skipped: true,
|
|
288
|
+
loop: 'initiative',
|
|
289
|
+
reason,
|
|
290
|
+
provider_error_type: cooldown.type,
|
|
291
|
+
provider: cooldown.provider,
|
|
292
|
+
model: cooldown.model,
|
|
293
|
+
retry_at: new Date(cooldown.until).toISOString(),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
242
297
|
/**
|
|
243
298
|
* Main initiative loop function. Called on an interval from agent.js
|
|
244
299
|
*/
|
|
245
300
|
async function runInitiativeLoop(opts = {}) {
|
|
246
301
|
const { shouldRunLoop, skipped } = require('./loop-precheck');
|
|
302
|
+
const now = typeof opts.now === 'function' ? opts.now() : Date.now();
|
|
303
|
+
const activeCooldown = getActiveProviderCooldown(now);
|
|
304
|
+
if (activeCooldown) {
|
|
305
|
+
return skippedProviderCooldown(activeCooldown);
|
|
306
|
+
}
|
|
307
|
+
|
|
247
308
|
const pre = shouldRunLoop({ loopName: 'initiative' });
|
|
248
309
|
if (!pre.run) {
|
|
249
310
|
if (pre.reason === 'no-provider' && !runInitiativeLoop._pauseLogged) {
|
|
@@ -312,6 +373,23 @@ async function runInitiativeLoop(opts = {}) {
|
|
|
312
373
|
const text = response.content || '';
|
|
313
374
|
|
|
314
375
|
decision = parseDecision(text);
|
|
376
|
+
providerCooldown = null;
|
|
377
|
+
} catch (err) {
|
|
378
|
+
const providerError = classifyProviderError(err, {
|
|
379
|
+
provider: provider.type || getDefaultProviderType(),
|
|
380
|
+
model,
|
|
381
|
+
});
|
|
382
|
+
const cooldown = setProviderCooldown(providerError, now);
|
|
383
|
+
if (cooldown) {
|
|
384
|
+
try {
|
|
385
|
+
const { default: providerAvailability } = require('../llm/provider-availability');
|
|
386
|
+
const registeredProv = providerAvailability.getConfiguredProviders()
|
|
387
|
+
.find(p => p.providerType === providerError.provider || p.providerId === providerError.provider);
|
|
388
|
+
if (registeredProv) providerAvailability.recordFailure(registeredProv.providerId, providerError.userMessage || err.message);
|
|
389
|
+
} catch {}
|
|
390
|
+
return skippedProviderCooldown(cooldown, 'provider-cooldown-started');
|
|
391
|
+
}
|
|
392
|
+
throw err;
|
|
315
393
|
} finally {
|
|
316
394
|
clearTimeout(timeout);
|
|
317
395
|
}
|
|
@@ -350,5 +428,12 @@ module.exports = {
|
|
|
350
428
|
buildInitiativePrompt,
|
|
351
429
|
isOnCooldown,
|
|
352
430
|
// Exported for testing
|
|
353
|
-
_test: {
|
|
431
|
+
_test: {
|
|
432
|
+
logInitiative,
|
|
433
|
+
executeDecision,
|
|
434
|
+
getTier,
|
|
435
|
+
getActiveProviderCooldown,
|
|
436
|
+
setProviderCooldown,
|
|
437
|
+
resetProviderCooldown: () => { providerCooldown = null; },
|
|
438
|
+
},
|
|
354
439
|
};
|