create-walle 0.9.26 → 0.9.28

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 (45) hide show
  1. package/README.md +1 -0
  2. package/package.json +1 -1
  3. package/template/claude-task-manager/api-prompts.js +11 -6
  4. package/template/claude-task-manager/docs/session-status-redesign.html +554 -0
  5. package/template/claude-task-manager/docs/terminal-rendering-redesign.html +529 -0
  6. package/template/claude-task-manager/lib/flush-redraw-markers.js +72 -0
  7. package/template/claude-task-manager/lib/macos-capabilities.js +190 -0
  8. package/template/claude-task-manager/lib/session-messages-projection.js +224 -3
  9. package/template/claude-task-manager/lib/ttl-memo.js +61 -0
  10. package/template/claude-task-manager/public/index.html +892 -11
  11. package/template/claude-task-manager/public/js/activation-render-check.js +40 -2
  12. package/template/claude-task-manager/public/js/session-phase.js +370 -0
  13. package/template/claude-task-manager/public/js/setup.js +74 -1
  14. package/template/claude-task-manager/public/js/stream-view.js +56 -2
  15. package/template/claude-task-manager/server.js +643 -68
  16. package/template/claude-task-manager/workers/read-pool-worker.js +10 -0
  17. package/template/package.json +1 -1
  18. package/template/wall-e/agent.js +130 -24
  19. package/template/wall-e/api-walle.js +12 -1
  20. package/template/wall-e/brain.js +290 -4
  21. package/template/wall-e/chat.js +30 -25
  22. package/template/wall-e/coding/session-plan.js +79 -0
  23. package/template/wall-e/coding-orchestrator.js +9 -3
  24. package/template/wall-e/coding-prompts.js +10 -3
  25. package/template/wall-e/embeddings.js +192 -17
  26. package/template/wall-e/http/model-admin.js +109 -0
  27. package/template/wall-e/lib/event-loop-monitor.js +2 -2
  28. package/template/wall-e/lib/scheduler-worker-jobs.js +156 -121
  29. package/template/wall-e/lib/scheduler.js +226 -13
  30. package/template/wall-e/lib/worker-thread-pool.js +58 -4
  31. package/template/wall-e/llm/ollama-library.js +126 -0
  32. package/template/wall-e/llm/ollama.js +13 -0
  33. package/template/wall-e/llm/provider-backpressure.js +134 -0
  34. package/template/wall-e/llm/provider-health-state.js +24 -0
  35. package/template/wall-e/loops/backfill.js +43 -16
  36. package/template/wall-e/loops/initiative.js +1 -0
  37. package/template/wall-e/loops/think.js +38 -5
  38. package/template/wall-e/mcp-server.js +20 -4
  39. package/template/wall-e/skills/skill-fallback.js +34 -1
  40. package/template/wall-e/skills/skill-planner.js +60 -2
  41. package/template/wall-e/sources/jsonl-utils.js +84 -11
  42. package/template/wall-e/telemetry.js +42 -7
  43. package/template/wall-e/tools/local-tools.js +16 -0
  44. package/template/wall-e/workers/runtime-worker.js +33 -1
  45. package/template/website/index.html +5 -0
@@ -261,6 +261,16 @@ async function _runOp(op, payload = {}) {
261
261
  return { __transfer: true, buffer: Buffer.from(JSON.stringify(obj), 'utf8') };
262
262
  }
263
263
 
264
+ case 'buildSessionMessagesFullResponse': {
265
+ // Stage C: the NON-paginated full read — project (merge + exclusions + image-refs) AND
266
+ // serialize the WHOLE conversation in the worker, returning BYTES (zero-copy). Removes the
267
+ // on-main projection that froze the loop while reading a large conversation. Returns either a
268
+ // bare array or { messages, ...extra } per payload.bareArray (mirrors server.js sendMessages).
269
+ const proj = require('../lib/session-messages-projection');
270
+ const obj = proj.buildFullMessagesResponse(payload);
271
+ return { __transfer: true, buffer: Buffer.from(JSON.stringify(obj), 'utf8') };
272
+ }
273
+
264
274
  case 'parseJsonlTail': {
265
275
  // Bounded (≤~1MB) tail read+parse of a live agent JSONL OFF the main loop — the
266
276
  // apiSessionMessages durable-tail reconcile (lagging importer on an actively-written
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "walle",
3
- "version": "0.9.26",
3
+ "version": "0.9.28",
4
4
  "private": true,
5
5
  "description": "Wall-E — your personal digital twin",
6
6
  "scripts": {
@@ -147,6 +147,48 @@ function clampTimeoutMs(value, fallback) {
147
147
  return Math.max(1000, Math.min(15 * 60 * 1000, Math.trunc(ms)));
148
148
  }
149
149
 
150
+ function restartCatchupMax(env = process.env) {
151
+ const raw = env.WALL_E_RESTART_CATCHUP_MAX ?? env.WALLE_RESTART_CATCHUP_MAX;
152
+ const n = Number(raw);
153
+ if (!Number.isFinite(n)) return 1;
154
+ return Math.max(0, Math.min(5, Math.trunc(n)));
155
+ }
156
+
157
+ function persistEventLoopWarning(warning = {}) {
158
+ try {
159
+ const { runtimeEventLedgerEnabled } = require('./lib/diagnostics-flags');
160
+ if (!runtimeEventLedgerEnabled() || shuttingDown) return;
161
+ const handle = setImmediate(() => {
162
+ try {
163
+ const append = typeof brain.appendRuntimeEvent === 'function' ? brain.appendRuntimeEvent : null;
164
+ const enqueue = typeof brain.enqueueOwnerWrite === 'function' ? brain.enqueueOwnerWrite : null;
165
+ if (!append || !enqueue) return;
166
+ const maxMs = Number(warning.maxMs) || 0;
167
+ const contextSummary = String(warning.contextSummary || '').slice(0, 360);
168
+ enqueue('runtime_event:event_loop', (d) => append({
169
+ source: 'wall-e.event-loop',
170
+ event_type: 'event_loop_blocked',
171
+ status: 'warning',
172
+ duration_ms: maxMs,
173
+ reason: 'event-loop-blocked',
174
+ summary: `Event loop blocked ${Math.round(maxMs)}ms${contextSummary ? `; ${contextSummary}` : ''}`,
175
+ detail: { kind: 'runtime-diagnostics', phase: 'event-loop-monitor' },
176
+ metrics: {
177
+ max_ms: maxMs,
178
+ mean_ms: Number(warning.meanMs) || 0,
179
+ p99_ms: Number(warning.p99Ms) || 0,
180
+ },
181
+ }, d)).catch((err) => {
182
+ console.warn('[wall-e] Could not persist event-loop warning:', err.message);
183
+ });
184
+ } catch (e) {
185
+ console.warn('[wall-e] Could not persist event-loop warning:', e.message);
186
+ }
187
+ });
188
+ handle.unref?.();
189
+ } catch {}
190
+ }
191
+
150
192
  function bootstrapSkills() {
151
193
  const existing = brain.listSkills({});
152
194
  if (existing.length > 0) return; // Already bootstrapped
@@ -352,6 +394,7 @@ async function main() {
352
394
  try { workerPool = require('./lib/runtime-worker-pool').getRuntimeWorkerPoolStatus(); } catch {}
353
395
  return { scheduler, runtime_active: runtime?.active || [], worker_pool: workerPool };
354
396
  },
397
+ onWarn: persistEventLoopWarning,
355
398
  });
356
399
  } catch (e) { console.warn('[wall-e] event-loop monitor unavailable:', e.message); }
357
400
  }
@@ -557,6 +600,8 @@ async function main() {
557
600
  }, {
558
601
  timeoutMs: options.timeoutMs || 0,
559
602
  heavy: options.heavy !== false,
603
+ lane: options.lane,
604
+ priority: options.priority,
560
605
  terminateOnTimeout: (options.timeoutMs || 0) > 0,
561
606
  });
562
607
  } catch (err) {
@@ -691,6 +736,14 @@ async function main() {
691
736
  process.env.WALL_E_SCHEDULER_WORKER_TIMEOUT_MS ?? process.env.WALLE_SCHEDULER_WORKER_TIMEOUT_MS,
692
737
  180000
693
738
  );
739
+ const schedulerTasksWorkerTimeoutMs = clampTimeoutMs(
740
+ process.env.WALL_E_SCHEDULER_TASKS_WORKER_TIMEOUT_MS ?? process.env.WALLE_SCHEDULER_TASKS_WORKER_TIMEOUT_MS,
741
+ 300000
742
+ );
743
+ const schedulerSkillsWorkerTimeoutMs = clampTimeoutMs(
744
+ process.env.WALL_E_SCHEDULER_SKILLS_WORKER_TIMEOUT_MS ?? process.env.WALLE_SCHEDULER_SKILLS_WORKER_TIMEOUT_MS,
745
+ 420000
746
+ );
694
747
  const scheduler = new Scheduler({
695
748
  tickMs: 5000,
696
749
  pools: { llm: 2, ollama: 1, embedding: 1, io: 3 },
@@ -734,6 +787,7 @@ async function main() {
734
787
  run: () => ingest.runOnce(adapters),
735
788
  intervalMs: clampInterval(config.intervals?.ingest_ms, 60000),
736
789
  pool: 'io',
790
+ lane: 0,
737
791
  priority: 1,
738
792
  startDelayMs: 2000,
739
793
  onResult: (r) => {
@@ -750,6 +804,7 @@ async function main() {
750
804
  run: () => think.runOnce(),
751
805
  intervalMs: clampInterval(config.intervals?.think_ms, 120000),
752
806
  pool: 'llm',
807
+ lane: 0,
753
808
  priority: 2,
754
809
  startDelayMs: 5000,
755
810
  dependsOn: ['ingest'],
@@ -770,8 +825,10 @@ async function main() {
770
825
  run: () => runDueTasks(),
771
826
  intervalMs: clampInterval(config.intervals?.tasks_ms, 30000),
772
827
  pool: 'llm',
828
+ lane: 0,
773
829
  priority: 3,
774
830
  startDelayMs: 8000,
831
+ workerTimeoutMs: schedulerTasksWorkerTimeoutMs,
775
832
  onResult: (r) => {
776
833
  if (r.processed > 0) {
777
834
  console.log(`[wall-e] Tasks: ${r.processed} completed`);
@@ -793,8 +850,10 @@ async function main() {
793
850
  run: ({ modelOverride } = {}) => runDueSkills({ modelOverride }),
794
851
  intervalMs: clampInterval(config.intervals?.skills_ms, 300000),
795
852
  pool: 'llm',
853
+ lane: 1,
796
854
  priority: 4,
797
855
  startDelayMs: 11000,
856
+ workerTimeoutMs: schedulerSkillsWorkerTimeoutMs,
798
857
  onResult: (r) => {
799
858
  if (r.executed > 0) {
800
859
  console.log(`[wall-e] Skills: ${r.executed} executed, ${r.memoriesCreated} memories`);
@@ -842,7 +901,9 @@ async function main() {
842
901
  },
843
902
  intervalMs: clampInterval(config.intervals?.training_ms, 300000),
844
903
  pool: 'ollama',
904
+ lane: 2,
845
905
  priority: 5,
906
+ restartCatchupPolicy: 'defer',
846
907
  startDelayMs: 20000,
847
908
  shouldRun: () => getShadowConfig().enabled,
848
909
  onError: (err) => console.error('[eval] Loop error:', err.message),
@@ -865,7 +926,9 @@ async function main() {
865
926
  },
866
927
  intervalMs: clampInterval(config.intervals?.replay_ms, 300000),
867
928
  pool: 'ollama',
929
+ lane: 2,
868
930
  priority: 5,
931
+ restartCatchupPolicy: 'defer',
869
932
  startDelayMs: 15000,
870
933
  shouldRun: () => getShadowConfig().enabled,
871
934
  onError: (err) => console.error('[replay] Loop error:', err.message),
@@ -879,6 +942,7 @@ async function main() {
879
942
  },
880
943
  intervalMs: clampInterval(config.intervals?.initiative_ms, 300000),
881
944
  pool: 'llm',
945
+ lane: 1,
882
946
  priority: 6,
883
947
  startDelayMs: 25000,
884
948
  debounceMs: 30000,
@@ -899,7 +963,9 @@ async function main() {
899
963
  run: () => reflect.runOnce(),
900
964
  intervalMs: clampInterval(config.intervals?.reflect_ms, 3600000),
901
965
  pool: 'llm',
966
+ lane: 2,
902
967
  priority: 7,
968
+ restartCatchupPolicy: 'defer',
903
969
  dependsOn: ['think'],
904
970
  startDelayMs: 30000,
905
971
  onResult: (r) => {
@@ -924,7 +990,9 @@ async function main() {
924
990
  },
925
991
  intervalMs: 600000,
926
992
  pool: 'io',
993
+ lane: 2,
927
994
  priority: 8,
995
+ restartCatchupPolicy: 'defer',
928
996
  startDelayMs: 30000,
929
997
  onError: (err) => console.error('[harvest] Loop error:', err.message),
930
998
  });
@@ -947,7 +1015,9 @@ async function main() {
947
1015
  },
948
1016
  intervalMs: 30000,
949
1017
  pool: 'embedding',
1018
+ lane: 2,
950
1019
  priority: 9,
1020
+ restartCatchupPolicy: 'defer',
951
1021
  startDelayMs: 35000,
952
1022
  timeoutMs: 90000,
953
1023
  onError: (err) => {
@@ -961,7 +1031,9 @@ async function main() {
961
1031
  run: async () => require('./loops/brain-optimize').runOnce(),
962
1032
  intervalMs: 3600000, // hourly; self-gates to a one-shot via kv (brain:optimize:v1)
963
1033
  pool: 'io',
1034
+ lane: 2,
964
1035
  priority: 4,
1036
+ restartCatchupPolicy: 'defer',
965
1037
  startDelayMs: 180000, // 3 min after boot — well past the boot storm
966
1038
  timeoutMs: 120000,
967
1039
  onError: (err) => console.error('[brain-optimize] Loop error:', err.message),
@@ -975,7 +1047,9 @@ async function main() {
975
1047
  },
976
1048
  intervalMs: 24 * 60 * 60 * 1000, // daily
977
1049
  pool: 'embedding',
1050
+ lane: 2,
978
1051
  priority: 10,
1052
+ restartCatchupPolicy: 'defer',
979
1053
  startDelayMs: 120000, // wait 2min after startup
980
1054
  onResult: (r) => {
981
1055
  if (r.archived > 0) console.log(`[wall-e] Dedup: archived ${r.archived} duplicates`);
@@ -996,8 +1070,10 @@ async function main() {
996
1070
  },
997
1071
  intervalMs: 24 * 60 * 60 * 1000, // daily
998
1072
  pool: 'io',
1073
+ lane: 2,
999
1074
  priority: 11,
1000
- startDelayMs: 180000, // 3min after startup — well off the boot/readiness path
1075
+ restartCatchupPolicy: 'defer',
1076
+ startDelayMs: 185000, // just after 3min — well off the boot/readiness path and unique in the IO pool
1001
1077
  onError: (err) => {
1002
1078
  console.error('[wall-e] Daily backup error:', err.message);
1003
1079
  telemetry.trackError('daily-backup', err);
@@ -1011,7 +1087,9 @@ async function main() {
1011
1087
  run: async () => brain.runBrainRetention(),
1012
1088
  intervalMs: 60 * 60 * 1000,
1013
1089
  pool: 'io',
1090
+ lane: 2,
1014
1091
  priority: 11,
1092
+ restartCatchupPolicy: 'defer',
1015
1093
  startDelayMs: 90000,
1016
1094
  onResult: (r) => {
1017
1095
  const nonZero = Object.entries(r || {}).filter(([, v]) => typeof v === 'number' && v > 0);
@@ -1031,7 +1109,9 @@ async function main() {
1031
1109
  },
1032
1110
  intervalMs: 60 * 60 * 1000,
1033
1111
  pool: 'io',
1112
+ lane: 2,
1034
1113
  priority: 11,
1114
+ restartCatchupPolicy: 'defer',
1035
1115
  startDelayMs: 100000,
1036
1116
  onResult: (r) => {
1037
1117
  if (r && r.sent) console.log(`[wall-e] Question digest sent (${r.count}): ${JSON.stringify(r.results)}`);
@@ -1048,6 +1128,7 @@ async function main() {
1048
1128
  },
1049
1129
  intervalMs: 300000,
1050
1130
  pool: 'io',
1131
+ lane: 1,
1051
1132
  priority: 7,
1052
1133
  startDelayMs: 12000,
1053
1134
  shouldRun: () => {
@@ -1072,7 +1153,9 @@ async function main() {
1072
1153
  },
1073
1154
  intervalMs: 24 * 60 * 60 * 1000,
1074
1155
  pool: 'io',
1156
+ lane: 2,
1075
1157
  priority: 10,
1158
+ restartCatchupPolicy: 'defer',
1076
1159
  startDelayMs: 300000,
1077
1160
  onResult: (r) => {
1078
1161
  if (r.cleaned > 0) console.log(`[wall-e] Cleaned ${r.cleaned} old recordings`);
@@ -1096,7 +1179,9 @@ async function main() {
1096
1179
  },
1097
1180
  intervalMs: 24 * 60 * 60 * 1000,
1098
1181
  pool: 'io',
1182
+ lane: 2,
1099
1183
  priority: 10,
1184
+ restartCatchupPolicy: 'defer',
1100
1185
  startDelayMs: 295000,
1101
1186
  shouldRun: () => process.env.WALLE_TELEMETRY_SERVER === '1',
1102
1187
  onResult: (r) => {
@@ -1140,7 +1225,9 @@ async function main() {
1140
1225
  },
1141
1226
  intervalMs: 7 * 24 * 60 * 60 * 1000, // Weekly
1142
1227
  pool: 'llm',
1228
+ lane: 2,
1143
1229
  priority: 10,
1230
+ restartCatchupPolicy: 'defer',
1144
1231
  startDelayMs: 300000, // 5min delay on startup
1145
1232
  shouldRun: () => {
1146
1233
  // Only run if we have coding-agent benchmarks
@@ -1171,7 +1258,9 @@ async function main() {
1171
1258
  },
1172
1259
  intervalMs: 7 * 24 * 60 * 60 * 1000, // Weekly
1173
1260
  pool: 'llm',
1261
+ lane: 2,
1174
1262
  priority: 5,
1263
+ restartCatchupPolicy: 'defer',
1175
1264
  startDelayMs: 600000, // 10min delay on startup
1176
1265
  shouldRun: () => {
1177
1266
  // Run on Sundays only
@@ -1226,31 +1315,48 @@ async function main() {
1226
1315
  telemetry.trackError('boot_check', e);
1227
1316
  }
1228
1317
 
1229
- // Restart catch-up replay jobs whose persisted nextEligibleAt fell
1230
- // while the daemon was down. Cap of 5 immediate runs (5s stagger);
1231
- // anything beyond that is deferred to the periodic tick. Borrowed
1232
- // from OpenClaw's runMissedJobs (src/cron/service/timer.ts:962-1040).
1233
- const restartCatchupOp = runtimeHealth.beginOperation('agent.restartCatchup');
1234
- try {
1235
- const catchup = await scheduler.runMissedJobs();
1236
- if (catchup.ran > 0 || catchup.deferred > 0) {
1237
- console.log(
1238
- `[wall-e] Restart catch-up: ran ${catchup.ran}, deferred ${catchup.deferred}`
1239
- );
1240
- telemetry.track('restart_catchup', {
1241
- ran: catchup.ran,
1242
- deferred: catchup.deferred,
1243
- });
1244
- }
1245
- restartCatchupOp.end({ ok: true, meta: { ran: catchup.ran || 0, deferred: catchup.deferred || 0 } });
1246
- } catch (e) {
1247
- restartCatchupOp.end({ ok: false, error: e });
1248
- console.warn('[wall-e] runMissedJobs threw (non-fatal):', e.message);
1249
- }
1250
-
1251
- // Start the scheduler
1318
+ // Start the scheduler before restart catch-up. Missed-job replay is useful,
1319
+ // but it must never be startup-critical or block the daemon from serving
1320
+ // chat/MCP requests after a restart.
1252
1321
  scheduler.start();
1253
1322
 
1323
+ // Restart catch-up — replay only small foreground jobs immediately; defer
1324
+ // expensive harvest/eval/maintenance work and persist that decision to the
1325
+ // runtime work-item queue for observability. This keeps the main process
1326
+ // responsive even after long downtime with many missed jobs.
1327
+ setTimeout(() => {
1328
+ (async () => {
1329
+ const restartCatchupOp = runtimeHealth.beginOperation('agent.restartCatchup');
1330
+ try {
1331
+ const catchup = await scheduler.runMissedJobs({
1332
+ nonBlocking: true,
1333
+ max: restartCatchupMax(),
1334
+ staggerMs: 0,
1335
+ maxWorkMs: 5000,
1336
+ deferDelayMs: 120000,
1337
+ enqueueWorkItem: brain.enqueueRuntimeWorkItem,
1338
+ });
1339
+ if (catchup.ran > 0 || catchup.deferred > 0) {
1340
+ console.log(
1341
+ `[wall-e] Restart catch-up: dispatched ${catchup.ran}, deferred ${catchup.deferred}`
1342
+ );
1343
+ telemetry.track('restart_catchup', {
1344
+ ran: catchup.ran,
1345
+ deferred: catchup.deferred,
1346
+ non_blocking: 1,
1347
+ });
1348
+ }
1349
+ restartCatchupOp.end({
1350
+ ok: true,
1351
+ meta: { ran: catchup.ran || 0, deferred: catchup.deferred || 0, non_blocking: 1 },
1352
+ });
1353
+ } catch (e) {
1354
+ restartCatchupOp.end({ ok: false, error: e });
1355
+ console.warn('[wall-e] runMissedJobs threw (non-fatal):', e.message);
1356
+ }
1357
+ })().catch((e) => console.warn('[wall-e] Restart catch-up failed (non-fatal):', e.message));
1358
+ }, 1000).unref?.();
1359
+
1254
1360
  // Hot-reload config on file changes
1255
1361
  const { ConfigWatcher } = require('./lib/config-watcher');
1256
1362
  const configWatcher = new ConfigWatcher();
@@ -45,7 +45,10 @@ const TELEMETRY_DIAGNOSTIC_EVENTS = new Set([
45
45
  'error',
46
46
  'skill_fallback_attempt',
47
47
  'initiative_provider_cooldown',
48
+ 'sqlite_write_retry',
48
49
  'compat_usage',
50
+ 'cli_update_started',
51
+ 'cli_update_completed',
49
52
  'upgrade',
50
53
  'upgrade_prompt',
51
54
  'funnel',
@@ -6210,7 +6213,7 @@ function handleWalleApi(req, res, url) {
6210
6213
  `SELECT te.event, te.install_id, ti.machine_bucket, te.meta
6211
6214
  FROM telemetry_events te
6212
6215
  LEFT JOIN telemetry_installs ti ON ti.install_id = te.install_id
6213
- WHERE te.event IN ('funnel', 'upgrade', 'upgrade_prompt')
6216
+ WHERE te.event IN ('funnel', 'upgrade', 'upgrade_prompt', 'cli_update_started', 'cli_update_completed')
6214
6217
  AND te.received_at >= ?
6215
6218
  UNION ALL
6216
6219
  SELECT te.event, te.install_id, ti.machine_bucket, te.meta
@@ -6253,6 +6256,14 @@ function handleWalleApi(req, res, url) {
6253
6256
  if (meta.action === 'apply') upgradeSets.apply_clicked.add(identity);
6254
6257
  continue;
6255
6258
  }
6259
+ if (row.event === 'cli_update_started') {
6260
+ upgradeSets.apply_started.add(identity);
6261
+ continue;
6262
+ }
6263
+ if (row.event === 'cli_update_completed') {
6264
+ upgradeSets.completed.add(identity);
6265
+ continue;
6266
+ }
6256
6267
  if (row.event === 'ctm_update_update_available') upgradeSets.available.add(identity);
6257
6268
  if (row.event === 'ctm_update_ui_prompt_shown') upgradeSets.prompted.add(identity);
6258
6269
  if (row.event === 'ctm_update_ui_action') {