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.
Files changed (98) hide show
  1. package/README.md +8 -3
  2. package/bin/create-walle.js +232 -32
  3. package/bin/mcp-inject.js +18 -53
  4. package/package.json +3 -1
  5. package/template/claude-task-manager/api-prompts.js +11 -2
  6. package/template/claude-task-manager/approval-agent.js +7 -0
  7. package/template/claude-task-manager/db.js +94 -75
  8. package/template/claude-task-manager/docs/session-standup-command-center-design.md +242 -0
  9. package/template/claude-task-manager/docs/session-tooltip-freshness-design.md +224 -0
  10. package/template/claude-task-manager/docs/session-ux-issue-review-2026-05-01.md +369 -0
  11. package/template/claude-task-manager/fuzzy-utils.js +10 -2
  12. package/template/claude-task-manager/git-utils.js +140 -10
  13. package/template/claude-task-manager/lib/agent-capabilities.js +1 -1
  14. package/template/claude-task-manager/lib/agent-presets.js +38 -5
  15. package/template/claude-task-manager/lib/codex-terminal-final.js +53 -0
  16. package/template/claude-task-manager/lib/ctm-session-context-api.js +222 -0
  17. package/template/claude-task-manager/lib/session-diagnostics.js +56 -0
  18. package/template/claude-task-manager/lib/session-history.js +309 -16
  19. package/template/claude-task-manager/lib/session-standup.js +409 -0
  20. package/template/claude-task-manager/lib/session-stream.js +253 -20
  21. package/template/claude-task-manager/lib/standup-attention.js +200 -0
  22. package/template/claude-task-manager/lib/status-hooks.js +8 -2
  23. package/template/claude-task-manager/lib/update-telemetry.js +114 -0
  24. package/template/claude-task-manager/lib/walle-ctm-history.js +49 -6
  25. package/template/claude-task-manager/lib/walle-default-model.js +55 -0
  26. package/template/claude-task-manager/lib/walle-mcp-auto-config.js +66 -0
  27. package/template/claude-task-manager/lib/walle-supervisor.js +86 -19
  28. package/template/claude-task-manager/lib/walle-transcript.js +1 -3
  29. package/template/claude-task-manager/lib/worktree-cwd.js +82 -0
  30. package/template/claude-task-manager/package.json +1 -0
  31. package/template/claude-task-manager/providers/codex-mcp.js +104 -0
  32. package/template/claude-task-manager/providers/index.js +2 -0
  33. package/template/claude-task-manager/public/css/setup.css +2 -1
  34. package/template/claude-task-manager/public/css/walle.css +71 -0
  35. package/template/claude-task-manager/public/index.html +2388 -429
  36. package/template/claude-task-manager/public/js/message-renderer.js +314 -35
  37. package/template/claude-task-manager/public/js/session-search-utils.js +185 -3
  38. package/template/claude-task-manager/public/js/session-status-precedence.js +125 -0
  39. package/template/claude-task-manager/public/js/setup.js +62 -19
  40. package/template/claude-task-manager/public/js/stream-view.js +396 -55
  41. package/template/claude-task-manager/public/js/terminal-restore-state.js +57 -0
  42. package/template/claude-task-manager/public/js/walle-session.js +234 -26
  43. package/template/claude-task-manager/public/js/walle.js +143 -2
  44. package/template/claude-task-manager/server.js +1402 -433
  45. package/template/claude-task-manager/session-integrity.js +77 -28
  46. package/template/claude-task-manager/workers/approval-widget-validator.js +15 -5
  47. package/template/claude-task-manager/workers/scrollback-worker.js +5 -6
  48. package/template/claude-task-manager/workers/state-detectors/codex.js +6 -0
  49. package/template/package.json +1 -1
  50. package/template/wall-e/agent-runners/claude-code.js +2 -0
  51. package/template/wall-e/agent.js +63 -8
  52. package/template/wall-e/api-walle.js +330 -52
  53. package/template/wall-e/brain.js +291 -42
  54. package/template/wall-e/chat.js +172 -15
  55. package/template/wall-e/coding/compaction-service.js +19 -5
  56. package/template/wall-e/coding/stream-processor.js +22 -2
  57. package/template/wall-e/coding/workspace-replay.js +1 -4
  58. package/template/wall-e/coding-orchestrator.js +250 -80
  59. package/template/wall-e/compat.js +0 -28
  60. package/template/wall-e/context/context-builder.js +3 -1
  61. package/template/wall-e/embeddings.js +2 -7
  62. package/template/wall-e/eval/agent-runner.js +30 -9
  63. package/template/wall-e/eval/benchmark-generator.js +21 -1
  64. package/template/wall-e/eval/benchmarks/chat-eval.json +66 -6
  65. package/template/wall-e/eval/benchmarks/coding-agent.json +0 -596
  66. package/template/wall-e/eval/cc-replay.js +1 -0
  67. package/template/wall-e/eval/codex-cli-baseline.js +633 -0
  68. package/template/wall-e/eval/debug-agent003.js +1 -0
  69. package/template/wall-e/eval/eval-orchestrator.js +3 -3
  70. package/template/wall-e/eval/run-agent-benchmarks.js +11 -3
  71. package/template/wall-e/eval/run-codex-cli-baseline.js +177 -0
  72. package/template/wall-e/eval/run-model-comparison.js +1 -0
  73. package/template/wall-e/eval/swebench-adapter.js +1 -0
  74. package/template/wall-e/evaluation/quorum-evaluator.js +0 -1
  75. package/template/wall-e/extraction/knowledge-extractor.js +1 -2
  76. package/template/wall-e/lib/mcp-integration.js +336 -0
  77. package/template/wall-e/llm/ollama.js +47 -8
  78. package/template/wall-e/llm/ollama.plugin.json +1 -1
  79. package/template/wall-e/llm/tool-adapter.js +1 -0
  80. package/template/wall-e/loops/ingest.js +42 -8
  81. package/template/wall-e/loops/initiative.js +87 -2
  82. package/template/wall-e/mcp-server.js +872 -19
  83. package/template/wall-e/memory/ctm-context-client.js +230 -0
  84. package/template/wall-e/memory/ctm-session-context.js +1376 -0
  85. package/template/wall-e/prompts/coding/memory-protocol.md +6 -0
  86. package/template/wall-e/server.js +30 -1
  87. package/template/wall-e/skills/_bundled/memory-search/SKILL.md +8 -0
  88. package/template/wall-e/skills/_bundled/scan-ctm-sessions/SKILL.md +20 -0
  89. package/template/wall-e/skills/_bundled/scan-ctm-sessions/run.js +43 -0
  90. package/template/wall-e/skills/_bundled/slack-mentions/run.js +471 -188
  91. package/template/wall-e/skills/skill-planner.js +86 -4
  92. package/template/wall-e/slack/socket-mode-listener.js +276 -0
  93. package/template/wall-e/telemetry.js +70 -2
  94. package/template/wall-e/tools/builtin-middleware.js +55 -2
  95. package/template/wall-e/tools/shell-policy.js +1 -1
  96. package/template/wall-e/tools/slack-owner.js +104 -0
  97. package/template/website/index.html +4 -4
  98. 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: model || 'gemma4:e4b',
68
+ model: modelId,
35
69
  messages: openaiMessages,
36
70
  stream: false,
71
+ think: resolvedThink !== undefined ? resolvedThink : defaultThink,
37
72
  };
38
- if (maxTokens != null) nativeBody.options = { ...nativeBody.options, num_predict: maxTokens };
39
- if (temperature != null) nativeBody.options = { ...nativeBody.options, temperature };
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: model || 'gemma4:e4b',
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: model || 'gemma4:e4b',
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: model || 'gemma4:e4b', provider: 'ollama', raw };
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 };
@@ -13,7 +13,7 @@
13
13
  "chat": true,
14
14
  "tools": true,
15
15
  "vision": false,
16
- "thinking": false,
16
+ "thinking": true,
17
17
  "streaming": true,
18
18
  "local": true
19
19
  },
@@ -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 checkpoint = brain.getCheckpoint('ingest');
6
- const since = checkpoint?.last_memory_at || null;
16
+ const legacyCheckpoint = brain.getCheckpoint('ingest');
7
17
  let memoriesIngested = 0;
8
- let latestTimestamp = since;
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
- if (!latestTimestamp || mem.timestamp > latestTimestamp) {
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: { logInitiative, executeDecision, getTier },
431
+ _test: {
432
+ logInitiative,
433
+ executeDecision,
434
+ getTier,
435
+ getActiveProviderCooldown,
436
+ setProviderCooldown,
437
+ resetProviderCooldown: () => { providerCooldown = null; },
438
+ },
354
439
  };