create-walle 0.9.21 → 0.9.23

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 (500) hide show
  1. package/README.md +27 -5
  2. package/package.json +2 -2
  3. package/template/CLAUDE.md +2 -2
  4. package/template/LICENSE +1 -1
  5. package/template/bin/ctm-dev-cleanup.js +24 -3
  6. package/template/bin/ctm-launch.sh +13 -0
  7. package/template/bin/dev.sh +156 -18
  8. package/template/bin/node-bin.sh +84 -0
  9. package/template/bin/pin-node.sh +51 -0
  10. package/template/claude-task-manager/api-prompts.js +1203 -182
  11. package/template/claude-task-manager/api-reviews.js +109 -15
  12. package/template/claude-task-manager/approval-agent.js +1360 -280
  13. package/template/claude-task-manager/bin/restart-ctm.sh +64 -23
  14. package/template/claude-task-manager/bin/storage-migration-supervisor.js +338 -0
  15. package/template/claude-task-manager/db.js +4417 -295
  16. package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
  17. package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
  18. package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
  19. package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
  20. package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
  21. package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
  22. package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
  23. package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
  24. package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
  25. package/template/claude-task-manager/docs/image-paste-ux.md +13 -0
  26. package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
  27. package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
  28. package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
  29. package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
  30. package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
  31. package/template/claude-task-manager/docs/phone-access-design.md +53 -15
  32. package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
  33. package/template/claude-task-manager/docs/phone-setup.md +3 -0
  34. package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
  35. package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
  36. package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
  37. package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
  38. package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
  39. package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
  40. package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
  41. package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
  42. package/template/claude-task-manager/docs/session-title-authority.md +32 -0
  43. package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
  44. package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
  45. package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
  46. package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
  47. package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
  48. package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
  49. package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
  50. package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
  51. package/template/claude-task-manager/git-utils.js +897 -27
  52. package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
  53. package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
  54. package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
  55. package/template/claude-task-manager/lib/agent-presets.js +17 -1
  56. package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
  57. package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
  58. package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
  59. package/template/claude-task-manager/lib/async-semaphore.js +44 -0
  60. package/template/claude-task-manager/lib/auth-context.js +5 -0
  61. package/template/claude-task-manager/lib/auth-rate-limit.js +47 -4
  62. package/template/claude-task-manager/lib/auth-rules.js +29 -2
  63. package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
  64. package/template/claude-task-manager/lib/background-llm.js +144 -17
  65. package/template/claude-task-manager/lib/branch-inventory.js +212 -0
  66. package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
  67. package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
  68. package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
  69. package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
  70. package/template/claude-task-manager/lib/codex-zst.js +124 -0
  71. package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
  72. package/template/claude-task-manager/lib/connection-health.js +232 -0
  73. package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
  74. package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
  75. package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
  76. package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
  77. package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
  78. package/template/claude-task-manager/lib/document-review.js +141 -6
  79. package/template/claude-task-manager/lib/escalation-review.js +152 -0
  80. package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
  81. package/template/claude-task-manager/lib/headless-term-service.js +678 -0
  82. package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
  83. package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
  84. package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
  85. package/template/claude-task-manager/lib/main-db-census.js +216 -0
  86. package/template/claude-task-manager/lib/message-pagination.js +106 -4
  87. package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +750 -26
  88. package/template/claude-task-manager/lib/mobile-auth-api.js +274 -7
  89. package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
  90. package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
  91. package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
  92. package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
  93. package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
  94. package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
  95. package/template/claude-task-manager/lib/perf-tracker.js +242 -6
  96. package/template/claude-task-manager/lib/permission-match.js +76 -0
  97. package/template/claude-task-manager/lib/permission-sync.js +133 -20
  98. package/template/claude-task-manager/lib/process-title.js +35 -0
  99. package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
  100. package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
  101. package/template/claude-task-manager/lib/prompt-intent.js +132 -0
  102. package/template/claude-task-manager/lib/provider-user-context.js +34 -0
  103. package/template/claude-task-manager/lib/read-pool-client.js +313 -0
  104. package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
  105. package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
  106. package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
  107. package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
  108. package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
  109. package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
  110. package/template/claude-task-manager/lib/restart-guard.js +109 -0
  111. package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
  112. package/template/claude-task-manager/lib/restore-policy.js +13 -0
  113. package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
  114. package/template/claude-task-manager/lib/restore-runtime.js +68 -0
  115. package/template/claude-task-manager/lib/restore-storm.js +34 -0
  116. package/template/claude-task-manager/lib/resume-cwd.js +36 -0
  117. package/template/claude-task-manager/lib/resume-preflight.js +313 -0
  118. package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
  119. package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
  120. package/template/claude-task-manager/lib/scheduler.js +21 -1
  121. package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
  122. package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
  123. package/template/claude-task-manager/lib/server-listeners.js +239 -0
  124. package/template/claude-task-manager/lib/session-capture.js +42 -7
  125. package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
  126. package/template/claude-task-manager/lib/session-history.js +388 -43
  127. package/template/claude-task-manager/lib/session-host-manager.js +287 -0
  128. package/template/claude-task-manager/lib/session-image-refs.js +209 -0
  129. package/template/claude-task-manager/lib/session-jobs.js +399 -59
  130. package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
  131. package/template/claude-task-manager/lib/session-restore.js +53 -0
  132. package/template/claude-task-manager/lib/session-standup.js +123 -23
  133. package/template/claude-task-manager/lib/session-state-bus.js +14 -0
  134. package/template/claude-task-manager/lib/session-stream.js +64 -16
  135. package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
  136. package/template/claude-task-manager/lib/session-token-usage.js +494 -0
  137. package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
  138. package/template/claude-task-manager/lib/setup-network-config.js +9 -0
  139. package/template/claude-task-manager/lib/size-cap.js +45 -0
  140. package/template/claude-task-manager/lib/size-cap.test.js +62 -0
  141. package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
  142. package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
  143. package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
  144. package/template/claude-task-manager/lib/standup-attention.js +7 -3
  145. package/template/claude-task-manager/lib/status-authority.js +39 -0
  146. package/template/claude-task-manager/lib/status-hooks.js +4 -0
  147. package/template/claude-task-manager/lib/storage-migration.js +235 -0
  148. package/template/claude-task-manager/lib/structured-capture.js +298 -0
  149. package/template/claude-task-manager/lib/sync-io-census.js +163 -0
  150. package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
  151. package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
  152. package/template/claude-task-manager/lib/terminal-choice.js +364 -0
  153. package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
  154. package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
  155. package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
  156. package/template/claude-task-manager/lib/timeline-order.js +122 -0
  157. package/template/claude-task-manager/lib/transcript-store.js +348 -43
  158. package/template/claude-task-manager/lib/transport-security.js +84 -1
  159. package/template/claude-task-manager/lib/wait-state.js +184 -0
  160. package/template/claude-task-manager/lib/walle-client.js +47 -5
  161. package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
  162. package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
  163. package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
  164. package/template/claude-task-manager/lib/walle-native-health.js +403 -0
  165. package/template/claude-task-manager/lib/walle-repair.js +701 -0
  166. package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
  167. package/template/claude-task-manager/lib/walle-session-context.js +57 -21
  168. package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
  169. package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
  170. package/template/claude-task-manager/lib/walle-transcript.js +52 -0
  171. package/template/claude-task-manager/lib/worktree-active-sync.js +11 -7
  172. package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
  173. package/template/claude-task-manager/package.json +1 -1
  174. package/template/claude-task-manager/prompt-harvest.js +89 -66
  175. package/template/claude-task-manager/providers/claude-code.js +51 -3
  176. package/template/claude-task-manager/providers/cursor.js +140 -45
  177. package/template/claude-task-manager/public/css/reviews.css +551 -61
  178. package/template/claude-task-manager/public/css/setup.css +191 -0
  179. package/template/claude-task-manager/public/css/walle-session.css +865 -10
  180. package/template/claude-task-manager/public/css/walle.css +154 -0
  181. package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
  182. package/template/claude-task-manager/public/index.html +18516 -2058
  183. package/template/claude-task-manager/public/ipad.html +363 -0
  184. package/template/claude-task-manager/public/js/document-review-links.js +301 -0
  185. package/template/claude-task-manager/public/js/image-normalize.js +69 -36
  186. package/template/claude-task-manager/public/js/message-renderer.js +1265 -77
  187. package/template/claude-task-manager/public/js/prompts.js +66 -29
  188. package/template/claude-task-manager/public/js/reviews.js +901 -133
  189. package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
  190. package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
  191. package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
  192. package/template/claude-task-manager/public/js/setup.js +1273 -176
  193. package/template/claude-task-manager/public/js/stream-view.js +691 -73
  194. package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
  195. package/template/claude-task-manager/public/js/walle-session.js +2455 -158
  196. package/template/claude-task-manager/public/js/walle.js +455 -28
  197. package/template/claude-task-manager/public/m/app.css +2909 -262
  198. package/template/claude-task-manager/public/m/app.js +6601 -398
  199. package/template/claude-task-manager/public/m/claim.html +224 -17
  200. package/template/claude-task-manager/public/m/index.html +117 -21
  201. package/template/claude-task-manager/public/m/sw.js +3 -1
  202. package/template/claude-task-manager/public/manifest.json +2 -2
  203. package/template/claude-task-manager/public/prompts.html +30 -14
  204. package/template/claude-task-manager/queue-engine.js +507 -28
  205. package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
  206. package/template/claude-task-manager/server.js +14341 -2197
  207. package/template/claude-task-manager/session-integrity.js +160 -18
  208. package/template/claude-task-manager/session-search-ranking.js +1 -0
  209. package/template/claude-task-manager/session-utils.js +25 -5
  210. package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
  211. package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
  212. package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
  213. package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
  214. package/template/claude-task-manager/workers/harvest-worker.js +9 -55
  215. package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
  216. package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
  217. package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
  218. package/template/claude-task-manager/workers/session-host-process.js +146 -0
  219. package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
  220. package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
  221. package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
  222. package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
  223. package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
  224. package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
  225. package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
  226. package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
  227. package/template/docs/design/markdown-review-pane.md +206 -0
  228. package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +129 -38
  229. package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
  230. package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
  231. package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
  232. package/template/docs/private-memory-and-pii-policy.md +69 -0
  233. package/template/package.json +2 -1
  234. package/template/scripts/check-private-data.js +201 -0
  235. package/template/shared/sqlite-owner-guard.js +30 -0
  236. package/template/shared/sqlite-owner-write-queue.js +225 -0
  237. package/template/shared/sqlite-storage-policy.js +111 -0
  238. package/template/shared/sqlite-write-lock.js +428 -0
  239. package/template/wall-e/agent-runners/claude-code.js +5 -0
  240. package/template/wall-e/agent.js +166 -22
  241. package/template/wall-e/api-walle.js +524 -70
  242. package/template/wall-e/auth/provider-flows.js +11 -1
  243. package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
  244. package/template/wall-e/brain.js +1614 -141
  245. package/template/wall-e/chat/attachment-blocks.js +96 -0
  246. package/template/wall-e/chat/attachments.js +2 -1
  247. package/template/wall-e/chat/capability-resolver.js +7 -7
  248. package/template/wall-e/chat/context-messages.js +28 -0
  249. package/template/wall-e/chat/conversation-frame.js +630 -0
  250. package/template/wall-e/chat/provider-messages.js +125 -0
  251. package/template/wall-e/chat.js +1002 -233
  252. package/template/wall-e/coding/acceptance-contract.js +170 -0
  253. package/template/wall-e/coding/acp-adapter.js +1 -1
  254. package/template/wall-e/coding/agent-catalog.js +3 -0
  255. package/template/wall-e/coding/artifact-store.js +93 -0
  256. package/template/wall-e/coding/capability-router.js +120 -0
  257. package/template/wall-e/coding/coding-run-controller.js +423 -0
  258. package/template/wall-e/coding/compaction-service.js +157 -12
  259. package/template/wall-e/coding/frontend-verification.js +258 -0
  260. package/template/wall-e/coding/lifecycle-hooks.js +75 -0
  261. package/template/wall-e/coding/local-preview-contract.js +157 -0
  262. package/template/wall-e/coding/permission-service.js +57 -13
  263. package/template/wall-e/coding/prompt-bundle.js +19 -1
  264. package/template/wall-e/coding/prompt-section-registry.js +227 -0
  265. package/template/wall-e/coding/provider-compat.js +15 -0
  266. package/template/wall-e/coding/runtime-events.js +224 -0
  267. package/template/wall-e/coding/runtime-mode.js +3 -0
  268. package/template/wall-e/coding/side-git-snapshot.js +160 -4
  269. package/template/wall-e/coding/snapshot-service.js +143 -1
  270. package/template/wall-e/coding/stream-processor.js +388 -34
  271. package/template/wall-e/coding/task-tool.js +141 -4
  272. package/template/wall-e/coding/tool-execution-controller.js +365 -0
  273. package/template/wall-e/coding/tool-registry.js +43 -5
  274. package/template/wall-e/coding/user-hooks.js +217 -0
  275. package/template/wall-e/coding-orchestrator.js +1330 -221
  276. package/template/wall-e/coding-prompts.js +20 -4
  277. package/template/wall-e/context/context-builder.js +15 -2
  278. package/template/wall-e/decision/confidence.js +1 -1
  279. package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
  280. package/template/wall-e/docs/external-action-controller.md +26 -6
  281. package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
  282. package/template/wall-e/embeddings.js +591 -53
  283. package/template/wall-e/external-action-controller.js +12 -0
  284. package/template/wall-e/http/auth.js +1 -0
  285. package/template/wall-e/http/chat-api.js +46 -11
  286. package/template/wall-e/http/model-admin.js +836 -34
  287. package/template/wall-e/lib/boot-profile.js +88 -0
  288. package/template/wall-e/lib/event-loop-monitor.js +93 -0
  289. package/template/wall-e/lib/service-health.js +194 -0
  290. package/template/wall-e/llm/anthropic.js +130 -5
  291. package/template/wall-e/llm/client.js +266 -63
  292. package/template/wall-e/llm/default-fallback.js +382 -0
  293. package/template/wall-e/llm/health.js +19 -0
  294. package/template/wall-e/llm/message-guard.js +78 -0
  295. package/template/wall-e/llm/model-catalog.js +252 -1
  296. package/template/wall-e/llm/openai.js +26 -4
  297. package/template/wall-e/llm/portkey-sync.js +654 -0
  298. package/template/wall-e/llm/provider-error.js +30 -2
  299. package/template/wall-e/llm/registry.js +5 -1
  300. package/template/wall-e/llm/request-compat.js +67 -0
  301. package/template/wall-e/loops/backfill.js +79 -23
  302. package/template/wall-e/loops/brain-optimize.js +67 -0
  303. package/template/wall-e/loops/ingest.js +25 -10
  304. package/template/wall-e/loops/question-digest.js +160 -0
  305. package/template/wall-e/loops/reflect.js +6 -4
  306. package/template/wall-e/loops/think.js +39 -12
  307. package/template/wall-e/mcp-server.js +318 -36
  308. package/template/wall-e/memory/ctm-context-client.js +52 -14
  309. package/template/wall-e/memory/ctm-operational-context.js +237 -0
  310. package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
  311. package/template/wall-e/memory/ctm-session-context.js +111 -63
  312. package/template/wall-e/prompts/coding/deepseek.txt +3 -0
  313. package/template/wall-e/prompts/coding/gemini.txt +6 -0
  314. package/template/wall-e/prompts/coding/gpt.txt +6 -0
  315. package/template/wall-e/prompts/coding/local.txt +7 -0
  316. package/template/wall-e/runtime/decision-hooks.js +115 -0
  317. package/template/wall-e/runtime/devbox-gateway.js +82 -8
  318. package/template/wall-e/runtime/prompt-manifest.js +86 -0
  319. package/template/wall-e/runtime/tool-executor.js +269 -0
  320. package/template/wall-e/runtime/tool-result-envelope.js +138 -0
  321. package/template/wall-e/runtime/transcript-projection.js +60 -0
  322. package/template/wall-e/runtime/walle-runtime.js +224 -0
  323. package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
  324. package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
  325. package/template/wall-e/server.js +15 -0
  326. package/template/wall-e/session-files.js +9 -0
  327. package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
  328. package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
  329. package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
  330. package/template/wall-e/skills/claude-code-reader.js +7 -3
  331. package/template/wall-e/skills/script-skill-runner.js +10 -0
  332. package/template/wall-e/skills/skill-planner.js +38 -0
  333. package/template/wall-e/tools/builtin-middleware.js +19 -9
  334. package/template/wall-e/tools/local-tools.js +1428 -16
  335. package/template/wall-e/tools/permission-checker.js +73 -5
  336. package/template/wall-e/tools/question-manager.js +117 -7
  337. package/template/wall-e/training/harvester.js +12 -28
  338. package/template/wall-e/training/replay.js +25 -80
  339. package/template/website/index.html +10 -10
  340. package/template/wall-e/eval/ab-test.js +0 -203
  341. package/template/wall-e/eval/agent-runner.js +0 -772
  342. package/template/wall-e/eval/agent-scorer.js +0 -461
  343. package/template/wall-e/eval/aggregator.js +0 -414
  344. package/template/wall-e/eval/allowed-test-commands.js +0 -34
  345. package/template/wall-e/eval/benchmark-generator.js +0 -113
  346. package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
  347. package/template/wall-e/eval/benchmarks/chat.json +0 -82
  348. package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
  349. package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
  350. package/template/wall-e/eval/benchmarks/coding.json +0 -122
  351. package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
  352. package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
  353. package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
  354. package/template/wall-e/eval/benchmarks.js +0 -669
  355. package/template/wall-e/eval/cc-replay.js +0 -719
  356. package/template/wall-e/eval/chat-eval.js +0 -525
  357. package/template/wall-e/eval/check-keys.js +0 -15
  358. package/template/wall-e/eval/check-providers.js +0 -42
  359. package/template/wall-e/eval/codex-cli-baseline.js +0 -669
  360. package/template/wall-e/eval/coding-agent-real.js +0 -570
  361. package/template/wall-e/eval/context-compactor.js +0 -251
  362. package/template/wall-e/eval/debug-agent003.js +0 -68
  363. package/template/wall-e/eval/diagnostics.js +0 -216
  364. package/template/wall-e/eval/eval-orchestrator.js +0 -642
  365. package/template/wall-e/eval/evaluate.js +0 -202
  366. package/template/wall-e/eval/evaluator.js +0 -373
  367. package/template/wall-e/eval/exporter.js +0 -212
  368. package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
  369. package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
  370. package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
  371. package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
  372. package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
  373. package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
  374. package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
  375. package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
  376. package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
  377. package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
  378. package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
  379. package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
  380. package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
  381. package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
  382. package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
  383. package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
  384. package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
  385. package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
  386. package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
  387. package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
  388. package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
  389. package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
  390. package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
  391. package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
  392. package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
  393. package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
  394. package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
  395. package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
  396. package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
  397. package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
  398. package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
  399. package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
  400. package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
  401. package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
  402. package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
  403. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
  404. package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
  405. package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
  406. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
  407. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
  408. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
  409. package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
  410. package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
  411. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
  412. package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
  413. package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
  414. package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
  415. package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
  416. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
  417. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
  418. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
  419. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
  420. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
  421. package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
  422. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
  423. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
  424. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
  425. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
  426. package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
  427. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
  428. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
  429. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
  430. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
  431. package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
  432. package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
  433. package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
  434. package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
  435. package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
  436. package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
  437. package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
  438. package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
  439. package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
  440. package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
  441. package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
  442. package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
  443. package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
  444. package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
  445. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
  446. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
  447. package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
  448. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
  449. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
  450. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
  451. package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
  452. package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
  453. package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
  454. package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
  455. package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
  456. package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
  457. package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
  458. package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
  459. package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
  460. package/template/wall-e/eval/harvester.js +0 -685
  461. package/template/wall-e/eval/head-to-head.js +0 -388
  462. package/template/wall-e/eval/humaneval-adapter.js +0 -321
  463. package/template/wall-e/eval/list-models.js +0 -31
  464. package/template/wall-e/eval/livecodebench-adapter.js +0 -291
  465. package/template/wall-e/eval/mail-integration.js +0 -443
  466. package/template/wall-e/eval/manifest.js +0 -186
  467. package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
  468. package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
  469. package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
  470. package/template/wall-e/eval/meta-harness/cli.js +0 -86
  471. package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
  472. package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
  473. package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
  474. package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
  475. package/template/wall-e/eval/meta-harness/frontier.js +0 -96
  476. package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
  477. package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
  478. package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
  479. package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
  480. package/template/wall-e/eval/meta-harness/reporting.js +0 -58
  481. package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
  482. package/template/wall-e/eval/meta-harness/validation.js +0 -81
  483. package/template/wall-e/eval/promoter.js +0 -228
  484. package/template/wall-e/eval/provider-normalizer.js +0 -33
  485. package/template/wall-e/eval/replay.js +0 -395
  486. package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
  487. package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
  488. package/template/wall-e/eval/run-coding-agent-real.js +0 -187
  489. package/template/wall-e/eval/run-eval.js +0 -435
  490. package/template/wall-e/eval/run-model-comparison.js +0 -142
  491. package/template/wall-e/eval/session-evaluator.js +0 -187
  492. package/template/wall-e/eval/session-miner.js +0 -207
  493. package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
  494. package/template/wall-e/eval/session-transcripts.js +0 -509
  495. package/template/wall-e/eval/shadow.js +0 -161
  496. package/template/wall-e/eval/swebench-adapter.js +0 -345
  497. package/template/wall-e/eval/swebench-docker.js +0 -192
  498. package/template/wall-e/eval/train.py +0 -320
  499. package/template/wall-e/eval/trainer.js +0 -232
  500. package/template/wall-e/eval/weekly-eval-loop.js +0 -241
@@ -0,0 +1,630 @@
1
+ 'use strict';
2
+
3
+ const FRAME_SCHEMA_VERSION = 1;
4
+ const MAX_CONTEXT_MESSAGES = 60;
5
+ const MAX_RECOMMENDATION_NAMES = 12;
6
+
7
+ const DOMAIN_PATTERNS = [
8
+ ['email', /\b(e-?mail|inbox|reply|respond|thread|recipient|cc|bcc|gmail|mail)\b/i],
9
+ ['calendar', /\b(calendar|meeting|schedule|appointment|event|availability|free|busy)\b/i],
10
+ ['coding', /\b(code|repo|repository|branch|commit|merge|test|build|bug|fix|implement|refactor|deploy|website|app|ui|ux)\b/i],
11
+ ['travel', /\b(trip|travel|flight|hotel|lodging|airbnb|vrbo|stay|rental car|car rental|airport|cruise|booking|reservation|itinerary|destination)\b/i],
12
+ ['recommendation', /\b(recommend|recommendation|top\s+\d+|best|rank|choose|pick|options?)\b/i],
13
+ ];
14
+
15
+ const TRAVEL_RE = /\b(trip|travel|flight|hotel|lodging|airbnb|vrbo|stay|rental car|car rental|airport|cruise|booking|reservation|itinerary|destination)\b/i;
16
+ const RECOMMENDATION_RE = /\b(recommend|recommendation|top\s+\d+|best|rank|choose|pick|options?)\b/i;
17
+ const LODGING_RE = /\b(hotel|lodging|airbnb|vrbo|vacation rental|short[- ]term rental|stay|staying|room|resort|inn|booking|book directly|direct booking)\b/i;
18
+ const TRANSPORT_RE = /\b(flight|airport|rental car|car rental|drop ?off|pick ?up|drive|driving|terminal)\b/i;
19
+ const CORRECTION_RE = /\b(doesn'?t change|does not change|same|unchanged|wrong|not what|lost|different|instead|rather than|not just|not limited|doesn'?t have to be|can be|go back|stick with|you missed|you ignored)\b/i;
20
+
21
+ const STOP_LOCATION_WORDS = new Set([
22
+ 'Given', 'Give', 'Can', 'Should', 'What', 'When', 'Where', 'Why', 'How', 'The',
23
+ 'This', 'That', 'There', 'Hotel', 'Hotels', 'Airbnb', 'Vrbo', 'Dec', 'Jan',
24
+ 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Sept', 'Oct', 'Nov',
25
+ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday',
26
+ 'Plan', 'Help', 'Recommend', 'Recommendation', 'Recommendations', 'Top', 'Book',
27
+ 'Booking', 'Direct', 'New',
28
+ ]);
29
+
30
+ function parseSessionMetadata(raw) {
31
+ if (!raw) return {};
32
+ if (typeof raw === 'object') return raw;
33
+ try {
34
+ const parsed = JSON.parse(raw);
35
+ return parsed && typeof parsed === 'object' ? parsed : {};
36
+ } catch {
37
+ return {};
38
+ }
39
+ }
40
+
41
+ function normalizeSemanticMessages(messages, currentMessage = '') {
42
+ const raw = Array.isArray(messages) ? messages : [];
43
+ const normalized = [];
44
+ for (const item of raw) {
45
+ if (!item || typeof item !== 'object') continue;
46
+ const role = String(item.role || '').trim();
47
+ if (role !== 'user' && role !== 'assistant') continue;
48
+ const content = contentToText(item.content != null ? item.content : item.text);
49
+ if (!content.trim()) continue;
50
+ normalized.push({ role, content: content.trim() });
51
+ }
52
+ const current = String(currentMessage || '').trim();
53
+ if (current) {
54
+ const last = normalized[normalized.length - 1];
55
+ if (!last || last.role !== 'user' || last.content !== current) {
56
+ normalized.push({ role: 'user', content: current });
57
+ } else {
58
+ last.content = current;
59
+ }
60
+ }
61
+ return normalized.slice(-MAX_CONTEXT_MESSAGES);
62
+ }
63
+
64
+ function contentToText(content) {
65
+ if (content == null) return '';
66
+ if (typeof content === 'string') return content;
67
+ if (Array.isArray(content)) {
68
+ return content.map((part) => {
69
+ if (!part) return '';
70
+ if (typeof part === 'string') return part;
71
+ if (part.type === 'text') return part.text || '';
72
+ if (part.type === 'tool_result') return part.content || '';
73
+ if (part.type === 'tool_use') return `[tool:${part.name || 'unknown'}]`;
74
+ return '';
75
+ }).filter(Boolean).join('\n');
76
+ }
77
+ try { return JSON.stringify(content); } catch { return String(content); }
78
+ }
79
+
80
+ function buildConversationRuntime({ messages, currentMessage, previousFrame, channel, cwd, taskType } = {}) {
81
+ const semanticMessages = normalizeSemanticMessages(messages, currentMessage);
82
+ const frame = deriveConversationFrame(semanticMessages, {
83
+ previousFrame,
84
+ channel,
85
+ cwd,
86
+ taskType,
87
+ });
88
+ return {
89
+ schemaVersion: FRAME_SCHEMA_VERSION,
90
+ messages: semanticMessages,
91
+ frame,
92
+ validators: validatorsForFrame(frame),
93
+ };
94
+ }
95
+
96
+ function deriveConversationFrame(messages, opts = {}) {
97
+ const previous = normalizeFrame(opts.previousFrame);
98
+ const currentUser = [...messages].reverse().find((msg) => msg.role === 'user')?.content || '';
99
+ const allText = messages.map((msg) => msg.content).join('\n');
100
+ const currentDomain = inferDomain(currentUser) || previous.domain || inferDomain(allText) || 'general';
101
+ const isCorrection = CORRECTION_RE.test(currentUser);
102
+ const now = new Date().toISOString();
103
+
104
+ const frame = {
105
+ schemaVersion: FRAME_SCHEMA_VERSION,
106
+ updatedAt: now,
107
+ domain: currentDomain,
108
+ activeGoal: inferActiveGoal(currentUser, previous.activeGoal),
109
+ scope: {
110
+ ...(previous.scope || {}),
111
+ ...(opts.cwd ? { projectPath: opts.cwd } : {}),
112
+ },
113
+ constraints: [...(previous.constraints || [])],
114
+ recentCorrections: [...(previous.recentCorrections || [])],
115
+ inactiveRelatedFrames: [...(previous.inactiveRelatedFrames || [])],
116
+ evidenceRefs: [],
117
+ previousRecommendations: Array.isArray(previous.previousRecommendations)
118
+ ? previous.previousRecommendations.slice(0, MAX_RECOMMENDATION_NAMES)
119
+ : [],
120
+ confidence: previous.confidence || 0.45,
121
+ };
122
+
123
+ const travelLike = currentDomain === 'travel' || TRAVEL_RE.test(allText) || RECOMMENDATION_RE.test(currentUser);
124
+ if (travelLike) applyTravelFrame(frame, messages, currentUser, isCorrection);
125
+ applyActionFrame(frame, messages, currentUser);
126
+
127
+ const constraints = inferConstraints(currentUser);
128
+ for (const constraint of constraints) addUnique(frame.constraints, constraint);
129
+ if (isCorrection) addRecentCorrection(frame, currentUser);
130
+
131
+ const explicitDates = extractDates(allText);
132
+ if (explicitDates.length) frame.scope.dates = explicitDates.slice(-4);
133
+ const travelerSummary = extractTravelerSummary(allText);
134
+ if (travelerSummary) frame.scope.travelers = travelerSummary;
135
+
136
+ frame.previousRecommendations = collectPreviousRecommendations(messages)
137
+ .concat(frame.previousRecommendations || [])
138
+ .filter(uniqueByLower)
139
+ .slice(0, MAX_RECOMMENDATION_NAMES);
140
+
141
+ frame.confidence = scoreFrameConfidence(frame);
142
+ return pruneFrame(frame);
143
+ }
144
+
145
+ function normalizeFrame(frame) {
146
+ if (!frame || typeof frame !== 'object') return {};
147
+ return {
148
+ schemaVersion: frame.schemaVersion || FRAME_SCHEMA_VERSION,
149
+ domain: typeof frame.domain === 'string' ? frame.domain : undefined,
150
+ activeGoal: typeof frame.activeGoal === 'string' ? frame.activeGoal : undefined,
151
+ scope: frame.scope && typeof frame.scope === 'object' ? { ...frame.scope } : {},
152
+ constraints: Array.isArray(frame.constraints) ? frame.constraints.filter(Boolean).map(String) : [],
153
+ recentCorrections: Array.isArray(frame.recentCorrections) ? frame.recentCorrections.filter(Boolean).map(String).slice(-5) : [],
154
+ inactiveRelatedFrames: Array.isArray(frame.inactiveRelatedFrames) ? frame.inactiveRelatedFrames.filter(Boolean).map(String).slice(-8) : [],
155
+ evidenceRefs: Array.isArray(frame.evidenceRefs) ? frame.evidenceRefs : [],
156
+ previousRecommendations: Array.isArray(frame.previousRecommendations) ? frame.previousRecommendations.filter(Boolean).map(String) : [],
157
+ confidence: Number(frame.confidence || 0) || undefined,
158
+ };
159
+ }
160
+
161
+ function inferDomain(text) {
162
+ for (const [domain, re] of DOMAIN_PATTERNS) {
163
+ if (re.test(text || '')) return domain;
164
+ }
165
+ return null;
166
+ }
167
+
168
+ function inferActiveGoal(text, fallback = '') {
169
+ const value = String(text || '');
170
+ if (/\b(reply|respond)\b/i.test(value) && /\b(email|mail|thread)\b/i.test(value)) return 'reply to email thread';
171
+ if (/\b(send|draft|write)\b/i.test(value) && /\b(email|message)\b/i.test(value)) return 'draft or send outbound message';
172
+ if (/\b(calendar|schedule|create.*event|add.*event)\b/i.test(value)) return 'schedule calendar event';
173
+ if (/\b(hotel|lodging|airbnb|vrbo|stay)\b/i.test(value) && RECOMMENDATION_RE.test(value)) return 'recommend lodging options';
174
+ if (TRAVEL_RE.test(value)) return 'plan travel';
175
+ if (/\b(fix|implement|build|debug|refactor|test)\b/i.test(value)) return 'complete coding task';
176
+ if (RECOMMENDATION_RE.test(value)) return 'recommend options';
177
+ return fallback || 'answer current user request';
178
+ }
179
+
180
+ function applyTravelFrame(frame, messages, currentUser, isCorrection) {
181
+ const latestLodgingLocation = findLatestLocationForContext(messages, LODGING_RE);
182
+ if (latestLodgingLocation) frame.scope.lodgingLocation = latestLodgingLocation;
183
+
184
+ const latestTravelDestination = findLatestTravelDestination(messages);
185
+ if (latestTravelDestination && !frame.scope.destination) frame.scope.destination = latestTravelDestination;
186
+
187
+ const inactiveLocations = findInactiveTravelLocations(messages, frame.scope.lodgingLocation || frame.scope.destination);
188
+ for (const loc of inactiveLocations) addUnique(frame.inactiveRelatedFrames, loc);
189
+
190
+ if (/\bdirect(?:ly)?\b/i.test(currentUser) && /\b(book|booking|hotel|lodging|reservation)\b/i.test(currentUser)) {
191
+ addUnique(frame.constraints, 'booking_channel:direct');
192
+ }
193
+ if (/\bairbnb|vrbo|vacation rental|short[- ]term rental|home rental|condo\b/i.test(currentUser)) {
194
+ addUnique(frame.constraints, 'include_supply_type:alternative_rentals');
195
+ }
196
+ if (/\bnot\s+(?:just|only|limited to)\s+hilton|doesn'?t have to be hilton|not hilton-only\b/i.test(currentUser)) {
197
+ addUnique(frame.constraints, 'not_brand_limited:hilton');
198
+ }
199
+ if (/\b(new|different|changed|doesn'?t change|same)\b/i.test(currentUser) && RECOMMENDATION_RE.test(currentUser + ' recommendation')) {
200
+ addUnique(frame.constraints, 'requires_novel_recommendations');
201
+ }
202
+ if (isCorrection) addUnique(frame.constraints, 'latest_user_correction_wins');
203
+ }
204
+
205
+ function applyActionFrame(frame, messages, currentUser) {
206
+ if (frame.domain === 'email') {
207
+ if (/\b(reply|respond|original email|same thread)\b/i.test(currentUser)) {
208
+ addUnique(frame.constraints, 'email_action:reply_to_original_thread');
209
+ }
210
+ const recipients = extractEmails(messages.map(m => m.content).join('\n'));
211
+ if (recipients.length) frame.scope.recipients = recipients.slice(-8);
212
+ }
213
+ if (frame.domain === 'calendar') {
214
+ const calendars = [];
215
+ if (/\bwork\b|examplecorp/i.test(currentUser)) calendars.push('work');
216
+ if (/\bpersonal\b|gmail/i.test(currentUser)) calendars.push('personal');
217
+ if (calendars.length) frame.scope.calendars = calendars;
218
+ }
219
+ if (frame.domain === 'coding' && !frame.scope.projectPath) {
220
+ const pathMatch = messages.map(m => m.content).join('\n').match(/(?:cwd|worktree|repo|project)\s*[:=]\s*(`?)(\/[^\s`]+)\1/i);
221
+ if (pathMatch) frame.scope.projectPath = pathMatch[2];
222
+ }
223
+ }
224
+
225
+ function inferConstraints(text) {
226
+ const value = String(text || '');
227
+ const constraints = [];
228
+ if (/\bno approval needed|just do it|go ahead\b/i.test(value)) constraints.push('user_authorized_execution');
229
+ if (/\bdon'?t send\b|\bdo not send\b/i.test(value)) constraints.push('external_action:draft_only');
230
+ if (/\btest (it|your changes)|validate|verify\b/i.test(value)) constraints.push('verification_required');
231
+ return constraints;
232
+ }
233
+
234
+ function findLatestLocationForContext(messages, contextRe) {
235
+ const ordered = [
236
+ ...[...messages].filter(msg => msg.role === 'user').reverse(),
237
+ ...[...messages].filter(msg => msg.role !== 'user').reverse(),
238
+ ];
239
+ for (const msg of ordered) {
240
+ const text = msg.content || '';
241
+ if (!contextRe.test(text) && !TRAVEL_RE.test(text)) continue;
242
+ const explicit = findPrepositionalLocation(text);
243
+ if (explicit) return explicit;
244
+ const locations = extractLocationCandidates(text);
245
+ const contextual = locations.find((loc) => appearsNearContext(text, loc, contextRe));
246
+ if (contextual) return contextual;
247
+ }
248
+ return '';
249
+ }
250
+
251
+ function findLatestTravelDestination(messages) {
252
+ const ordered = [
253
+ ...[...messages].filter(msg => msg.role === 'user').reverse(),
254
+ ...[...messages].filter(msg => msg.role !== 'user').reverse(),
255
+ ];
256
+ for (const msg of ordered) {
257
+ const text = msg.content || '';
258
+ if (!TRAVEL_RE.test(text) && !RECOMMENDATION_RE.test(text)) continue;
259
+ const explicit = findPrepositionalLocation(text);
260
+ if (explicit) return explicit;
261
+ const locations = extractLocationCandidates(text);
262
+ if (locations.length) return locations[0];
263
+ }
264
+ return '';
265
+ }
266
+
267
+ function findInactiveTravelLocations(messages, activeLocation) {
268
+ const active = normalizeComparable(activeLocation);
269
+ const out = [];
270
+ for (const msg of messages) {
271
+ const text = msg.content || '';
272
+ if (!TRANSPORT_RE.test(text)) continue;
273
+ for (const loc of extractLocationCandidates(text)) {
274
+ if (normalizeComparable(loc) && normalizeComparable(loc) !== active) addUnique(out, loc);
275
+ }
276
+ }
277
+ return out.slice(-6);
278
+ }
279
+
280
+ function extractLocationCandidates(text) {
281
+ const value = stripUrls(String(text || ''));
282
+ const out = [];
283
+ const airportRe = /\b[A-Z]{3}\b/g;
284
+ for (const match of value.matchAll(airportRe)) addUnique(out, match[0]);
285
+
286
+ const properRe = /\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}\b/g;
287
+ for (const match of value.matchAll(properRe)) {
288
+ const candidate = match[0].trim();
289
+ const first = candidate.split(/\s+/)[0];
290
+ if (STOP_LOCATION_WORDS.has(first)) continue;
291
+ if (candidate.length < 4 || candidate.length > 60) continue;
292
+ if (/^(Wall|You|Today|Thanks|Please|Hi|Hello|Best|Review|Summary|Final|Action|Status)$/i.test(candidate)) continue;
293
+ addUnique(out, candidate);
294
+ }
295
+ return out;
296
+ }
297
+
298
+ function findPrepositionalLocation(text) {
299
+ const value = stripUrls(String(text || ''));
300
+ const patterns = [
301
+ /\b(?:in|near|around|for|to)\s+([A-Z][A-Za-z]+(?:\s+[A-Z][A-Za-z]+){0,3}|[A-Z]{3})\b(?=[^\n]{0,80}\b(?:hotel|lodging|airbnb|vrbo|stay|recommend|trip|travel|booking|reservation)\b)/i,
302
+ /\b(?:hotel|lodging|airbnb|vrbo|stay|recommend|trip|travel|booking|reservation)\b[^\n]{0,80}\b(?:in|near|around|for|to)\s+([A-Z][A-Za-z]+(?:\s+[A-Z][A-Za-z]+){0,3}|[A-Z]{3})\b/i,
303
+ /\b([A-Z][A-Za-z]+(?:\s+[A-Z][A-Za-z]+){0,3}|[A-Z]{3})\s+(?:hotel|hotels|lodging|airbnb|vrbo|stay|trip|travel|booking|reservation|recommendations?)\b/i,
304
+ ];
305
+ for (const pattern of patterns) {
306
+ const match = value.match(pattern);
307
+ if (match?.[1]) {
308
+ const location = cleanLocation(match[1]);
309
+ if (isLikelyLocationCandidate(location)) return location;
310
+ }
311
+ }
312
+ return '';
313
+ }
314
+
315
+ function appearsNearContext(text, location, contextRe) {
316
+ if (!location) return false;
317
+ const value = String(text || '');
318
+ const idx = value.toLowerCase().indexOf(location.toLowerCase());
319
+ if (idx < 0) return false;
320
+ const window = value.slice(Math.max(0, idx - 100), Math.min(value.length, idx + location.length + 100));
321
+ return contextRe.test(window);
322
+ }
323
+
324
+ function cleanLocation(value) {
325
+ const clean = String(value || '')
326
+ .replace(/[.,;:!?)}\]]+$/g, '')
327
+ .trim();
328
+ const parts = clean.split(/\s+/).filter(Boolean);
329
+ while (parts.length > 1 && STOP_LOCATION_WORDS.has(parts[0])) parts.shift();
330
+ if (parts.length === 1 && STOP_LOCATION_WORDS.has(parts[0])) return '';
331
+ return parts.join(' ');
332
+ }
333
+
334
+ function isLikelyLocationCandidate(value) {
335
+ const clean = String(value || '').trim();
336
+ if (!clean) return false;
337
+ if (/^[A-Z]{3}$/.test(clean)) return true;
338
+ return /^[A-Z]/.test(clean) && !STOP_LOCATION_WORDS.has(clean.split(/\s+/)[0]);
339
+ }
340
+
341
+ function stripUrls(value) {
342
+ return String(value || '').replace(/https?:\/\/\S+/g, ' ');
343
+ }
344
+
345
+ function extractDates(text) {
346
+ const value = String(text || '');
347
+ const out = [];
348
+ for (const match of value.matchAll(/\b20\d{2}-\d{2}-\d{2}\b/g)) addUnique(out, match[0]);
349
+ for (const match of value.matchAll(/\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)[a-z]*\.?\s+\d{1,2}(?:\s*(?:-|to|through|–)\s*(?:(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)[a-z]*\.?\s*)?\d{1,2})?(?:,\s*20\d{2})?\b/gi)) {
350
+ addUnique(out, match[0]);
351
+ }
352
+ return out.slice(-8);
353
+ }
354
+
355
+ function extractTravelerSummary(text) {
356
+ const value = String(text || '');
357
+ const parts = [];
358
+ const adults = value.match(/\b(\d+)\s+adults?\b/i);
359
+ if (adults) parts.push(`${adults[1]} adult${adults[1] === '1' ? '' : 's'}`);
360
+ const children = value.match(/\b(\d+)\s+(?:kids?|children|child)\b/i);
361
+ if (children) parts.push(`${children[1]} child${children[1] === '1' ? '' : 'ren'}`);
362
+ const age = value.match(/\b(\d{1,2})\s*years?\s*old\b/i);
363
+ if (age && !children) parts.push(`${age[1]}-year-old traveler`);
364
+ return parts.join(' + ');
365
+ }
366
+
367
+ function extractEmails(text) {
368
+ return [...new Set(String(text || '').match(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi) || [])];
369
+ }
370
+
371
+ function collectPreviousRecommendations(messages) {
372
+ const names = [];
373
+ for (const msg of messages) {
374
+ if (msg.role !== 'assistant') continue;
375
+ for (const name of extractRecommendationNames(msg.content)) addUnique(names, name);
376
+ }
377
+ return names.slice(-MAX_RECOMMENDATION_NAMES);
378
+ }
379
+
380
+ function extractRecommendationNames(text) {
381
+ const out = [];
382
+ const value = String(text || '');
383
+ for (const match of value.matchAll(/\*\*([^*\n]{3,80})\*\*/g)) {
384
+ const name = cleanRecommendationName(match[1]);
385
+ if (name) addUnique(out, name);
386
+ }
387
+ for (const line of value.split('\n')) {
388
+ const match = line.match(/^\s*(?:[-*]|\d+[.)])\s+(?:\*\*)?([^:*—-][^:—\n]{2,80}?)(?:\*\*)?(?:\s*[:—-]|\s+\(|$)/);
389
+ if (!match) continue;
390
+ const name = cleanRecommendationName(match[1]);
391
+ if (name) addUnique(out, name);
392
+ }
393
+ return out.slice(0, MAX_RECOMMENDATION_NAMES);
394
+ }
395
+
396
+ function cleanRecommendationName(value) {
397
+ const name = String(value || '')
398
+ .replace(/^[^\wA-Z]+/, '')
399
+ .replace(/\s+/g, ' ')
400
+ .trim();
401
+ if (!name || name.length < 3 || name.length > 80) return '';
402
+ if (/^(why|pros|cons|price|location|recommendation|option|summary|final|notes?|action|status|detail|field)$/i.test(name)) return '';
403
+ if (!/[A-Z]/.test(name)) return '';
404
+ return name;
405
+ }
406
+
407
+ function addRecentCorrection(frame, text) {
408
+ const sentence = String(text || '').replace(/\s+/g, ' ').trim().slice(0, 240);
409
+ if (!sentence) return;
410
+ addUnique(frame.recentCorrections, sentence);
411
+ if (frame.recentCorrections.length > 5) frame.recentCorrections = frame.recentCorrections.slice(-5);
412
+ }
413
+
414
+ function scoreFrameConfidence(frame) {
415
+ let score = 0.35;
416
+ const semanticScopeKeys = Object.keys(frame.scope || {}).filter((key) => key !== 'projectPath');
417
+ if (frame.domain && frame.domain !== 'general') score += 0.15;
418
+ if (frame.activeGoal && frame.activeGoal !== 'answer current user request') score += 0.1;
419
+ if (semanticScopeKeys.length > 0) score += 0.15;
420
+ if ((frame.constraints || []).length > 0) score += 0.1;
421
+ if (frame.domain === 'coding' && frame.scope?.projectPath) score += 0.1;
422
+ if (frame.scope?.lodgingLocation || frame.scope?.destination) score += 0.1;
423
+ return Math.max(0.1, Math.min(0.95, Number(score.toFixed(2))));
424
+ }
425
+
426
+ function pruneFrame(frame) {
427
+ const out = JSON.parse(JSON.stringify(frame || {}));
428
+ out.constraints = (out.constraints || []).filter(Boolean).slice(-16);
429
+ out.recentCorrections = (out.recentCorrections || []).filter(Boolean).slice(-5);
430
+ out.inactiveRelatedFrames = (out.inactiveRelatedFrames || []).filter(Boolean).slice(-8);
431
+ out.previousRecommendations = (out.previousRecommendations || []).filter(Boolean).slice(0, MAX_RECOMMENDATION_NAMES);
432
+ out.evidenceRefs = (out.evidenceRefs || []).slice(-8);
433
+ return out;
434
+ }
435
+
436
+ function validatorsForFrame(frame) {
437
+ const validators = [];
438
+ if (frame?.domain === 'travel' || frame?.activeGoal?.includes('recommend') || frame?.scope?.lodgingLocation) {
439
+ validators.push('travel_recommendation_scope');
440
+ }
441
+ if (frame?.domain === 'email') validators.push('email_action_scope');
442
+ if (frame?.domain === 'calendar') validators.push('calendar_action_scope');
443
+ if (frame?.domain === 'coding') validators.push('coding_completion_scope');
444
+ return validators;
445
+ }
446
+
447
+ function buildConversationFramePrompt(frame) {
448
+ if (!frame || frame.confidence < 0.45) return '';
449
+ const lines = [];
450
+ lines.push('## Active Conversation Frame');
451
+ lines.push('This frame is the source of truth for the current task. The latest user correction wins over older assistant suggestions, memories, artifacts, and neighboring CTM tabs.');
452
+ lines.push(`Domain: ${frame.domain || 'general'}`);
453
+ lines.push(`Active goal: ${frame.activeGoal || 'answer current user request'}`);
454
+ const scopeLines = Object.entries(frame.scope || {})
455
+ .filter(([, value]) => value != null && value !== '' && (!Array.isArray(value) || value.length))
456
+ .map(([key, value]) => `- ${key}: ${Array.isArray(value) ? value.join(', ') : value}`);
457
+ if (scopeLines.length) lines.push(`Scope:\n${scopeLines.join('\n')}`);
458
+ if (frame.constraints?.length) lines.push(`Constraints:\n${frame.constraints.map(c => `- ${c}`).join('\n')}`);
459
+ if (frame.recentCorrections?.length) lines.push(`Recent user corrections:\n${frame.recentCorrections.map(c => `- ${c}`).join('\n')}`);
460
+ if (frame.inactiveRelatedFrames?.length) lines.push(`Related but inactive context:\n${frame.inactiveRelatedFrames.map(c => `- ${c}`).join('\n')}`);
461
+ if (frame.previousRecommendations?.length) lines.push(`Previously recommended candidates to avoid repeating unless justified:\n${frame.previousRecommendations.map(c => `- ${c}`).join('\n')}`);
462
+ lines.push('Before finalizing, verify the answer matches this frame. If it conflicts, repair the answer rather than claiming success.');
463
+ return `\n\n${lines.join('\n')}`;
464
+ }
465
+
466
+ function validateFrameAnswer(text, frame) {
467
+ const violations = [];
468
+ const value = String(text || '');
469
+ if (!value.trim() || !frame) return { ok: true, violations };
470
+
471
+ if (validatorsForFrame(frame).includes('travel_recommendation_scope')) {
472
+ validateTravelRecommendationAnswer(value, frame, violations);
473
+ }
474
+ if (validatorsForFrame(frame).includes('email_action_scope')) {
475
+ validateEmailActionAnswer(value, frame, violations);
476
+ }
477
+ if (validatorsForFrame(frame).includes('calendar_action_scope')) {
478
+ validateCalendarActionAnswer(value, frame, violations);
479
+ }
480
+ if (validatorsForFrame(frame).includes('coding_completion_scope')) {
481
+ validateCodingAnswer(value, frame, violations);
482
+ }
483
+
484
+ return { ok: violations.length === 0, violations };
485
+ }
486
+
487
+ function validateTravelRecommendationAnswer(text, frame, violations) {
488
+ const activeLocation = frame.scope?.lodgingLocation || frame.scope?.destination || '';
489
+ const recommendationLike = RECOMMENDATION_RE.test(text) || LODGING_RE.test(text);
490
+ if (activeLocation && recommendationLike) {
491
+ const hasActive = containsComparable(text, activeLocation);
492
+ const inactiveHit = (frame.inactiveRelatedFrames || []).find((loc) => loc && containsComparable(text, loc));
493
+ if (inactiveHit && !hasActive) {
494
+ violations.push({
495
+ code: 'scope_drift',
496
+ message: `Answer appears to use inactive location "${inactiveHit}" instead of active location "${activeLocation}".`,
497
+ });
498
+ }
499
+ }
500
+ if ((frame.constraints || []).includes('include_supply_type:alternative_rentals')) {
501
+ if (!/\b(airbnb|vrbo|vacation rental|short[- ]term rental|home rental|condo|rental home|rental house|apartment rental)\b/i.test(text)) {
502
+ violations.push({
503
+ code: 'missing_allowed_supply_type',
504
+ message: 'User explicitly allowed Airbnb/alternative rentals, but the answer does not include that supply type.',
505
+ });
506
+ }
507
+ }
508
+ if ((frame.constraints || []).includes('requires_novel_recommendations')) {
509
+ const previous = (frame.previousRecommendations || []).filter(Boolean);
510
+ if (previous.length > 0) {
511
+ const repeated = previous.filter((name) => containsComparable(text, name));
512
+ const currentNames = extractRecommendationNames(text);
513
+ const newNames = currentNames.filter((name) => !previous.some((prev) => normalizeComparable(prev) === normalizeComparable(name)));
514
+ if (repeated.length >= Math.min(2, previous.length) && newNames.length < 2) {
515
+ violations.push({
516
+ code: 'not_novel_recommendations',
517
+ message: `User asked for changed recommendations, but the answer repeats prior candidates: ${repeated.slice(0, 4).join(', ')}.`,
518
+ });
519
+ }
520
+ }
521
+ }
522
+ if ((frame.constraints || []).includes('not_brand_limited:hilton')) {
523
+ const mentionsHilton = /\bhilton\b/i.test(text);
524
+ const hasNonHiltonAlternative = /\b(airbnb|vrbo|marriott|hyatt|ihg|independent|boutique|vacation rental|condo|rental)\b/i.test(text);
525
+ if (mentionsHilton && !hasNonHiltonAlternative) {
526
+ violations.push({
527
+ code: 'brand_limited',
528
+ message: 'User said the answer does not have to be Hilton-only, but the response remains brand-limited.',
529
+ });
530
+ }
531
+ }
532
+ }
533
+
534
+ function validateEmailActionAnswer(text, frame, violations) {
535
+ if (!(frame.constraints || []).includes('email_action:reply_to_original_thread')) return;
536
+ if (/\b(new email|separate email|fresh email|new thread)\b/i.test(text) && !/\boriginal thread|same thread|reply\b/i.test(text)) {
537
+ violations.push({
538
+ code: 'email_thread_drift',
539
+ message: 'User asked to reply/respond to the original email thread, but the answer describes a new email/thread.',
540
+ });
541
+ }
542
+ }
543
+
544
+ function validateCalendarActionAnswer(text, frame, violations) {
545
+ const calendars = frame.scope?.calendars || [];
546
+ if (calendars.length < 2) return;
547
+ for (const cal of calendars) {
548
+ if (!new RegExp(`\\b${escapeRegExp(cal)}\\b`, 'i').test(text)) {
549
+ violations.push({
550
+ code: 'calendar_target_missing',
551
+ message: `User requested calendar target "${cal}", but the final answer does not mention it.`,
552
+ });
553
+ }
554
+ }
555
+ }
556
+
557
+ function validateCodingAnswer(text, frame, violations) {
558
+ if (!(frame.constraints || []).includes('verification_required')) return;
559
+ const claimsCompletion = /\b(done|complete|completed|fixed|implemented)\b/i.test(text);
560
+ const mentionsVerificationEvidence = /\b(test(?:ed|s)?|verified|validated|checked|ran|screenshot|browser_screenshot|check_url|build|lint|typecheck|evidence)\b/i.test(text);
561
+ if (claimsCompletion && !mentionsVerificationEvidence) {
562
+ violations.push({
563
+ code: 'missing_verification_evidence',
564
+ message: 'User asked for verification, but the final completion claim does not mention validation evidence.',
565
+ });
566
+ }
567
+ }
568
+
569
+ function buildFrameRepairPrompt({ frame, violations, originalUserMessage } = {}) {
570
+ const issueLines = (violations || []).map((v) => `- ${v.code}: ${v.message}`).join('\n') || '- frame_mismatch';
571
+ const frameJson = JSON.stringify(frame || {}, null, 2);
572
+ return [
573
+ 'Your previous final answer failed Wall-E active-frame validation.',
574
+ 'Rewrite the final answer only. Do not mention this validator or internal frame.',
575
+ 'The latest user message and Active Conversation Frame are authoritative over older assistant replies, memories, artifacts, and neighboring sessions.',
576
+ '',
577
+ `Latest user message:\n${String(originalUserMessage || '').slice(0, 1200)}`,
578
+ '',
579
+ `Active Conversation Frame:\n${frameJson.slice(0, 4000)}`,
580
+ '',
581
+ `Violations:\n${issueLines}`,
582
+ '',
583
+ 'Return a corrected final answer that satisfies every violation. Keep the same response language as the latest user message.',
584
+ ].join('\n');
585
+ }
586
+
587
+ function containsComparable(text, needle) {
588
+ const source = normalizeComparable(text);
589
+ const target = normalizeComparable(needle);
590
+ if (!source || !target) return false;
591
+ if (source.includes(target)) return true;
592
+ if (/^[a-z]{3}$/.test(target)) return new RegExp(`\\b${escapeRegExp(target)}\\b`, 'i').test(String(text || ''));
593
+ return false;
594
+ }
595
+
596
+ function normalizeComparable(value) {
597
+ return String(value || '')
598
+ .toLowerCase()
599
+ .replace(/[^a-z0-9]+/g, ' ')
600
+ .trim();
601
+ }
602
+
603
+ function addUnique(list, value) {
604
+ const clean = typeof value === 'string' ? value.trim() : value;
605
+ if (!clean) return;
606
+ const key = normalizeComparable(clean);
607
+ if (!key) return;
608
+ if (!list.some((item) => normalizeComparable(item) === key)) list.push(clean);
609
+ }
610
+
611
+ function uniqueByLower(value, index, array) {
612
+ const key = normalizeComparable(value);
613
+ return key && array.findIndex((item) => normalizeComparable(item) === key) === index;
614
+ }
615
+
616
+ function escapeRegExp(value) {
617
+ return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
618
+ }
619
+
620
+ module.exports = {
621
+ FRAME_SCHEMA_VERSION,
622
+ buildConversationRuntime,
623
+ buildConversationFramePrompt,
624
+ buildFrameRepairPrompt,
625
+ deriveConversationFrame,
626
+ extractRecommendationNames,
627
+ normalizeSemanticMessages,
628
+ parseSessionMetadata,
629
+ validateFrameAnswer,
630
+ };