create-walle 0.9.11 → 0.9.13

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 (167) hide show
  1. package/README.md +3 -3
  2. package/package.json +2 -2
  3. package/template/bin/dev.sh +7 -1
  4. package/template/bin/setup.js +53 -9
  5. package/template/bin/sync-images.js +53 -0
  6. package/template/builder-journal.md +17 -0
  7. package/template/claude-task-manager/api-prompts.js +98 -13
  8. package/template/claude-task-manager/api-reviews.js +82 -5
  9. package/template/claude-task-manager/db.js +32 -5
  10. package/template/claude-task-manager/docs/session-capture-foundation-design.md +1273 -0
  11. package/template/claude-task-manager/lib/claude-desktop-sessions.js +696 -0
  12. package/template/claude-task-manager/lib/coding-agent-models.js +49 -1
  13. package/template/claude-task-manager/lib/session-capture.js +421 -0
  14. package/template/claude-task-manager/lib/session-history.js +135 -15
  15. package/template/claude-task-manager/lib/session-jobs.js +10 -5
  16. package/template/claude-task-manager/lib/session-stream.js +87 -19
  17. package/template/claude-task-manager/lib/setup-provider-config.js +115 -0
  18. package/template/claude-task-manager/lib/walle-ctm-history.js +72 -0
  19. package/template/claude-task-manager/lib/walle-session-context.js +61 -0
  20. package/template/claude-task-manager/lib/walle-transcript.js +176 -0
  21. package/template/claude-task-manager/public/css/setup.css +35 -8
  22. package/template/claude-task-manager/public/css/walle-session.css +56 -0
  23. package/template/claude-task-manager/public/css/walle.css +120 -0
  24. package/template/claude-task-manager/public/index.html +814 -181
  25. package/template/claude-task-manager/public/js/message-renderer.js +148 -19
  26. package/template/claude-task-manager/public/js/reviews.js +120 -62
  27. package/template/claude-task-manager/public/js/setup.js +75 -31
  28. package/template/claude-task-manager/public/js/stream-view.js +115 -55
  29. package/template/claude-task-manager/public/js/walle-session.js +84 -2
  30. package/template/claude-task-manager/public/js/walle.js +308 -54
  31. package/template/claude-task-manager/server.js +1092 -146
  32. package/template/claude-task-manager/session-integrity.js +181 -54
  33. package/template/claude-task-manager/session-utils.js +123 -41
  34. package/template/claude-task-manager/workers/state-detectors/codex.js +5 -2
  35. package/template/package.json +1 -1
  36. package/template/wall-e/adapters/ctm.js +39 -18
  37. package/template/wall-e/agent-runners/contract.js +17 -0
  38. package/template/wall-e/agent-runners/index.js +22 -0
  39. package/template/wall-e/agent-runtime/harness.js +212 -0
  40. package/template/wall-e/agent-runtime/index.js +8 -0
  41. package/template/wall-e/agent-runtime/registry.js +67 -0
  42. package/template/wall-e/agent-runtime/session-store.js +179 -0
  43. package/template/wall-e/agent-runtime/spawn.js +208 -0
  44. package/template/wall-e/api-walle.js +174 -7
  45. package/template/wall-e/brain.js +266 -28
  46. package/template/wall-e/channels/policy.js +88 -0
  47. package/template/wall-e/channels/registry.js +15 -1
  48. package/template/wall-e/channels/reply-dispatcher.js +70 -0
  49. package/template/wall-e/channels/session-bindings.js +51 -0
  50. package/template/wall-e/chat/code-review-context.js +29 -0
  51. package/template/wall-e/chat.js +188 -42
  52. package/template/wall-e/coding/acp-adapter.js +188 -0
  53. package/template/wall-e/coding/agent-catalog.js +129 -0
  54. package/template/wall-e/coding/compaction-service.js +247 -0
  55. package/template/wall-e/coding/execution-trace.js +3 -0
  56. package/template/wall-e/coding/instruction-service.js +224 -0
  57. package/template/wall-e/coding/model-message.js +67 -0
  58. package/template/wall-e/coding/permission-rules-store.js +111 -0
  59. package/template/wall-e/coding/permission-service.js +266 -0
  60. package/template/wall-e/coding/prompt-bundle.js +67 -0
  61. package/template/wall-e/coding/prompt-runtime.js +243 -0
  62. package/template/wall-e/coding/provider-transform.js +188 -0
  63. package/template/wall-e/coding/runtime-mode.js +132 -0
  64. package/template/wall-e/coding/snapshot-service.js +155 -0
  65. package/template/wall-e/coding/stream-processor.js +268 -0
  66. package/template/wall-e/coding/task-tool.js +255 -0
  67. package/template/wall-e/coding/tool-registry.js +361 -0
  68. package/template/wall-e/coding/transcript-writer.js +143 -0
  69. package/template/wall-e/coding/workspace-replay.js +324 -0
  70. package/template/wall-e/coding-context.js +4 -22
  71. package/template/wall-e/coding-orchestrator.js +307 -18
  72. package/template/wall-e/coding-prompts.js +44 -3
  73. package/template/wall-e/context/context-builder.js +43 -1
  74. package/template/wall-e/context/topic-matcher.js +1 -1
  75. package/template/wall-e/eval/agent-runner.js +59 -13
  76. package/template/wall-e/eval/benchmarks/memory-retrieval.json +155 -57
  77. package/template/wall-e/eval/benchmarks.js +100 -16
  78. package/template/wall-e/eval/eval-orchestrator.js +218 -8
  79. package/template/wall-e/eval/harvester.js +62 -5
  80. package/template/wall-e/eval/head-to-head.js +23 -2
  81. package/template/wall-e/eval/humaneval-adapter.js +30 -5
  82. package/template/wall-e/eval/livecodebench-adapter.js +29 -5
  83. package/template/wall-e/eval/manifest.js +186 -0
  84. package/template/wall-e/eval/run-agent-benchmarks.js +66 -2
  85. package/template/wall-e/eval/session-retrieval-benchmark.js +150 -0
  86. package/template/wall-e/eval/session-transcripts.js +57 -4
  87. package/template/wall-e/eval/swebench-adapter.js +109 -3
  88. package/template/wall-e/evaluation/agent-router.js +53 -1
  89. package/template/wall-e/evaluation/coding-quorum.js +48 -1
  90. package/template/wall-e/evaluation/router.js +4 -2
  91. package/template/wall-e/evaluation/tier-selector.js +11 -1
  92. package/template/wall-e/extraction/contradiction.js +2 -2
  93. package/template/wall-e/extraction/indexer.js +2 -1
  94. package/template/wall-e/extraction/knowledge-extractor.js +2 -2
  95. package/template/wall-e/hooks/cli.js +92 -0
  96. package/template/wall-e/hooks/discovery.js +119 -0
  97. package/template/wall-e/hooks/index.js +7 -0
  98. package/template/wall-e/hooks/manifest.js +55 -0
  99. package/template/wall-e/hooks/runtime.js +84 -0
  100. package/template/wall-e/hooks/session-memory.js +225 -0
  101. package/template/wall-e/http/auth.js +6 -2
  102. package/template/wall-e/http/chat-api.js +54 -8
  103. package/template/wall-e/integrations/claude-plugin/hooks/hooks.json +27 -0
  104. package/template/wall-e/integrations/claude-plugin/hooks/walle-precompact-hook.sh +5 -0
  105. package/template/wall-e/integrations/claude-plugin/hooks/walle-stop-hook.sh +5 -0
  106. package/template/wall-e/integrations/codex-plugin/hooks/walle-hook.sh +7 -0
  107. package/template/wall-e/integrations/codex-plugin/hooks.json +37 -0
  108. package/template/wall-e/listening/calendar.js +3 -1
  109. package/template/wall-e/llm/client.js +64 -10
  110. package/template/wall-e/llm/google.js +39 -5
  111. package/template/wall-e/llm/ollama.js +1 -1
  112. package/template/wall-e/llm/ollama.plugin.json +1 -1
  113. package/template/wall-e/llm/provider-availability.js +10 -0
  114. package/template/wall-e/llm/provider-error.js +269 -0
  115. package/template/wall-e/llm/tool-adapter.js +48 -12
  116. package/template/wall-e/loops/boot.js +2 -1
  117. package/template/wall-e/loops/initiative.js +2 -2
  118. package/template/wall-e/loops/tasks.js +8 -47
  119. package/template/wall-e/loops/workspace-prompts.js +20 -0
  120. package/template/wall-e/mcp-server.js +442 -1
  121. package/template/wall-e/memory/session-ingest-service.js +159 -0
  122. package/template/wall-e/memory/source-indexer.js +289 -0
  123. package/template/wall-e/plugins/discovery.js +83 -0
  124. package/template/wall-e/plugins/manifest-loader.js +50 -10
  125. package/template/wall-e/plugins/manifest-schema.js +69 -0
  126. package/template/wall-e/plugins/model-catalog.js +55 -0
  127. package/template/wall-e/prompts/coding/base.txt +2 -0
  128. package/template/wall-e/prompts/coding/deepseek.txt +1 -0
  129. package/template/wall-e/prompts/coding/memory-protocol.md +9 -0
  130. package/template/wall-e/prompts/coding/plan.txt +1 -0
  131. package/template/wall-e/runtime/execution-trace.js +220 -0
  132. package/template/wall-e/security/audit.js +266 -0
  133. package/template/wall-e/security/ssrf.js +236 -0
  134. package/template/wall-e/session-files.js +303 -0
  135. package/template/wall-e/skills/_bundled/slack-backfill/SKILL.md +3 -0
  136. package/template/wall-e/skills/_bundled/slack-sync/SKILL.md +3 -0
  137. package/template/wall-e/skills/internal-skill-registry.js +2 -2
  138. package/template/wall-e/skills/script-skill-runner.js +143 -0
  139. package/template/wall-e/skills/skill-executor.js +5 -6
  140. package/template/wall-e/skills/skill-fallback.js +3 -1
  141. package/template/wall-e/skills/skill-harness-registry.js +7 -8
  142. package/template/wall-e/skills/skill-planner.js +52 -4
  143. package/template/wall-e/skills/slack-ingest.js +11 -3
  144. package/template/wall-e/sources/base.js +90 -0
  145. package/template/wall-e/sources/builtin.js +33 -0
  146. package/template/wall-e/sources/claude-code-jsonl.js +78 -0
  147. package/template/wall-e/sources/codex-jsonl.js +125 -0
  148. package/template/wall-e/sources/coding-session-utils.js +117 -0
  149. package/template/wall-e/sources/contract-suite.js +59 -0
  150. package/template/wall-e/sources/gemini-jsonl.js +85 -0
  151. package/template/wall-e/sources/index.js +9 -0
  152. package/template/wall-e/sources/jsonl-utils.js +181 -0
  153. package/template/wall-e/sources/record-types.js +252 -0
  154. package/template/wall-e/sources/registry.js +92 -0
  155. package/template/wall-e/sources/transforms.js +100 -0
  156. package/template/wall-e/sources/walle-jsonl.js +108 -0
  157. package/template/wall-e/tools/coding-middleware.js +31 -1
  158. package/template/wall-e/tools/file-tracker.js +25 -1
  159. package/template/wall-e/tools/local-tools.js +75 -47
  160. package/template/wall-e/tools/session-sharing.js +68 -1
  161. package/template/wall-e/tools/shell-analyzer.js +1 -1
  162. package/template/wall-e/tools/shell-policy.js +47 -0
  163. package/template/wall-e/tools/snapshot.js +42 -0
  164. package/template/wall-e/training/harvester.js +62 -5
  165. package/template/wall-e/utils/repair.js +253 -1
  166. package/template/website/index.html +3 -3
  167. package/template/wall-e/skills/_bundled/slack-mentions/.watched-threads.json +0 -18
@@ -0,0 +1,208 @@
1
+ 'use strict';
2
+
3
+ const { createAgentRunId, normalizeAgentMode, normalizeAgentRunResult } = require('../agent-runners/contract');
4
+ const { createDefaultHarnessRegistry, selectAgentHarness } = require('./harness');
5
+ const { createDefaultRuntimeRegistry } = require('./registry');
6
+ const { defaultSessionStore } = require('./session-store');
7
+ const {
8
+ attachTraceToResult,
9
+ createExecutionTrace,
10
+ finalizeTrace,
11
+ recordAttemptFinish,
12
+ recordAttemptStart,
13
+ recordSelection,
14
+ } = require('../runtime/execution-trace');
15
+
16
+ function normalizeAgentTaskRequest(input = {}) {
17
+ const task = input.task || input.prompt || '';
18
+ return {
19
+ id: input.id || input.runId || createAgentRunId(),
20
+ parentRunId: input.parentRunId || input.parentId || null,
21
+ harnessId: input.harnessId || null,
22
+ runtime: input.runtime || input.runtimePreference || 'cli-agent',
23
+ runnerId: input.runnerId || input.runner || input.agent || 'claude-code',
24
+ providerType: input.providerType || input.provider || 'unknown',
25
+ model: input.model || null,
26
+ mode: normalizeAgentMode(input.mode),
27
+ task,
28
+ prompt: task,
29
+ cwd: input.cwd || process.cwd(),
30
+ writeScope: Array.isArray(input.writeScope) ? input.writeScope : (input.writeScope ? [input.writeScope] : ['.']),
31
+ sandbox: input.sandbox || null,
32
+ timeoutMs: input.timeoutMs || 300000,
33
+ sessionId: input.sessionId || null,
34
+ persistent: Boolean(input.persistent),
35
+ cleanupPolicy: input.cleanupPolicy || (input.persistent ? 'manual' : 'delete-on-complete'),
36
+ threadId: input.threadId || null,
37
+ contextPolicy: input.contextPolicy || null,
38
+ attachments: Array.isArray(input.attachments) ? input.attachments : [],
39
+ reviewPolicy: input.reviewPolicy || null,
40
+ metadata: { ...(input.metadata || {}) },
41
+ };
42
+ }
43
+
44
+ async function spawnAgentSession(input = {}, deps = {}) {
45
+ const request = normalizeAgentTaskRequest(input);
46
+ const trace = deps.trace || createExecutionTrace({
47
+ runId: request.id,
48
+ parentRunId: request.parentRunId,
49
+ mode: request.mode,
50
+ provider: request.providerType,
51
+ model: request.model,
52
+ runnerId: request.runnerId,
53
+ harnessId: request.harnessId,
54
+ sessionId: request.sessionId || '',
55
+ cwd: request.cwd,
56
+ sandbox: request.sandbox,
57
+ taskType: 'agent-runtime',
58
+ });
59
+ if (deps.harnessRegistry || request.harnessId) {
60
+ const harnessRegistry = deps.harnessRegistry || createDefaultHarnessRegistry();
61
+ const selection = selectAgentHarness(request, [], {
62
+ registry: harnessRegistry,
63
+ providerAvailability: deps.providerAvailability,
64
+ });
65
+ if (!selection.selected) {
66
+ const error = `No supported agent harness found for ${request.harnessId || request.runnerId}`;
67
+ recordSelection(trace, selection);
68
+ finalizeTrace(trace, { status: 'failed', error });
69
+ return normalizeAgentRunResult({
70
+ runId: request.id,
71
+ parentRunId: request.parentRunId,
72
+ runnerId: request.runnerId,
73
+ providerType: request.providerType,
74
+ mode: request.mode,
75
+ sessionId: request.sessionId,
76
+ success: false,
77
+ stderr: error,
78
+ error,
79
+ trace,
80
+ });
81
+ }
82
+ recordSelection(trace, selection);
83
+ request.harnessId = selection.selected.id;
84
+ request.runnerId = selection.selected.runnerId;
85
+ request.providerType = selection.selected.providerType;
86
+ request.metadata = { ...request.metadata, harnessSelection: selection.reason };
87
+ }
88
+ const sessionStore = deps.sessionStore || defaultSessionStore;
89
+ const registry = deps.registry || createDefaultRuntimeRegistry({ runTask: deps.runTask });
90
+ const runtime = registry.getRuntime(request.runtime);
91
+ if (!runtime) {
92
+ const error = `Unknown agent runtime: ${request.runtime}`;
93
+ return normalizeAgentRunResult({
94
+ runId: request.id,
95
+ parentRunId: request.parentRunId,
96
+ runnerId: request.runnerId,
97
+ providerType: request.providerType,
98
+ mode: request.mode,
99
+ sessionId: request.sessionId,
100
+ success: false,
101
+ stderr: error,
102
+ error,
103
+ });
104
+ }
105
+
106
+ const existingSession = request.sessionId ? sessionStore.getSession(request.sessionId) : null;
107
+ const session = existingSession || sessionStore.createSession({
108
+ id: request.sessionId,
109
+ parentRunId: request.parentRunId,
110
+ runtime: request.runtime,
111
+ runnerId: request.runnerId,
112
+ harnessId: request.harnessId,
113
+ providerType: request.providerType,
114
+ model: request.model,
115
+ mode: request.mode,
116
+ cwd: request.cwd,
117
+ sandbox: request.sandbox,
118
+ task: request.task,
119
+ persistent: request.persistent,
120
+ cleanupPolicy: request.cleanupPolicy,
121
+ threadId: request.threadId,
122
+ metadata: request.metadata,
123
+ });
124
+
125
+ sessionStore.markRunning(session.id, {
126
+ parentRunId: request.parentRunId,
127
+ runtime: request.runtime,
128
+ runnerId: request.runnerId,
129
+ providerType: request.providerType,
130
+ model: request.model,
131
+ mode: request.mode,
132
+ cwd: request.cwd,
133
+ sandbox: request.sandbox,
134
+ task: request.task,
135
+ });
136
+
137
+ trace.sessionId = session.id;
138
+ trace.request.runnerId = request.runnerId;
139
+ trace.request.harnessId = request.harnessId;
140
+ trace.request.modelPreference = request.model || '';
141
+ const startedAt = Date.now();
142
+ const attempt = recordAttemptStart(trace, {
143
+ providerType: request.providerType,
144
+ model: request.model,
145
+ runnerId: request.runnerId,
146
+ harnessId: request.harnessId,
147
+ });
148
+
149
+ try {
150
+ const raw = await runtime.run({
151
+ ...request,
152
+ sessionId: session.id,
153
+ resumeHandle: session.resumeHandle,
154
+ });
155
+ const completedSession = sessionStore.markCompleted(session.id, raw || {}) || sessionStore.getSession(session.id);
156
+ recordAttemptFinish(trace, attempt.id, {
157
+ success: raw?.success !== false,
158
+ status: raw?.success === false ? 'failed' : 'completed',
159
+ error: raw?.error || raw?.stderr || null,
160
+ usage: raw?.usage || null,
161
+ });
162
+ trace.latencyMs = Date.now() - startedAt;
163
+ finalizeTrace(trace, { status: raw?.success === false ? 'failed' : 'completed' });
164
+
165
+ const normalized = normalizeAgentRunResult(raw || {}, {
166
+ runId: request.id,
167
+ parentRunId: request.parentRunId,
168
+ runnerId: request.runnerId,
169
+ harnessId: request.harnessId,
170
+ providerType: request.providerType,
171
+ mode: request.mode,
172
+ sessionId: session.id,
173
+ persistent: completedSession?.persistent,
174
+ cleanupPolicy: completedSession?.cleanupPolicy,
175
+ threadId: completedSession?.threadId,
176
+ sessionStatus: completedSession?.status,
177
+ trace,
178
+ });
179
+ return attachTraceToResult(normalized, trace);
180
+ } catch (err) {
181
+ const failedSession = sessionStore.markFailed(session.id, err) || sessionStore.getSession(session.id);
182
+ recordAttemptFinish(trace, attempt.id, { status: 'failed', error: err.message });
183
+ trace.latencyMs = Date.now() - startedAt;
184
+ finalizeTrace(trace, { status: 'failed', error: err.message });
185
+ return normalizeAgentRunResult({
186
+ runId: request.id,
187
+ parentRunId: request.parentRunId,
188
+ runnerId: request.runnerId,
189
+ harnessId: request.harnessId,
190
+ providerType: request.providerType,
191
+ mode: request.mode,
192
+ sessionId: session.id,
193
+ persistent: failedSession?.persistent,
194
+ cleanupPolicy: failedSession?.cleanupPolicy,
195
+ threadId: failedSession?.threadId,
196
+ sessionStatus: failedSession?.status,
197
+ success: false,
198
+ stderr: err.message,
199
+ error: err.message,
200
+ trace,
201
+ });
202
+ }
203
+ }
204
+
205
+ module.exports = {
206
+ normalizeAgentTaskRequest,
207
+ spawnAgentSession,
208
+ };
@@ -124,13 +124,25 @@ function _invalidateSkillSnapshot() {
124
124
  // Inspired by OpenCode Question service (packages/opencode/src/question/index.ts)
125
125
  const { QuestionManager } = require('./tools/question-manager');
126
126
  const _questionRegistry = new QuestionManager(null);
127
+ const { PermissionService } = require('./coding/permission-service');
128
+ const _permissionRegistry = new PermissionService();
127
129
 
128
130
  let _tasksBootstrapped = false;
131
+ let _runtimeDefaultsSynced = false;
129
132
  function ensureBrainInit() {
130
133
  if (!brain) return false;
131
134
  try { brain.getDb(); } catch {
132
135
  try { brain.initDb(); } catch { return false; }
133
136
  }
137
+ if (!_runtimeDefaultsSynced) {
138
+ try {
139
+ const provider = brain.getKv('walle_provider');
140
+ const model = brain.getKv('walle_model');
141
+ if (provider) process.env.WALLE_PROVIDER = provider;
142
+ if (model) process.env.WALLE_MODEL = model;
143
+ _runtimeDefaultsSynced = true;
144
+ } catch {}
145
+ }
134
146
  // Bootstrap core tasks if DB is empty (first run)
135
147
  if (!_tasksBootstrapped) {
136
148
  _tasksBootstrapped = true;
@@ -830,6 +842,18 @@ function handleWalleApi(req, res, url) {
830
842
  }
831
843
  if (!hasApiKey && walleProvider === 'openai' && process.env.OPENAI_API_KEY) hasApiKey = true;
832
844
  if (!hasApiKey && walleProvider === 'google' && (process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY)) hasApiKey = true;
845
+ if (!hasApiKey && walleProvider === 'anthropic') {
846
+ let authMethod = process.env.WALLE_AUTH_METHOD || '';
847
+ try { authMethod = brain?.getProviderAuthMethod?.('anthropic') || authMethod; } catch {}
848
+ if (authMethod === 'oauth_proxy' || authMethod === 'claude_cli' || authMethod === 'devbox') hasApiKey = true;
849
+ if (process.env.ANTHROPIC_AUTH_TOKEN || process.env.ANTHROPIC_BASE_URL) hasApiKey = true;
850
+ }
851
+ if (!hasApiKey && walleProvider === 'openai') {
852
+ let authMethod = process.env.WALLE_AUTH_METHOD || '';
853
+ try { authMethod = brain?.getProviderAuthMethod?.('openai') || authMethod; } catch {}
854
+ if (authMethod === 'codex_cli') hasApiKey = true;
855
+ }
856
+ if (!hasApiKey && walleProvider === 'deepseek' && process.env.DEEPSEEK_API_KEY) hasApiKey = true;
833
857
  if (!hasApiKey && (walleProvider === 'ollama' || walleProvider === 'mlx')) hasApiKey = true;
834
858
 
835
859
  let serviceAlerts = [];
@@ -939,8 +963,14 @@ function handleWalleApi(req, res, url) {
939
963
  const ownerName = body.ownerName || '';
940
964
  const providerConfig = body.providerConfig || null;
941
965
 
942
- if (provider) brain.setKv('walle_provider', provider);
943
- if (model) brain.setKv('walle_model', model);
966
+ if (provider) {
967
+ brain.setKv('walle_provider', provider);
968
+ process.env.WALLE_PROVIDER = provider;
969
+ }
970
+ if (model) {
971
+ brain.setKv('walle_model', model);
972
+ process.env.WALLE_MODEL = model;
973
+ }
944
974
  if (ownerName) brain.setKv('walle_owner_name', ownerName);
945
975
 
946
976
  if (provider && providerConfig) {
@@ -2616,6 +2646,63 @@ function handleWalleApi(req, res, url) {
2616
2646
  return true;
2617
2647
  }
2618
2648
 
2649
+ // GET /api/wall-e/coding/sessions/:id/transcript?offset=&sequence=&limit=
2650
+ // Replay durable Wall-E JSONL records for reconnecting clients without fs.watch.
2651
+ const transcriptRangeMatch = p.match(/^\/api\/wall-e\/coding\/sessions\/([^/]+)\/transcript$/);
2652
+ if (transcriptRangeMatch && m === 'GET') {
2653
+ try {
2654
+ const { WorkspaceReplay } = require('./coding/workspace-replay');
2655
+ const replay = new WorkspaceReplay();
2656
+ const range = replay.replayRange(decodeURIComponent(transcriptRangeMatch[1]), {
2657
+ offset: params.get('offset') || 0,
2658
+ sequence: params.get('sequence') || 0,
2659
+ limitBytes: params.get('limit') || params.get('limitBytes') || undefined,
2660
+ });
2661
+ return jsonResponse(res, { ok: true, ...range }), true;
2662
+ } catch (e) {
2663
+ return jsonResponse(res, { error: e.message }, 500), true;
2664
+ }
2665
+ }
2666
+
2667
+ // POST /api/wall-e/coding/sessions/:id/transcript/append
2668
+ // Direct append signal for CTM/ACP clients that already have structured parts.
2669
+ const transcriptAppendMatch = p.match(/^\/api\/wall-e\/coding\/sessions\/([^/]+)\/transcript\/append$/);
2670
+ if (transcriptAppendMatch && m === 'POST') {
2671
+ readBody(req).then(body => {
2672
+ try {
2673
+ const { WorkspaceReplay } = require('./coding/workspace-replay');
2674
+ const replay = new WorkspaceReplay();
2675
+ const sessionId = decodeURIComponent(transcriptAppendMatch[1]);
2676
+ const result = body.record
2677
+ ? replay.appendRecord(sessionId, body.record)
2678
+ : replay.appendPart(sessionId, body.partType || 'event', body.data || {}, {
2679
+ cwd: body.cwd || '',
2680
+ chatSessionId: body.chatSessionId || '',
2681
+ parentUuid: body.parentUuid || null,
2682
+ });
2683
+ jsonResponse(res, { ok: true, ...result });
2684
+ } catch (e) {
2685
+ jsonResponse(res, { error: e.message }, 500);
2686
+ }
2687
+ }).catch(e => jsonResponse(res, { error: e.message }, 500));
2688
+ return true;
2689
+ }
2690
+
2691
+ // GET /api/wall-e/coding/sessions/:id/replay — rebuild message/part state from JSONL.
2692
+ const replayStateMatch = p.match(/^\/api\/wall-e\/coding\/sessions\/([^/]+)\/replay$/);
2693
+ if (replayStateMatch && m === 'GET') {
2694
+ try {
2695
+ const { WorkspaceReplay } = require('./coding/workspace-replay');
2696
+ const replay = new WorkspaceReplay();
2697
+ const state = replay.rebuildState(decodeURIComponent(replayStateMatch[1]), {
2698
+ cwd: params.get('cwd') || '',
2699
+ });
2700
+ return jsonResponse(res, { ok: true, ...state }), true;
2701
+ } catch (e) {
2702
+ return jsonResponse(res, { error: e.message }, 500), true;
2703
+ }
2704
+ }
2705
+
2619
2706
  // POST /api/wall-e/coding/question/:sessionId/answer — answer an interactive question
2620
2707
  // Inspired by OpenCode Question.reply() (packages/opencode/src/question/index.ts)
2621
2708
  if (m === 'POST' && p.match(/^\/api\/wall-e\/coding\/question\/[^/]+\/answer$/)) {
@@ -3043,21 +3130,42 @@ function handleWalleApi(req, res, url) {
3043
3130
  if (p === '/api/wall-e/eval/dashboard' && m === 'GET') {
3044
3131
  try {
3045
3132
  const leaderboard = brain.getBenchmarkLeaderboardWithDimensions({ days: 30 });
3046
- const shadowResults = brain.getShadowResults({ limit: 1, offset: 0 });
3047
3133
  const shadowTotal = (typeof brain.getShadowResults === 'function')
3048
3134
  ? brain.getShadowResults({}).length : 0;
3135
+ const providerCount = new Set(leaderboard.map(r => r.provider).filter(Boolean)).size;
3136
+ const benchmarkEvals = leaderboard.reduce((s, r) => s + (Number(r.total_evals) || 0), 0);
3137
+ const trustedBenchmarkEvals = leaderboard.reduce((s, r) => s + (Number(r.trusted_evals) || 0), 0);
3138
+ const trustedBenchmarkModels = leaderboard.filter(r => r.trust_status === 'trusted').length;
3139
+ const provisionalBenchmarkModels = leaderboard.filter(r => r.trust_status === 'provisional').length;
3140
+ const legacyBenchmarkModels = leaderboard.filter(r => r.trust_status === 'legacy').length;
3049
3141
  return jsonResponse(res, {
3050
3142
  data: {
3051
- providers: leaderboard.length,
3052
- totalEvals: leaderboard.reduce((s, r) => s + r.total_evals, 0) + shadowTotal,
3143
+ providers: providerCount,
3144
+ modelGroups: leaderboard.length,
3145
+ totalEvals: benchmarkEvals + shadowTotal,
3053
3146
  shadowEvals: shadowTotal,
3054
- benchmarkEvals: leaderboard.reduce((s, r) => s + r.total_evals, 0),
3147
+ benchmarkEvals,
3148
+ trustedBenchmarkEvals,
3149
+ trustedBenchmarkModels,
3150
+ provisionalBenchmarkModels,
3151
+ legacyBenchmarkModels,
3055
3152
  leaderboard,
3056
3153
  }
3057
3154
  }), true;
3058
3155
  } catch (e) {
3059
3156
  return jsonResponse(res, {
3060
- data: { providers: 0, totalEvals: 0, shadowEvals: 0, benchmarkEvals: 0, leaderboard: [] },
3157
+ data: {
3158
+ providers: 0,
3159
+ modelGroups: 0,
3160
+ totalEvals: 0,
3161
+ shadowEvals: 0,
3162
+ benchmarkEvals: 0,
3163
+ trustedBenchmarkEvals: 0,
3164
+ trustedBenchmarkModels: 0,
3165
+ provisionalBenchmarkModels: 0,
3166
+ legacyBenchmarkModels: 0,
3167
+ leaderboard: [],
3168
+ },
3061
3169
  error: e.message
3062
3170
  }), true;
3063
3171
  }
@@ -3399,6 +3507,7 @@ function handleWalleApi(req, res, url) {
3399
3507
  if (p === '/api/wall-e/eval/benchmarks' && m === 'GET') {
3400
3508
  try {
3401
3509
  const { listBenchmarkSuites, loadBenchmarkSuite } = require('./eval/benchmarks');
3510
+ const { listAdapterSuites } = require('./eval/eval-orchestrator');
3402
3511
  const suites = [];
3403
3512
  const errors = [];
3404
3513
  for (const name of listBenchmarkSuites()) {
@@ -3415,6 +3524,7 @@ function handleWalleApi(req, res, url) {
3415
3524
  errors.push({ suite: name, error: e.message });
3416
3525
  }
3417
3526
  }
3527
+ suites.push(...listAdapterSuites());
3418
3528
  return jsonResponse(res, errors.length ? { data: suites, errors } : { data: suites }), true;
3419
3529
  } catch (e) {
3420
3530
  return jsonResponse(res, { data: [], error: e.message }), true;
@@ -3426,6 +3536,30 @@ function handleWalleApi(req, res, url) {
3426
3536
  readBody(req).then(async body => {
3427
3537
  try {
3428
3538
  const data = typeof body === 'string' ? JSON.parse(body) : body;
3539
+ const { isAdapterSuite, EvalOrchestrator } = require('./eval/eval-orchestrator');
3540
+ if (isAdapterSuite(data.suite)) {
3541
+ const { getAvailableProviders } = require('./eval/head-to-head');
3542
+ const models = Array.isArray(data.models) && data.models.length
3543
+ ? data.models
3544
+ : Array.isArray(data.providers) && data.providers.length
3545
+ ? data.providers.map(p => p.model).filter(Boolean)
3546
+ : getAvailableProviders(brain).map(p => p.model);
3547
+ const { runAgentLoop } = require('./coding-orchestrator');
3548
+ const orchestrator = new EvalOrchestrator({
3549
+ brain,
3550
+ timeoutMs: data.timeoutMs || 600000,
3551
+ concurrency: data.concurrency || 1,
3552
+ budgetDollars: data.budgetDollars || 10,
3553
+ });
3554
+ const result = await orchestrator.run({
3555
+ suite: data.suite,
3556
+ models,
3557
+ runAgentLoop,
3558
+ maxTasks: data.maxTasks,
3559
+ afterDate: data.afterDate,
3560
+ });
3561
+ return jsonResponse(res, { data: result });
3562
+ }
3429
3563
  const { runBenchmark } = require('./eval/benchmarks');
3430
3564
  const result = await runBenchmark(brain, {
3431
3565
  suite: data.suite,
@@ -4179,6 +4313,38 @@ function handleWalleApi(req, res, url) {
4179
4313
  return true;
4180
4314
  }
4181
4315
 
4316
+ // ── Coding Permission Requests ──
4317
+
4318
+ // GET /api/wall-e/permissions?session_id=... — list pending approvals
4319
+ if (p === '/api/wall-e/permissions' && m === 'GET') {
4320
+ try {
4321
+ return jsonResponse(res, {
4322
+ data: _permissionRegistry.list({ sessionId: params.get('session_id') || '' }),
4323
+ }), true;
4324
+ } catch (e) {
4325
+ return jsonResponse(res, { error: e.message }, 500), true;
4326
+ }
4327
+ }
4328
+
4329
+ // POST /api/wall-e/permissions/:id/reply — reply once/always/reject
4330
+ const permissionReplyMatch = p.match(/^\/api\/wall-e\/permissions\/([^/]+)\/reply$/);
4331
+ if (permissionReplyMatch && m === 'POST') {
4332
+ readBody(req).then(body => {
4333
+ const reply = body.reply || body.decision;
4334
+ if (!['once', 'always', 'reject'].includes(reply)) {
4335
+ return jsonResponse(res, { error: 'reply must be once, always, or reject' }, 400);
4336
+ }
4337
+ const ok = _permissionRegistry.reply({
4338
+ requestId: decodeURIComponent(permissionReplyMatch[1]),
4339
+ reply,
4340
+ message: body.message || '',
4341
+ });
4342
+ if (!ok) return jsonResponse(res, { error: 'Permission request not found' }, 404);
4343
+ jsonResponse(res, { ok: true });
4344
+ }).catch(e => jsonResponse(res, { error: e.message }, 500));
4345
+ return true;
4346
+ }
4347
+
4182
4348
  const listeningHandled = handleListeningApi(req, res, url, { brain, ensureBrainInit });
4183
4349
  if (listeningHandled) return true;
4184
4350
 
@@ -4210,4 +4376,5 @@ module.exports = {
4210
4376
  // Test helpers
4211
4377
  _setReadDb,
4212
4378
  _setBrain,
4379
+ _permissionRegistry,
4213
4380
  };