quoxcli 0.0.0-stage → 0.3.1

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 (516) hide show
  1. package/LICENSE +35 -0
  2. package/README.md +144 -2
  3. package/bin/quox.js +265 -0
  4. package/bin/quoxon.js +8 -0
  5. package/lib/api/endpoints/admin.js +151 -0
  6. package/lib/api/endpoints/aee.js +38 -0
  7. package/lib/api/endpoints/agent-attach.js +126 -0
  8. package/lib/api/endpoints/agent-schedules.js +26 -0
  9. package/lib/api/endpoints/agents.js +63 -0
  10. package/lib/api/endpoints/assistants.js +62 -0
  11. package/lib/api/endpoints/audit.js +14 -0
  12. package/lib/api/endpoints/auth.js +50 -0
  13. package/lib/api/endpoints/awareness.js +34 -0
  14. package/lib/api/endpoints/away.js +15 -0
  15. package/lib/api/endpoints/backup-schedules.js +20 -0
  16. package/lib/api/endpoints/backups.js +86 -0
  17. package/lib/api/endpoints/brain.js +140 -0
  18. package/lib/api/endpoints/budgets.js +62 -0
  19. package/lib/api/endpoints/calendarq.js +71 -0
  20. package/lib/api/endpoints/catalog.js +14 -0
  21. package/lib/api/endpoints/chat.js +61 -0
  22. package/lib/api/endpoints/consent.js +26 -0
  23. package/lib/api/endpoints/contactsq.js +68 -0
  24. package/lib/api/endpoints/conversations.js +46 -0
  25. package/lib/api/endpoints/custom-roles.js +92 -0
  26. package/lib/api/endpoints/divisions.js +56 -0
  27. package/lib/api/endpoints/domains.js +38 -0
  28. package/lib/api/endpoints/engagement.js +231 -0
  29. package/lib/api/endpoints/engine.js +11 -0
  30. package/lib/api/endpoints/entity.js +50 -0
  31. package/lib/api/endpoints/executions.js +45 -0
  32. package/lib/api/endpoints/fetchAllowlist.js +27 -0
  33. package/lib/api/endpoints/file.js +20 -0
  34. package/lib/api/endpoints/files.js +40 -0
  35. package/lib/api/endpoints/github.js +21 -0
  36. package/lib/api/endpoints/governance.js +43 -0
  37. package/lib/api/endpoints/hat.js +66 -0
  38. package/lib/api/endpoints/inbox.js +52 -0
  39. package/lib/api/endpoints/inboxq.js +120 -0
  40. package/lib/api/endpoints/index.js +242 -0
  41. package/lib/api/endpoints/invites.js +19 -0
  42. package/lib/api/endpoints/llm-policy.js +43 -0
  43. package/lib/api/endpoints/lucid.js +89 -0
  44. package/lib/api/endpoints/members.js +32 -0
  45. package/lib/api/endpoints/memory.js +94 -0
  46. package/lib/api/endpoints/mfa.js +32 -0
  47. package/lib/api/endpoints/mind.js +13 -0
  48. package/lib/api/endpoints/mon.js +184 -0
  49. package/lib/api/endpoints/notesq.js +43 -0
  50. package/lib/api/endpoints/objectives.js +68 -0
  51. package/lib/api/endpoints/orgs.js +38 -0
  52. package/lib/api/endpoints/plugins.js +152 -0
  53. package/lib/api/endpoints/powers.js +75 -0
  54. package/lib/api/endpoints/presence.js +33 -0
  55. package/lib/api/endpoints/prompts.js +69 -0
  56. package/lib/api/endpoints/qlarity.js +122 -0
  57. package/lib/api/endpoints/quoxbox.js +55 -0
  58. package/lib/api/endpoints/quoxchat.js +54 -0
  59. package/lib/api/endpoints/quoxflow.js +32 -0
  60. package/lib/api/endpoints/quoxplan.js +121 -0
  61. package/lib/api/endpoints/quoxseo.js +114 -0
  62. package/lib/api/endpoints/reach.js +47 -0
  63. package/lib/api/endpoints/router.js +17 -0
  64. package/lib/api/endpoints/runs.js +38 -0
  65. package/lib/api/endpoints/safety.js +53 -0
  66. package/lib/api/endpoints/scim.js +46 -0
  67. package/lib/api/endpoints/scopes.js +14 -0
  68. package/lib/api/endpoints/security.js +69 -0
  69. package/lib/api/endpoints/setup.js +90 -0
  70. package/lib/api/endpoints/skills.js +200 -0
  71. package/lib/api/endpoints/sso.js +67 -0
  72. package/lib/api/endpoints/stepup.js +20 -0
  73. package/lib/api/endpoints/tags.js +32 -0
  74. package/lib/api/endpoints/teams.js +56 -0
  75. package/lib/api/endpoints/telephony.js +46 -0
  76. package/lib/api/endpoints/tg.js +56 -0
  77. package/lib/api/endpoints/tools.js +50 -0
  78. package/lib/api/endpoints/traces.js +157 -0
  79. package/lib/api/endpoints/triggers.js +96 -0
  80. package/lib/api/endpoints/user-library.js +41 -0
  81. package/lib/api/endpoints/users.js +20 -0
  82. package/lib/api/endpoints/v1.js +585 -0
  83. package/lib/api/endpoints/vault.js +192 -0
  84. package/lib/api/endpoints/voice.js +22 -0
  85. package/lib/api/endpoints/volt.js +84 -0
  86. package/lib/api/endpoints/ward.js +47 -0
  87. package/lib/api/endpoints/webhooks.js +46 -0
  88. package/lib/api/endpoints/workflows.js +169 -0
  89. package/lib/api/endpoints/x.js +32 -0
  90. package/lib/api/endpoints.js +15 -0
  91. package/lib/commands/actions.js +61 -0
  92. package/lib/commands/admin.js +375 -0
  93. package/lib/commands/aee.js +99 -0
  94. package/lib/commands/agent.js +337 -0
  95. package/lib/commands/agentAttach.js +1528 -0
  96. package/lib/commands/approvals.js +119 -0
  97. package/lib/commands/aside.js +24 -0
  98. package/lib/commands/ask.js +144 -0
  99. package/lib/commands/assistant.js +169 -0
  100. package/lib/commands/auth.js +464 -0
  101. package/lib/commands/awareness.js +88 -0
  102. package/lib/commands/backup.js +377 -0
  103. package/lib/commands/brain.js +1085 -0
  104. package/lib/commands/budget.js +241 -0
  105. package/lib/commands/build.js +170 -0
  106. package/lib/commands/calendar.js +216 -0
  107. package/lib/commands/capabilities.js +34 -0
  108. package/lib/commands/chat.js +295 -0
  109. package/lib/commands/claude.js +380 -0
  110. package/lib/commands/config-cmd.js +106 -0
  111. package/lib/commands/consent.js +84 -0
  112. package/lib/commands/contacts.js +1016 -0
  113. package/lib/commands/contactsSelftest.js +132 -0
  114. package/lib/commands/conversation.js +105 -0
  115. package/lib/commands/dashboard.js +43 -0
  116. package/lib/commands/department.js +96 -0
  117. package/lib/commands/dev.js +620 -0
  118. package/lib/commands/devResident.js +326 -0
  119. package/lib/commands/devSupervisor.js +1062 -0
  120. package/lib/commands/discord.js +132 -0
  121. package/lib/commands/doctor.js +181 -0
  122. package/lib/commands/domain.js +114 -0
  123. package/lib/commands/done.js +347 -0
  124. package/lib/commands/engagement.js +597 -0
  125. package/lib/commands/entity.js +119 -0
  126. package/lib/commands/facebook.js +212 -0
  127. package/lib/commands/fetchAllowlist.js +71 -0
  128. package/lib/commands/file.js +165 -0
  129. package/lib/commands/fleet.js +1189 -0
  130. package/lib/commands/governance.js +647 -0
  131. package/lib/commands/gripe.js +355 -0
  132. package/lib/commands/hat.js +374 -0
  133. package/lib/commands/helpers.js +96 -0
  134. package/lib/commands/hyperion.js +58 -0
  135. package/lib/commands/inbox.js +173 -0
  136. package/lib/commands/instagram.js +264 -0
  137. package/lib/commands/integration.js +100 -0
  138. package/lib/commands/inventory.js +115 -0
  139. package/lib/commands/invites.js +45 -0
  140. package/lib/commands/licence.js +139 -0
  141. package/lib/commands/linkedin.js +178 -0
  142. package/lib/commands/llm-policy.js +117 -0
  143. package/lib/commands/llm.js +45 -0
  144. package/lib/commands/lucid.js +597 -0
  145. package/lib/commands/mail.js +531 -0
  146. package/lib/commands/memory.js +361 -0
  147. package/lib/commands/mind.js +71 -0
  148. package/lib/commands/mon.js +581 -0
  149. package/lib/commands/monitoring.js +86 -0
  150. package/lib/commands/motion.js +22 -0
  151. package/lib/commands/notes.js +1246 -0
  152. package/lib/commands/notesBatch.js +160 -0
  153. package/lib/commands/notesBridges.js +56 -0
  154. package/lib/commands/notesLinks.js +60 -0
  155. package/lib/commands/notification.js +57 -0
  156. package/lib/commands/nucleus.js +123 -0
  157. package/lib/commands/objective.js +240 -0
  158. package/lib/commands/offload.js +89 -0
  159. package/lib/commands/onboard.js +329 -0
  160. package/lib/commands/open.js +56 -0
  161. package/lib/commands/org.js +276 -0
  162. package/lib/commands/peek.js +28 -0
  163. package/lib/commands/plan.js +944 -0
  164. package/lib/commands/plugin-doctor.js +1091 -0
  165. package/lib/commands/plugin.js +649 -0
  166. package/lib/commands/policy.js +78 -0
  167. package/lib/commands/powers.js +296 -0
  168. package/lib/commands/presence.js +89 -0
  169. package/lib/commands/prompt.js +350 -0
  170. package/lib/commands/provider.js +159 -0
  171. package/lib/commands/qlarity.js +706 -0
  172. package/lib/commands/quoxbox.js +270 -0
  173. package/lib/commands/quoxchat.js +275 -0
  174. package/lib/commands/quoxseo.js +378 -0
  175. package/lib/commands/reach.js +159 -0
  176. package/lib/commands/role.js +286 -0
  177. package/lib/commands/router.js +77 -0
  178. package/lib/commands/rows.js +358 -0
  179. package/lib/commands/run.js +187 -0
  180. package/lib/commands/safety.js +107 -0
  181. package/lib/commands/scim.js +88 -0
  182. package/lib/commands/scope.js +58 -0
  183. package/lib/commands/security.js +157 -0
  184. package/lib/commands/server.js +292 -0
  185. package/lib/commands/service.js +89 -0
  186. package/lib/commands/settings.js +99 -0
  187. package/lib/commands/setup.js +569 -0
  188. package/lib/commands/skillManifest.js +113 -0
  189. package/lib/commands/skillRightsFormat.js +38 -0
  190. package/lib/commands/skills.js +687 -0
  191. package/lib/commands/slot.js +1451 -0
  192. package/lib/commands/sso.js +229 -0
  193. package/lib/commands/status.js +137 -0
  194. package/lib/commands/tag.js +91 -0
  195. package/lib/commands/team.js +142 -0
  196. package/lib/commands/tg.js +240 -0
  197. package/lib/commands/tiktok.js +218 -0
  198. package/lib/commands/tips.js +23 -0
  199. package/lib/commands/tool.js +304 -0
  200. package/lib/commands/trace.js +530 -0
  201. package/lib/commands/trigger.js +223 -0
  202. package/lib/commands/update.js +46 -0
  203. package/lib/commands/vault.js +1447 -0
  204. package/lib/commands/vm.js +237 -0
  205. package/lib/commands/voice.js +213 -0
  206. package/lib/commands/volt.js +570 -0
  207. package/lib/commands/walkthrough.js +32 -0
  208. package/lib/commands/watch.js +167 -0
  209. package/lib/commands/webhook.js +137 -0
  210. package/lib/commands/welcome.js +14 -0
  211. package/lib/commands/workflow.js +660 -0
  212. package/lib/commands/x.js +448 -0
  213. package/lib/core/actingAs.js +136 -0
  214. package/lib/core/activeSummary.js +186 -0
  215. package/lib/core/aee.js +53 -0
  216. package/lib/core/agentAlias.js +163 -0
  217. package/lib/core/approvalContext.js +61 -0
  218. package/lib/core/aside.js +54 -0
  219. package/lib/core/askDecision.js +60 -0
  220. package/lib/core/auth.js +501 -0
  221. package/lib/core/banner.js +143 -0
  222. package/lib/core/brainPrime.js +90 -0
  223. package/lib/core/callerSource.js +129 -0
  224. package/lib/core/claudeLs.js +94 -0
  225. package/lib/core/cliBuild.js +62 -0
  226. package/lib/core/client.js +693 -0
  227. package/lib/core/commandRegistrars.js +121 -0
  228. package/lib/core/completions.js +163 -0
  229. package/lib/core/config.js +669 -0
  230. package/lib/core/confirm.js +37 -0
  231. package/lib/core/customHats.js +168 -0
  232. package/lib/core/deeplinks.js +76 -0
  233. package/lib/core/doneEngineFetch.js +227 -0
  234. package/lib/core/errors.js +441 -0
  235. package/lib/core/global-flags.js +190 -0
  236. package/lib/core/governedSession.js +402 -0
  237. package/lib/core/hyperion.js +102 -0
  238. package/lib/core/inboxResolution.js +65 -0
  239. package/lib/core/lazyCommands.js +122 -0
  240. package/lib/core/liveSessionsReport.js +112 -0
  241. package/lib/core/offload.js +504 -0
  242. package/lib/core/org-override.js +52 -0
  243. package/lib/core/orgResolve.js +70 -0
  244. package/lib/core/output.js +239 -0
  245. package/lib/core/peek.js +126 -0
  246. package/lib/core/prodEnv.js +7 -0
  247. package/lib/core/profileUsers.js +51 -0
  248. package/lib/core/promptCancel.js +18 -0
  249. package/lib/core/quoxglyph.js +158 -0
  250. package/lib/core/repoPath.js +11 -0
  251. package/lib/core/residentAlarm.js +282 -0
  252. package/lib/core/residentAuth.js +243 -0
  253. package/lib/core/residentPump.js +229 -0
  254. package/lib/core/residentSeed.js +106 -0
  255. package/lib/core/rootOptions.js +39 -0
  256. package/lib/core/router.js +168 -0
  257. package/lib/core/selfUpdate.js +188 -0
  258. package/lib/core/sessionHeartbeat.js +192 -0
  259. package/lib/core/sessionsOverview.js +224 -0
  260. package/lib/core/slotKeeper.js +527 -0
  261. package/lib/core/slotWake.js +120 -0
  262. package/lib/core/supervisorEngine.js +933 -0
  263. package/lib/core/systemdInstaller.js +256 -0
  264. package/lib/core/version.js +87 -0
  265. package/lib/core/vm.js +361 -0
  266. package/lib/core/vmTui.js +86 -0
  267. package/lib/core/walkthrough.js +35 -0
  268. package/lib/hyperion/deck.js +29 -0
  269. package/lib/lucid/LucidCard.jsx +68 -0
  270. package/lib/lucid/LucidIntro.jsx +92 -0
  271. package/lib/lucid/blind.js +71 -0
  272. package/lib/lucid/cards.js +86 -0
  273. package/lib/lucid/deck.js +32 -0
  274. package/lib/lucid/fmt.js +139 -0
  275. package/lib/lucid/markdown.js +81 -0
  276. package/lib/lucid/motion.js +56 -0
  277. package/lib/lucid/sitrep.js +173 -0
  278. package/lib/plugins/loader.js +93 -0
  279. package/lib/quoxon/activity.js +116 -0
  280. package/lib/quoxon/aocl.js +117 -0
  281. package/lib/quoxon/api.js +952 -0
  282. package/lib/quoxon/app.js +4032 -0
  283. package/lib/quoxon/approvalFacts.js +94 -0
  284. package/lib/quoxon/approvalsView.js +325 -0
  285. package/lib/quoxon/asideView.js +40 -0
  286. package/lib/quoxon/ask.js +121 -0
  287. package/lib/quoxon/away.js +52 -0
  288. package/lib/quoxon/bang.js +224 -0
  289. package/lib/quoxon/bulkRisk.js +17 -0
  290. package/lib/quoxon/capabilities.js +82 -0
  291. package/lib/quoxon/cells.js +53 -0
  292. package/lib/quoxon/commandShort.js +112 -0
  293. package/lib/quoxon/commands.js +479 -0
  294. package/lib/quoxon/connect.js +654 -0
  295. package/lib/quoxon/connectCard.js +45 -0
  296. package/lib/quoxon/consent.js +86 -0
  297. package/lib/quoxon/context.js +68 -0
  298. package/lib/quoxon/copy.js +34 -0
  299. package/lib/quoxon/countdownLine.js +11 -0
  300. package/lib/quoxon/demo.autoplay.js +29 -0
  301. package/lib/quoxon/demo.fixtures.js +24 -0
  302. package/lib/quoxon/demo.js +76 -0
  303. package/lib/quoxon/devObserver.js +20 -0
  304. package/lib/quoxon/dividerAocl.js +191 -0
  305. package/lib/quoxon/dividerRule.js +57 -0
  306. package/lib/quoxon/dragonModes.js +150 -0
  307. package/lib/quoxon/dragonView.js +41 -0
  308. package/lib/quoxon/earlyInput.js +40 -0
  309. package/lib/quoxon/echo.js +15 -0
  310. package/lib/quoxon/egg.js +36 -0
  311. package/lib/quoxon/events.js +677 -0
  312. package/lib/quoxon/feedback.js +50 -0
  313. package/lib/quoxon/gallery.js +237 -0
  314. package/lib/quoxon/handoffOffer.js +78 -0
  315. package/lib/quoxon/hatSwitch.js +37 -0
  316. package/lib/quoxon/headless.js +161 -0
  317. package/lib/quoxon/hostUrls.js +41 -0
  318. package/lib/quoxon/indicator.js +197 -0
  319. package/lib/quoxon/intent.js +52 -0
  320. package/lib/quoxon/keys.js +164 -0
  321. package/lib/quoxon/launcher.js +103 -0
  322. package/lib/quoxon/main.js +176 -0
  323. package/lib/quoxon/markdown.js +341 -0
  324. package/lib/quoxon/markdownSettle.js +19 -0
  325. package/lib/quoxon/matrixDomains.json +46 -0
  326. package/lib/quoxon/mentions.js +90 -0
  327. package/lib/quoxon/mind.js +103 -0
  328. package/lib/quoxon/mode.js +46 -0
  329. package/lib/quoxon/model.js +311 -0
  330. package/lib/quoxon/modelPrefs.js +345 -0
  331. package/lib/quoxon/motion/clock.js +70 -0
  332. package/lib/quoxon/motion/color.js +35 -0
  333. package/lib/quoxon/motion/frame.js +14 -0
  334. package/lib/quoxon/motion/level.js +91 -0
  335. package/lib/quoxon/motion/reveal.js +54 -0
  336. package/lib/quoxon/motion/ring.js +79 -0
  337. package/lib/quoxon/mouse.js +211 -0
  338. package/lib/quoxon/noai.js +57 -0
  339. package/lib/quoxon/objects/agent.js +61 -0
  340. package/lib/quoxon/objects/away.js +72 -0
  341. package/lib/quoxon/objects/crystal.js +100 -0
  342. package/lib/quoxon/objects/fetch.js +182 -0
  343. package/lib/quoxon/objects/fleet.js +149 -0
  344. package/lib/quoxon/objects/health.js +186 -0
  345. package/lib/quoxon/objects/index.js +106 -0
  346. package/lib/quoxon/objects/ladder.js +71 -0
  347. package/lib/quoxon/objects/memory.js +44 -0
  348. package/lib/quoxon/objects/mirror.js +137 -0
  349. package/lib/quoxon/objects/model.js +118 -0
  350. package/lib/quoxon/objects/peek.js +17 -0
  351. package/lib/quoxon/objects/proof.js +110 -0
  352. package/lib/quoxon/objects/prove.js +130 -0
  353. package/lib/quoxon/objects/replay.js +125 -0
  354. package/lib/quoxon/objects/sitrep.js +58 -0
  355. package/lib/quoxon/objects/skill.js +105 -0
  356. package/lib/quoxon/objects/view.js +87 -0
  357. package/lib/quoxon/objects/workflow.js +119 -0
  358. package/lib/quoxon/observers.js +42 -0
  359. package/lib/quoxon/onboarding/copy.js +263 -0
  360. package/lib/quoxon/onboarding/credential.js +108 -0
  361. package/lib/quoxon/onboarding/detect.js +22 -0
  362. package/lib/quoxon/onboarding/dockerOffer.js +45 -0
  363. package/lib/quoxon/onboarding/failureCause.js +33 -0
  364. package/lib/quoxon/onboarding/finish.js +134 -0
  365. package/lib/quoxon/onboarding/index.js +94 -0
  366. package/lib/quoxon/onboarding/installRunner.js +284 -0
  367. package/lib/quoxon/onboarding/intent.js +53 -0
  368. package/lib/quoxon/onboarding/journey.js +73 -0
  369. package/lib/quoxon/onboarding/preflight.js +156 -0
  370. package/lib/quoxon/onboarding/probe.js +65 -0
  371. package/lib/quoxon/onboarding/runner.js +58 -0
  372. package/lib/quoxon/onboarding/view.js +682 -0
  373. package/lib/quoxon/orgLabel.js +10 -0
  374. package/lib/quoxon/orgSwitch.js +33 -0
  375. package/lib/quoxon/ownBroadcast.js +22 -0
  376. package/lib/quoxon/palette.js +43 -0
  377. package/lib/quoxon/print.js +98 -0
  378. package/lib/quoxon/profileSwitch.js +53 -0
  379. package/lib/quoxon/promptHistory.js +41 -0
  380. package/lib/quoxon/providers.js +48 -0
  381. package/lib/quoxon/qart.js +348 -0
  382. package/lib/quoxon/qdragon.js +410 -0
  383. package/lib/quoxon/qdragonFly.js +61 -0
  384. package/lib/quoxon/qdragonScenes.js +282 -0
  385. package/lib/quoxon/qeyes.js +213 -0
  386. package/lib/quoxon/qglyph.js +143 -0
  387. package/lib/quoxon/qhello.js +58 -0
  388. package/lib/quoxon/qmotion.js +132 -0
  389. package/lib/quoxon/render.js +1167 -0
  390. package/lib/quoxon/resend.js +72 -0
  391. package/lib/quoxon/roster.js +307 -0
  392. package/lib/quoxon/route.js +39 -0
  393. package/lib/quoxon/rows.js +711 -0
  394. package/lib/quoxon/rowsStore.js +80 -0
  395. package/lib/quoxon/rules.js +130 -0
  396. package/lib/quoxon/screenContext.js +150 -0
  397. package/lib/quoxon/screenFrames.js +254 -0
  398. package/lib/quoxon/session.js +238 -0
  399. package/lib/quoxon/sessionDead.js +17 -0
  400. package/lib/quoxon/signin.js +125 -0
  401. package/lib/quoxon/skills.js +30 -0
  402. package/lib/quoxon/slash/agents.js +112 -0
  403. package/lib/quoxon/slash/argSources.js +65 -0
  404. package/lib/quoxon/slash/args.js +65 -0
  405. package/lib/quoxon/slash/automate.js +89 -0
  406. package/lib/quoxon/slash/broadcast.js +140 -0
  407. package/lib/quoxon/slash/broadcastRun.js +108 -0
  408. package/lib/quoxon/slash/card.js +30 -0
  409. package/lib/quoxon/slash/challenge.js +114 -0
  410. package/lib/quoxon/slash/cliCommand.js +53 -0
  411. package/lib/quoxon/slash/cliLeaves.json +948 -0
  412. package/lib/quoxon/slash/compact.js +90 -0
  413. package/lib/quoxon/slash/confirm.js +30 -0
  414. package/lib/quoxon/slash/crystallize.js +69 -0
  415. package/lib/quoxon/slash/dashboard.js +136 -0
  416. package/lib/quoxon/slash/draft.js +54 -0
  417. package/lib/quoxon/slash/hatWalk.js +206 -0
  418. package/lib/quoxon/slash/inverses.js +77 -0
  419. package/lib/quoxon/slash/killer.js +24 -0
  420. package/lib/quoxon/slash/killerApi.js +16 -0
  421. package/lib/quoxon/slash/ledger.js +88 -0
  422. package/lib/quoxon/slash/listView.js +30 -0
  423. package/lib/quoxon/slash/modelCmd.js +261 -0
  424. package/lib/quoxon/slash/palette.js +118 -0
  425. package/lib/quoxon/slash/paletteController.js +169 -0
  426. package/lib/quoxon/slash/panels.js +116 -0
  427. package/lib/quoxon/slash/picker.js +56 -0
  428. package/lib/quoxon/slash/restart.js +202 -0
  429. package/lib/quoxon/slash/resume.js +59 -0
  430. package/lib/quoxon/slash/rowCmd.js +157 -0
  431. package/lib/quoxon/slash/rowWalk.js +175 -0
  432. package/lib/quoxon/slash/settings.js +60 -0
  433. package/lib/quoxon/slash/spawn.js +177 -0
  434. package/lib/quoxon/slash/stepUp.js +274 -0
  435. package/lib/quoxon/slash/stepUpCard.js +60 -0
  436. package/lib/quoxon/slash/stepUpCmds.js +78 -0
  437. package/lib/quoxon/slash/undoApi.js +38 -0
  438. package/lib/quoxon/slash/undoCmd.js +49 -0
  439. package/lib/quoxon/slash/undoWish.js +15 -0
  440. package/lib/quoxon/slash/watch.js +164 -0
  441. package/lib/quoxon/slash/wish.js +23 -0
  442. package/lib/quoxon/slash/wishCmd.js +32 -0
  443. package/lib/quoxon/slotChat.js +300 -0
  444. package/lib/quoxon/slotFormat.js +195 -0
  445. package/lib/quoxon/slotRun.js +131 -0
  446. package/lib/quoxon/sound/index.js +186 -0
  447. package/lib/quoxon/sound/mapping.js +120 -0
  448. package/lib/quoxon/sound/output.js +52 -0
  449. package/lib/quoxon/sound/synth.js +122 -0
  450. package/lib/quoxon/stray.js +22 -0
  451. package/lib/quoxon/suggestCommand.js +59 -0
  452. package/lib/quoxon/term.js +141 -0
  453. package/lib/quoxon/textBuffer.js +416 -0
  454. package/lib/quoxon/theme.js +286 -0
  455. package/lib/quoxon/tips.js +433 -0
  456. package/lib/quoxon/toolError.js +41 -0
  457. package/lib/quoxon/transcript.js +188 -0
  458. package/lib/quoxon/web/qface.js +89 -0
  459. package/lib/quoxon/welcome.js +52 -0
  460. package/lib/quoxon/whatsnew.js +136 -0
  461. package/lib/quoxon/whatsnew.jsonl +89 -0
  462. package/lib/quoxon/windowOrg.js +113 -0
  463. package/lib/server/diag.js +129 -0
  464. package/lib/server/doctor.js +71 -0
  465. package/lib/server/install.js +149 -0
  466. package/lib/server/pluginEnable.js +161 -0
  467. package/lib/server/skew.js +56 -0
  468. package/lib/server/snapshot.js +131 -0
  469. package/lib/server/status.js +128 -0
  470. package/lib/server/sys.js +65 -0
  471. package/lib/server/uninstall.js +63 -0
  472. package/lib/server/update.js +234 -0
  473. package/lib/toolOutputShape.js +315 -0
  474. package/lib/tui/app.jsx +289 -0
  475. package/lib/tui/components/Breadcrumb.jsx +42 -0
  476. package/lib/tui/components/ChatInput.jsx +68 -0
  477. package/lib/tui/components/Confirm.jsx +32 -0
  478. package/lib/tui/components/DataTable.jsx +270 -0
  479. package/lib/tui/components/DetailPanel.jsx +241 -0
  480. package/lib/tui/components/HelpOverlay.jsx +67 -0
  481. package/lib/tui/components/Layout.jsx +129 -0
  482. package/lib/tui/components/Palette.jsx +141 -0
  483. package/lib/tui/components/SearchInput.jsx +125 -0
  484. package/lib/tui/components/Sparkline.jsx +55 -0
  485. package/lib/tui/components/StatusBar.jsx +133 -0
  486. package/lib/tui/components/Toast.jsx +52 -0
  487. package/lib/tui/hooks/useApi.js +44 -0
  488. package/lib/tui/hooks/useHealth.js +90 -0
  489. package/lib/tui/hooks/usePolledApi.js +86 -0
  490. package/lib/tui/hooks/useTerminalSize.js +46 -0
  491. package/lib/tui/index.js +56 -0
  492. package/lib/tui/intro/Intro.jsx +332 -0
  493. package/lib/tui/jsx-loader.js +32 -0
  494. package/lib/tui/keys.js +87 -0
  495. package/lib/tui/registry.js +49 -0
  496. package/lib/tui/theme.js +103 -0
  497. package/lib/tui/views/AdminView.jsx +398 -0
  498. package/lib/tui/views/AgentView.jsx +204 -0
  499. package/lib/tui/views/BackupView.jsx +225 -0
  500. package/lib/tui/views/BrainView.jsx +223 -0
  501. package/lib/tui/views/ChatView.jsx +468 -0
  502. package/lib/tui/views/FilesView.jsx +218 -0
  503. package/lib/tui/views/FleetView.jsx +234 -0
  504. package/lib/tui/views/GovernanceView.jsx +453 -0
  505. package/lib/tui/views/HealthView.jsx +216 -0
  506. package/lib/tui/views/HyperionDeck.jsx +79 -0
  507. package/lib/tui/views/InboxView.jsx +228 -0
  508. package/lib/tui/views/LucidDeck.jsx +719 -0
  509. package/lib/tui/views/MainMenu.jsx +123 -0
  510. package/lib/tui/views/MemoryView.jsx +377 -0
  511. package/lib/tui/views/ObjectivesView.jsx +230 -0
  512. package/lib/tui/views/OrgView.jsx +229 -0
  513. package/lib/tui/views/ToolView.jsx +221 -0
  514. package/lib/tui/views/VaultView.jsx +422 -0
  515. package/lib/tui/views/WorkflowView.jsx +329 -0
  516. package/package.json +75 -4
@@ -0,0 +1,416 @@
1
+ /**
2
+ * QUOXON composer text buffer: a pure reducer over { text, cursor, undo, redo,
3
+ * pastes, pasteSeq }. Written for Quox (no third-party code). The pattern is
4
+ * the usual one for Ink editors: all edits are actions, the view is derived.
5
+ *
6
+ * - cursor is a UTF-16 index that always sits on a grapheme boundary
7
+ * (Intl.Segmenter), so emoji, ZWJ sequences and combining marks move as one.
8
+ * - Large pastes collapse to `[Pasted text #N +L lines]` and expand on send.
9
+ * - Invisible Unicode (zero width, bidi controls, tag characters) is detected
10
+ * and stripped on Enter with a review round trip (see sanitizeForSend).
11
+ */
12
+
13
+ const seg = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
14
+
15
+ export const PASTE_CHAR_LIMIT = 800;
16
+ export const PASTE_LINE_LIMIT = 3;
17
+ const UNDO_LIMIT = 100;
18
+ const PASTE_RE = /\[Pasted text #(\d+) \+\d+ lines\]/g;
19
+
20
+ export function initialBuffer(text = '') {
21
+ return { text, cursor: text.length, anchor: null, undo: [], redo: [], pastes: {}, pasteSeq: 0 };
22
+ }
23
+
24
+ /**
25
+ * QUOXON-MOUSE: the composer selection, { from, to } (from < to), or null. A selection is the range between
26
+ * `anchor` (where a drag started) and the cursor; only the mouse sets an anchor.
27
+ */
28
+ export function selection(s) {
29
+ if (s?.anchor == null || s.anchor === s.cursor) return null;
30
+ return { from: Math.min(s.anchor, s.cursor), to: Math.max(s.anchor, s.cursor) };
31
+ }
32
+
33
+ /** The word under `idx` for a double-click: a run of non-space characters, or the one space character itself. */
34
+ export function wordAt(text, idx) {
35
+ const t = String(text);
36
+ const i = Math.max(0, Math.min(idx, t.length - 1));
37
+ if (!t.length) return { from: 0, to: 0 };
38
+ if (/\s/.test(t[i])) return { from: i, to: nextBoundary(t, i) };
39
+ let a = i; let b = i;
40
+ while (a > 0 && !/\s/.test(t[a - 1])) a--;
41
+ while (b < t.length && !/\s/.test(t[b])) b++;
42
+ return { from: a, to: b };
43
+ }
44
+
45
+ /** Snap an index onto a grapheme boundary (at or before it). */
46
+ function snap(text, idx) {
47
+ const i = Math.max(0, Math.min(Number(idx) || 0, text.length));
48
+ return i === text.length ? i : prevBoundary(text, i + 1);
49
+ }
50
+
51
+ /** The selected range widened so it never leaves half a paste placeholder behind. */
52
+ function selRange(s, sel) {
53
+ let { from, to } = sel;
54
+ PASTE_RE.lastIndex = 0;
55
+ let m;
56
+ while ((m = PASTE_RE.exec(s.text))) {
57
+ const a = m.index, b = a + m[0].length;
58
+ if (to > a && from < b) { from = Math.min(from, a); to = Math.max(to, b); }
59
+ }
60
+ return { from, to };
61
+ }
62
+
63
+ const EDITS = new Set(['insert', 'newline', 'paste', 'backspace', 'delete', 'deleteWordBack', 'killLineBack', 'killLineForward', 'deleteWordForward']);
64
+
65
+ /** COMPOSER-EDIT: the selected text, paste placeholders expanded (what Shift selection copies). '' when none. */
66
+ export function selectedText(s) {
67
+ const sel = selection(s);
68
+ if (!sel) return '';
69
+ const { from, to } = selRange(s, sel);
70
+ return expandPastes({ text: s.text.slice(from, to), pastes: s.pastes || {} });
71
+ }
72
+
73
+ // ---- COMPOSER-EDIT: words and soft-wrapped rows ------------------------------------------------
74
+ const WORD = /[\p{L}\p{N}_]/u;
75
+ /** Readline backward-word: skip non-word characters, then the word. */
76
+ function wordLeftIndex(t, i) {
77
+ while (i > 0 && !WORD.test(t[i - 1])) i--;
78
+ while (i > 0 && WORD.test(t[i - 1])) i--;
79
+ return i;
80
+ }
81
+ /** Readline forward-word: skip non-word characters, then the word (lands at its end). */
82
+ function wordRightIndex(t, i) {
83
+ while (i < t.length && !WORD.test(t[i])) i++;
84
+ while (i < t.length && WORD.test(t[i])) i++;
85
+ return i;
86
+ }
87
+ /** Remove [from, to), widened so no paste placeholder is left half cut. */
88
+ function cutRange(s, from, to) {
89
+ if (from >= to) return s;
90
+ const r = selRange(s, { from, to });
91
+ return commit(s, { text: s.text.slice(0, r.from) + s.text.slice(r.to), cursor: r.from });
92
+ }
93
+
94
+ const cellW = (g) => (/[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-꓏가-힣豈-﫿︰-﹏＀-⦆¢-₩]/u.test(g) || (/\p{Extended_Pictographic}/u.test(g) && (g.codePointAt(0) >= 0x1f000 || g.includes('\uFE0F'))) ? 2 : 1);
95
+ /**
96
+ * The draft as the rows the terminal shows at `width` cells: [{ start, end }] (UTF-16 indices, end exclusive,
97
+ * newline not included). Word wrap like Ink's: a word that does not fit starts a new row, spaces hang at the row
98
+ * end, and a word longer than a row is split hard.
99
+ */
100
+ export function visualRows(text, width) {
101
+ const out = [];
102
+ let o = 0;
103
+ for (const line of String(text).split('\n')) {
104
+ if (!(width > 0) || !line.length) { out.push({ start: o, end: o + line.length }); o += line.length + 1; continue; }
105
+ const tokens = line.match(/\s+|\S+/g);
106
+ let rowStart = o; let w = 0; let p = o;
107
+ for (const tok of tokens) {
108
+ const gs = graphemes(tok);
109
+ const tw = gs.reduce((n, g) => n + cellW(g.text), 0);
110
+ if (/^\s/.test(tok) || w + tw <= width) { w += tw; p += tok.length; continue; }
111
+ if (tw <= width) { out.push({ start: rowStart, end: p }); rowStart = p; w = tw; p += tok.length; continue; }
112
+ for (const g of gs) { // longer than a row: split it hard
113
+ const cw = cellW(g.text);
114
+ if (w + cw > width && w > 0) { out.push({ start: rowStart, end: p }); rowStart = p; w = 0; }
115
+ w += cw; p += g.text.length;
116
+ }
117
+ }
118
+ out.push({ start: rowStart, end: o + line.length });
119
+ o += line.length + 1;
120
+ }
121
+ return out;
122
+ }
123
+ /** Index of the visual row holding `cursor`. A cursor at a soft break sits at the start of the next row. */
124
+ function rowOf(rows, cursor) {
125
+ for (let i = rows.length - 1; i >= 0; i--) if (cursor >= rows[i].start) return i;
126
+ return 0;
127
+ }
128
+ /** COMPOSER-EDIT: is the cursor on the first (`up`) or last (`down`) row the terminal shows? */
129
+ export function atVisualEdge(s, dir, width) {
130
+ if (!(width > 0)) {
131
+ return dir === 'up' ? !s.text.slice(0, s.cursor).includes('\n') : !s.text.slice(s.cursor).includes('\n');
132
+ }
133
+ const rows = visualRows(s.text, width);
134
+ const r = rowOf(rows, s.cursor);
135
+ return dir === 'up' ? r === 0 : r === rows.length - 1;
136
+ }
137
+ function moveVisual(s, dir, width) {
138
+ const rows = visualRows(s.text, width);
139
+ const r = rowOf(rows, s.cursor);
140
+ const t = dir === 'up' ? r - 1 : r + 1;
141
+ if (t < 0 || t >= rows.length) return s;
142
+ const col = graphemes(s.text.slice(rows[r].start, s.cursor)).reduce((n, g) => n + cellW(g.text), 0);
143
+ const row = rows[t];
144
+ const soft = t + 1 < rows.length && rows[t + 1].start === row.end; // the row breaks softly: its end is the next row's start
145
+ const gs = graphemes(s.text.slice(row.start, row.end));
146
+ let at = 0; let idx = row.end;
147
+ for (const g of gs) { const w = cellW(g.text); if (col < at + w) { idx = row.start + g.index; break; } at += w; }
148
+ if (soft && idx >= row.end) idx = prevBoundary(s.text, row.end);
149
+ return { ...s, cursor: idx };
150
+ }
151
+
152
+ export function graphemes(text) {
153
+ return Array.from(seg.segment(text), s => ({ index: s.index, text: s.segment }));
154
+ }
155
+
156
+ /** Boundary strictly before `idx` (0 if none). */
157
+ export function prevBoundary(text, idx) {
158
+ let prev = 0;
159
+ for (const s of seg.segment(text)) {
160
+ if (s.index >= idx) break;
161
+ prev = s.index;
162
+ }
163
+ return prev;
164
+ }
165
+
166
+ /** Boundary strictly after `idx` (text.length if none). */
167
+ export function nextBoundary(text, idx) {
168
+ for (const s of seg.segment(text)) {
169
+ if (s.index > idx) return s.index;
170
+ }
171
+ return text.length;
172
+ }
173
+
174
+ export function displayWidthOfLine(s) { return graphemes(s).length; }
175
+
176
+ /** Placeholder range under or touching `idx` when deleting in a direction. */
177
+ function placeholderAt(text, from, to) {
178
+ PASTE_RE.lastIndex = 0;
179
+ let m;
180
+ while ((m = PASTE_RE.exec(text))) {
181
+ const a = m.index, b = a + m[0].length;
182
+ if (to > a && from < b) return { a, b };
183
+ }
184
+ return null;
185
+ }
186
+
187
+ function snapshot(s) { return { text: s.text, cursor: s.cursor, pastes: s.pastes, pasteSeq: s.pasteSeq }; }
188
+
189
+ function commit(s, next) {
190
+ const undo = [...s.undo, snapshot(s)].slice(-UNDO_LIMIT);
191
+ return { ...s, ...next, undo, redo: [] };
192
+ }
193
+
194
+ export function lineCol(text, cursor) {
195
+ const before = text.slice(0, cursor);
196
+ const line = before.split('\n').length - 1;
197
+ const start = before.lastIndexOf('\n') + 1;
198
+ return { line, col: graphemes(text.slice(start, cursor)).length, start };
199
+ }
200
+
201
+ function lineStarts(text) {
202
+ const starts = [0];
203
+ for (let i = 0; i < text.length; i++) if (text[i] === '\n') starts.push(i + 1);
204
+ return starts;
205
+ }
206
+
207
+ function indexAtLineCol(text, line, col) {
208
+ const starts = lineStarts(text);
209
+ const l = Math.max(0, Math.min(line, starts.length - 1));
210
+ const start = starts[l];
211
+ const end = l + 1 < starts.length ? starts[l + 1] - 1 : text.length;
212
+ const gs = graphemes(text.slice(start, end));
213
+ if (col >= gs.length) return end;
214
+ return start + gs[col].index;
215
+ }
216
+
217
+ /** First logical line the composer shows (it shows at most `maxLines`, keeping the cursor line in view). */
218
+ export function composerFrom(buf, maxLines = 8) {
219
+ const n = buf.text.split('\n').length;
220
+ if (n <= maxLines) return 0;
221
+ const { line } = lineCol(buf.text, buf.cursor);
222
+ return Math.min(Math.max(0, line - Math.floor(maxLines / 2)), n - maxLines);
223
+ }
224
+
225
+ export function normalizeNewlines(t) { return String(t).replace(/\r\n?/g, '\n'); }
226
+
227
+ /** Build the collapsed form of a paste, or null when it is small enough to inline. */
228
+ export function shouldCollapse(text) {
229
+ const lines = text.split('\n').length;
230
+ return text.length > PASTE_CHAR_LIMIT || lines > PASTE_LINE_LIMIT;
231
+ }
232
+
233
+ export function bufferReducer(s0, a) {
234
+ const sel = selection(s0);
235
+ const s = s0.anchor == null ? s0 : { ...s0, anchor: null };
236
+ if (sel && EDITS.has(a.type)) {
237
+ // QUOXON-MOUSE: Backspace/Delete remove the selection; typing or pasting replaces it. One undo step.
238
+ const { from, to } = selRange(s, sel);
239
+ const cut = { ...s, text: s.text.slice(0, from) + s.text.slice(to), cursor: from };
240
+ if (a.type !== 'insert' && a.type !== 'newline' && a.type !== 'paste') return commit(s, { text: cut.text, cursor: from });
241
+ const next = baseReducer({ ...cut, undo: [] }, a);
242
+ return commit(s, { text: next.text, cursor: next.cursor, pastes: next.pastes, pasteSeq: next.pasteSeq });
243
+ }
244
+ if (sel && a.type === 'left') return { ...s, cursor: sel.from };
245
+ if (sel && a.type === 'right') return { ...s, cursor: sel.to };
246
+ switch (a.type) {
247
+ case 'extend': { // COMPOSER-EDIT: Shift+movement grows the selection from where it started
248
+ const anchor = s0.anchor ?? s0.cursor;
249
+ const moved = baseReducer({ ...s, anchor: null }, { type: a.move, width: a.width });
250
+ return { ...s, cursor: moved.cursor, anchor: moved.cursor === anchor ? null : anchor };
251
+ }
252
+ case 'selectAll': return s.text ? { ...s, anchor: 0, cursor: s.text.length } : s;
253
+ case 'place': return { ...s, cursor: snap(s.text, a.index) };
254
+ case 'select': {
255
+ const anchor = snap(s.text, a.anchor); const cursor = snap(s.text, a.cursor);
256
+ return { ...s, anchor: anchor === cursor ? null : anchor, cursor };
257
+ }
258
+ default: return baseReducer(s, a);
259
+ }
260
+ }
261
+
262
+ function baseReducer(s, a) {
263
+ switch (a.type) {
264
+ case 'insert': {
265
+ const t = normalizeNewlines(a.text);
266
+ if (!t) return s;
267
+ return commit(s, { text: s.text.slice(0, s.cursor) + t + s.text.slice(s.cursor), cursor: s.cursor + t.length });
268
+ }
269
+ case 'newline':
270
+ return bufferReducer(s, { type: 'insert', text: '\n' });
271
+ case 'paste': {
272
+ const t = normalizeNewlines(a.text);
273
+ if (!t) return s;
274
+ if (!shouldCollapse(t)) return bufferReducer(s, { type: 'insert', text: t });
275
+ const n = s.pasteSeq + 1;
276
+ const ph = `[Pasted text #${n} +${t.split('\n').length} lines]`;
277
+ return commit(s, {
278
+ text: s.text.slice(0, s.cursor) + ph + s.text.slice(s.cursor),
279
+ cursor: s.cursor + ph.length,
280
+ pastes: { ...s.pastes, [n]: t },
281
+ pasteSeq: n
282
+ });
283
+ }
284
+ case 'backspace': {
285
+ if (s.cursor === 0) return s;
286
+ const ph = placeholderAt(s.text, prevBoundary(s.text, s.cursor), s.cursor);
287
+ const from = ph ? ph.a : prevBoundary(s.text, s.cursor);
288
+ const to = ph ? Math.max(ph.b, s.cursor) : s.cursor;
289
+ return commit(s, { text: s.text.slice(0, from) + s.text.slice(to), cursor: from });
290
+ }
291
+ case 'delete': {
292
+ if (s.cursor >= s.text.length) return s;
293
+ const nb = nextBoundary(s.text, s.cursor);
294
+ const ph = placeholderAt(s.text, s.cursor, nb);
295
+ const from = ph ? Math.min(ph.a, s.cursor) : s.cursor;
296
+ const to = ph ? ph.b : nb;
297
+ return commit(s, { text: s.text.slice(0, from) + s.text.slice(to), cursor: from });
298
+ }
299
+ case 'deleteWordBack': {
300
+ let i = s.cursor;
301
+ while (i > 0 && /\s/.test(s.text[i - 1])) i--;
302
+ while (i > 0 && !/\s/.test(s.text[i - 1])) i--;
303
+ if (i === s.cursor) return s;
304
+ return commit(s, { text: s.text.slice(0, i) + s.text.slice(s.cursor), cursor: i });
305
+ }
306
+ case 'killLineBack': {
307
+ const { start } = lineCol(s.text, s.cursor);
308
+ const from = start === s.cursor && start > 0 ? start - 1 : start;
309
+ if (from === s.cursor) return s;
310
+ return commit(s, { text: s.text.slice(0, from) + s.text.slice(s.cursor), cursor: from });
311
+ }
312
+ case 'wordLeft': return { ...s, cursor: wordLeftIndex(s.text, s.cursor) };
313
+ case 'wordRight': return { ...s, cursor: wordRightIndex(s.text, s.cursor) };
314
+ case 'killLineForward': { // Ctrl+K: to the end of the line; at the end, join the next line
315
+ const nl = s.text.indexOf('\n', s.cursor);
316
+ const end = nl === -1 ? s.text.length : (nl === s.cursor ? nl + 1 : nl);
317
+ return cutRange(s, s.cursor, end);
318
+ }
319
+ case 'deleteWordForward': return cutRange(s, s.cursor, wordRightIndex(s.text, s.cursor)); // Alt+D
320
+ case 'left': return { ...s, cursor: prevBoundary(s.text, s.cursor) };
321
+ case 'right': return { ...s, cursor: nextBoundary(s.text, s.cursor) };
322
+ case 'home': return { ...s, cursor: lineCol(s.text, s.cursor).start };
323
+ case 'end': {
324
+ const nl = s.text.indexOf('\n', s.cursor);
325
+ return { ...s, cursor: nl === -1 ? s.text.length : nl };
326
+ }
327
+ case 'up': case 'down': {
328
+ if (a.width > 0) return moveVisual(s, a.type, a.width); // COMPOSER-EDIT: through soft-wrapped rows
329
+ const { line, col } = lineCol(s.text, s.cursor);
330
+ const target = a.type === 'up' ? line - 1 : line + 1;
331
+ const count = s.text.split('\n').length;
332
+ if (target < 0 || target >= count) return s;
333
+ return { ...s, cursor: indexAtLineCol(s.text, target, col) };
334
+ }
335
+ case 'undo': {
336
+ if (!s.undo.length) return s;
337
+ const prev = s.undo[s.undo.length - 1];
338
+ return { ...s, ...prev, undo: s.undo.slice(0, -1), redo: [...s.redo, snapshot(s)] };
339
+ }
340
+ case 'redo': {
341
+ if (!s.redo.length) return s;
342
+ const next = s.redo[s.redo.length - 1];
343
+ return { ...s, ...next, redo: s.redo.slice(0, -1), undo: [...s.undo, snapshot(s)] };
344
+ }
345
+ case 'set': {
346
+ const t = normalizeNewlines(a.text ?? '');
347
+ return commit(s, { text: t, cursor: t.length, pastes: a.keepPastes ? s.pastes : {}, pasteSeq: a.keepPastes ? s.pasteSeq : 0 });
348
+ }
349
+ case 'clear':
350
+ return s.text === '' ? s : commit(s, { text: '', cursor: 0, pastes: {}, pasteSeq: 0 });
351
+ default:
352
+ return s;
353
+ }
354
+ }
355
+
356
+ /** Replace placeholders with the pasted text. */
357
+ export function expandPastes(s) {
358
+ return s.text.replace(PASTE_RE, (m, n) => (s.pastes[n] !== undefined ? s.pastes[n] : m));
359
+ }
360
+
361
+ // Invisible characters that can hide instructions in pasted text.
362
+ const INVISIBLE_RE = /[­͏؜ᅟᅠ឴឵᠋-᠏​-‏‪-‮⁠-⁤⁦-ㅤ︀-︎ᅠ\u{E0000}-\u{E007F}\u{E0100}-\u{E01EF}]/u;
363
+ const PICTO = /\p{Extended_Pictographic}/u;
364
+
365
+ /**
366
+ * Strip invisible characters. ZWJ (U+200D) is kept only between two
367
+ * pictographs (family/flag emoji); VS16 is kept (not in the set above).
368
+ * Returns { clean, removed: [{ code, count }], total }.
369
+ */
370
+ export function stripInvisible(text) {
371
+ const chars = Array.from(text);
372
+ const counts = new Map();
373
+ let out = '';
374
+ for (let i = 0; i < chars.length; i++) {
375
+ const ch = chars[i];
376
+ const isZwj = ch === '‍';
377
+ const keepZwj = isZwj && i > 0 && i + 1 < chars.length && PICTO.test(chars[i - 1]) && PICTO.test(chars[i + 1]);
378
+ if (!keepZwj && INVISIBLE_RE.test(ch)) {
379
+ const code = 'U+' + ch.codePointAt(0).toString(16).toUpperCase().padStart(4, '0');
380
+ counts.set(code, (counts.get(code) || 0) + 1);
381
+ continue;
382
+ }
383
+ out += ch;
384
+ }
385
+ const removed = [...counts].map(([code, count]) => ({ code, count }));
386
+ return { clean: out, removed, total: removed.reduce((n, r) => n + r.count, 0) };
387
+ }
388
+
389
+ /**
390
+ * Enter-time check. If anything invisible is present (in typed text or in a
391
+ * collapsed paste), return a cleaned buffer plus a note; the caller must NOT
392
+ * send, so the user reviews the cleaned text and presses Enter again.
393
+ * Otherwise returns { ok: true, text } with pastes expanded.
394
+ */
395
+ export function sanitizeForSend(s) {
396
+ const typed = stripInvisible(s.text);
397
+ const pastes = {};
398
+ let total = typed.total;
399
+ const removed = [...typed.removed];
400
+ for (const [k, v] of Object.entries(s.pastes)) {
401
+ const r = stripInvisible(v);
402
+ pastes[k] = r.clean;
403
+ total += r.total;
404
+ removed.push(...r.removed);
405
+ }
406
+ if (total === 0) return { ok: true, text: expandPastes(s) };
407
+ const merged = new Map();
408
+ for (const r of removed) merged.set(r.code, (merged.get(r.code) || 0) + r.count);
409
+ const detail = [...merged].map(([c, n]) => (n > 1 ? `${c} x${n}` : c)).join(', ');
410
+ const cursor = Math.min(s.cursor, typed.clean.length);
411
+ return {
412
+ ok: false,
413
+ buffer: { ...s, text: typed.clean, cursor, pastes },
414
+ note: `Removed ${total} invisible character${total === 1 ? '' : 's'} (${detail}). Review, then press Enter again to send.`
415
+ };
416
+ }