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
@@ -49,6 +49,38 @@ function splitPathAndLine(input, fallbackLine = 1) {
49
49
  return { rawPath: raw, line };
50
50
  }
51
51
 
52
+ function realpathOrResolve(value) {
53
+ const resolved = path.resolve(value);
54
+ try { return fs.realpathSync.native ? fs.realpathSync.native(resolved) : fs.realpathSync(resolved); }
55
+ catch { return resolved; }
56
+ }
57
+
58
+ function normalizeBaseCwd(cwd) {
59
+ if (!cwd) return '';
60
+ const raw = String(cwd || '').trim();
61
+ if (!raw || !path.isAbsolute(raw)) return '';
62
+ try {
63
+ const stat = fs.statSync(raw);
64
+ const dir = stat.isDirectory() ? raw : path.dirname(raw);
65
+ return realpathOrResolve(dir);
66
+ } catch {
67
+ return '';
68
+ }
69
+ }
70
+
71
+ function resolveInputPath(rawPath, options = {}) {
72
+ if (!rawPath) return '';
73
+ if (/^~(?:\/|$)/.test(rawPath)) {
74
+ const home = options.home || process.env.HOME || os.homedir();
75
+ if (!home) return '';
76
+ return path.resolve(home, rawPath === '~' ? '' : rawPath.slice(2));
77
+ }
78
+ if (path.isAbsolute(rawPath)) return path.resolve(rawPath);
79
+ const baseCwd = normalizeBaseCwd(options.cwd);
80
+ if (!baseCwd) throw httpError('relative document path requires a session cwd', 400, 'EINVAL');
81
+ return path.resolve(baseCwd, rawPath);
82
+ }
83
+
52
84
  function projectRootFor(filePath) {
53
85
  const cwd = fs.statSync(filePath).isDirectory() ? filePath : path.dirname(filePath);
54
86
  try {
@@ -62,12 +94,102 @@ function projectRootFor(filePath) {
62
94
  }
63
95
  }
64
96
 
97
+ function reviewRootFor(filePath, options = {}) {
98
+ const gitRoot = projectRootFor(filePath);
99
+ if (gitRoot) return gitRoot;
100
+
101
+ const realPath = realpathOrResolve(filePath);
102
+ const baseCwd = normalizeBaseCwd(options.cwd);
103
+ if (baseCwd && isInside(baseCwd, realPath)) return baseCwd;
104
+ return realpathOrResolve(path.dirname(realPath));
105
+ }
106
+
107
+ function normalizeRelativeDocRef(rawPath) {
108
+ let value = String(rawPath || '').trim().replace(/\\/g, '/');
109
+ while (value.startsWith('./')) value = value.slice(2);
110
+ return value.replace(/^\/+/, '');
111
+ }
112
+
113
+ function trackedDocumentPaths(projectRoot, options = {}) {
114
+ const maxPaths = Math.max(1, Number(options.maxSearchPaths) || 10000);
115
+ let output = '';
116
+ try {
117
+ output = execFileSync('git', ['ls-files', '-z', '--cached', '--others', '--exclude-standard'], {
118
+ cwd: projectRoot,
119
+ encoding: 'utf8',
120
+ stdio: ['ignore', 'pipe', 'ignore'],
121
+ maxBuffer: Math.max(1024 * 1024, Number(options.maxSearchBytes) || 8 * 1024 * 1024),
122
+ });
123
+ } catch {
124
+ return [];
125
+ }
126
+ const paths = output.split('\0').filter(Boolean);
127
+ const docPaths = paths.filter((entry) => SUPPORTED_EXTENSIONS.has(path.extname(entry).toLowerCase()));
128
+ if (docPaths.length > maxPaths) return [];
129
+ return docPaths;
130
+ }
131
+
132
+ function projectDocumentCandidates(rawPath, baseCwd, options = {}) {
133
+ const normalized = normalizeRelativeDocRef(rawPath);
134
+ if (!normalized || path.isAbsolute(rawPath) || /^~(?:\/|$)/.test(rawPath)) return [];
135
+ if (!SUPPORTED_EXTENSIONS.has(path.extname(normalized).toLowerCase())) return [];
136
+ const projectRoot = projectRootFor(baseCwd);
137
+ if (!projectRoot) return [];
138
+ let realProjectRoot = '';
139
+ try {
140
+ realProjectRoot = fs.realpathSync.native ? fs.realpathSync.native(projectRoot) : fs.realpathSync(projectRoot);
141
+ } catch {
142
+ realProjectRoot = path.resolve(projectRoot);
143
+ }
144
+ const roots = allowedRoots(options);
145
+ const seen = new Set();
146
+ return trackedDocumentPaths(projectRoot, options)
147
+ .filter((entry) => entry === normalized || entry.endsWith('/' + normalized))
148
+ .filter((entry) => {
149
+ if (seen.has(entry)) return false;
150
+ seen.add(entry);
151
+ return true;
152
+ })
153
+ .map((entry) => {
154
+ const candidatePath = path.join(projectRoot, entry);
155
+ try {
156
+ const stat = fs.statSync(candidatePath);
157
+ if (!stat.isFile()) return null;
158
+ const realPath = fs.realpathSync.native ? fs.realpathSync.native(candidatePath) : fs.realpathSync(candidatePath);
159
+ if (!isInside(realProjectRoot, realPath)) return null;
160
+ if (!roots.some((root) => isInside(root, realPath))) return null;
161
+ return {
162
+ path: realPath,
163
+ relativePath: path.relative(realProjectRoot, realPath),
164
+ projectRoot: realProjectRoot,
165
+ };
166
+ } catch {
167
+ return null;
168
+ }
169
+ })
170
+ .filter(Boolean);
171
+ }
172
+
173
+ function resolveUniqueProjectDocument(rawPath, baseCwd, options = {}) {
174
+ const candidates = projectDocumentCandidates(rawPath, baseCwd, options);
175
+ if (candidates.length === 1) return candidates[0].path;
176
+ if (candidates.length > 1) {
177
+ const err = httpError(
178
+ `multiple documents match ${String(rawPath || '').trim()}`,
179
+ 409,
180
+ 'EAMBIGUOUS'
181
+ );
182
+ err.candidates = candidates.slice(0, 50);
183
+ throw err;
184
+ }
185
+ return '';
186
+ }
187
+
65
188
  function resolveDocumentTarget(input, options = {}) {
66
189
  const { rawPath, line } = splitPathAndLine(input, options.line || 1);
67
190
  if (!rawPath) throw httpError('path required', 400, 'EINVAL');
68
- if (!path.isAbsolute(rawPath)) throw httpError('absolute path required', 400, 'EINVAL');
69
191
 
70
- const resolved = path.resolve(rawPath);
192
+ let resolved = resolveInputPath(rawPath, options);
71
193
  let realPath = '';
72
194
  try {
73
195
  const stat = fs.statSync(resolved);
@@ -78,7 +200,19 @@ function resolveDocumentTarget(input, options = {}) {
78
200
  realPath = fs.realpathSync.native ? fs.realpathSync.native(resolved) : fs.realpathSync(resolved);
79
201
  } catch (err) {
80
202
  if (err.statusCode) throw err;
81
- throw httpError('document not found', 404, 'ENOENT');
203
+ const baseCwd = normalizeBaseCwd(options.cwd);
204
+ const found = baseCwd ? resolveUniqueProjectDocument(rawPath, baseCwd, options) : '';
205
+ if (found) {
206
+ resolved = found;
207
+ const stat = fs.statSync(resolved);
208
+ if (!stat.isFile()) throw httpError('document path must be a file', 400, 'EINVAL');
209
+ if (stat.size > (options.maxBytes || DEFAULT_MAX_BYTES)) {
210
+ throw httpError('document too large for review', 413, 'ETOOLARGE');
211
+ }
212
+ realPath = fs.realpathSync.native ? fs.realpathSync.native(resolved) : fs.realpathSync(resolved);
213
+ } else {
214
+ throw httpError('document not found', 404, 'ENOENT');
215
+ }
82
216
  }
83
217
 
84
218
  const roots = allowedRoots(options);
@@ -91,11 +225,10 @@ function resolveDocumentTarget(input, options = {}) {
91
225
  throw httpError('unsupported document type', 400, 'EINVAL');
92
226
  }
93
227
 
94
- const projectRoot = projectRootFor(realPath);
95
- if (!projectRoot) throw httpError('document must be inside a git project', 403, 'ENOPROJECT');
228
+ const projectRoot = reviewRootFor(realPath, options);
96
229
  const realProjectRoot = fs.realpathSync.native ? fs.realpathSync.native(projectRoot) : fs.realpathSync(projectRoot);
97
230
  if (!isInside(realProjectRoot, realPath)) {
98
- throw httpError('document is outside the project root', 403, 'EACCES');
231
+ throw httpError('document is outside the review root', 403, 'EACCES');
99
232
  }
100
233
 
101
234
  return {
@@ -247,4 +380,6 @@ module.exports = {
247
380
  parseDocumentPathname,
248
381
  resolveDocumentTarget,
249
382
  splitPathAndLine,
383
+ projectDocumentCandidates,
384
+ resolveUniqueProjectDocument,
250
385
  };
@@ -0,0 +1,152 @@
1
+ 'use strict';
2
+
3
+ // Helpers for the Permission "Needs Review" surface: mask secrets so the review UI
4
+ // never re-spills real credentials, group escalation rows by command type so a huge
5
+ // pile collapses to a handful of reviewable types, and suggest a narrow allow rule.
6
+
7
+ function _maskValue(v, keep = 4) {
8
+ const s = String(v || '');
9
+ if (s.length <= keep + 2) return '••••';
10
+ return s.slice(0, keep) + '••••' + s.slice(-2);
11
+ }
12
+
13
+ // Mask AWS keys, KEY/SECRET/TOKEN/PASSWORD assignments, bearer + common provider
14
+ // tokens in any command/context text shown to the client. Best-effort, not crypto.
15
+ function maskSecrets(text) {
16
+ let out = String(text || '');
17
+ out = out.replace(/\bAKIA[0-9A-Z]{12,}\b/g, (m) => m.slice(0, 4) + '••••' + m.slice(-2));
18
+ out = out.replace(
19
+ /\b([A-Za-z0-9_]*(?:SECRET|TOKEN|PASSWORD|PASSWD|API[_-]?KEY|ACCESS[_-]?KEY|PRIVATE[_-]?KEY|CREDENTIAL)[A-Za-z0-9_]*)(\s*=\s*)(["']?)([^\s"'&;|]{4,})\3/gi,
20
+ (m, name, eq, q, val) => `${name}${eq}${q}${_maskValue(val)}${q}`,
21
+ );
22
+ out = out.replace(/\b(Bearer\s+)([A-Za-z0-9._-]{12,})/gi, (m, p, t) => p + _maskValue(t));
23
+ out = out.replace(
24
+ /\b((?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{16,}|sk-[A-Za-z0-9]{16,}|xox[baprs]-[A-Za-z0-9-]{10,})\b/g,
25
+ (m) => _maskValue(m, 6),
26
+ );
27
+ return out;
28
+ }
29
+
30
+ // Strip leading shell environment-variable assignments (FOO=bar, including
31
+ // secret VALUES) from a command. "AWS_ACCESS_KEY_ID=AKIA… AWS_SECRET_ACCESS_KEY=…
32
+ // aws ecr get-login" → { rest: "aws ecr get-login", envNames: ["AWS_ACCESS_KEY_ID",
33
+ // "AWS_SECRET_ACCESS_KEY"] }. Without this, the (varying) secret value lands in the
34
+ // grouping key and every invocation becomes its own one-off "type".
35
+ function stripEnvAssignments(text) {
36
+ const tokens = String(text || '').trim().split(/\s+/).filter(Boolean);
37
+ let i = 0;
38
+ const envNames = [];
39
+ while (i < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[i])) {
40
+ envNames.push(tokens[i].slice(0, tokens[i].indexOf('=')));
41
+ i += 1;
42
+ }
43
+ return { rest: tokens.slice(i).join(' '), envNames };
44
+ }
45
+
46
+ // The "command head" is the command + its subcommands, up to the first flag or
47
+ // normalized-arg placeholder — e.g. "aws eks update-kubeconfig --name octo" and
48
+ // "… --name prod" both reduce to "aws eks update-kubeconfig". This is the grouping
49
+ // TYPE: arg variations collapse, but distinct operations stay distinct. Leading
50
+ // env-credential prefixes are stripped first so they don't fragment the grouping.
51
+ // Skip a leading run of `cd <path>` / `echo <arg>` framing token-pairs so the head
52
+ // is the operative command, not navigation/logging. Defense-in-depth: new signatures
53
+ // are already framing-stripped upstream (approval-agent `_stripFramingPrefix`), but
54
+ // OLD escalation rows persisted before that fix still lead with `cd`. Keeps the
55
+ // original tokens if framing was the ENTIRE command (a bare `cd x` stays `cd`).
56
+ function _stripLeadingFramingTokens(tokens) {
57
+ let i = 0;
58
+ while (i + 1 < tokens.length && (tokens[i] === 'cd' || tokens[i] === 'echo')) i += 2;
59
+ const rest = tokens.slice(i);
60
+ return rest.length ? rest : tokens;
61
+ }
62
+ function commandHead(text) {
63
+ const s = String(text || '').trim();
64
+ if (!s || s === 'unknown') return '';
65
+ const { rest, envNames } = stripEnvAssignments(s);
66
+ const head = [];
67
+ for (const t of _stripLeadingFramingTokens(rest.split(/\s+/).filter(Boolean))) {
68
+ if (!t) continue;
69
+ if (t.startsWith('-')) break; // flag
70
+ if (/^[<{[(].*[>}\])]$/.test(t)) break; // <arg>/<path>/<num> placeholder
71
+ if (/[/@:=]/.test(t) && head.length >= 1) break; // a path/url/image arg after the verb
72
+ head.push(t);
73
+ if (head.length >= 4) break;
74
+ }
75
+ if (head.length) return head.join(' ').trim();
76
+ // The command was nothing but env assignments — group by the sorted var NAMES so
77
+ // distinct secret VALUES still collapse into one reviewable type.
78
+ if (envNames.length) return 'env ' + envNames.slice().sort().join(' ');
79
+ return '';
80
+ }
81
+
82
+ // Display title for a group: prefer the command with any leading env-credential
83
+ // prefix stripped so the row reads "aws ecr get-login", not the masked secret.
84
+ function cleanTitle(text) {
85
+ const raw = String(text || '').trim();
86
+ if (!raw || raw === 'Bash command') return '';
87
+ const { rest } = stripEnvAssignments(raw);
88
+ return rest || raw;
89
+ }
90
+
91
+ // Group escalation rows (db approval_decisions where decision='escalated') by
92
+ // command type. keyFn(row) -> a stable grouping signature; defaults to the row's
93
+ // command_signature, falling back to summary/tool. Returns groups sorted lastSeen↓.
94
+ function groupEscalations(rows, keyFn) {
95
+ const groups = new Map();
96
+ for (const row of (Array.isArray(rows) ? rows : [])) {
97
+ const base = String(row.command_signature || (keyFn ? keyFn(row) : '') || row.command_summary || row.tool_name || '').trim();
98
+ const key = commandHead(base) || base || 'unknown';
99
+ let g = groups.get(key);
100
+ if (!g) {
101
+ g = {
102
+ key,
103
+ title: cleanTitle(row.command_summary) || (key && key !== 'unknown' && !key.startsWith('env ') ? key : (row.tool_name || 'command')),
104
+ toolName: row.tool_name || '',
105
+ riskLevel: row.risk_level || 'medium',
106
+ count: 0,
107
+ ids: [],
108
+ sessions: new Set(),
109
+ firstSeen: row.created_at || '',
110
+ lastSeen: row.created_at || '',
111
+ reasoning: row.reasoning || '',
112
+ sampleContext: row.full_context || '',
113
+ };
114
+ groups.set(key, g);
115
+ }
116
+ g.count += 1;
117
+ g.ids.push(row.id);
118
+ if (row.session_id) g.sessions.add(row.session_id);
119
+ if (row.created_at && (!g.lastSeen || row.created_at > g.lastSeen)) {
120
+ g.lastSeen = row.created_at;
121
+ g.reasoning = row.reasoning || g.reasoning;
122
+ g.sampleContext = row.full_context || g.sampleContext;
123
+ }
124
+ if (row.created_at && (!g.firstSeen || row.created_at < g.firstSeen)) g.firstSeen = row.created_at;
125
+ if (row.risk_level === 'high') g.riskLevel = 'high';
126
+ }
127
+ return Array.from(groups.values())
128
+ .map((g) => ({
129
+ key: g.key,
130
+ title: maskSecrets(g.title),
131
+ toolName: g.toolName,
132
+ riskLevel: g.riskLevel,
133
+ count: g.count,
134
+ ids: g.ids,
135
+ sessionCount: g.sessions.size,
136
+ firstSeen: g.firstSeen,
137
+ lastSeen: g.lastSeen,
138
+ reasoning: g.reasoning,
139
+ sampleCommand: maskSecrets(String(g.sampleContext || '').slice(0, 1200)),
140
+ suggestedRule: suggestAllowRule(g),
141
+ }))
142
+ .sort((a, b) => String(b.lastSeen || '').localeCompare(String(a.lastSeen || '')));
143
+ }
144
+
145
+ // Suggest a NARROW Claude-syntax allow pattern from a group's signature/title.
146
+ // "aws eks update-kubeconfig <arg>" -> "Bash(aws eks update-kubeconfig:*)".
147
+ function suggestAllowRule(group) {
148
+ const prefix = commandHead(String((group && (group.key || group.title)) || '').trim());
149
+ return prefix ? `Bash(${prefix}:*)` : '';
150
+ }
151
+
152
+ module.exports = { maskSecrets, groupEscalations, suggestAllowRule, commandHead };
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_HTTP_FORCE_AFTER_MS = 1000;
4
+ const DEFAULT_HTTP_TIMEOUT_MS = 2000;
5
+ const DEFAULT_WS_GRACE_MS = 750;
6
+
7
+ function _unref(timer) {
8
+ if (timer && typeof timer.unref === 'function') timer.unref();
9
+ return timer;
10
+ }
11
+
12
+ function installHttpConnectionTracking(listenerServers = []) {
13
+ for (const listener of listenerServers) {
14
+ const server = listener && listener.server;
15
+ if (!server || server.__ctmShutdownTrackingInstalled) continue;
16
+ const sockets = new Set();
17
+ server.on('connection', (socket) => {
18
+ sockets.add(socket);
19
+ socket.on('close', () => sockets.delete(socket));
20
+ });
21
+ listener.trackedSockets = sockets;
22
+ Object.defineProperty(server, '__ctmShutdownTrackingInstalled', { value: true });
23
+ }
24
+ }
25
+
26
+ function stopAcceptingHttpRequests(listenerServers = [], options = {}) {
27
+ const forceAfterMs = Number.isFinite(Number(options.forceAfterMs))
28
+ ? Math.max(0, Number(options.forceAfterMs))
29
+ : DEFAULT_HTTP_FORCE_AFTER_MS;
30
+ const timeoutMs = Number.isFinite(Number(options.timeoutMs))
31
+ ? Math.max(forceAfterMs, Number(options.timeoutMs))
32
+ : Math.max(DEFAULT_HTTP_TIMEOUT_MS, forceAfterMs);
33
+ const logger = options.logger || console;
34
+
35
+ return Promise.all(listenerServers.map((listener) => new Promise((resolve) => {
36
+ const server = listener && listener.server;
37
+ if (!server) return resolve({ id: listener?.id, closed: true, missing: true });
38
+
39
+ let settled = false;
40
+ const done = (result = {}) => {
41
+ if (settled) return;
42
+ settled = true;
43
+ listener.started = false;
44
+ if (forceTimer) clearTimeout(forceTimer);
45
+ if (timeoutTimer) clearTimeout(timeoutTimer);
46
+ resolve({ id: listener.id, ...result });
47
+ };
48
+
49
+ let forceTimer = null;
50
+ let timeoutTimer = null;
51
+
52
+ try {
53
+ server.close((err) => {
54
+ if (err && err.code !== 'ERR_SERVER_NOT_RUNNING') {
55
+ try { logger.warn(`[shutdown] ${listener.id || 'http'} listener close error: ${err.message}`); } catch {}
56
+ }
57
+ done({ closed: true, error: err?.code || null });
58
+ });
59
+ } catch (err) {
60
+ if (err?.code === 'ERR_SERVER_NOT_RUNNING') return done({ closed: true, alreadyClosed: true });
61
+ try { logger.warn(`[shutdown] ${listener.id || 'http'} listener close threw: ${err.message}`); } catch {}
62
+ }
63
+
64
+ try { if (typeof server.closeIdleConnections === 'function') server.closeIdleConnections(); } catch {}
65
+
66
+ forceTimer = _unref(setTimeout(() => {
67
+ try { if (typeof server.closeAllConnections === 'function') server.closeAllConnections(); } catch {}
68
+ const tracked = listener.trackedSockets || new Set();
69
+ for (const socket of tracked) {
70
+ try { if (socket && !socket.destroyed) socket.destroy(); } catch {}
71
+ }
72
+ }, forceAfterMs));
73
+
74
+ timeoutTimer = _unref(setTimeout(() => {
75
+ done({
76
+ closed: false,
77
+ timedOut: true,
78
+ trackedSockets: listener.trackedSockets ? listener.trackedSockets.size : 0,
79
+ });
80
+ }, timeoutMs));
81
+ })));
82
+ }
83
+
84
+ function closeWebSocketClients(wss, options = {}) {
85
+ const clients = Array.from(wss?.clients || []);
86
+ const code = Number.isFinite(Number(options.code)) ? Number(options.code) : 1012;
87
+ const reason = String(options.reason || 'Service restart').slice(0, 123);
88
+ const graceMs = Number.isFinite(Number(options.graceMs))
89
+ ? Math.max(0, Number(options.graceMs))
90
+ : DEFAULT_WS_GRACE_MS;
91
+ const preCloseDelayMs = Number.isFinite(Number(options.preCloseDelayMs))
92
+ ? Math.max(0, Number(options.preCloseDelayMs))
93
+ : 0;
94
+
95
+ return new Promise((resolve) => {
96
+ if (!wss) return resolve({ closed: 0, terminated: 0, total: 0 });
97
+
98
+ const startClose = () => {
99
+ if (clients.length === 0) {
100
+ try { wss.close(() => {}); } catch {}
101
+ return resolve({ closed: 0, terminated: 0, total: 0 });
102
+ }
103
+
104
+ let closed = 0;
105
+ let settled = false;
106
+ const pending = new Set(clients);
107
+ const listeners = new Map();
108
+ let timer = null;
109
+
110
+ const finish = (timedOut = false) => {
111
+ if (settled) return;
112
+ settled = true;
113
+ if (timer) clearTimeout(timer);
114
+ let terminated = 0;
115
+ for (const ws of pending) {
116
+ const onClose = listeners.get(ws);
117
+ if (onClose) {
118
+ try { ws.off('close', onClose); } catch {}
119
+ }
120
+ try {
121
+ if (timedOut && ws.readyState !== ws.CLOSED) {
122
+ ws.terminate();
123
+ terminated += 1;
124
+ }
125
+ } catch {}
126
+ }
127
+ try { wss.close(() => {}); } catch {}
128
+ resolve({ closed, terminated, total: clients.length, timedOut });
129
+ };
130
+
131
+ for (const ws of clients) {
132
+ const onClose = () => {
133
+ if (!pending.delete(ws)) return;
134
+ closed += 1;
135
+ if (pending.size === 0) finish(false);
136
+ };
137
+ listeners.set(ws, onClose);
138
+ try { ws.once('close', onClose); } catch {}
139
+ try {
140
+ if (ws.readyState === ws.OPEN || ws.readyState === ws.CONNECTING) ws.close(code, reason);
141
+ else if (ws.readyState === ws.CLOSED) onClose();
142
+ } catch {
143
+ onClose();
144
+ }
145
+ }
146
+
147
+ if (!settled) timer = _unref(setTimeout(() => finish(true), graceMs));
148
+ };
149
+
150
+ if (preCloseDelayMs > 0) _unref(setTimeout(startClose, preCloseDelayMs));
151
+ else startClose();
152
+ });
153
+ }
154
+
155
+ module.exports = {
156
+ closeWebSocketClients,
157
+ installHttpConnectionTracking,
158
+ stopAcceptingHttpRequests,
159
+ };