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
@@ -1,7 +1,32 @@
1
1
  'use strict';
2
2
 
3
3
  const VERIFICATION_TOOL_NAME_RE = /(?:test|verify|screenshot|smoke|diagnostic|lint|build|check_url|url_check|pdf_info|pdf_render_pages|make_pdf)/i;
4
- const VERIFICATION_COMMAND_RE = /\b(?:test|spec|lint|build|typecheck|tsc|pytest|jest|mocha|vitest|playwright|node\s+--(?:test|check)|npm\s+(?:test|run)|pnpm\s+(?:test|run)|yarn\s+(?:test|run)|git\s+diff\s+--check|curl\s+.*(?:localhost|127\.0\.0\.1)|check_url|pdfinfo|pdftoppm|pdftotext)\b/i;
4
+ // A run_shell command counts as verification ONLY when it invokes a real
5
+ // test/lint/build runner. The old pattern matched the bare words
6
+ // test|spec|lint|build|typecheck anywhere in the command JSON, so the
7
+ // shell-builtin existence probe `test -d node_modules && echo ...` (and any
8
+ // path containing "test") classified as a SUCCESSFUL verification — which both
9
+ // over-credited acceptance evidence and tripped the stream loop's
10
+ // verified-after-edit early exit, ending coding turns with no final answer
11
+ // (session c3f3af97: reply stopped at "...Let me check package.json").
12
+ const VERIFICATION_COMMAND_RE = new RegExp([
13
+ // package-manager script runners (`npm test`, `npm run build`, `bun run lint`, …)
14
+ String.raw`\b(?:npm|pnpm|yarn|bun)\s+(?:test\b|run\s+\S+)`,
15
+ // direct test/lint/type runners
16
+ String.raw`\b(?:pytest|jest|mocha|vitest|playwright|tsc|eslint|stylelint|ruff|mypy|flake8|rspec|phpunit)\b`,
17
+ String.raw`\bnode\s+--(?:test|check)\b`,
18
+ // language toolchains
19
+ String.raw`\b(?:cargo|go)\s+(?:test|build|check|vet)\b`,
20
+ String.raw`\bmake\s+(?:test|check|lint|build)\b`,
21
+ String.raw`\bmvn\s+(?:test|verify|package)\b`,
22
+ String.raw`\bgradlew?\s+(?:test|build|check)\b`,
23
+ // frontend build/lint CLIs
24
+ String.raw`\b(?:vue-cli-service|vite|webpack|rollup|esbuild|next|nuxt|ng)\s+(?:test|lint|build|typecheck|e2e)\b`,
25
+ // misc verification probes
26
+ String.raw`\bgit\s+diff\s+--check\b`,
27
+ String.raw`\bcurl\s+[^|;&]*(?:localhost|127\.0\.0\.1)`,
28
+ String.raw`\b(?:check_url|pdfinfo|pdftoppm|pdftotext)\b`,
29
+ ].join('|'), 'i');
5
30
 
6
31
  function callName(call = {}) {
7
32
  return String(call.name || call.tool || '');
@@ -0,0 +1,353 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_MIN_SCORE = 120;
4
+ const DEFAULT_MIN_MARGIN = 15;
5
+ const DEFAULT_LIMIT = 8;
6
+ const MODES = new Set(['off', 'audit', 'assist', 'apply']);
7
+ const SELECTORS = new Set(['store', 'replay', 'auto']);
8
+ const REPLAY_SELECTOR_TOOLS = new Set(['run_shell', 'apply_patch', 'edit_file', 'write_file']);
9
+
10
+ class ActionMemoryPolicy {
11
+ constructor({
12
+ store = null,
13
+ mode = 'audit',
14
+ selector = 'store',
15
+ minScore = DEFAULT_MIN_SCORE,
16
+ minMargin = DEFAULT_MIN_MARGIN,
17
+ limit = DEFAULT_LIMIT,
18
+ maxCandidates = DEFAULT_LIMIT,
19
+ memoryLimit = 200,
20
+ logger = console,
21
+ } = {}) {
22
+ this.store = store;
23
+ this.mode = normalizeMode(mode);
24
+ this.selector = normalizeSelector(selector);
25
+ this.minScore = finiteNumber(minScore, DEFAULT_MIN_SCORE);
26
+ this.minMargin = finiteNumber(minMargin, DEFAULT_MIN_MARGIN);
27
+ this.limit = Math.max(1, Math.trunc(finiteNumber(limit, DEFAULT_LIMIT)));
28
+ this.maxCandidates = Math.max(1, Math.trunc(finiteNumber(maxCandidates, this.limit)));
29
+ this.memoryLimit = Math.max(1, Math.trunc(finiteNumber(memoryLimit, 200)));
30
+ this.logger = logger;
31
+ }
32
+
33
+ async evaluateToolCall({
34
+ call = {},
35
+ context = {},
36
+ model = {},
37
+ sessionId = '',
38
+ prompt = '',
39
+ } = {}) {
40
+ const clonedCall = cloneToolCall(call);
41
+ const toolName = toolNameFromCall(clonedCall);
42
+ const prediction = { name: toolName, input: clonedCall.input || {} };
43
+ const baseDecision = {
44
+ mode: this.mode,
45
+ selector: this.selector,
46
+ action: 'skip',
47
+ accepted: false,
48
+ selectedEntryId: '',
49
+ selectedScore: null,
50
+ candidateCount: 0,
51
+ reasons: [],
52
+ reason: '',
53
+ };
54
+
55
+ if (!this.store || this.mode === 'off' || !toolName) {
56
+ return { call: clonedCall, decision: { ...baseDecision, reason: 'disabled_or_unavailable' }, candidates: [] };
57
+ }
58
+
59
+ const selected = await this.selectCandidate({ context, prediction, toolName });
60
+ const selection = selected.selection || null;
61
+ const candidates = selected.candidates || [];
62
+ const selectedInput = safeObject(selection?.input);
63
+ const canUseSelection = Boolean(selection?.accepted && selection?.tool === toolName && Object.keys(selectedInput).length);
64
+ const decision = {
65
+ ...baseDecision,
66
+ selectedEntryId: selection?.id || '',
67
+ selectedScore: Number.isFinite(Number(selection?.score)) ? Number(selection.score) : null,
68
+ candidateCount: candidates.length,
69
+ reasons: Array.isArray(selection?.reasons) ? selection.reasons.slice() : [],
70
+ reason: canUseSelection ? 'candidate_selected' : 'no_accepted_candidate',
71
+ };
72
+
73
+ let preparedCall = clonedCall;
74
+ if (this.mode === 'audit') {
75
+ decision.action = 'observe';
76
+ decision.accepted = false;
77
+ decision.reason = selection?.id ? 'audit_only' : 'no_candidate';
78
+ } else if (canUseSelection && this.mode === 'assist') {
79
+ const mergedInput = fillMissingFields(clonedCall.input || {}, selectedInput);
80
+ preparedCall = { ...clonedCall, input: mergedInput };
81
+ decision.action = 'assist';
82
+ decision.accepted = true;
83
+ } else if (canUseSelection && this.mode === 'apply') {
84
+ preparedCall = { ...clonedCall, input: cloneJson(selectedInput) };
85
+ decision.action = 'apply';
86
+ decision.accepted = true;
87
+ }
88
+
89
+ this.recordAudit({
90
+ mode: this.mode,
91
+ context,
92
+ prediction,
93
+ selected: selection ? { ...selection, accepted: decision.accepted } : null,
94
+ candidates,
95
+ reason: decision.reason,
96
+ model,
97
+ sessionId,
98
+ prompt,
99
+ });
100
+
101
+ return { call: preparedCall, decision, candidates };
102
+ }
103
+
104
+ async selectCandidate({ context, prediction, toolName }) {
105
+ if ((this.selector === 'replay' || this.selector === 'auto') && REPLAY_SELECTOR_TOOLS.has(toolName)) {
106
+ const replaySelected = this.selectReplayCandidate({ context, prediction, toolName });
107
+ if (replaySelected.selection || replaySelected.candidates?.length || this.selector === 'replay') {
108
+ return replaySelected;
109
+ }
110
+ }
111
+ if (!this.store?.selectActionMemoryCandidate) return { selection: null, candidates: [] };
112
+ return this.store.selectActionMemoryCandidate({
113
+ context,
114
+ prediction,
115
+ toolName,
116
+ minScore: this.minScore,
117
+ minMargin: this.minMargin,
118
+ limit: this.limit,
119
+ }) || { selection: null, candidates: [] };
120
+ }
121
+
122
+ selectReplayCandidate({ context = {}, prediction = null, toolName = '' } = {}) {
123
+ if (!this.store?.searchActionMemoryCandidates) return { selection: null, candidates: [] };
124
+ const example = replayExampleFromRuntimeContext(context, { toolName, prediction });
125
+ try {
126
+ if (toolName === 'run_shell') {
127
+ const {
128
+ applyRunShellCommandSelection,
129
+ } = require('../training/run-shell-command-selector');
130
+ const selected = applyRunShellCommandSelection(example, prediction, {
131
+ actionMemoryStore: this.store,
132
+ maxCandidates: this.maxCandidates,
133
+ memoryLimit: this.memoryLimit,
134
+ });
135
+ return normalizeRunShellPolicySelection(selected, prediction);
136
+ }
137
+ const {
138
+ applyTrajectoryActionSelection,
139
+ } = require('../training/trajectory-action-selector');
140
+ return applyTrajectoryActionSelection(example, prediction, {
141
+ actionMemoryStore: this.store,
142
+ maxCandidates: this.maxCandidates,
143
+ memoryLimit: this.memoryLimit,
144
+ minScore: this.minScore,
145
+ });
146
+ } catch (err) {
147
+ this.logger?.warn?.(`[action-memory] replay selector failed: ${err.message}`);
148
+ return { selection: null, candidates: [] };
149
+ }
150
+ }
151
+
152
+ recordAudit(row) {
153
+ if (!this.store?.recordSelectionAudit) return null;
154
+ try {
155
+ return this.store.recordSelectionAudit(row);
156
+ } catch (err) {
157
+ this.logger?.warn?.(`[action-memory] failed to record selection audit: ${err.message}`);
158
+ return null;
159
+ }
160
+ }
161
+ }
162
+
163
+ async function executeToolCallWithActionMemoryPolicy({
164
+ call = {},
165
+ context = {},
166
+ model = {},
167
+ sessionId = '',
168
+ prompt = '',
169
+ policy = null,
170
+ execute,
171
+ } = {}) {
172
+ if (typeof execute !== 'function') throw new Error('executeToolCallWithActionMemoryPolicy requires execute');
173
+ const evaluated = policy?.evaluateToolCall
174
+ ? await policy.evaluateToolCall({ call, context, model, sessionId, prompt })
175
+ : { call: cloneToolCall(call), decision: { mode: 'off', action: 'skip', accepted: false }, candidates: [] };
176
+ const execution = await execute(evaluated.call, evaluated.decision);
177
+ if (execution && typeof execution === 'object') {
178
+ execution.actionMemory = {
179
+ decision: evaluated.decision,
180
+ candidateCount: evaluated.candidates.length,
181
+ };
182
+ }
183
+ return execution;
184
+ }
185
+
186
+ function cloneToolCall(call = {}) {
187
+ return {
188
+ ...call,
189
+ input: cloneJson(call.input || {}),
190
+ };
191
+ }
192
+
193
+ function toolNameFromCall(call = {}) {
194
+ return String(call.name || call.tool || '').trim();
195
+ }
196
+
197
+ function fillMissingFields(originalInput = {}, memoryInput = {}) {
198
+ const merged = cloneJson(originalInput || {});
199
+ for (const [key, value] of Object.entries(memoryInput || {})) {
200
+ if (merged[key] === undefined || merged[key] === null || merged[key] === '') {
201
+ merged[key] = cloneJson(value);
202
+ }
203
+ }
204
+ return merged;
205
+ }
206
+
207
+ function cloneJson(value) {
208
+ try {
209
+ return JSON.parse(JSON.stringify(value ?? null));
210
+ } catch {
211
+ return value;
212
+ }
213
+ }
214
+
215
+ function safeObject(value) {
216
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
217
+ }
218
+
219
+ function finiteNumber(value, fallback) {
220
+ const numeric = Number(value);
221
+ return Number.isFinite(numeric) ? numeric : fallback;
222
+ }
223
+
224
+ function normalizeMode(mode) {
225
+ const value = String(mode || 'audit').trim().toLowerCase();
226
+ return MODES.has(value) ? value : 'audit';
227
+ }
228
+
229
+ function normalizeSelector(selector) {
230
+ const value = String(selector || 'store').trim().toLowerCase();
231
+ return SELECTORS.has(value) ? value : 'store';
232
+ }
233
+
234
+ function normalizeRunShellPolicySelection(selected = {}, prediction = null) {
235
+ const candidates = (selected.candidates || []).map((candidate) => ({
236
+ ...candidate,
237
+ tool: 'run_shell',
238
+ input: { command: candidate.command },
239
+ id: candidate.memoryEntryId || '',
240
+ candidateId: candidate.id || '',
241
+ reasons: [
242
+ candidate.reason,
243
+ candidate.source,
244
+ ...(candidate.memoryReasons || []),
245
+ ].filter(Boolean),
246
+ }));
247
+ const selection = selected.selection || null;
248
+ if (!selection?.command) return { selection: null, candidates };
249
+ const matched = candidates.find((candidate) => candidate.command === selection.command) || {};
250
+ return {
251
+ selection: {
252
+ id: selection.memoryEntryId || matched.id || '',
253
+ candidateId: selection.id || matched.candidateId || '',
254
+ tool: 'run_shell',
255
+ input: {
256
+ ...(prediction?.input || {}),
257
+ command: selection.command,
258
+ },
259
+ score: finiteNumber(selection.score ?? matched.score, null),
260
+ reasons: [
261
+ selection.reason,
262
+ selection.source,
263
+ ...(selection.memoryReasons || matched.memoryReasons || []),
264
+ ].filter(Boolean),
265
+ sourceKind: selection.source,
266
+ sourceRowId: selection.sourceRowId || matched.sourceRowId,
267
+ sourceSessionId: selection.sourceSessionId || matched.sourceSessionId,
268
+ sourceToolCallId: selection.sourceToolCallId || matched.sourceToolCallId,
269
+ targetHint: selection.command,
270
+ accepted: true,
271
+ },
272
+ candidates,
273
+ };
274
+ }
275
+
276
+ function replayExampleFromRuntimeContext(context = {}, { toolName = '', prediction = null } = {}) {
277
+ const labels = {
278
+ workflow_family_id: context.familyId || context.workflowFamilyId || context.workflow_family_id || null,
279
+ workflow_repo: context.repoKey || context.repo || context.workflowRepo || context.workflow_repo || null,
280
+ workflow_intent: context.intent || context.workflowIntent || context.workflow_intent || null,
281
+ tool_name: toolName || prediction?.name || null,
282
+ };
283
+ const verificationCommands = normalizeCommandList(
284
+ context.verificationCommands || context.verification_commands || context.rememberedTaskCommands
285
+ );
286
+ if (verificationCommands.length) labels.verification_commands = verificationCommands;
287
+
288
+ const stateLines = [
289
+ ['tool sequence', context.sequence || context.stateSequence || context.toolSequence],
290
+ ['last tool', context.lastTool || context.last_tool],
291
+ ['recent shell commands', formatRecentShellCommands(context.recentShellCommands || context.recent_shell_commands)],
292
+ ['known failures', formatListValue(context.knownFailures || context.known_failures)],
293
+ ['verification status', context.verificationStatus || context.verification_status],
294
+ ['remembered task commands', formatCommandList(context.rememberedTaskCommands || context.remembered_task_commands)],
295
+ ['remembered workflow commands', formatCommandList(context.rememberedWorkflowCommands || context.remembered_workflow_commands)],
296
+ ]
297
+ .filter(([, value]) => value)
298
+ .map(([label, value]) => `- ${label}: ${value}`);
299
+
300
+ const taskText = String(context.taskText || context.task || context.userTask || context.taskPreview || '').trim();
301
+ const content = [
302
+ `Task:\n${taskText}`,
303
+ '',
304
+ 'Current Wall-E state:',
305
+ ...stateLines,
306
+ ].join('\n').trim();
307
+
308
+ return {
309
+ labels,
310
+ messages: [{ role: 'user', content }],
311
+ };
312
+ }
313
+
314
+ function normalizeCommandList(value) {
315
+ if (!value) return [];
316
+ if (Array.isArray(value)) return value.map((item) => String(item || '').trim()).filter(Boolean);
317
+ return String(value).split(/\n|,\s+(?=(?:cd |git |npm |node |pnpm |yarn |uv |rg |sed |python|pytest|cargo|go |make ))/i)
318
+ .map((item) => item.trim())
319
+ .filter(Boolean);
320
+ }
321
+
322
+ function formatCommandList(value) {
323
+ return normalizeCommandList(value).join(', ');
324
+ }
325
+
326
+ function formatRecentShellCommands(value) {
327
+ if (!value) return '';
328
+ if (typeof value === 'string') return value.trim();
329
+ if (!Array.isArray(value)) return '';
330
+ return value.map((item) => {
331
+ if (typeof item === 'string') return item.trim();
332
+ const command = item.command || item.input?.command || item.args?.command || '';
333
+ if (!command) return '';
334
+ const status = item.status || (item.ok === false ? 'tool_error' : 'tool_result');
335
+ return `${status}: ${command}`;
336
+ }).filter(Boolean).join(', ');
337
+ }
338
+
339
+ function formatListValue(value) {
340
+ if (!value) return '';
341
+ if (Array.isArray(value)) return value.map((item) => String(item || '').trim()).filter(Boolean).join(', ');
342
+ return String(value || '').trim();
343
+ }
344
+
345
+ module.exports = {
346
+ ActionMemoryPolicy,
347
+ DEFAULT_LIMIT,
348
+ DEFAULT_MIN_MARGIN,
349
+ DEFAULT_MIN_SCORE,
350
+ executeToolCallWithActionMemoryPolicy,
351
+ fillMissingFields,
352
+ replayExampleFromRuntimeContext,
353
+ };