create-walle 0.9.25 → 0.9.26

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 (179) hide show
  1. package/README.md +8 -0
  2. package/bin/create-walle.js +815 -45
  3. package/package.json +2 -2
  4. package/template/bin/ctm-dev-cleanup.js +90 -4
  5. package/template/bin/ctm-launch.sh +49 -1
  6. package/template/bin/dev.sh +45 -1
  7. package/template/bin/ensure-stable-node.js +132 -0
  8. package/template/bin/install-service.sh +9 -0
  9. package/template/claude-task-manager/api-prompts.js +899 -119
  10. package/template/claude-task-manager/approval-agent.js +360 -40
  11. package/template/claude-task-manager/bin/ctm-disclaim.c +42 -0
  12. package/template/claude-task-manager/bin/ctm-hotkey.swift +67 -81
  13. package/template/claude-task-manager/bin/ctm-screen-auth.swift +37 -0
  14. package/template/claude-task-manager/bin/install-hotkey.sh +97 -49
  15. package/template/claude-task-manager/bin/restart-ctm.sh +14 -0
  16. package/template/claude-task-manager/db.js +399 -48
  17. package/template/claude-task-manager/docs/approval-hook-sandbox.md +84 -0
  18. package/template/claude-task-manager/docs/codex-app-server-approvals.md +72 -0
  19. package/template/claude-task-manager/docs/codex-native-sandbox.md +47 -0
  20. package/template/claude-task-manager/docs/prompt-editing-tree-design.md +18 -1
  21. package/template/claude-task-manager/lib/approval-hook.js +200 -0
  22. package/template/claude-task-manager/lib/approval-self-adapt.js +1 -0
  23. package/template/claude-task-manager/lib/auth-rules.js +11 -0
  24. package/template/claude-task-manager/lib/background-llm.js +32 -4
  25. package/template/claude-task-manager/lib/codesign-identity.js +140 -0
  26. package/template/claude-task-manager/lib/codex-app-server-client.js +119 -0
  27. package/template/claude-task-manager/lib/codex-approval-bridge.js +118 -0
  28. package/template/claude-task-manager/lib/codex-history-terminal-renderer.js +571 -0
  29. package/template/claude-task-manager/lib/codex-paths.js +73 -0
  30. package/template/claude-task-manager/lib/codex-rollout-snapshot.js +164 -0
  31. package/template/claude-task-manager/lib/codex-rollout-tail.js +72 -0
  32. package/template/claude-task-manager/lib/codex-sandbox-args.js +47 -0
  33. package/template/claude-task-manager/lib/coding-agent-models.js +118 -71
  34. package/template/claude-task-manager/lib/command-targets.js +163 -0
  35. package/template/claude-task-manager/lib/conversation-tail-merge.js +61 -19
  36. package/template/claude-task-manager/lib/db-owner-worker-client.js +29 -1
  37. package/template/claude-task-manager/lib/escalation-review.js +80 -3
  38. package/template/claude-task-manager/lib/flow-control.js +52 -0
  39. package/template/claude-task-manager/lib/fs-watcher.js +24 -15
  40. package/template/claude-task-manager/lib/ingest-cooldown.js +68 -0
  41. package/template/claude-task-manager/lib/jsonl-conversation-parser.js +8 -4
  42. package/template/claude-task-manager/lib/launchd-recovery.js +92 -0
  43. package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +207 -52
  44. package/template/claude-task-manager/lib/mobile-push-store.js +7 -0
  45. package/template/claude-task-manager/lib/model-overview-brain-fallback.js +102 -1
  46. package/template/claude-task-manager/lib/model-overview-cache.js +1 -0
  47. package/template/claude-task-manager/lib/oauth-proxy-supervisor.js +2 -1
  48. package/template/claude-task-manager/lib/perf-tracker.js +29 -2
  49. package/template/claude-task-manager/lib/permission-match.js +146 -16
  50. package/template/claude-task-manager/lib/project-slug.js +33 -0
  51. package/template/claude-task-manager/lib/prompt-intent.js +51 -4
  52. package/template/claude-task-manager/lib/read-pool-client.js +48 -3
  53. package/template/claude-task-manager/lib/real-node.js +73 -0
  54. package/template/claude-task-manager/lib/runtime-work-registry.js +131 -14
  55. package/template/claude-task-manager/lib/session-content-backfill.js +24 -5
  56. package/template/claude-task-manager/lib/session-diagnostics-batch.js +87 -0
  57. package/template/claude-task-manager/lib/session-history.js +5 -7
  58. package/template/claude-task-manager/lib/session-host-manager.js +19 -0
  59. package/template/claude-task-manager/lib/session-jobs.js +6 -0
  60. package/template/claude-task-manager/lib/session-message-response-cache.js +89 -0
  61. package/template/claude-task-manager/lib/session-messages-page.js +211 -0
  62. package/template/claude-task-manager/lib/session-messages-projection.js +170 -0
  63. package/template/claude-task-manager/lib/session-standup.js +8 -0
  64. package/template/claude-task-manager/lib/session-timeline-summary.js +16 -2
  65. package/template/claude-task-manager/lib/session-token-usage.js +30 -8
  66. package/template/claude-task-manager/lib/session-workspace-binding.js +29 -15
  67. package/template/claude-task-manager/lib/storage-migration.js +2 -1
  68. package/template/claude-task-manager/lib/transcript-store.js +179 -12
  69. package/template/claude-task-manager/lib/walle-ctm-history.js +298 -11
  70. package/template/claude-task-manager/lib/walle-permission-reply.js +49 -0
  71. package/template/claude-task-manager/lib/walle-session-cache.js +22 -1
  72. package/template/claude-task-manager/lib/walle-supervisor.js +42 -3
  73. package/template/claude-task-manager/package.json +5 -2
  74. package/template/claude-task-manager/prompt-harvest.js +31 -11
  75. package/template/claude-task-manager/providers/claude-code.js +29 -1
  76. package/template/claude-task-manager/providers/codex.js +13 -1
  77. package/template/claude-task-manager/public/css/setup.css +11 -0
  78. package/template/claude-task-manager/public/css/walle-session.css +132 -4
  79. package/template/claude-task-manager/public/css/walle.css +89 -0
  80. package/template/claude-task-manager/public/icon-16.png +0 -0
  81. package/template/claude-task-manager/public/icon-32.png +0 -0
  82. package/template/claude-task-manager/public/icon-512.png +0 -0
  83. package/template/claude-task-manager/public/index.html +2483 -165
  84. package/template/claude-task-manager/public/js/activation-render-check.js +55 -0
  85. package/template/claude-task-manager/public/js/flow-control-policy.js +52 -0
  86. package/template/claude-task-manager/public/js/message-renderer.js +60 -1
  87. package/template/claude-task-manager/public/js/prompts.js +13 -1
  88. package/template/claude-task-manager/public/js/session-status-precedence.js +9 -3
  89. package/template/claude-task-manager/public/js/setup.js +54 -10
  90. package/template/claude-task-manager/public/js/stream-resize-policy.js +80 -0
  91. package/template/claude-task-manager/public/js/stream-view.js +78 -0
  92. package/template/claude-task-manager/public/js/terminal-reconciler.js +52 -2
  93. package/template/claude-task-manager/public/js/tool-state.js +155 -0
  94. package/template/claude-task-manager/public/js/walle-session.js +887 -326
  95. package/template/claude-task-manager/public/js/walle.js +306 -195
  96. package/template/claude-task-manager/public/m/app.css +1 -0
  97. package/template/claude-task-manager/public/m/app.js +33 -3
  98. package/template/claude-task-manager/queue-engine.js +45 -1
  99. package/template/claude-task-manager/server.js +3367 -540
  100. package/template/claude-task-manager/workers/approval-blocklist.js +130 -17
  101. package/template/claude-task-manager/workers/db-owner-worker.js +31 -1
  102. package/template/claude-task-manager/workers/read-pool-worker.js +92 -5
  103. package/template/claude-task-manager/workers/session-host-process.js +10 -0
  104. package/template/claude-task-manager/workers/state-detectors/codex.js +58 -7
  105. package/template/package.json +2 -3
  106. package/template/shared/icons/AppIcon-ctm.icns +0 -0
  107. package/template/shared/icons/AppIcon-walle.icns +0 -0
  108. package/template/wall-e/agent.js +139 -18
  109. package/template/wall-e/api-walle.js +201 -22
  110. package/template/wall-e/bin/train-gemma-e4b-tooluse.js +1981 -0
  111. package/template/wall-e/brain.js +1049 -39
  112. package/template/wall-e/chat.js +427 -86
  113. package/template/wall-e/coding/acceptance-contract.js +26 -1
  114. package/template/wall-e/coding/action-memory-policy.js +353 -0
  115. package/template/wall-e/coding/action-memory-store.js +814 -0
  116. package/template/wall-e/coding/initial-messages.js +197 -0
  117. package/template/wall-e/coding/no-progress-guard.js +327 -0
  118. package/template/wall-e/coding/permission-service.js +88 -22
  119. package/template/wall-e/coding/session-workspaces.js +81 -0
  120. package/template/wall-e/coding/shell-sandbox.js +124 -0
  121. package/template/wall-e/coding/stream-processor.js +63 -2
  122. package/template/wall-e/coding/tool-execution-controller.js +14 -1
  123. package/template/wall-e/coding/tool-registry.js +1 -1
  124. package/template/wall-e/coding/transcript-writer.js +3 -0
  125. package/template/wall-e/coding-orchestrator.js +636 -35
  126. package/template/wall-e/coding-prompts.js +51 -2
  127. package/template/wall-e/docs/model-routing-policy.md +59 -0
  128. package/template/wall-e/docs/walle-shell-sandbox.md +61 -0
  129. package/template/wall-e/extraction/knowledge-extractor.js +76 -23
  130. package/template/wall-e/http/chat-api.js +30 -12
  131. package/template/wall-e/http/model-admin.js +93 -1
  132. package/template/wall-e/lib/background-lanes.js +133 -0
  133. package/template/wall-e/lib/boot-profile.js +11 -0
  134. package/template/wall-e/lib/brain-owner-worker-client.js +324 -0
  135. package/template/wall-e/lib/brain-read-pool-client.js +311 -0
  136. package/template/wall-e/lib/diagnostics-flags.js +87 -0
  137. package/template/wall-e/lib/event-loop-monitor.js +74 -3
  138. package/template/wall-e/lib/mcp-integration.js +7 -1
  139. package/template/wall-e/lib/real-node.js +98 -0
  140. package/template/wall-e/lib/runtime-health.js +206 -0
  141. package/template/wall-e/lib/runtime-worker-pool.js +101 -0
  142. package/template/wall-e/lib/scheduler-worker-jobs.js +231 -0
  143. package/template/wall-e/lib/scheduler.js +446 -17
  144. package/template/wall-e/lib/service-health.js +61 -2
  145. package/template/wall-e/lib/service-readiness.js +258 -0
  146. package/template/wall-e/lib/usage.js +152 -0
  147. package/template/wall-e/lib/worker-thread-pool.js +389 -0
  148. package/template/wall-e/llm/client.js +81 -4
  149. package/template/wall-e/llm/default-fallback.js +54 -8
  150. package/template/wall-e/llm/mlx.js +536 -73
  151. package/template/wall-e/llm/mlx.plugin.json +1 -1
  152. package/template/wall-e/llm/ollama.js +342 -43
  153. package/template/wall-e/llm/provider-error.js +18 -1
  154. package/template/wall-e/llm/provider-health-state.js +176 -0
  155. package/template/wall-e/llm/routing-policy.js +796 -0
  156. package/template/wall-e/llm/supported-models.js +5 -0
  157. package/template/wall-e/loops/tasks.js +60 -14
  158. package/template/wall-e/loops/think.js +89 -24
  159. package/template/wall-e/mcp-server.js +192 -28
  160. package/template/wall-e/server.js +32 -7
  161. package/template/wall-e/skills/script-skill-runner.js +8 -1
  162. package/template/wall-e/skills/skill-planner.js +64 -1
  163. package/template/wall-e/tools/builtin-middleware.js +67 -2
  164. package/template/wall-e/tools/local-tools.js +116 -26
  165. package/template/wall-e/tools/permission-checker.js +52 -4
  166. package/template/wall-e/tools/permission-rules.js +36 -0
  167. package/template/wall-e/tools/shell-analyzer.js +46 -1
  168. package/template/wall-e/training/gemma-e4b-qlora.js +314 -0
  169. package/template/wall-e/training/real-trajectory-miner.js +2617 -0
  170. package/template/wall-e/training/replay-eval-analysis.js +151 -0
  171. package/template/wall-e/training/run-shell-command-selector.js +277 -0
  172. package/template/wall-e/training/tool-sft-dataset.js +312 -0
  173. package/template/wall-e/training/tool-sft-renderers.js +144 -0
  174. package/template/wall-e/training/tool-trace-harvester.js +1440 -0
  175. package/template/wall-e/training/trajectory-action-selector.js +364 -0
  176. package/template/wall-e/weather-runtime.js +232 -0
  177. package/template/wall-e/workers/brain-owner-worker.js +162 -0
  178. package/template/wall-e/workers/brain-read-worker.js +148 -0
  179. package/template/wall-e/workers/runtime-worker.js +145 -0
@@ -0,0 +1,206 @@
1
+ 'use strict';
2
+
3
+ const { runtimeDiagnosticsEnabled } = require('./diagnostics-flags');
4
+
5
+ const DEFAULT_RECENT_LIMIT = 80;
6
+ const DEFAULT_TOP_LIMIT = 20;
7
+ const MAX_ACTIVE_AGE_MS = 60 * 60 * 1000;
8
+
9
+ const state = {
10
+ startedAt: Date.now(),
11
+ sequence: 0,
12
+ active: new Map(),
13
+ byName: new Map(),
14
+ recent: [],
15
+ };
16
+
17
+ function _now() {
18
+ return Date.now();
19
+ }
20
+
21
+ function _durationMs(value, fallback = 0) {
22
+ const n = Number(value);
23
+ return Number.isFinite(n) ? Math.max(0, Math.round(n)) : fallback;
24
+ }
25
+
26
+ function _safeName(name) {
27
+ const raw = String(name || 'operation').trim() || 'operation';
28
+ return raw
29
+ .replace(/\/Users\/[^/\s]+/g, '/Users/<user>')
30
+ .replace(/[A-Za-z]:\\Users\\[^\\\s]+/g, 'C:\\Users\\<user>')
31
+ .replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi, '<uuid>')
32
+ .replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, '<email>')
33
+ .slice(0, 120);
34
+ }
35
+
36
+ function _safeMeta(meta = {}) {
37
+ const out = {};
38
+ for (const [key, value] of Object.entries(meta || {})) {
39
+ if (value == null) continue;
40
+ const safeKey = String(key).replace(/[^a-zA-Z0-9_.:-]/g, '').slice(0, 48);
41
+ if (!safeKey) continue;
42
+ if (typeof value === 'number' || typeof value === 'boolean') {
43
+ out[safeKey] = value;
44
+ } else if (typeof value === 'string') {
45
+ out[safeKey] = _safeName(value);
46
+ }
47
+ }
48
+ return out;
49
+ }
50
+
51
+ function beginOperation(name, meta = {}) {
52
+ if (!runtimeDiagnosticsEnabled()) {
53
+ return { id: 0, end() { return null; } };
54
+ }
55
+ const id = ++state.sequence;
56
+ const safeName = _safeName(name);
57
+ const startedAt = _now();
58
+ state.active.set(id, {
59
+ id,
60
+ name: safeName,
61
+ startedAt,
62
+ meta: _safeMeta(meta),
63
+ });
64
+ return {
65
+ id,
66
+ end(extra = {}) {
67
+ return endOperation(id, extra);
68
+ },
69
+ };
70
+ }
71
+
72
+ function endOperation(id, extra = {}) {
73
+ const active = state.active.get(id);
74
+ if (!active) return null;
75
+ state.active.delete(id);
76
+ const endedAt = _now();
77
+ const durationMs = _durationMs(extra.durationMs, endedAt - active.startedAt);
78
+ const ok = extra.ok !== false;
79
+ const entry = {
80
+ name: active.name,
81
+ durationMs,
82
+ ok,
83
+ at: new Date(endedAt).toISOString(),
84
+ meta: { ...active.meta, ..._safeMeta(extra.meta || {}) },
85
+ };
86
+ if (extra.error) entry.error = _safeName(extra.error?.code || extra.error?.name || 'error');
87
+ recordOperation(entry.name, durationMs, { ok, error: entry.error, meta: entry.meta, atMs: endedAt });
88
+ return entry;
89
+ }
90
+
91
+ function recordOperation(name, durationMs, options = {}) {
92
+ if (!runtimeDiagnosticsEnabled()) return null;
93
+ const safeName = _safeName(name);
94
+ const ms = _durationMs(durationMs);
95
+ let bucket = state.byName.get(safeName);
96
+ if (!bucket) {
97
+ bucket = {
98
+ name: safeName,
99
+ count: 0,
100
+ ok: 0,
101
+ failed: 0,
102
+ totalMs: 0,
103
+ maxMs: 0,
104
+ samples: [],
105
+ lastAt: null,
106
+ lastError: null,
107
+ lastMeta: {},
108
+ };
109
+ state.byName.set(safeName, bucket);
110
+ }
111
+ const safeMeta = _safeMeta(options.meta || {});
112
+ bucket.count += 1;
113
+ bucket.totalMs += ms;
114
+ bucket.maxMs = Math.max(bucket.maxMs, ms);
115
+ bucket.lastAt = new Date(options.atMs || _now()).toISOString();
116
+ bucket.lastMeta = safeMeta;
117
+ if (options.ok === false) {
118
+ bucket.failed += 1;
119
+ bucket.lastError = _safeName(options.error || 'error');
120
+ } else {
121
+ bucket.ok += 1;
122
+ }
123
+ bucket.samples.push(ms);
124
+ if (bucket.samples.length > 128) bucket.samples.splice(0, bucket.samples.length - 128);
125
+
126
+ state.recent.push({
127
+ name: safeName,
128
+ durationMs: ms,
129
+ ok: options.ok !== false,
130
+ at: bucket.lastAt,
131
+ ...(Object.keys(safeMeta).length ? { meta: safeMeta } : {}),
132
+ ...(options.error ? { error: _safeName(options.error) } : {}),
133
+ });
134
+ if (state.recent.length > DEFAULT_RECENT_LIMIT) {
135
+ state.recent.splice(0, state.recent.length - DEFAULT_RECENT_LIMIT);
136
+ }
137
+ }
138
+
139
+ function _percentile(values, p) {
140
+ if (!values.length) return 0;
141
+ const sorted = values.slice().sort((a, b) => a - b);
142
+ const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * p) - 1));
143
+ return sorted[idx];
144
+ }
145
+
146
+ function snapshot(options = {}) {
147
+ if (!runtimeDiagnosticsEnabled()) {
148
+ state.active.clear();
149
+ return {
150
+ enabled: false,
151
+ uptimeMs: _now() - state.startedAt,
152
+ active: [],
153
+ operations: [],
154
+ recent: [],
155
+ };
156
+ }
157
+ const now = _now();
158
+ for (const [id, op] of state.active.entries()) {
159
+ if (now - op.startedAt > MAX_ACTIVE_AGE_MS) state.active.delete(id);
160
+ }
161
+ const topLimit = Math.max(1, Math.min(100, Number(options.topLimit) || DEFAULT_TOP_LIMIT));
162
+ const recentLimit = Math.max(0, Math.min(DEFAULT_RECENT_LIMIT, Number(options.recentLimit) || 10));
163
+ const operations = Array.from(state.byName.values())
164
+ .map((bucket) => ({
165
+ name: bucket.name,
166
+ count: bucket.count,
167
+ failed: bucket.failed,
168
+ avgMs: bucket.count ? Math.round(bucket.totalMs / bucket.count) : 0,
169
+ maxMs: bucket.maxMs,
170
+ p95Ms: _percentile(bucket.samples, 0.95),
171
+ lastAt: bucket.lastAt,
172
+ lastError: bucket.lastError,
173
+ lastMeta: bucket.lastMeta || {},
174
+ }))
175
+ .sort((a, b) => b.maxMs - a.maxMs || b.count - a.count)
176
+ .slice(0, topLimit);
177
+
178
+ return {
179
+ enabled: true,
180
+ uptimeMs: now - state.startedAt,
181
+ active: Array.from(state.active.values()).map((op) => ({
182
+ id: op.id,
183
+ name: op.name,
184
+ ageMs: now - op.startedAt,
185
+ meta: op.meta,
186
+ })),
187
+ operations,
188
+ recent: recentLimit > 0 ? state.recent.slice(-recentLimit) : [],
189
+ };
190
+ }
191
+
192
+ function resetForTests() {
193
+ state.startedAt = _now();
194
+ state.sequence = 0;
195
+ state.active.clear();
196
+ state.byName.clear();
197
+ state.recent.splice(0);
198
+ }
199
+
200
+ module.exports = {
201
+ beginOperation,
202
+ endOperation,
203
+ recordOperation,
204
+ snapshot,
205
+ _resetForTests: resetForTests,
206
+ };
@@ -0,0 +1,101 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { WorkerThreadPool } = require('./worker-thread-pool');
5
+
6
+ let _pool = null;
7
+
8
+ function _truthy(value) {
9
+ return ['1', 'true', 'yes', 'on'].includes(String(value || '').trim().toLowerCase());
10
+ }
11
+
12
+ function _falsey(value) {
13
+ return ['0', 'false', 'no', 'off'].includes(String(value || '').trim().toLowerCase());
14
+ }
15
+
16
+ function runtimeWorkerPoolEnabled(env = process.env) {
17
+ const value = env.WALL_E_RUNTIME_WORKER_POOL ?? env.WALLE_RUNTIME_WORKER_POOL;
18
+ if (_falsey(value)) return false;
19
+ if (_truthy(value)) return true;
20
+ return true;
21
+ }
22
+
23
+ function runtimeWorkerPoolSize(env = process.env) {
24
+ const n = Number(env.WALL_E_RUNTIME_WORKER_POOL_SIZE ?? env.WALLE_RUNTIME_WORKER_POOL_SIZE);
25
+ if (!Number.isFinite(n) || n <= 0) return 2;
26
+ return Math.min(8, Math.max(1, Math.trunc(n)));
27
+ }
28
+
29
+ function runtimeWorkerRequestTimeoutMs(env = process.env) {
30
+ const n = Number(env.WALL_E_RUNTIME_WORKER_TIMEOUT_MS ?? env.WALLE_RUNTIME_WORKER_TIMEOUT_MS);
31
+ if (!Number.isFinite(n) || n <= 0) return 180 * 1000;
32
+ return Math.max(1000, Math.trunc(n));
33
+ }
34
+
35
+ function runtimeWorkerEnv(env = process.env) {
36
+ const workerEnv = {
37
+ ...env,
38
+ WALL_E_PROCESS_ROLE: 'wall-e-runtime-worker',
39
+ };
40
+ // Wall-E runtime workers own Wall-E work. They must not inherit CTM's
41
+ // process role because brain/session DB guards use that role to reject
42
+ // direct embedded access from the CTM server process.
43
+ delete workerEnv.CTM_PROCESS_ROLE;
44
+ return workerEnv;
45
+ }
46
+
47
+ function getRuntimeWorkerPool(options = {}) {
48
+ if (!runtimeWorkerPoolEnabled(options.env || process.env)) return null;
49
+ if (_pool) return _pool;
50
+ const env = options.env || process.env;
51
+ _pool = new WorkerThreadPool({
52
+ name: 'wall-e-runtime',
53
+ workerPath: options.workerPath || path.resolve(__dirname, '..', 'workers', 'runtime-worker.js'),
54
+ size: options.size || runtimeWorkerPoolSize(env),
55
+ requestTimeoutMs: options.requestTimeoutMs || runtimeWorkerRequestTimeoutMs(env),
56
+ closeTimeoutMs: options.closeTimeoutMs || 5000,
57
+ env: runtimeWorkerEnv(env),
58
+ workerData: {
59
+ role: 'wall-e-runtime-worker',
60
+ ...(options.workerData || {}),
61
+ },
62
+ logger: options.logger || console,
63
+ });
64
+ return _pool;
65
+ }
66
+
67
+ async function closeRuntimeWorkerPool(options = {}) {
68
+ if (!_pool) return { ok: true, alreadyClosed: true };
69
+ const pool = _pool;
70
+ _pool = null;
71
+ return pool.close(options);
72
+ }
73
+
74
+ function getRuntimeWorkerPoolStatus() {
75
+ if (!_pool) return { enabled: runtimeWorkerPoolEnabled(), started: false };
76
+ return { enabled: true, started: true, ..._pool.getStatus() };
77
+ }
78
+
79
+ async function requestRuntimeWorker(op, payload = {}, options = {}) {
80
+ const pool = getRuntimeWorkerPool(options);
81
+ if (!pool) throw new Error('Wall-E runtime worker pool is disabled');
82
+ return pool.request(op, payload, options);
83
+ }
84
+
85
+ async function broadcastRuntimeWorker(op, payload = {}, options = {}) {
86
+ const pool = getRuntimeWorkerPool(options);
87
+ if (!pool) return [];
88
+ return pool.broadcast(op, payload, options);
89
+ }
90
+
91
+ module.exports = {
92
+ getRuntimeWorkerPool,
93
+ closeRuntimeWorkerPool,
94
+ getRuntimeWorkerPoolStatus,
95
+ requestRuntimeWorker,
96
+ broadcastRuntimeWorker,
97
+ runtimeWorkerPoolEnabled,
98
+ runtimeWorkerPoolSize,
99
+ runtimeWorkerRequestTimeoutMs,
100
+ runtimeWorkerEnv,
101
+ };
@@ -0,0 +1,231 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { performance } = require('node:perf_hooks');
6
+
7
+ const WALL_E_DIR = path.resolve(__dirname, '..');
8
+ const CONFIG_PATH = path.join(WALL_E_DIR, 'wall-e-config.json');
9
+ const LEARNER_INTERVAL_MS = 3600000;
10
+
11
+ let _brain = null;
12
+ let _lastLearnerRun = 0;
13
+ let _replayCycle = 0;
14
+
15
+ function _readConfig() {
16
+ if (!fs.existsSync(CONFIG_PATH)) return {};
17
+ try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); }
18
+ catch { return {}; }
19
+ }
20
+
21
+ function _ensureBrain() {
22
+ if (_brain) return _brain;
23
+ const brain = require('../brain');
24
+ brain.initDb();
25
+ _brain = brain;
26
+ return _brain;
27
+ }
28
+
29
+ function _buildPollAdapters() {
30
+ const config = _readConfig();
31
+ const adapterRegistry = require('../adapters/registry');
32
+ adapterRegistry.ensureBootstrapped();
33
+ const adapters = [];
34
+ const adapterCtorOpts = {
35
+ ctm: () => ({}),
36
+ slack: () => ({
37
+ ownerNames: [config.owner?.name, config.owner?.first_name].filter(Boolean),
38
+ }),
39
+ };
40
+ for (const entry of adapterRegistry.list({ mode: 'poll' })) {
41
+ if (!adapterRegistry.isPollEnabledByConfig(entry.manifest, config)) continue;
42
+ const optsFn = adapterRegistry.has(entry.id) && adapterCtorOpts[entry.id];
43
+ const inst = adapterRegistry.instantiate(entry.id, optsFn ? optsFn() : {});
44
+ if (inst) adapters.push(inst);
45
+ }
46
+ return adapters;
47
+ }
48
+
49
+ function _phase(phases, name, startedAt, extra = {}) {
50
+ phases.push({ name, duration_ms: Math.round(performance.now() - startedAt), ...extra });
51
+ }
52
+
53
+ async function _measurePhase(phases, name, fn) {
54
+ const startedAt = performance.now();
55
+ try {
56
+ const result = await fn();
57
+ _phase(phases, name, startedAt, { ok: true });
58
+ return result;
59
+ } catch (err) {
60
+ _phase(phases, name, startedAt, { ok: false, error: String(err?.message || err).slice(0, 160) });
61
+ throw err;
62
+ }
63
+ }
64
+
65
+ async function runSchedulerJob(jobName, payload = {}) {
66
+ const phases = [];
67
+ const trigger = payload.trigger || 'interval';
68
+ const modelOverride = payload.modelOverride || null;
69
+ const brain = _ensureBrain();
70
+ let result;
71
+
72
+ switch (jobName) {
73
+ case 'ingest': {
74
+ const adapters = await _measurePhase(phases, 'build_adapters', () => _buildPollAdapters());
75
+ result = await _measurePhase(phases, 'ingest.runOnce', () => require('../loops/ingest').runOnce(adapters));
76
+ break;
77
+ }
78
+ case 'think':
79
+ result = await _measurePhase(phases, 'think.runOnce', () => require('../loops/think').runOnce());
80
+ break;
81
+ case 'tasks':
82
+ result = await _measurePhase(phases, 'tasks.runDueTasks', () => require('../loops/tasks').runDueTasks());
83
+ break;
84
+ case 'skills':
85
+ result = await _measurePhase(phases, 'skills.runDueSkills', () => require('../skills/skill-planner').runDueSkills({ modelOverride }));
86
+ break;
87
+ case 'training': {
88
+ const { runBatchEval } = require('../eval/evaluator');
89
+ const { updateAllModelTaskScores } = require('../eval/aggregator');
90
+ const { checkPromotions } = require('../eval/promoter');
91
+ const evalResult = await _measurePhase(phases, 'training.runBatchEval', () => runBatchEval(brain, { limit: 50 }));
92
+ await _measurePhase(phases, 'training.aggregateScores', () => updateAllModelTaskScores(brain));
93
+ await _measurePhase(phases, 'training.checkPromotions', () => checkPromotions(brain));
94
+ if (Date.now() - _lastLearnerRun >= LEARNER_INTERVAL_MS) {
95
+ await _measurePhase(phases, 'training.updateModelPreferences', () => require('../evaluation/learner').updateModelPreferences(brain));
96
+ _lastLearnerRun = Date.now();
97
+ }
98
+ result = evalResult;
99
+ break;
100
+ }
101
+ case 'replay': {
102
+ const { runReplay, runMultiTurnReplay } = require('../eval/replay');
103
+ result = _replayCycle % 2 === 0
104
+ ? await _measurePhase(phases, 'replay.singleTurn', () => runReplay(brain, { limit: 50 }))
105
+ : await _measurePhase(phases, 'replay.multiTurn', () => runMultiTurnReplay(brain, { sessionLimit: 5, turnsPerSession: 5 }));
106
+ _replayCycle++;
107
+ break;
108
+ }
109
+ case 'initiative':
110
+ result = await _measurePhase(phases, 'initiative.runLoop', () => require('../loops/initiative').runInitiativeLoop({ trigger }));
111
+ break;
112
+ case 'reflect':
113
+ result = await _measurePhase(phases, 'reflect.runOnce', () => require('../loops/reflect').runOnce());
114
+ break;
115
+ case 'harvest':
116
+ result = await _measurePhase(phases, 'harvester.runHarvest', () => require('../eval/harvester').runHarvest({ brain }));
117
+ break;
118
+ case 'backfill':
119
+ result = await _measurePhase(phases, 'backfill.runOnce', () => require('../loops/backfill').runOnce());
120
+ if (result && result.done) result = { ...result, _done: true };
121
+ break;
122
+ case 'brain-optimize':
123
+ result = await _measurePhase(phases, 'brainOptimize.runOnce', () => require('../loops/brain-optimize').runOnce());
124
+ break;
125
+ case 'dedup':
126
+ result = await _measurePhase(phases, 'dedup.runOnce', () => require('../utils/dedup').runOnce({ threshold: 0.85, limit: 500, dryRun: false }));
127
+ break;
128
+ case 'daily-backup':
129
+ result = await _measurePhase(phases, 'brain.ensureDailyBackup', () => brain.ensureDailyBackup());
130
+ break;
131
+ case 'brain-retention':
132
+ result = await _measurePhase(phases, 'brain.runBrainRetention', () => brain.runBrainRetention());
133
+ break;
134
+ case 'question-digest':
135
+ result = await _measurePhase(phases, 'questionDigest.runDigestJob', () => require('../loops/question-digest').runDigestJob());
136
+ break;
137
+ case 'listening:calendar-sync':
138
+ result = await _measurePhase(phases, 'calendar.syncUpcomingMeetings', () => require('../listening/calendar').syncUpcomingMeetings(2));
139
+ break;
140
+ case 'listening:cleanup':
141
+ result = await _measurePhase(phases, 'recording.cleanupOldRecordings', () => ({ cleaned: require('../listening/storage').cleanupOldRecordings(90) }));
142
+ break;
143
+ case 'telemetry:cleanup': {
144
+ const { runTelemetryCleanup } = require('../api-walle');
145
+ const live = process.env.WALLE_TELEMETRY_CLEANUP_ENABLED === '1';
146
+ result = await _measurePhase(phases, 'telemetry.runCleanup', () => runTelemetryCleanup({
147
+ dryRun: !live,
148
+ confirm: live ? 'delete' : '',
149
+ source: 'scheduler',
150
+ }));
151
+ break;
152
+ }
153
+ case 'agent-benchmark': {
154
+ const { runAgentBenchmarkSuite, resolveModelName } = require('../eval/agent-runner');
155
+ const { runAgentLoop } = require('../coding-orchestrator');
156
+ result = await _measurePhase(phases, 'agentBenchmark.runSuite', () => runAgentBenchmarkSuite({
157
+ brain,
158
+ runAgentLoop,
159
+ model: resolveModelName(),
160
+ timeoutMs: 120000,
161
+ }));
162
+ break;
163
+ }
164
+ case 'weekly-eval-loop': {
165
+ const { run: runWeeklyEval } = require('../eval/weekly-eval-loop');
166
+ const { runAgentLoop } = require('../coding-orchestrator');
167
+ result = await _measurePhase(phases, 'weeklyEval.run', () => runWeeklyEval(brain, { runAgentLoop }));
168
+ break;
169
+ }
170
+ case 'deferred:embeddings-init':
171
+ result = await _measurePhase(phases, 'embeddings.init', () => {
172
+ require('../embeddings').init();
173
+ return { ok: true };
174
+ });
175
+ break;
176
+ case 'deferred:temporal-backfill':
177
+ result = await _measurePhase(phases, 'brain.backfillTemporalValidity', () => ({ rows: brain.backfillTemporalValidity() || 0 }));
178
+ break;
179
+ case 'deferred:skill-reference-sync':
180
+ result = await _measurePhase(phases, 'skillReference.sync', () => {
181
+ const { syncSkillReferences, checkImportedSkillUpdates } = require('../skills/claude-code-reader');
182
+ const refResult = syncSkillReferences();
183
+ const updates = checkImportedSkillUpdates();
184
+ return { synced: refResult.synced || 0, updates: updates.length || 0 };
185
+ });
186
+ break;
187
+ default: {
188
+ const err = new Error(`Unsupported scheduler worker job: ${jobName}`);
189
+ err.code = 'WALL_E_UNSUPPORTED_WORKER_JOB';
190
+ throw err;
191
+ }
192
+ }
193
+
194
+ if (result && typeof result === 'object' && !Array.isArray(result)) {
195
+ return { ...result, _worker_phases: phases };
196
+ }
197
+ return { value: result, _worker_phases: phases };
198
+ }
199
+
200
+ function supportedSchedulerWorkerJobs() {
201
+ return [
202
+ 'ingest',
203
+ 'think',
204
+ 'tasks',
205
+ 'skills',
206
+ 'training',
207
+ 'replay',
208
+ 'initiative',
209
+ 'reflect',
210
+ 'harvest',
211
+ 'backfill',
212
+ 'brain-optimize',
213
+ 'dedup',
214
+ 'daily-backup',
215
+ 'brain-retention',
216
+ 'question-digest',
217
+ 'listening:calendar-sync',
218
+ 'listening:cleanup',
219
+ 'telemetry:cleanup',
220
+ 'agent-benchmark',
221
+ 'weekly-eval-loop',
222
+ 'deferred:embeddings-init',
223
+ 'deferred:temporal-backfill',
224
+ 'deferred:skill-reference-sync',
225
+ ];
226
+ }
227
+
228
+ module.exports = {
229
+ runSchedulerJob,
230
+ supportedSchedulerWorkerJobs,
231
+ };