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
@@ -6,12 +6,22 @@ const DEFAULT_FEEDBACK_URL = process.env.CTM_FEEDBACK_URL
6
6
  || process.env.WALLE_FEEDBACK_URL
7
7
  || 'https://create-walle.fly.dev/api/wall-e/feedback/submit';
8
8
 
9
+ const DEFAULT_TELEMETRY_URL = process.env.CTM_TELEMETRY_URL
10
+ || process.env.WALLE_TELEMETRY_URL
11
+ || 'https://create-walle.fly.dev/api/wall-e/telemetry/ingest';
12
+
9
13
  function endpoint() {
10
14
  return process.env.CTM_FEEDBACK_URL
11
15
  || process.env.WALLE_FEEDBACK_URL
12
16
  || DEFAULT_FEEDBACK_URL;
13
17
  }
14
18
 
19
+ function telemetryEndpoint() {
20
+ return process.env.CTM_TELEMETRY_URL
21
+ || process.env.WALLE_TELEMETRY_URL
22
+ || DEFAULT_TELEMETRY_URL;
23
+ }
24
+
15
25
  function buildEnvelope(feedback) {
16
26
  const envelope = {
17
27
  id: telemetry.getInstallId(),
@@ -26,11 +36,34 @@ function buildEnvelope(feedback) {
26
36
  return envelope;
27
37
  }
28
38
 
39
+ function buildTelemetryEnvelope(event, meta = {}) {
40
+ const envelope = {
41
+ id: telemetry.getInstallId(),
42
+ machine: telemetry.getMachineBucket(),
43
+ v: telemetry.getVersion(),
44
+ os: process.platform,
45
+ node: process.version,
46
+ events: [{
47
+ e: String(event || 'ctm_event'),
48
+ m: meta || {},
49
+ t: Date.now(),
50
+ }],
51
+ };
52
+ const user = telemetry.getTelemetryUser();
53
+ if (user) envelope.user = user;
54
+ return envelope;
55
+ }
56
+
29
57
  function isFeedbackDisabled() {
30
58
  const value = String(process.env.CTM_FEEDBACK || '').toLowerCase();
31
59
  return value === '0' || value === 'false';
32
60
  }
33
61
 
62
+ function isTelemetryDisabled() {
63
+ const value = String(process.env.CTM_TELEMETRY || '').toLowerCase();
64
+ return value === '0' || value === 'false';
65
+ }
66
+
34
67
  async function submitFeedback(feedback, options = {}) {
35
68
  if (isFeedbackDisabled()) {
36
69
  return { ok: false, skipped: true, reason: 'feedback_disabled' };
@@ -66,9 +99,48 @@ async function submitFeedback(feedback, options = {}) {
66
99
  }
67
100
  }
68
101
 
102
+ async function submitTelemetryEvent(event, meta = {}, options = {}) {
103
+ if (isTelemetryDisabled()) {
104
+ return { ok: false, skipped: true, reason: 'telemetry_disabled' };
105
+ }
106
+ const fetchImpl = options.fetchImpl || global.fetch;
107
+ if (typeof fetchImpl !== 'function') {
108
+ return { ok: false, skipped: true, reason: 'fetch_unavailable' };
109
+ }
110
+ const url = options.url || telemetryEndpoint();
111
+ const body = buildTelemetryEnvelope(event, meta);
112
+ const controller = typeof AbortController === 'function' ? new AbortController() : null;
113
+ let timer = null;
114
+ if (controller) {
115
+ timer = setTimeout(() => controller.abort(), Number(options.timeoutMs || 10000));
116
+ if (typeof timer.unref === 'function') timer.unref();
117
+ }
118
+ try {
119
+ const res = await fetchImpl(url, {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json' },
122
+ body: JSON.stringify(body),
123
+ signal: controller?.signal,
124
+ });
125
+ const json = await res.json().catch(() => ({}));
126
+ if (!res.ok || json.ok === false) {
127
+ return { ok: false, status: res.status, error: json.error || `HTTP ${res.status}` };
128
+ }
129
+ return { ok: true, status: res.status, ...json };
130
+ } catch (err) {
131
+ return { ok: false, error: err.name === 'AbortError' ? 'timeout' : err.message };
132
+ } finally {
133
+ if (timer) clearTimeout(timer);
134
+ }
135
+ }
136
+
69
137
  module.exports = {
70
138
  buildEnvelope,
139
+ buildTelemetryEnvelope,
71
140
  endpoint,
141
+ telemetryEndpoint,
72
142
  isFeedbackDisabled,
143
+ isTelemetryDisabled,
73
144
  submitFeedback,
145
+ submitTelemetryEvent,
74
146
  };
@@ -4,6 +4,25 @@ const relayStore = require('./remote-relay-store');
4
4
 
5
5
  const REPLAY_WINDOW_MS = 5 * 60 * 1000;
6
6
 
7
+ // AUTHORIZATION LAYERING (not a duplicate of lib/auth-rules.js).
8
+ //
9
+ // This is the LOCAL envelope relay (`POST /api/remote/messages`) that the phone
10
+ // uses for typed, idempotent message delivery with the durable submission ledger
11
+ // (ctm_remote_submissions). It is active and current — distinct from the
12
+ // deprecated *hosted* "Walle Relay" product in walle-relay-phone-access-design.md.
13
+ //
14
+ // Authorization is intentionally two-layered, at two different granularities:
15
+ // 1. lib/auth-rules.js gates the `/api/remote/messages` HTTP ROUTE
16
+ // (scope=respond) — the transport-level gate every request passes.
17
+ // 2. MESSAGE_REGISTRY below gates each ENVELOPE MESSAGE TYPE (scope, risk,
18
+ // step-up, enabled) — finer-grained policy for the action the envelope
19
+ // carries (e.g. approval.respond requires step-up; high-risk admin types
20
+ // are disabled). The route gate cannot express per-message-type policy, so
21
+ // this layer is additive defense-in-depth, not a second copy of the same
22
+ // rules.
23
+ // Keep both in sync conceptually: a message type that mutates session/agent
24
+ // state must require step-up here just as its WS/HTTP equivalent does in
25
+ // auth-rules.js.
7
26
  const MESSAGE_REGISTRY = Object.freeze({
8
27
  'instance.status': rule('read', 'low', false),
9
28
  'sessions.list': rule('read', 'low', false),
@@ -12,16 +31,18 @@ const MESSAGE_REGISTRY = Object.freeze({
12
31
  'wall_e.status': rule('read', 'low', false),
13
32
 
14
33
  'session.send_message': rule('respond', 'low', false),
34
+ 'session.submit_input': rule('respond', 'medium', false),
15
35
  'session.cancel_stream': rule('respond', 'medium', false),
16
36
  'session.prompt_history': rule('respond', 'medium', false),
37
+ 'session.select_choice': rule('respond', 'medium', false),
17
38
  'wall_e.send_message': rule('respond', 'low', false),
18
39
  'wall_e.set_model': rule('respond', 'low', false),
19
40
 
20
- 'approval.respond': rule('respond', 'high', true, { enabled: false }),
41
+ 'approval.respond': rule('respond', 'high', true),
21
42
  'session.kill': rule('admin', 'high', true, { enabled: false }),
22
43
  'session.close': rule('admin', 'high', true, { enabled: false }),
23
44
  'network.setup_change': rule('admin', 'high', true, { enabled: false }),
24
- 'ctm.restart': rule('admin', 'high', true, { enabled: false }),
45
+ 'ctm.restart': rule('admin', 'high', false, { enabled: false }),
25
46
  });
26
47
 
27
48
  function rule(scope, risk, stepUp, extra = {}) {
@@ -123,7 +144,11 @@ async function dispatchMessage(message, context = {}) {
123
144
  return handlers.wallEStatus ? handlers.wallEStatus(message.body) : { ok: true, status: 'unknown' };
124
145
  case 'session.send_message':
125
146
  return handlers.sendSessionMessage
126
- ? handlers.sendSessionMessage(sessionIdFromBody(message.body), String(message.body.text || message.body.message || ''))
147
+ ? handlers.sendSessionMessage(sessionIdFromBody(message.body), String(message.body.text || message.body.message || ''), message.body, message)
148
+ : safeError('handler_missing');
149
+ case 'session.submit_input':
150
+ return handlers.submitSessionInput
151
+ ? handlers.submitSessionInput(sessionIdFromBody(message.body), message.body, message)
127
152
  : safeError('handler_missing');
128
153
  case 'session.cancel_stream':
129
154
  return handlers.cancelSessionStream
@@ -136,14 +161,22 @@ async function dispatchMessage(message, context = {}) {
136
161
  ? handlers.navigatePromptHistory(sessionIdFromBody(message.body), direction, message.body)
137
162
  : safeError('handler_missing');
138
163
  }
164
+ case 'session.select_choice':
165
+ return handlers.selectChoice
166
+ ? handlers.selectChoice(sessionIdFromBody(message.body), message.body, message)
167
+ : safeError('handler_missing');
139
168
  case 'wall_e.send_message':
140
169
  return handlers.sendWalleMessage
141
- ? handlers.sendWalleMessage(sessionIdFromBody(message.body), String(message.body.text || message.body.message || ''), message.body)
170
+ ? handlers.sendWalleMessage(sessionIdFromBody(message.body), String(message.body.text || message.body.message || ''), message.body, message)
142
171
  : safeError('handler_missing');
143
172
  case 'wall_e.set_model':
144
173
  return handlers.setWalleModel
145
174
  ? handlers.setWalleModel(sessionIdFromBody(message.body), message.body)
146
175
  : safeError('handler_missing');
176
+ case 'approval.respond':
177
+ return handlers.respondApproval
178
+ ? handlers.respondApproval(sessionIdFromBody(message.body), String(message.body.decision || message.body.action || ''), message.body, message)
179
+ : safeError('handler_missing');
147
180
  default:
148
181
  return safeError('remote_action_not_enabled');
149
182
  }
@@ -69,8 +69,27 @@ function ensureRemoteRelaySchema(db) {
69
69
  created_at INTEGER NOT NULL
70
70
  );
71
71
 
72
+ CREATE TABLE IF NOT EXISTS ctm_remote_submissions (
73
+ message_id TEXT PRIMARY KEY,
74
+ session_id TEXT NOT NULL,
75
+ device_id TEXT,
76
+ message_type TEXT NOT NULL,
77
+ text_hash TEXT NOT NULL,
78
+ text_preview TEXT,
79
+ status TEXT NOT NULL,
80
+ result_json TEXT,
81
+ attempts INTEGER NOT NULL DEFAULT 0,
82
+ created_at INTEGER NOT NULL,
83
+ updated_at INTEGER NOT NULL,
84
+ accepted_at INTEGER,
85
+ expires_at INTEGER,
86
+ last_error TEXT
87
+ );
88
+
72
89
  CREATE INDEX IF NOT EXISTS idx_ctm_remote_audit_created ON ctm_remote_audit_log(created_at DESC);
73
90
  CREATE INDEX IF NOT EXISTS idx_ctm_remote_audit_device ON ctm_remote_audit_log(device_id, created_at DESC);
91
+ CREATE INDEX IF NOT EXISTS idx_ctm_remote_submissions_session ON ctm_remote_submissions(session_id, updated_at DESC);
92
+ CREATE INDEX IF NOT EXISTS idx_ctm_remote_submissions_status ON ctm_remote_submissions(status, updated_at DESC);
74
93
  `);
75
94
  }
76
95
 
@@ -218,6 +237,140 @@ function listAudit(db, options = {}) {
218
237
  }));
219
238
  }
220
239
 
240
+ function submissionTextHash(text) {
241
+ return crypto.createHash('sha256').update(String(text || '')).digest('hex').slice(0, 32);
242
+ }
243
+
244
+ function submissionTextPreview(text, limit = 240) {
245
+ return String(text || '').replace(/\s+/g, ' ').trim().slice(0, Math.max(16, Number(limit) || 240));
246
+ }
247
+
248
+ function submissionFromRow(row) {
249
+ if (!row) return null;
250
+ return {
251
+ message_id: row.message_id,
252
+ session_id: row.session_id,
253
+ device_id: row.device_id || '',
254
+ message_type: row.message_type,
255
+ text_hash: row.text_hash,
256
+ text_preview: row.text_preview || '',
257
+ status: row.status,
258
+ result: safeJsonParse(row.result_json, null),
259
+ attempts: Number(row.attempts || 0) || 0,
260
+ created_at: row.created_at,
261
+ updated_at: row.updated_at,
262
+ accepted_at: row.accepted_at || null,
263
+ expires_at: row.expires_at || null,
264
+ last_error: row.last_error || '',
265
+ };
266
+ }
267
+
268
+ function upsertRemoteSubmission(db, entry = {}, atMs = nowMs()) {
269
+ ensureRemoteRelaySchema(db);
270
+ const messageId = String(entry.messageId || entry.message_id || '').trim();
271
+ const sessionId = String(entry.sessionId || entry.session_id || '').trim();
272
+ const messageType = String(entry.messageType || entry.message_type || '').trim();
273
+ if (!messageId || !sessionId || !messageType) return null;
274
+ const text = String(entry.text || '');
275
+ const textHash = String(entry.textHash || entry.text_hash || submissionTextHash(text));
276
+ const textPreview = String(entry.textPreview || entry.text_preview || submissionTextPreview(text));
277
+ const status = String(entry.status || 'received');
278
+ const resultJson = JSON.stringify(entry.result || {});
279
+ const attempts = Math.max(0, Number(entry.attempts || 0) || 0);
280
+ const expiresAt = entry.expiresAt || entry.expires_at || null;
281
+ db.prepare(`
282
+ INSERT INTO ctm_remote_submissions
283
+ (message_id, session_id, device_id, message_type, text_hash, text_preview, status, result_json, attempts, created_at, updated_at, accepted_at, expires_at, last_error)
284
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
285
+ ON CONFLICT(message_id) DO UPDATE SET
286
+ session_id = excluded.session_id,
287
+ device_id = excluded.device_id,
288
+ message_type = excluded.message_type,
289
+ text_hash = excluded.text_hash,
290
+ text_preview = excluded.text_preview,
291
+ status = excluded.status,
292
+ result_json = excluded.result_json,
293
+ attempts = excluded.attempts,
294
+ updated_at = excluded.updated_at,
295
+ accepted_at = COALESCE(excluded.accepted_at, ctm_remote_submissions.accepted_at),
296
+ expires_at = excluded.expires_at,
297
+ last_error = excluded.last_error
298
+ `).run(
299
+ messageId,
300
+ sessionId,
301
+ String(entry.deviceId || entry.device_id || ''),
302
+ messageType,
303
+ textHash,
304
+ textPreview,
305
+ status,
306
+ resultJson,
307
+ attempts,
308
+ entry.createdAt || entry.created_at || atMs,
309
+ atMs,
310
+ entry.acceptedAt || entry.accepted_at || null,
311
+ expiresAt,
312
+ String(entry.lastError || entry.last_error || ''),
313
+ );
314
+ return getRemoteSubmission(db, messageId);
315
+ }
316
+
317
+ function updateRemoteSubmission(db, messageId, patch = {}, atMs = nowMs()) {
318
+ ensureRemoteRelaySchema(db);
319
+ const id = String(messageId || '').trim();
320
+ if (!id) return null;
321
+ const existing = getRemoteSubmission(db, id);
322
+ if (!existing) return null;
323
+ const next = {
324
+ status: String(patch.status || existing.status),
325
+ result: patch.result == null ? (existing.result || {}) : patch.result,
326
+ attempts: patch.attempts == null ? existing.attempts : Math.max(0, Number(patch.attempts || 0) || 0),
327
+ accepted_at: patch.acceptedAt || patch.accepted_at || existing.accepted_at || null,
328
+ expires_at: patch.expiresAt || patch.expires_at || existing.expires_at || null,
329
+ last_error: patch.lastError || patch.last_error || existing.last_error || '',
330
+ };
331
+ db.prepare(`
332
+ UPDATE ctm_remote_submissions
333
+ SET status = ?, result_json = ?, attempts = ?, updated_at = ?, accepted_at = ?, expires_at = ?, last_error = ?
334
+ WHERE message_id = ?
335
+ `).run(
336
+ next.status,
337
+ JSON.stringify(next.result || {}),
338
+ next.attempts,
339
+ atMs,
340
+ next.accepted_at,
341
+ next.expires_at,
342
+ next.last_error,
343
+ id,
344
+ );
345
+ return getRemoteSubmission(db, id);
346
+ }
347
+
348
+ function getRemoteSubmission(db, messageId) {
349
+ ensureRemoteRelaySchema(db);
350
+ return submissionFromRow(db.prepare('SELECT * FROM ctm_remote_submissions WHERE message_id = ?').get(String(messageId || '')));
351
+ }
352
+
353
+ function listRemoteSubmissions(db, options = {}) {
354
+ ensureRemoteRelaySchema(db);
355
+ const limit = Math.max(1, Math.min(200, Number(options.limit || 50)));
356
+ const sessionId = String(options.sessionId || options.session_id || '').trim();
357
+ const sinceMs = Math.max(0, Number(options.sinceMs || options.since_ms || 0) || 0);
358
+ const rows = sessionId
359
+ ? db.prepare(`
360
+ SELECT * FROM ctm_remote_submissions
361
+ WHERE session_id = ? AND updated_at >= ?
362
+ ORDER BY updated_at DESC
363
+ LIMIT ?
364
+ `).all(sessionId, sinceMs, limit)
365
+ : db.prepare(`
366
+ SELECT * FROM ctm_remote_submissions
367
+ WHERE updated_at >= ?
368
+ ORDER BY updated_at DESC
369
+ LIMIT ?
370
+ `).all(sinceMs, limit);
371
+ return rows.map(submissionFromRow).filter(Boolean);
372
+ }
373
+
221
374
  function buildRemoteStatus(db, options = {}) {
222
375
  const instance = getOrCreateInstance(db, options.instance || {});
223
376
  const relayUrl = normalizeUrl(options.relayUrl || instance.relay_url, DEFAULT_RELAY_URL);
@@ -259,10 +412,16 @@ module.exports = {
259
412
  ensureRemoteRelaySchema,
260
413
  getIdempotentResult,
261
414
  getOrCreateInstance,
415
+ getRemoteSubmission,
262
416
  insertAudit,
263
417
  listAudit,
418
+ listRemoteSubmissions,
264
419
  markConnected,
265
420
  randomId,
266
421
  storeIdempotentResult,
422
+ submissionTextHash,
423
+ submissionTextPreview,
267
424
  updateInstance,
425
+ updateRemoteSubmission,
426
+ upsertRemoteSubmission,
268
427
  };
@@ -0,0 +1,278 @@
1
+ 'use strict';
2
+
3
+ function stripAnsi(value) {
4
+ return String(value || '')
5
+ .replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '')
6
+ .replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '')
7
+ .replace(/\x1b[@-Z\\-_]/g, '');
8
+ }
9
+
10
+ function normalizeSubmissionText(text) {
11
+ return String(text || '').replace(/\r\n?/g, '\n').replace(/\s+/g, ' ').trim();
12
+ }
13
+
14
+ const TERMINAL_BRACKETED_PASTE_START = '\x1b[200~';
15
+ const TERMINAL_BRACKETED_PASTE_END = '\x1b[201~';
16
+
17
+ function bracketedPastePayload(text) {
18
+ return `${TERMINAL_BRACKETED_PASTE_START}${String(text || '').replace(/\x1b/g, '')}${TERMINAL_BRACKETED_PASTE_END}`;
19
+ }
20
+
21
+ // Decide the exact bytes CTM writes into the PTY for a phone reply. Terminal
22
+ // line editors (Claude/Codex/Gemini/opencode) enable bracketed-paste mode; a
23
+ // real terminal wraps pasted multi-line text in ESC[200~ … ESC[201~ so embedded
24
+ // newlines stay literal text instead of being interpreted as submit/newline
25
+ // keypresses that fragment the message and leave it stuck in the composer.
26
+ // Single-line replies and plain shells are written verbatim (the proven path).
27
+ function remoteSubmissionWritePayload(text, options = {}) {
28
+ const normalized = String(text || '');
29
+ if (!normalized) return '';
30
+ if (normalized.includes('\n') && options.bracketedPasteCapable) {
31
+ return bracketedPastePayload(normalized);
32
+ }
33
+ return normalized;
34
+ }
35
+
36
+ function submissionTextMatches(candidate, submitted) {
37
+ const left = normalizeSubmissionText(candidate);
38
+ const right = normalizeSubmissionText(submitted);
39
+ if (!left || !right) return false;
40
+ if (left === right) return true;
41
+ if (right.length >= 80 && left.includes(right)) return true;
42
+ if (left.length >= 80 && right.includes(left)) return true;
43
+ return false;
44
+ }
45
+
46
+ function promptMessageText(message) {
47
+ if (message == null) return '';
48
+ if (typeof message === 'string') return message;
49
+ return message.text || message.content || message.message || '';
50
+ }
51
+
52
+ function timestampMs(value) {
53
+ if (value == null || value === '') return 0;
54
+ if (typeof value === 'number' && Number.isFinite(value)) {
55
+ return value > 0 && value < 100000000000 ? value * 1000 : value;
56
+ }
57
+ const text = String(value || '').trim();
58
+ if (!text) return 0;
59
+ if (/^\d+(?:\.\d+)?$/.test(text)) {
60
+ const n = Number(text);
61
+ return n > 0 && n < 100000000000 ? n * 1000 : n;
62
+ }
63
+ const parsed = Date.parse(text);
64
+ return Number.isFinite(parsed) ? parsed : 0;
65
+ }
66
+
67
+ function promptMessageTimestampMs(message) {
68
+ if (!message || typeof message !== 'object') return 0;
69
+ return timestampMs(message.timestamp || message.created_at || message.createdAt || message.time || message.ts);
70
+ }
71
+
72
+ function durablePromptMatch(messages, submitted, options = {}) {
73
+ const minTimestampMs = Math.max(0, Number(options.minTimestampMs || options.min_timestamp_ms || 0) || 0);
74
+ const toleranceMs = Math.max(0, Number(options.timestampToleranceMs ?? options.timestamp_tolerance_ms ?? 0) || 0);
75
+ const requireFreshTimestamp = minTimestampMs > 0 && options.requireFreshTimestamp !== false;
76
+ for (const message of (Array.isArray(messages) ? messages : [])) {
77
+ const text = promptMessageText(message);
78
+ if (!submissionTextMatches(text, submitted)) continue;
79
+ const messageTimestampMs = promptMessageTimestampMs(message);
80
+ if (requireFreshTimestamp) {
81
+ if (!messageTimestampMs) continue;
82
+ if (messageTimestampMs + toleranceMs < minTimestampMs) continue;
83
+ }
84
+ return {
85
+ matched: true,
86
+ text,
87
+ timestampMs: messageTimestampMs,
88
+ };
89
+ }
90
+ return {
91
+ matched: false,
92
+ text: '',
93
+ timestampMs: 0,
94
+ };
95
+ }
96
+
97
+ function promptSuffixMatchesSubmission(candidate, submitted) {
98
+ const left = normalizeSubmissionText(candidate);
99
+ const right = normalizeSubmissionText(submitted);
100
+ if (!left || !right) return false;
101
+ if (left === right) return true;
102
+ if (left.endsWith(right)) return true;
103
+ return submissionTextMatches(left, right);
104
+ }
105
+
106
+ function normalizeTerminalRows(rows) {
107
+ return (Array.isArray(rows) ? rows : [])
108
+ .map((row) => {
109
+ const source = typeof row === 'string' ? { text: row } : (row || {});
110
+ return {
111
+ text: stripAnsi(String(source.text || source.line || '')).replace(/\u00a0/g, ' ').replace(/\s+$/g, ''),
112
+ wrapped: !!(source.wrapped || source.isWrapped),
113
+ };
114
+ })
115
+ .filter((row) => row.text.trim());
116
+ }
117
+
118
+ function terminalRowsText(rows) {
119
+ return normalizeTerminalRows(rows).map((row) => row.text).join('\n');
120
+ }
121
+
122
+ function stripPromptPrefix(text) {
123
+ return String(text || '')
124
+ .replace(/^\s*[›>]\s*/, '')
125
+ .replace(/^\s*[$#]\s+/, '')
126
+ .replace(/^[^\s]{1,80}\s+[$#]\s+/, '');
127
+ }
128
+
129
+ function terminalRowsPromptText(rows) {
130
+ return normalizeTerminalRows(rows).map((row) => stripPromptPrefix(row.text)).join('\n');
131
+ }
132
+
133
+ function isPromptStartLine(text) {
134
+ const t = String(text || '').trim();
135
+ if (!t) return false;
136
+ if (/^[›>]\s*/.test(t)) return true;
137
+ if (/^[$#]\s+\S/.test(t)) return true;
138
+ return /^[^\s]{1,80}\s+[$#]\s+\S/.test(t);
139
+ }
140
+
141
+ function isAgentProgressLine(text) {
142
+ const t = String(text || '').trim();
143
+ if (!t) return false;
144
+ return /^(?:Working|Thinking|Explored|Ran|Read|Update|Edit|Write|Bash|Called|Context compacted|Result:|Commit:|Error:|Failed:|✓|✔|✗|✘|[-*]\s+\w)/i.test(t) ||
145
+ /\b(?:esc to interrupt|tokens?|tool block|tool call|ctrl\+o to expand|almost done thinking)\b/i.test(t);
146
+ }
147
+
148
+ function isTerminalStatusLine(text) {
149
+ const t = String(text || '').trim();
150
+ if (!t) return false;
151
+ if (/^(?:gpt|claude|gemini|deepseek|kimi|qwen|llama|mistral|codex)[\w.-]*\s+.*(?:·|•)\s*(?:[~./]|\/)/i.test(t)) return true;
152
+ return false;
153
+ }
154
+
155
+ function activePromptRowsFromSuffix(suffixRows, submittedText = '') {
156
+ const promptRows = [];
157
+ const submitted = normalizeSubmissionText(submittedText);
158
+ for (let idx = 0; idx < suffixRows.length; idx += 1) {
159
+ const row = suffixRows[idx];
160
+ if (idx === 0 || row.wrapped) {
161
+ promptRows.push(row);
162
+ continue;
163
+ }
164
+ const text = row.text.trim();
165
+ if (isPromptStartLine(text) || isAgentProgressLine(text) || isTerminalStatusLine(text)) break;
166
+ if (submitted) {
167
+ const current = normalizeSubmissionText(promptRows.map((r) => stripPromptPrefix(r.text)).join('\n'));
168
+ const candidate = normalizeSubmissionText([...promptRows, row].map((r) => stripPromptPrefix(r.text)).join('\n'));
169
+ if (candidate && candidate.length > current.length && submitted.startsWith(candidate)) {
170
+ promptRows.push(row);
171
+ continue;
172
+ }
173
+ }
174
+ break;
175
+ }
176
+ return promptRows;
177
+ }
178
+
179
+ function terminalSubmittedPromptState(rows, submittedText) {
180
+ const cleanRows = normalizeTerminalRows(rows);
181
+ let submittedVisible =
182
+ submissionTextMatches(cleanRows.map((row) => row.text).join('\n'), submittedText) ||
183
+ submissionTextMatches(cleanRows.map((row) => stripPromptPrefix(row.text)).join('\n'), submittedText);
184
+ let lastPromptIndex = -1;
185
+ for (let idx = 0; idx < cleanRows.length; idx += 1) {
186
+ if (isPromptStartLine(cleanRows[idx].text)) lastPromptIndex = idx;
187
+ }
188
+ if (lastPromptIndex < 0) {
189
+ return {
190
+ submittedVisible,
191
+ activePromptVisible: false,
192
+ agentProgressAfterPrompt: false,
193
+ lastPromptIndex,
194
+ rowCount: cleanRows.length,
195
+ };
196
+ }
197
+
198
+ const suffixRows = cleanRows.slice(lastPromptIndex);
199
+ const promptRows = activePromptRowsFromSuffix(suffixRows, submittedText);
200
+ const promptContainsSubmitted =
201
+ promptSuffixMatchesSubmission(promptRows.map((row) => row.text).join('\n'), submittedText) ||
202
+ promptSuffixMatchesSubmission(promptRows.map((row) => stripPromptPrefix(row.text)).join('\n'), submittedText);
203
+ submittedVisible = submittedVisible || promptContainsSubmitted;
204
+ let agentProgressAfterPrompt = false;
205
+ if (promptContainsSubmitted) {
206
+ for (let idx = promptRows.length; idx < suffixRows.length; idx += 1) {
207
+ const row = suffixRows[idx];
208
+ const text = row.text.trim();
209
+ if (!text || isPromptStartLine(text)) continue;
210
+ if (isAgentProgressLine(text)) {
211
+ agentProgressAfterPrompt = true;
212
+ break;
213
+ }
214
+ if (isTerminalStatusLine(text)) continue;
215
+ if (!row.wrapped) {
216
+ agentProgressAfterPrompt = true;
217
+ break;
218
+ }
219
+ }
220
+ }
221
+
222
+ return {
223
+ submittedVisible,
224
+ activePromptVisible: !!(promptContainsSubmitted && !agentProgressAfterPrompt),
225
+ agentProgressAfterPrompt,
226
+ lastPromptIndex,
227
+ rowCount: cleanRows.length,
228
+ };
229
+ }
230
+
231
+ function terminalSubmissionAcceptedByMarker(options = {}) {
232
+ const markerNow = String(options.markerNow || '');
233
+ const beforeEnterMarker = String(options.beforeEnterMarker || '');
234
+ if (beforeEnterMarker) {
235
+ if (markerNow && markerNow !== beforeEnterMarker) {
236
+ return {
237
+ accepted: true,
238
+ reason: 'terminal_output_advanced_after_enter',
239
+ };
240
+ }
241
+ return {
242
+ accepted: false,
243
+ reason: '',
244
+ };
245
+ }
246
+
247
+ const beforeMarker = String(options.beforeMarker || '');
248
+ if (!options.promptStillVisible && beforeMarker && markerNow && markerNow !== beforeMarker) {
249
+ return {
250
+ accepted: true,
251
+ reason: 'terminal_output_advanced',
252
+ };
253
+ }
254
+
255
+ return {
256
+ accepted: false,
257
+ reason: '',
258
+ };
259
+ }
260
+
261
+ module.exports = {
262
+ bracketedPastePayload,
263
+ remoteSubmissionWritePayload,
264
+ isAgentProgressLine,
265
+ isPromptStartLine,
266
+ isTerminalStatusLine,
267
+ normalizeSubmissionText,
268
+ normalizeTerminalRows,
269
+ durablePromptMatch,
270
+ stripAnsi,
271
+ stripPromptPrefix,
272
+ submissionTextMatches,
273
+ timestampMs,
274
+ terminalRowsPromptText,
275
+ terminalRowsText,
276
+ terminalSubmissionAcceptedByMarker,
277
+ terminalSubmittedPromptState,
278
+ };