quoxcli 0.2.0 → 0.4.0

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 (442) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -0
  3. package/bin/quox.js +99 -126
  4. package/lib/api/endpoints/actions.js +15 -0
  5. package/lib/api/endpoints/admin.js +46 -0
  6. package/lib/api/endpoints/agent-schedules.js +26 -0
  7. package/lib/api/endpoints/agents.js +7 -0
  8. package/lib/api/endpoints/assistants.js +12 -0
  9. package/lib/api/endpoints/away.js +15 -0
  10. package/lib/api/endpoints/blog.js +16 -0
  11. package/lib/api/endpoints/chat.js +14 -0
  12. package/lib/api/endpoints/contactsq.js +6 -0
  13. package/lib/api/endpoints/conversations.js +14 -0
  14. package/lib/api/endpoints/devServerRegistry.js +25 -0
  15. package/lib/api/endpoints/engagement.js +26 -0
  16. package/lib/api/endpoints/evidence.js +20 -0
  17. package/lib/api/endpoints/executions.js +13 -0
  18. package/lib/api/endpoints/fetchAllowlist.js +27 -0
  19. package/lib/api/endpoints/files.js +14 -0
  20. package/lib/api/endpoints/hat.js +44 -0
  21. package/lib/api/endpoints/inbox.js +20 -0
  22. package/lib/api/endpoints/index.js +26 -0
  23. package/lib/api/endpoints/loops.js +26 -0
  24. package/lib/api/endpoints/memory.js +19 -0
  25. package/lib/api/endpoints/mind.js +13 -0
  26. package/lib/api/endpoints/mon.js +179 -0
  27. package/lib/api/endpoints/objectives.js +12 -0
  28. package/lib/api/endpoints/plugins.js +18 -0
  29. package/lib/api/endpoints/presence.js +33 -0
  30. package/lib/api/endpoints/quoxplan.js +7 -0
  31. package/lib/api/endpoints/quoxseo.js +12 -0
  32. package/lib/api/endpoints/router.js +17 -0
  33. package/lib/api/endpoints/safety.js +7 -0
  34. package/lib/api/endpoints/server.js +21 -0
  35. package/lib/api/endpoints/setup.js +6 -0
  36. package/lib/api/endpoints/skills.js +20 -0
  37. package/lib/api/endpoints/stepup.js +20 -0
  38. package/lib/api/endpoints/teams.js +2 -2
  39. package/lib/api/endpoints/triggers.js +26 -0
  40. package/lib/api/endpoints/v1.js +120 -0
  41. package/lib/api/endpoints/ward.js +7 -0
  42. package/lib/api/endpoints/workflows.js +7 -0
  43. package/lib/api/endpoints/x.js +8 -0
  44. package/lib/commands/actions.js +131 -0
  45. package/lib/commands/admin.js +16 -0
  46. package/lib/commands/agent.js +170 -27
  47. package/lib/commands/agentAttach.js +140 -13
  48. package/lib/commands/approvals.js +78 -1
  49. package/lib/commands/aside.js +24 -0
  50. package/lib/commands/ask.js +146 -0
  51. package/lib/commands/assistant.js +59 -4
  52. package/lib/commands/auth.js +8 -2
  53. package/lib/commands/backup.js +8 -4
  54. package/lib/commands/blog.js +110 -0
  55. package/lib/commands/brain.js +105 -14
  56. package/lib/commands/build.js +19 -5
  57. package/lib/commands/chat.js +180 -26
  58. package/lib/commands/chatCompanion.js +27 -0
  59. package/lib/commands/chatState.js +45 -0
  60. package/lib/commands/claude.js +35 -38
  61. package/lib/commands/contacts.js +134 -4
  62. package/lib/commands/contactsSendAt.js +21 -0
  63. package/lib/commands/conversation.js +35 -4
  64. package/lib/commands/dashboard.js +43 -0
  65. package/lib/commands/department.js +28 -7
  66. package/lib/commands/devResident.js +15 -3
  67. package/lib/commands/devSupervisor.js +13 -1
  68. package/lib/commands/discord.js +75 -39
  69. package/lib/commands/done.js +98 -28
  70. package/lib/commands/engagement.js +107 -6
  71. package/lib/commands/evidence.js +154 -0
  72. package/lib/commands/ext.js +23 -0
  73. package/lib/commands/fetchAllowlist.js +71 -0
  74. package/lib/commands/file.js +38 -1
  75. package/lib/commands/fleet.js +336 -4
  76. package/lib/commands/governance.js +12 -3
  77. package/lib/commands/gripe.js +44 -13
  78. package/lib/commands/hat.js +254 -9
  79. package/lib/commands/helpers.js +18 -1
  80. package/lib/commands/hyperion.js +58 -0
  81. package/lib/commands/image.js +68 -0
  82. package/lib/commands/inbox.js +74 -23
  83. package/lib/commands/kuma.js +1050 -0
  84. package/lib/commands/kumaSla.js +174 -0
  85. package/lib/commands/licence.js +52 -155
  86. package/lib/commands/loop.js +291 -0
  87. package/lib/commands/lucid.js +10 -4
  88. package/lib/commands/memory.js +85 -8
  89. package/lib/commands/mind.js +71 -0
  90. package/lib/commands/mon.js +69 -2
  91. package/lib/commands/monitoring.js +1 -1
  92. package/lib/commands/motion.js +2 -2
  93. package/lib/commands/nucleus.js +48 -17
  94. package/lib/commands/objective.js +59 -1
  95. package/lib/commands/offload.js +89 -0
  96. package/lib/commands/onboard.js +2 -1
  97. package/lib/commands/org.js +30 -3
  98. package/lib/commands/peek.js +28 -0
  99. package/lib/commands/plan.js +38 -0
  100. package/lib/commands/plugin.js +110 -0
  101. package/lib/commands/presence.js +89 -0
  102. package/lib/commands/provider.js +217 -0
  103. package/lib/commands/qlarity.js +19 -3
  104. package/lib/commands/quoxchat.js +13 -4
  105. package/lib/commands/quoxseo.js +69 -0
  106. package/lib/commands/reach.js +2 -1
  107. package/lib/commands/research.js +79 -0
  108. package/lib/commands/router.js +77 -0
  109. package/lib/commands/rows.js +367 -0
  110. package/lib/commands/run.js +82 -10
  111. package/lib/commands/scope.js +2 -2
  112. package/lib/commands/server.js +405 -0
  113. package/lib/commands/setup.js +33 -7
  114. package/lib/commands/shortcut.js +41 -0
  115. package/lib/commands/skills.js +68 -9
  116. package/lib/commands/slot.js +260 -7
  117. package/lib/commands/status.js +1 -1
  118. package/lib/commands/team.js +3 -1
  119. package/lib/commands/tips.js +23 -0
  120. package/lib/commands/tool.js +133 -45
  121. package/lib/commands/trace.js +12 -7
  122. package/lib/commands/trigger.js +95 -0
  123. package/lib/commands/update.js +46 -0
  124. package/lib/commands/vault.js +31 -8
  125. package/lib/commands/vm.js +237 -0
  126. package/lib/commands/voice.js +34 -3
  127. package/lib/commands/volt.js +45 -3
  128. package/lib/commands/walkthrough.js +32 -0
  129. package/lib/commands/watch.js +73 -21
  130. package/lib/commands/welcome.js +14 -0
  131. package/lib/commands/workflow.js +189 -32
  132. package/lib/commands/x.js +13 -4
  133. package/lib/commands/youtube.js +172 -0
  134. package/lib/core/actingAs.js +2 -2
  135. package/lib/core/actionItems.js +115 -0
  136. package/lib/core/activeSummary.js +186 -0
  137. package/lib/core/agentAlias.js +2 -0
  138. package/lib/core/agentFamilies.js +7 -0
  139. package/lib/core/aside.js +54 -0
  140. package/lib/core/askDecision.js +60 -0
  141. package/lib/core/auth.js +164 -13
  142. package/lib/core/callerSource.js +1 -1
  143. package/lib/core/cliBuild.js +62 -0
  144. package/lib/core/client.js +152 -35
  145. package/lib/core/commandRegistrars.js +130 -0
  146. package/lib/core/config.js +143 -25
  147. package/lib/core/customHats.js +168 -0
  148. package/lib/core/doneEngineFetch.js +8 -0
  149. package/lib/core/errors.js +329 -20
  150. package/lib/core/findingReconcile.js +90 -0
  151. package/lib/core/global-flags.js +105 -5
  152. package/lib/core/governedSession.js +1 -3
  153. package/lib/core/hyperion.js +102 -0
  154. package/lib/core/inboxResolution.js +65 -0
  155. package/lib/core/lazyCommands.js +4 -1
  156. package/lib/core/liveSessionsReport.js +1 -1
  157. package/lib/core/loopGlyph.js +97 -0
  158. package/lib/core/offload.js +533 -0
  159. package/lib/core/org-override.js +1 -1
  160. package/lib/core/orgResolve.js +70 -0
  161. package/lib/core/output.js +11 -11
  162. package/lib/core/peek.js +126 -0
  163. package/lib/core/profileUsers.js +51 -0
  164. package/lib/core/promptCancel.js +18 -0
  165. package/lib/core/repoPath.js +11 -0
  166. package/lib/core/residentSeed.js +3 -1
  167. package/lib/core/rootOptions.js +39 -0
  168. package/lib/core/router.js +168 -0
  169. package/lib/core/selfUpdate.js +188 -0
  170. package/lib/core/sessionHeartbeat.js +12 -8
  171. package/lib/core/sessionsOverview.js +64 -0
  172. package/lib/core/shortcut.js +83 -0
  173. package/lib/core/slotKeeper.js +248 -3
  174. package/lib/core/slotWake.js +120 -0
  175. package/lib/core/supervisorEngine.js +71 -2
  176. package/lib/core/version.js +1 -1
  177. package/lib/core/vm.js +366 -0
  178. package/lib/core/vmTui.js +86 -0
  179. package/lib/core/walkthrough.js +35 -0
  180. package/lib/dragon/companionView.js +34 -0
  181. package/lib/dragon/frames.js +118 -0
  182. package/lib/dragon/jobs.js +123 -0
  183. package/lib/dragon/renderWorker.js +15 -0
  184. package/lib/dragon/select.js +72 -0
  185. package/lib/evidence/bundleExport.js +47 -0
  186. package/lib/evidence/bundleVerify.js +77 -0
  187. package/lib/hyperion/deck.js +29 -0
  188. package/lib/lucid/LucidCard.jsx +68 -0
  189. package/lib/lucid/LucidIntro.jsx +45 -58
  190. package/lib/lucid/cards.js +86 -0
  191. package/lib/lucid/markdown.js +81 -0
  192. package/lib/lucid/motion.js +56 -0
  193. package/lib/quoxon/EXTENSIONS.md +23 -0
  194. package/lib/quoxon/activity.js +14 -2
  195. package/lib/quoxon/aocl.js +117 -0
  196. package/lib/quoxon/api.js +690 -25
  197. package/lib/quoxon/app.js +3586 -250
  198. package/lib/quoxon/approvalFacts.js +96 -0
  199. package/lib/quoxon/approvalSentence.js +44 -0
  200. package/lib/quoxon/approvalsView.js +384 -0
  201. package/lib/quoxon/asideView.js +40 -0
  202. package/lib/quoxon/ask.js +121 -0
  203. package/lib/quoxon/away.js +52 -0
  204. package/lib/quoxon/bang.js +224 -0
  205. package/lib/quoxon/bulkRisk.js +17 -0
  206. package/lib/quoxon/capabilities.js +13 -6
  207. package/lib/quoxon/cells.js +53 -0
  208. package/lib/quoxon/commandShort.js +124 -0
  209. package/lib/quoxon/commands.js +429 -39
  210. package/lib/quoxon/connect.js +210 -23
  211. package/lib/quoxon/connectCard.js +19 -2
  212. package/lib/quoxon/consent.js +86 -0
  213. package/lib/quoxon/copy.js +4 -3
  214. package/lib/quoxon/countdownLine.js +20 -0
  215. package/lib/quoxon/deck.js +30 -0
  216. package/lib/quoxon/demo.autoplay.js +29 -0
  217. package/lib/quoxon/demo.fixtures.js +1 -1
  218. package/lib/quoxon/demo.js +46 -3
  219. package/lib/quoxon/devObserver.js +20 -0
  220. package/lib/quoxon/dividerAocl.js +192 -0
  221. package/lib/quoxon/dividerRule.js +57 -0
  222. package/lib/quoxon/dragonCompanion.js +255 -0
  223. package/lib/quoxon/dragonModes.js +151 -0
  224. package/lib/quoxon/dragonMoments.js +82 -0
  225. package/lib/quoxon/dragonReads.js +68 -0
  226. package/lib/quoxon/dragonView.js +41 -0
  227. package/lib/quoxon/earlyInput.js +40 -0
  228. package/lib/quoxon/echo.js +15 -0
  229. package/lib/quoxon/egg.js +2 -2
  230. package/lib/quoxon/events.js +266 -26
  231. package/lib/quoxon/extensions.js +276 -0
  232. package/lib/quoxon/feedback.js +50 -0
  233. package/lib/quoxon/gallery.js +21 -7
  234. package/lib/quoxon/handoffOffer.js +78 -0
  235. package/lib/quoxon/hatSwitch.js +37 -0
  236. package/lib/quoxon/headless.js +161 -0
  237. package/lib/quoxon/hostUrls.js +41 -0
  238. package/lib/quoxon/inboxView.js +79 -0
  239. package/lib/quoxon/indicator.js +6 -2
  240. package/lib/quoxon/installCli.js +79 -0
  241. package/lib/quoxon/intent.js +12 -1
  242. package/lib/quoxon/keys.js +51 -8
  243. package/lib/quoxon/launcher.js +103 -0
  244. package/lib/quoxon/loopCard.js +456 -0
  245. package/lib/quoxon/loopCards.js +180 -0
  246. package/lib/quoxon/loopStream.js +87 -0
  247. package/lib/quoxon/main.js +101 -160
  248. package/lib/quoxon/markdown.js +45 -21
  249. package/lib/quoxon/markdownSettle.js +19 -0
  250. package/lib/quoxon/matrixDomains.json +46 -0
  251. package/lib/quoxon/mentions.js +90 -0
  252. package/lib/quoxon/mind.js +103 -0
  253. package/lib/quoxon/mode.js +46 -0
  254. package/lib/quoxon/model.js +132 -26
  255. package/lib/quoxon/modelPrefs.js +345 -0
  256. package/lib/quoxon/motion/clock.js +3 -2
  257. package/lib/quoxon/motion/level.js +91 -0
  258. package/lib/quoxon/mouse.js +222 -0
  259. package/lib/quoxon/noai.js +57 -0
  260. package/lib/quoxon/objectiveOffer.js +82 -0
  261. package/lib/quoxon/objects/away.js +72 -0
  262. package/lib/quoxon/objects/fetch.js +17 -5
  263. package/lib/quoxon/objects/fleet.js +149 -0
  264. package/lib/quoxon/objects/health.js +186 -0
  265. package/lib/quoxon/objects/index.js +46 -7
  266. package/lib/quoxon/objects/mirror.js +137 -0
  267. package/lib/quoxon/objects/model.js +33 -0
  268. package/lib/quoxon/objects/peek.js +17 -0
  269. package/lib/quoxon/objects/proof.js +12 -4
  270. package/lib/quoxon/objects/prove.js +130 -0
  271. package/lib/quoxon/objects/replay.js +125 -0
  272. package/lib/quoxon/objects/sitrep.js +58 -0
  273. package/lib/quoxon/objects/view.js +11 -0
  274. package/lib/quoxon/onboarding/copy.js +32 -4
  275. package/lib/quoxon/onboarding/credential.js +17 -4
  276. package/lib/quoxon/onboarding/dockerOffer.js +45 -0
  277. package/lib/quoxon/onboarding/failureCause.js +33 -0
  278. package/lib/quoxon/onboarding/finish.js +15 -0
  279. package/lib/quoxon/onboarding/index.js +34 -7
  280. package/lib/quoxon/onboarding/installRunner.js +64 -5
  281. package/lib/quoxon/onboarding/preflight.js +17 -3
  282. package/lib/quoxon/onboarding/probe.js +65 -0
  283. package/lib/quoxon/onboarding/shortcutOffer.js +28 -0
  284. package/lib/quoxon/onboarding/view.js +76 -19
  285. package/lib/quoxon/orgLabel.js +10 -0
  286. package/lib/quoxon/orgSwitch.js +59 -0
  287. package/lib/quoxon/ownBroadcast.js +22 -0
  288. package/lib/quoxon/palette.js +43 -0
  289. package/lib/quoxon/planSteps.js +51 -0
  290. package/lib/quoxon/print.js +12 -5
  291. package/lib/quoxon/profileSwitch.js +53 -0
  292. package/lib/quoxon/promptHistory.js +41 -0
  293. package/lib/quoxon/providers.js +48 -0
  294. package/lib/quoxon/qart.js +20 -51
  295. package/lib/quoxon/qdragon.js +410 -0
  296. package/lib/quoxon/qdragonFly.js +62 -0
  297. package/lib/quoxon/qdragonScenes.js +282 -0
  298. package/lib/quoxon/qeyes.js +213 -0
  299. package/lib/quoxon/qmotion.js +75 -37
  300. package/lib/quoxon/render.js +740 -124
  301. package/lib/quoxon/resend.js +72 -0
  302. package/lib/quoxon/roster.js +117 -17
  303. package/lib/quoxon/route.js +6 -5
  304. package/lib/quoxon/rows.js +954 -0
  305. package/lib/quoxon/rowsStore.js +80 -0
  306. package/lib/quoxon/screenContext.js +150 -0
  307. package/lib/quoxon/screenFrames.js +254 -0
  308. package/lib/quoxon/serverChip.js +42 -0
  309. package/lib/quoxon/session.js +182 -2
  310. package/lib/quoxon/sessionDead.js +17 -0
  311. package/lib/quoxon/signin.js +125 -0
  312. package/lib/quoxon/slash/agents.js +112 -0
  313. package/lib/quoxon/slash/argSources.js +65 -0
  314. package/lib/quoxon/slash/args.js +65 -0
  315. package/lib/quoxon/slash/automate.js +89 -0
  316. package/lib/quoxon/slash/broadcast.js +140 -0
  317. package/lib/quoxon/slash/broadcastRun.js +108 -0
  318. package/lib/quoxon/slash/card.js +30 -0
  319. package/lib/quoxon/slash/challenge.js +114 -0
  320. package/lib/quoxon/slash/cliCommand.js +94 -0
  321. package/lib/quoxon/slash/cliLeaves.json +1023 -0
  322. package/lib/quoxon/slash/compact.js +1 -1
  323. package/lib/quoxon/slash/confirm.js +30 -0
  324. package/lib/quoxon/slash/crystallize.js +69 -0
  325. package/lib/quoxon/slash/dashboard.js +136 -0
  326. package/lib/quoxon/slash/draft.js +54 -0
  327. package/lib/quoxon/slash/evidenceCmd.js +34 -0
  328. package/lib/quoxon/slash/hatWalk.js +206 -0
  329. package/lib/quoxon/slash/inverses.js +77 -0
  330. package/lib/quoxon/slash/killer.js +24 -0
  331. package/lib/quoxon/slash/killerApi.js +16 -0
  332. package/lib/quoxon/slash/ledger.js +13 -8
  333. package/lib/quoxon/slash/listView.js +3 -1
  334. package/lib/quoxon/slash/modelCmd.js +261 -0
  335. package/lib/quoxon/slash/palette.js +22 -8
  336. package/lib/quoxon/slash/paletteController.js +117 -7
  337. package/lib/quoxon/slash/panels.js +21 -6
  338. package/lib/quoxon/slash/picker.js +20 -2
  339. package/lib/quoxon/slash/qpeCmd.js +35 -0
  340. package/lib/quoxon/slash/restart.js +95 -13
  341. package/lib/quoxon/slash/resume.js +18 -1
  342. package/lib/quoxon/slash/rowCmd.js +157 -0
  343. package/lib/quoxon/slash/rowWalk.js +175 -0
  344. package/lib/quoxon/slash/settings.js +16 -6
  345. package/lib/quoxon/slash/spawn.js +177 -0
  346. package/lib/quoxon/slash/stepUp.js +274 -0
  347. package/lib/quoxon/slash/stepUpCard.js +60 -0
  348. package/lib/quoxon/slash/stepUpCmds.js +78 -0
  349. package/lib/quoxon/slash/todoCmd.js +122 -0
  350. package/lib/quoxon/slash/undoApi.js +38 -0
  351. package/lib/quoxon/slash/undoCmd.js +49 -0
  352. package/lib/quoxon/slash/undoWish.js +15 -0
  353. package/lib/quoxon/slash/watch.js +164 -0
  354. package/lib/quoxon/slash/wish.js +23 -0
  355. package/lib/quoxon/slash/wishCmd.js +32 -0
  356. package/lib/quoxon/slotChat.js +300 -0
  357. package/lib/quoxon/slotFormat.js +195 -0
  358. package/lib/quoxon/slotRun.js +131 -0
  359. package/lib/quoxon/sound/mapping.js +2 -1
  360. package/lib/quoxon/stray.js +22 -0
  361. package/lib/quoxon/suggestCommand.js +90 -0
  362. package/lib/quoxon/term.js +46 -11
  363. package/lib/quoxon/textBuffer.js +176 -2
  364. package/lib/quoxon/theme.js +58 -25
  365. package/lib/quoxon/tips.js +433 -0
  366. package/lib/quoxon/toolCards.js +30 -0
  367. package/lib/quoxon/toolError.js +41 -0
  368. package/lib/quoxon/transcript.js +42 -4
  369. package/lib/quoxon/web/qface.js +18 -10
  370. package/lib/quoxon/welcome.js +1 -1
  371. package/lib/quoxon/whatsnew.js +136 -0
  372. package/lib/quoxon/whatsnew.jsonl +134 -0
  373. package/lib/quoxon/windowOrg.js +123 -0
  374. package/lib/server/devServers.js +137 -0
  375. package/lib/server/diag.js +129 -0
  376. package/lib/server/doctor.js +71 -0
  377. package/lib/server/install.js +149 -0
  378. package/lib/server/pluginEnable.js +161 -0
  379. package/lib/server/skew.js +56 -0
  380. package/lib/server/snapshot.js +131 -0
  381. package/lib/server/status.js +128 -0
  382. package/lib/server/sys.js +65 -0
  383. package/lib/server/uninstall.js +63 -0
  384. package/lib/server/update.js +234 -0
  385. package/lib/toolOutputShape.js +315 -0
  386. package/lib/tui/views/HyperionDeck.jsx +79 -0
  387. package/lib/tui/views/LucidDeck.jsx +209 -217
  388. package/package.json +5 -1
  389. package/vendor/dragonlab/SOURCE +4 -0
  390. package/vendor/dragonlab/design/cipher/v1/cipher-master.svg +46 -0
  391. package/vendor/dragonlab/design/cipher/v2/character.mjs +49 -0
  392. package/vendor/dragonlab/design/cipher/v2/review.mjs +33 -0
  393. package/vendor/dragonlab/design/cipher/v2/watch.mjs +56 -0
  394. package/vendor/dragonlab/design/drago/v1/character.mjs +19 -0
  395. package/vendor/dragonlab/design/drago/v1/drago-master.svg +56 -0
  396. package/vendor/dragonlab/design/drago/v2/character.mjs +87 -0
  397. package/vendor/dragonlab/design/drago/v4/character.mjs +67 -0
  398. package/vendor/dragonlab/design/drago/v4/profile.mjs +35 -0
  399. package/vendor/dragonlab/design/drago/v4/review.mjs +33 -0
  400. package/vendor/dragonlab/design/drago/v4/watch.mjs +56 -0
  401. package/vendor/dragonlab/design/hyperion/v1/hyperion-master.svg +56 -0
  402. package/vendor/dragonlab/design/hyperion/v2/character.mjs +79 -0
  403. package/vendor/dragonlab/design/hyperion/v2/review.mjs +33 -0
  404. package/vendor/dragonlab/design/hyperion/v2/watch.mjs +56 -0
  405. package/vendor/dragonlab/design/lucid/recovery/arrival.mjs +22 -0
  406. package/vendor/dragonlab/design/lucid/recovery/character.mjs +106 -0
  407. package/vendor/dragonlab/design/lucid/recovery/layout.mjs +50 -0
  408. package/vendor/dragonlab/design/lucid/recovery/lucid-master.svg +78 -0
  409. package/vendor/dragonlab/design/lucid/recovery/motion.mjs +42 -0
  410. package/vendor/dragonlab/design/lucid/recovery/render.mjs +121 -0
  411. package/vendor/dragonlab/design/lucid/recovery/review.mjs +68 -0
  412. package/vendor/dragonlab/design/lucid/recovery/verify.mjs +84 -0
  413. package/vendor/dragonlab/design/lucid/recovery/watch.mjs +56 -0
  414. package/vendor/dragonlab/design/seer/v1/seer-master.svg +53 -0
  415. package/vendor/dragonlab/design/seer/v2/character.mjs +88 -0
  416. package/vendor/dragonlab/design/seer/v3/character.mjs +33 -0
  417. package/vendor/dragonlab/design/seer/v4/character.mjs +66 -0
  418. package/vendor/dragonlab/design/seer/v4/review.mjs +33 -0
  419. package/vendor/dragonlab/design/seer/v4/watch.mjs +56 -0
  420. package/vendor/dragonlab/design/stage3/companion-review.mjs +28 -0
  421. package/vendor/dragonlab/design/stage3/sentinel-art.js +39 -0
  422. package/vendor/dragonlab/design/stage3/sentinel-braille.js +216 -0
  423. package/vendor/dragonlab/design/stage3/sentinel-character.js +257 -0
  424. package/vendor/dragonlab/design/stage3/sentinel-icon.js +103 -0
  425. package/vendor/dragonlab/design/stage3/sentinel-warden-eyes.js +95 -0
  426. package/vendor/dragonlab/design/stage3/svg-coverage.js +148 -0
  427. package/vendor/dragonlab/factory/svg-rig.mjs +63 -0
  428. package/vendor/dragonlab/integration/companions-v1/characters.mjs +88 -0
  429. package/vendor/dragonlab/integration/companions-v1/cipher.mjs +2 -0
  430. package/vendor/dragonlab/integration/companions-v1/companion.mjs +63 -0
  431. package/vendor/dragonlab/integration/companions-v1/drago.mjs +2 -0
  432. package/vendor/dragonlab/integration/companions-v1/hyperion.mjs +2 -0
  433. package/vendor/dragonlab/integration/companions-v1/lucid.mjs +2 -0
  434. package/vendor/dragonlab/integration/companions-v1/seer.mjs +2 -0
  435. package/vendor/dragonlab/integration/companions-v1/sentinel.mjs +2 -0
  436. package/vendor/dragonlab/integration/companions-v1/view.mjs +28 -0
  437. package/vendor/dragonlab/integration/hyperion-v3/activity.mjs +76 -0
  438. package/vendor/dragonlab/lib/power-effects.js +294 -0
  439. package/vendor/dragonlab/lib/power-registry.js +74 -0
  440. package/vendor/dragonlab/vendor/quox-dragon/qart.js +348 -0
  441. package/vendor/dragonlab/vendor/quox-dragon/qeyes.js +213 -0
  442. package/lib/core/websiteSession.js +0 -148
@@ -0,0 +1,433 @@
1
+ /**
2
+ * "Did you know...?" tips (QUOXON-TIPS, plan v3.1 workstream N). One registry, one honesty gate.
3
+ *
4
+ * Every feature ships canned tips here. A tip is shown only when:
5
+ * - its maturity is stable or beta (prototype features NEVER show, whatever the install says), and
6
+ * - every capability it requires is `installed` on THIS install per GET /api/v1/capabilities
7
+ * (a missing or unreadable contract means only tips that require nothing can show), and
8
+ * - the /tip-settings mode allows its tier (off = none, core = free tier and stable only, all = everything shipped), and
9
+ * - the user is not already using the thing (skipIf signals, or its `try` slash command already run).
10
+ *
11
+ * `tier` is advisory and follows the capabilities catalog: a feature gated behind a plugin licence is
12
+ * 'paid', everything in the open core is 'free'. When unsure of a product's shipped state mark it
13
+ * 'prototype' (hidden) until docs/STATUS_AND_LIMITS_*.md says beta or better.
14
+ *
15
+ * The registry is plain data and `tipsJson()` exports it, so the dashboard and cli.quox.ai can reuse it.
16
+ * Pure module: no I/O, no clock, rng injected.
17
+ */
18
+
19
+ export const MODES = ['all', 'core', 'off'];
20
+ export const DEFAULT_MODE = 'all';
21
+ export const TIP_FOOTER = '(/tip for another · /tip-settings off to stop)';
22
+
23
+ // requires: capability ids from the server contract. skipIf: usage signals ('hosts' = fleet hosts already connected).
24
+ const BASE = [
25
+ // ---- core: talking to Quox --------------------------------------------------------------
26
+ { id: 'chat-attach', product: 'Agents', tier: 'free', maturity: 'beta', requires: ['agents'], skipIf: [],
27
+ text: 'You can talk to a specialist agent directly instead of going through CommanderQ.', try: '/attach' },
28
+ { id: 'chat-resume', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
29
+ text: 'Every conversation is kept on your server, so you can pick one up again later.', try: '/resume' },
30
+ { id: 'chat-compact', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
31
+ text: 'A long conversation getting heavy? Quox can summarise it and keep the decisions.', try: '/compact' },
32
+ { id: 'chat-copy', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
33
+ text: 'Copy the last code block from an answer straight to your clipboard.', try: '/copy code' },
34
+ { id: 'chat-cost', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
35
+ text: 'See the tokens and cost of this session per model, from real usage and never an estimate.', try: '/cost' },
36
+ { id: 'chat-model', product: 'Providers', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
37
+ text: 'Choose which AI provider and model answers your next turns, including a local Ollama.', try: '/model' },
38
+ { id: 'chat-model-chooser', product: 'Providers', tier: 'free', maturity: 'beta',
39
+ requires: [], skipIf: [],
40
+ text: '/model alone opens a chooser: pick a model with the arrow keys, press f to ' +
41
+ 'favourite it and d to make it your default.',
42
+ try: '/model' },
43
+ { id: 'chat-model-favs', product: 'Providers', tier: 'free', maturity: 'beta',
44
+ requires: [], skipIf: ['modelFavs'],
45
+ text: 'Switching models is one step with favourites: save the ones you use, then ' +
46
+ '/model 1 jumps to the first. They follow your account to other machines.',
47
+ try: '/model fav add' },
48
+ { id: 'chat-rows', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: ['rows'],
49
+ text: 'Put your own live stats above the status bar: /row add watches a URL or a command and shows a line you template, like 12/14 pass.',
50
+ try: '/row' },
51
+ { id: 'chat-bang', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
52
+ text: 'Need a quick command without opening a new session? Start a line with ! and it runs here, like !quox inbox list. The output stays with you unless you /share it.', try: '!ls' },
53
+ { id: 'chat-dashboard', product: 'Dashboard', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
54
+ text: 'Open the web dashboard already signed in, with a one-time link.', try: '/dashboard' },
55
+ { id: 'chat-sessions', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
56
+ text: 'See every live session, conversation, held slot and running job in your org in one view.', try: '/sessions all' },
57
+ { id: 'chat-doctor', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
58
+ text: 'Something feels off? Doctor checks services, sign-in, your provider and the CLI version.', try: '/doctor' },
59
+ { id: 'chat-gripe', product: 'Gripe', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
60
+ text: 'Found something broken or missing? Type it as a gripe and Quox files a GitHub issue after showing you first.', try: '/gripe' },
61
+
62
+ // ---- core: trust and evidence -----------------------------------------------------------
63
+ { id: 'trust-approvals', product: 'Approvals', tier: 'free', maturity: 'beta', requires: ['approvals'], skipIf: [],
64
+ text: 'Risky actions wait for your decision, and every decision is recorded with who, when and what.', try: '/approvals' },
65
+ { id: 'trust-proof', product: 'Receipts', tier: 'free', maturity: 'beta', requires: ['receipts'], skipIf: [],
66
+ text: 'Every tool call leaves an evidence chain you can read back and check.', try: '/proof' },
67
+ { id: 'trust-vault-lend', product: 'Vault', tier: 'free', maturity: 'stable', requires: ['vault'], skipIf: [],
68
+ text: 'Give AIs access to things without them ever knowing the credentials: the Vault lends a secret just in time and audits every lease by label, never by value.', try: 'quox vault list' },
69
+ { id: 'trust-connect', product: 'Vault', tier: 'free', maturity: 'beta', requires: ['vault'], skipIf: [],
70
+ text: 'Add an AI provider key safely. It goes into the Vault and never passes through the conversation.', try: '/connect openai' },
71
+
72
+ // ---- core: knowledge and awareness ------------------------------------------------------
73
+ { id: 'know-memory', product: 'Memory', tier: 'free', maturity: 'beta', requires: ['memory'], skipIf: [],
74
+ text: 'Quox remembers facts for your org and shows where each one came from, how old it is and how far to trust it.', try: '/memory' },
75
+ { id: 'know-qortex', product: 'Qortex', tier: 'free', maturity: 'beta', requires: ['qortex'], skipIf: [],
76
+ text: 'Search your org knowledge for past decisions and history, with the stored entries cited.', try: 'quox brain search' },
77
+ { id: 'know-sitrep', product: 'LUCID', tier: 'free', maturity: 'beta', requires: ['lucid'], skipIf: [],
78
+ text: 'One command for the situation now: what waits for you, what is running, and what Quox cannot see.', try: '/sitrep' },
79
+ { id: 'know-away', product: 'LUCID', tier: 'free', maturity: 'beta', requires: ['lucid'], skipIf: [],
80
+ text: 'Been away? Get a factual digest of what happened since you last used Quox.', try: '/away' },
81
+
82
+ // ---- core: automation -------------------------------------------------------------------
83
+ { id: 'auto-workflow', product: 'Workflows', tier: 'free', maturity: 'beta', requires: ['workflows'], skipIf: [],
84
+ text: 'See the step-by-step flow of a workflow run, with timings and the human gates it passed.', try: '/workflow' },
85
+ { id: 'auto-crystal', product: 'Crystals', tier: 'free', maturity: 'beta', requires: ['crystals'], skipIf: [],
86
+ text: 'A workflow that keeps succeeding can be turned into a Crystal. Quox checks it against the real rule.', try: '/crystal' },
87
+ { id: 'auto-skills', product: 'Skills', tier: 'free', maturity: 'beta', requires: ['skills'], skipIf: [],
88
+ text: 'Your org keeps a library of procedural skills that agents can look up and improve over time.', try: '/skills' },
89
+ { id: 'auto-hat', product: 'QuoxHat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
90
+ text: 'Hats decide which tools an agent may use on a turn, so a narrow job gets a narrow toolbox.', try: 'quox hat list' },
91
+
92
+ // ---- paid plugins -----------------------------------------------------------------------
93
+ { id: 'fleet-hosts', product: 'Fleet', tier: 'paid', maturity: 'beta', requires: ['fleet'], skipIf: ['hosts'],
94
+ text: 'Give AIs access to any host: install QuoxAgent on it and add it to QuoxBastion. Commands run governed, and risky ones wait for your approval.', try: 'quox fleet hosts' },
95
+ { id: 'seo-quoxseo', product: 'QuoxSEO', tier: 'paid', maturity: 'stable', requires: ['seo'], skipIf: [],
96
+ text: 'Save money on SEO tools: QuoxSEO crawls your sites, joins Search Console data and shows signed change events.', try: 'quox seo status' },
97
+ { id: 'mail-inboxq', product: 'InboxQ', tier: 'paid', maturity: 'beta', requires: ['mail'], skipIf: [],
98
+ text: 'InboxQ sorts your mail into focus, fyi, auto and screened, and proposes filing or drafting for your approval.', try: 'quox mail zones' },
99
+ { id: 'qlarity-inspect', product: 'QLarity', tier: 'paid', maturity: 'beta', requires: ['qlarity'], skipIf: [],
100
+ text: 'Ask what a live page is really doing: computed styles, console, network errors and screenshots, all receipted.', try: 'quox qlarity screenshot' },
101
+ { id: 'plan-quoxplan', product: 'QuoxPlan', tier: 'paid', maturity: 'beta', requires: ['plan'], skipIf: [],
102
+ text: 'Keep your roadmap in QuoxPlan and read or update streams, phases and notes from here.', try: 'quox plan focus' },
103
+ { id: 'slots-address', product: 'QuoxSlots', tier: 'paid', maturity: 'beta', requires: ['slots'], skipIf: [],
104
+ text: 'Give this session a durable address that people can message while you are away.', try: '/slot' },
105
+
106
+ // ---- more tips (QUOXON-TIPS search): social, integrations, models, parity, sessions --------
107
+ { id: 'social-x-post', product: 'X', tier: 'paid', maturity: 'beta', requires: [], skipIf: [],
108
+ plugins: ['x'],
109
+ text: 'With the X plugin, ask for a post and Quox files an approval card. Nothing is published until you approve it.',
110
+ try: 'quox x post' },
111
+ { id: 'social-x-thread', product: 'X', tier: 'paid', maturity: 'beta', requires: [], skipIf: [],
112
+ plugins: ['x'],
113
+ text: 'Write a thread in a file, blank line between posts, and Quox files one approval card for the whole thread.',
114
+ try: 'quox x thread' },
115
+ { id: 'social-x-engage', product: 'X', tier: 'paid', maturity: 'beta', requires: [], skipIf: [],
116
+ plugins: ['x'],
117
+ text: 'Likes, reposts and deletes on X also wait for your approval, and a daily engagement budget caps them.',
118
+ try: 'quox x like' },
119
+ { id: 'social-linkedin', product: 'LinkedIn', tier: 'paid', maturity: 'beta', requires: [], skipIf: [],
120
+ plugins: ['linkedin'],
121
+ text: 'With the LinkedIn plugin, drafts go to your approvals first and publish only after you say yes.',
122
+ try: 'quox linkedin post' },
123
+ { id: 'integrations-vault-types', product: 'Vault', tier: 'free', maturity: 'stable', requires: ['vault'], skipIf: [],
124
+ text: 'Not sure how to connect something? Ask which integration types your vault supports and what each needs.',
125
+ try: 'quox vault create' },
126
+ { id: 'integrations-vault-test', product: 'Vault', tier: 'free', maturity: 'stable', requires: ['vault'], skipIf: [],
127
+ text: 'Check that every stored credential still works with one command. Secrets show masked, never in full.',
128
+ try: 'quox vault test-all' },
129
+ { id: 'integrations-lan-host', product: 'Fleet', tier: 'free', maturity: 'beta', requires: [], skipIf: ['hosts'],
130
+ text: 'A machine on your LAN joins your fleet with one install command. /hosts add prints it, no AI needed.',
131
+ try: '/hosts add' },
132
+ { id: 'integrations-capabilities', product: 'Capabilities', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
133
+ text: 'Wondering what is ready here and what needs setup? Capabilities says why, and how to fix each gap.',
134
+ try: '/capabilities' },
135
+ { id: 'models-auto', product: 'Providers', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
136
+ text: 'Let your server policy choose the model for you instead of picking one yourself.',
137
+ try: '/model auto' },
138
+ { id: 'approvals-terminal', product: 'Approvals', tier: 'free', maturity: 'beta', requires: ['approvals'], skipIf: [],
139
+ text: 'Decide what is waiting without leaving the terminal: /approvals lists it, /approve N or /deny N decides.',
140
+ try: '/approve' },
141
+ { id: 'trust-aocl', product: 'Receipts', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
142
+ text: 'Expand the last turn into every AOCL layer, L0 ingress to L10 audit, with outcome and time for each.',
143
+ try: '/aocl' },
144
+ { id: 'sessions-rename', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
145
+ text: 'Name this session so you can tell it apart in the top rule and find it again with /resume.',
146
+ try: '/rename' },
147
+ { id: 'sessions-cli', product: 'Chat', tier: 'free', maturity: 'stable', requires: [], skipIf: [],
148
+ text: 'The same view of live sessions, held slots and running jobs is a plain command outside the terminal too.',
149
+ try: 'quox sessions' },
150
+ { id: 'chat-dragon', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
151
+ text: 'Lost in the last answer? The dragon explains it in the simplest possible words.',
152
+ try: '/dragon explain' },
153
+ { id: 'mail-search', product: 'InboxQ', tier: 'paid', maturity: 'beta', requires: ['mail'], skipIf: [],
154
+ text: 'Ask in plain words to find an email. InboxQ searches your connected accounts for you.',
155
+ try: 'quox mail list' },
156
+ { id: 'security-engagement', product: 'QuoxSecurity', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
157
+ text: 'Run a security check as an engagement: scope targets, scanners and recorded findings, not a one-off script.',
158
+ try: 'quox engagement list' },
159
+ { id: 'safety-estop', product: 'Safety', tier: 'free', maturity: 'stable', requires: [], skipIf: [],
160
+ text: 'An agent misbehaving? The kill switch stops that one agent at once, and a human releases it later.',
161
+ try: 'quox safety status' },
162
+ { id: 'spend-budget', product: 'Budgets', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
163
+ text: 'Put a budget on workflow spend and usage, by org, objective, execution or iteration.',
164
+ try: 'quox budget' },
165
+
166
+ { id: 'bang-share', product: 'Chat', tier: 'free', maturity: 'beta', requires: [], skipIf: [],
167
+ text: 'Output of a ! command stays on your screen. /share attaches the latest one to your next message.',
168
+ try: '/share' },
169
+
170
+ // ---- not shipped: never shown (the gate holds even when the install reports the capability) ----
171
+ { id: 'voice-phone-call', product: 'QuoxVoice', tier: 'paid', maturity: 'prototype', requires: ['voice'], skipIf: [],
172
+ text: 'Quox can call you on the telephone.', try: 'quox voice calls' },
173
+ { id: 'calendar-calendarq', product: 'Calendar-Q', tier: 'paid', maturity: 'prototype', requires: ['calendar'], skipIf: [],
174
+ text: 'Read your agenda and let Quox propose events for your approval.', try: 'quox calendar agenda' }
175
+ ];
176
+
177
+ export const SHOWN_MATURITY = ['stable', 'beta'];
178
+
179
+ // Search metadata per tip: title, topics, domain (a proof-matrix domain id, matrixDomains.json) and the
180
+ // optional matrixRef: the agent tool (x_post), CLI path (quox x post) or slash command (/aocl) the tip advertises.
181
+ const M = (title, domain, topics, matrixRef = null) => ({ title, domain, topics, matrixRef });
182
+ const META = {
183
+ 'chat-attach': M('Talk to a specialist agent', 'agents', ['agents', 'sessions', 'chat'], '/attach'),
184
+ 'chat-resume': M('Pick a conversation up again', 'chat', ['sessions', 'chat', 'memory'], '/resume'),
185
+ 'chat-compact': M('Compact a long conversation', 'chat', ['sessions', 'chat'], '/compact'),
186
+ 'chat-copy': M('Copy the last code block', 'chat', ['chat'], '/copy'),
187
+ 'chat-cost': M('See tokens and cost', 'providers', ['models', 'plans', 'chat'], '/cost'),
188
+ 'chat-model': M('Choose the provider and model', 'providers', ['models'], '/model'),
189
+ 'chat-model-chooser': M('The model chooser', 'providers', ['models'], '/model'),
190
+ 'chat-model-favs': M('Favourite models', 'providers', ['models'], '/model'),
191
+ 'chat-rows': M('Custom rows above the status bar', 'chat', ['chat'], '/row'),
192
+ 'chat-dashboard': M('Open the dashboard signed in', 'identity', ['plans', 'sessions'], '/dashboard'),
193
+ 'chat-sessions': M('Every live session in one view', 'sessions', ['sessions', 'fleet'], '/sessions'),
194
+ 'chat-doctor': M('Run Doctor', 'install', ['fleet', 'models'], '/doctor'),
195
+ 'chat-gripe': M('File a gripe', 'chat', ['sessions'], '/gripe'),
196
+ 'trust-approvals': M('Risky actions wait for you', 'approvals', ['approvals', 'security'], '/approvals'),
197
+ 'trust-proof': M('Read back the evidence chain', 'receipts', ['security', 'approvals'], '/proof'),
198
+ 'trust-vault-lend': M('Lend access without sharing secrets', 'vault', ['vault', 'security', 'integrations'], 'vault_list'),
199
+ 'trust-connect': M('Add a provider key safely', 'vault', ['vault', 'integrations', 'models'], '/connect'),
200
+ 'know-memory': M('Memory with provenance', 'memory', ['memory'], '/memory'),
201
+ 'know-qortex': M('Search your org knowledge', 'qortex', ['memory'], 'quox brain search'),
202
+ 'know-sitrep': M('The situation right now', 'lucid', ['sessions', 'approvals'], '/sitrep'),
203
+ 'know-away': M('What happened while you were away', 'lucid', ['sessions'], '/away'),
204
+ 'auto-workflow': M('Read a workflow run', 'workflows', ['approvals', 'plans'], '/workflow'),
205
+ 'auto-crystal': M('Turn a workflow into a Crystal', 'skills', ['plans'], '/crystal'),
206
+ 'auto-skills': M('The skills library', 'skills', ['memory'], '/skills'),
207
+ 'auto-hat': M('Hats narrow the toolbox', 'hats', ['security', 'agents'], 'quox hat list'),
208
+ 'fleet-hosts': M('Give AIs access to any host', 'fleet', ['fleet', 'security', 'approvals'], 'quox fleet hosts'),
209
+ 'seo-quoxseo': M('Self-host your SEO', 'seo', ['seo'], 'quoxseo_summary'),
210
+ 'mail-inboxq': M('InboxQ sorts your mail', 'mail', ['email', 'approvals'], 'quox mail zones'),
211
+ 'qlarity-inspect': M('Inspect a live page', 'browser', ['security'], 'quox qlarity screenshot'),
212
+ 'plan-quoxplan': M('Roadmap in QuoxPlan', 'plan', ['plans'], 'quox plan focus'),
213
+ 'slots-address': M('A durable address for this session', 'sessions', ['sessions'], '/slot'),
214
+ 'voice-phone-call': M('Quox calls you', 'voice', ['voice'], 'quox voice calls'),
215
+ 'calendar-calendarq': M('Your agenda', 'calendar', ['plans'], 'quox calendar agenda'),
216
+ 'social-x-post': M('Post to X with approval', 'social', ['social', 'x', 'twitter', 'approvals'], 'x_post'),
217
+ 'social-x-thread': M('Post an X thread', 'social', ['social', 'x', 'twitter', 'approvals'], 'x_thread'),
218
+ 'social-x-engage': M('Engagement on X is approved too', 'social', ['social', 'x', 'twitter', 'approvals'], 'x_budget_status'),
219
+ 'social-linkedin': M('Post to LinkedIn with approval', 'social', ['social', 'linkedin', 'approvals'], 'quox linkedin post'),
220
+ 'integrations-vault-types': M('Connect an integration', 'vault', ['integrations', 'vault'], 'vault_types'),
221
+ 'integrations-vault-test': M('Test every credential', 'vault', ['integrations', 'vault', 'security'], 'quox vault test-all'),
222
+ 'integrations-lan-host': M('Add a LAN machine', 'fleet', ['integrations', 'fleet', 'lan'], '/hosts'),
223
+ 'integrations-capabilities': M('What is ready here', 'install', ['integrations', 'plans'], '/capabilities'),
224
+ 'models-auto': M('Let policy pick the model', 'providers', ['models'], '/model'),
225
+ 'approvals-terminal': M('Decide approvals in the terminal', 'approvals', ['approvals'], 'list_approvals'),
226
+ 'trust-aocl': M('Every AOCL layer of a turn', 'receipts', ['security', 'approvals'], '/aocl'),
227
+ 'sessions-rename': M('Name this session', 'sessions', ['sessions'], '/rename'),
228
+ 'sessions-cli': M('Sessions from the shell', 'sessions', ['sessions', 'fleet'], 'quox sessions'),
229
+ 'chat-bang': M('Run a shell command with !', 'chat', ['chat', 'sessions'], '/share'),
230
+ 'bang-share': M('Share ! output with Quox', 'chat', ['chat', 'sessions'], '/share'),
231
+ 'chat-dragon': M('The dragon explains', 'chat', ['dragon'], '/dragon'),
232
+ 'mail-search': M('Find an email by asking', 'mail', ['email'], 'inbox_search'),
233
+ 'security-engagement': M('Security checks as engagements', 'security', ['security'], 'quox engagement list'),
234
+ 'safety-estop': M('The agent kill switch', 'safety', ['security', 'agents'], 'quox safety status'),
235
+ 'spend-budget': M('Budget your spend', 'spend', ['plans', 'models'], 'quox budget')
236
+ };
237
+
238
+ export const TIPS = BASE.map(t => ({ ...t, ...(META[t.id] || {}) }));
239
+
240
+ export const normaliseMode = (m) => (MODES.includes(String(m ?? '').trim().toLowerCase()) ? String(m).trim().toLowerCase() : null);
241
+
242
+ /** The slash command or quox verb a tip points at, e.g. '/sitrep' for '/sitrep now'. */
243
+ export const tryCommand = (tip) => String(tip.try || '').trim().split(/\s+/)[0];
244
+
245
+ /**
246
+ * Is this tip allowed right now?
247
+ * ctx = { mode, contract, signals, used, plugins }
248
+ * plugins: installed plugin ids for the org; a tip with `plugins: [...]` needs all of them (unread = hidden)
249
+ * contract: capabilityMap() result ({ id: { available } }) or null when the server cannot answer
250
+ * signals: { hosts: number } cheap usage facts; unknown signals are absent, which never skips a tip
251
+ * used: slash commands the user has already run (e.g. ['/sitrep'])
252
+ */
253
+ export function eligible(tip, ctx = {}) {
254
+ const mode = normaliseMode(ctx.mode) || DEFAULT_MODE;
255
+ if (mode === 'off') return false;
256
+ if (!SHOWN_MATURITY.includes(tip.maturity)) return false;
257
+ if (mode === 'core' && (tip.tier !== 'free' || tip.maturity !== 'stable')) return false;
258
+ for (const id of tip.requires || []) if (ctx.contract?.[id]?.available !== true) return false;
259
+ // plugin-gated tips show only when the org's installed plugin list was read and has every one (unknown = hidden)
260
+ for (const id of tip.plugins || []) if (!(ctx.plugins || []).includes(id)) return false;
261
+ for (const sig of tip.skipIf || []) if (Number(ctx.signals?.[sig]) > 0) return false;
262
+ if (tryCommand(tip).startsWith('/') && (ctx.used || []).includes(tryCommand(tip))) return false;
263
+ return true;
264
+ }
265
+
266
+ export const eligibleTips = (ctx, tips = TIPS) => tips.filter(t => eligible(t, ctx));
267
+
268
+ /**
269
+ * The next tip. Rotates without repeats until every eligible tip has been shown, then starts a new round
270
+ * (never the one just shown first). ctx.seen = ids shown so far; ctx.rng defaults to Math.random.
271
+ * -> { tip, seen } (persist `seen`), or { tip: null, seen } when nothing is eligible.
272
+ */
273
+ export function nextTip(ctx = {}, tips = TIPS) {
274
+ const rng = ctx.rng || Math.random;
275
+ const pool = eligibleTips(ctx, tips);
276
+ const seen = Array.isArray(ctx.seen) ? ctx.seen : [];
277
+ if (!pool.length) return { tip: null, seen };
278
+ let fresh = pool.filter(t => !seen.includes(t.id));
279
+ let nextSeen = seen;
280
+ if (!fresh.length) { // round complete: start over, but not with the tip that just ended the last round
281
+ const last = seen[seen.length - 1];
282
+ fresh = pool.length > 1 ? pool.filter(t => t.id !== last) : pool;
283
+ nextSeen = [];
284
+ }
285
+ const tip = fresh[Math.min(fresh.length - 1, Math.floor(rng() * fresh.length))];
286
+ return { tip, seen: [...nextSeen.filter(id => id !== tip.id), tip.id] };
287
+ }
288
+
289
+ /** The plain one-line form: "Tip: <text> Try: <cmd> (/tip for another · /tip-settings off to stop)". */
290
+ /** Beta tips carry a visible "(beta)" marker; stable tips show bare. */
291
+ export const BETA_MARK = '(beta)';
292
+ export const tipText = (tip) => (tip.maturity === 'beta' ? `${tip.text} ${BETA_MARK}` : tip.text);
293
+ export const tipLine = (tip) => `Tip: ${tipText(tip)} Try: ${tip.try} ${TIP_FOOTER}`;
294
+
295
+ /** Registry as JSON-safe data for `quox tips --json`, the dashboard and cli.quox.ai. */
296
+ export function tipsJson(tips = TIPS) {
297
+ return {
298
+ version: 2, modes: MODES, defaultMode: DEFAULT_MODE, shownMaturity: SHOWN_MATURITY,
299
+ tips: tips.map(t => ({
300
+ id: t.id, title: t.title, text: t.text, try: t.try, topics: [...(t.topics || [])], domain: t.domain,
301
+ matrixRef: t.matrixRef ?? null, tier: t.tier, product: t.product, maturity: t.maturity,
302
+ requires: [...t.requires], skipIf: [...t.skipIf]
303
+ }))
304
+ };
305
+ }
306
+
307
+ /** Tip counts by product, tier and maturity (for reports and drift checks). */
308
+ export function tipCounts(tips = TIPS) {
309
+ const by = (k) => tips.reduce((o, t) => ((o[t[k]] = (o[t[k]] || 0) + 1), o), {});
310
+ return { total: tips.length, product: by('product'), tier: by('tier'), maturity: by('maturity') };
311
+ }
312
+
313
+ /** /tip-settings [off|core|all] -> { mode?, text, tone }. mode is set only when the setting should be saved. */
314
+ export function tipSettingsReply(arg, current = DEFAULT_MODE) {
315
+ const a = String(arg ?? '').trim().toLowerCase();
316
+ const cur = normaliseMode(current) || DEFAULT_MODE;
317
+ const about = { off: 'no tips', core: 'only tips about free, proven (stable) features', all: 'tips about everything that is shipped' };
318
+ if (!a) return { text: `Tips: ${cur} (${about[cur]}). Change it with /tip-settings off|core|all`, tone: 'plain' };
319
+ const m = normaliseMode(a);
320
+ if (!m) return { text: 'Usage: /tip-settings off|core|all (off = no tips, core = free features only, all = everything shipped)', tone: 'warn' };
321
+ return { mode: m, text: `Tips set to ${m}: ${about[m]}.${m === 'off' ? ' /tip-settings all turns them back on.' : ''}`, tone: 'ok' };
322
+ }
323
+
324
+ // ---- /tips <query>: search by topic and free text -----------------------------------------
325
+ export const PAGE = 5;
326
+ const STOP = new Set(['a', 'an', 'the', 'and', 'or', 'to', 'of', 'for', 'in', 'on', 'my', 'how', 'do', 'i', 'with']);
327
+ const stem = (w) => (w.length > 3 && w.endsWith('s') && !w.endsWith('ss') ? w.slice(0, -1) : w);
328
+ const words = (s) => String(s ?? '').toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
329
+ const queryTokens = (q) => words(q).filter(w => !STOP.has(w)).map(stem);
330
+
331
+ /** Rank score of one tip for the query tokens (0 = no match). Topic > domain > title > command > text. */
332
+ export function scoreTip(tip, tokens) {
333
+ if (!tokens.length) return 0;
334
+ const topics = (tip.topics || []).map(stem);
335
+ const title = words(tip.title).map(stem);
336
+ const text = words(`${tip.text} ${tip.product}`).map(stem);
337
+ const cmd = words(`${tip.try} ${tip.matrixRef || ''}`).map(stem);
338
+ let score = 0;
339
+ for (const t of tokens) {
340
+ let s = 0;
341
+ if (topics.includes(t)) s = 10;
342
+ else if (stem(String(tip.domain || '')) === t) s = 8;
343
+ else if (title.includes(t)) s = 6;
344
+ else if (cmd.includes(t)) s = 4;
345
+ else if (text.includes(t)) s = 2;
346
+ else if (t.length >= 3 && [...topics, ...title, ...cmd, ...text].some(w => w.startsWith(t))) s = 1;
347
+ score += s;
348
+ }
349
+ return score;
350
+ }
351
+
352
+ // core = free tier AND stable (beta tips, which the matrix has not proven, are hidden under core)
353
+ const allowedByMode = (tip, mode) => mode !== 'off' && !(mode === 'core' && (tip.tier !== 'free' || tip.maturity !== 'stable'));
354
+
355
+ /**
356
+ * Search the shipped tips. ctx = { mode }. Prototype tips never match. Tips the /tip-settings mode hides
357
+ * are not returned but counted in `hidden`. -> { matches, hidden } (matches ranked best first).
358
+ */
359
+ export function searchTips(query, ctx = {}, tips = TIPS) {
360
+ const mode = normaliseMode(ctx.mode) || DEFAULT_MODE;
361
+ const tokens = queryTokens(query);
362
+ // ctx.plugins: installed plugin ids. Undefined = not checked (offline `quox tips`); a list gates plugin tips.
363
+ const pluginOk = (t) => !ctx.plugins || (t.plugins || []).every(id => ctx.plugins.includes(id));
364
+ const scored = tips.filter(t => SHOWN_MATURITY.includes(t.maturity) && pluginOk(t))
365
+ .map((tip, i) => ({ tip, i, s: scoreTip(tip, tokens) })).filter(x => x.s > 0)
366
+ .sort((a, b) => b.s - a.s || a.i - b.i);
367
+ const matches = scored.filter(x => allowedByMode(x.tip, mode)).map(x => x.tip);
368
+ return { matches, hidden: scored.length - matches.length };
369
+ }
370
+
371
+ /** Topics of the shipped tips with counts the mode lets through: [{ topic, count }] biggest first. */
372
+ export function topicList(ctx = {}, tips = TIPS) {
373
+ const mode = normaliseMode(ctx.mode) || DEFAULT_MODE;
374
+ const by = new Map();
375
+ for (const t of tips) {
376
+ if (!SHOWN_MATURITY.includes(t.maturity) || !allowedByMode(t, mode)) continue;
377
+ for (const topic of t.topics || []) by.set(topic, (by.get(topic) || 0) + 1);
378
+ }
379
+ return [...by].map(([topic, count]) => ({ topic, count }))
380
+ .sort((a, b) => b.count - a.count || a.topic.localeCompare(b.topic));
381
+ }
382
+
383
+ const wrap = (text, width, indent) => {
384
+ const out = []; let line = '';
385
+ for (const w of String(text).split(/\s+/)) {
386
+ if (line && line.length + 1 + w.length > width) { out.push(line); line = w; } else line = line ? `${line} ${w}` : w;
387
+ }
388
+ if (line) out.push(line);
389
+ return out.map((l, i) => (i ? indent : '') + l);
390
+ };
391
+
392
+ /**
393
+ * /tips [query] [more] -> { lines: [string], tone }. Bare: the topic list. With a query: up to 5 ranked tips;
394
+ * a trailing "more" shows the rest. ctx = { mode }. Lines are at most 96 columns.
395
+ */
396
+ export function tipsReply(arg, ctx = {}, tips = TIPS) {
397
+ const raw = String(arg ?? '').trim();
398
+ const mode = normaliseMode(ctx.mode) || DEFAULT_MODE;
399
+ if (!raw) {
400
+ const topics = topicList({ mode }, tips);
401
+ if (!topics.length) {
402
+ return { lines: [mode === 'off' ? 'Tips are off. /tip-settings all turns them back on.' : 'No tips to show.'], tone: 'warn' };
403
+ }
404
+ const lines = ['Tip topics (try /tips <topic> or /tips <any words>):'];
405
+ lines.push(...wrap(topics.map(t => `${t.topic} (${t.count})`).join(' · '), 94, ' ').map((l, i) => (i ? l : ` ${l}`)));
406
+ return { lines, tone: 'plain' };
407
+ }
408
+ const more = /\smore$|^more$/i.test(raw);
409
+ const query = raw.replace(/\s*\bmore$/i, '').trim();
410
+ if (!query) return tipsReply('', ctx, tips);
411
+ const { matches, hidden } = searchTips(query, { mode, plugins: ctx.plugins }, tips);
412
+ const hiddenNote = hidden ? `${hidden} tip${hidden === 1 ? '' : 's'} hidden by your tip-settings (${mode}). ` +
413
+ '/tip-settings all shows them.' : '';
414
+ if (!matches.length) {
415
+ const lines = [`No tips match "${query}". /tips alone lists the topics.`];
416
+ if (hiddenNote) lines.push(hiddenNote);
417
+ return { lines, tone: 'warn' };
418
+ }
419
+ const shown = more ? matches.slice(PAGE) : matches.slice(0, PAGE);
420
+ if (!shown.length) return { lines: [`No more tips for "${query}".`], tone: 'plain' };
421
+ const head = `Tips for "${query}" (${matches.length}${more ? `, from ${PAGE + 1}` : ''}):`;
422
+ const lines = [head];
423
+ shown.forEach((t, i) => {
424
+ const n = (more ? PAGE : 0) + i + 1;
425
+ lines.push(...wrap(`${n}. ${t.title}: ${tipText(t)}`, 92, ' '));
426
+ lines.push(` Try: ${t.try}`);
427
+ });
428
+ const rest = more ? 0 : matches.length - shown.length;
429
+ if (rest > 0) lines.push(`(${rest} more: /tips ${query} more)`);
430
+ if (hiddenNote) lines.push(hiddenNote);
431
+ // Structured form for the TUI: one framed tip per match; `tail` = the lines that follow the frames.
432
+ return { lines, tone: 'plain', head, tips: shown.map(t => ({ text: `${t.title}: ${tipText(t)}`, cmd: t.try })), tail: [...(rest > 0 ? [`(${rest} more: /tips ${query} more)`] : []), ...(hiddenNote ? [hiddenNote] : [])] };
433
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * HITL-INBOX-BAR (owner 2026-10-26: "more programmatic output, so nice and clear"): when a tool's result is a list, the
3
+ * facts are drawn as a table from the tool result, above whatever the model then says. Keyed by tool name; the server
4
+ * puts a bounded `card` on tool.finished (services/collector/lib/conversationToolEvents.js RESULT_CARDS). To add a
5
+ * list answer: add a renderer here and the matching projection there.
6
+ */
7
+ import { ageOf, fromOf } from './inboxView.js';
8
+
9
+ const clip = (s, n) => { const t = String(s ?? '').replace(/\s+/g, ' ').trim(); return t.length > n ? `${t.slice(0, Math.max(0, n - 1))}…` : t; };
10
+ const pad = (s, n) => { const t = clip(s, n); return t + ' '.repeat(Math.max(0, n - [...t].length)); };
11
+
12
+ const RENDERERS = {
13
+ /** inbox_pending rows -> `# when kind priority title state` */
14
+ inbox_pending(card, { cols, now }) {
15
+ const rows = card.rows || [];
16
+ const tw = Math.max(16, Math.min(60, cols - 46));
17
+ const out = [{ head: true, text: `${pad('#', 3)}${pad('when', 6)}${pad('kind', 10)}${pad('prio', 8)}${pad('title', tw)} state` }];
18
+ rows.forEach((r, i) => out.push({ text: `${pad(i + 1, 3)}${pad(ageOf(r.created_at, now), 6)}${pad(r.kind || fromOf(r), 10)}${pad(r.priority || '', 8)}${pad(r.title || 'untitled', tw)} ${r.status || ''}` }));
19
+ if (Number.isInteger(card.total) && card.total > rows.length) out.push({ head: true, text: `showing ${rows.length} of ${card.total} · /inbox` });
20
+ return out;
21
+ },
22
+ };
23
+
24
+ /** -> [{ text, head? }] for a settled tool block that carries a card; [] otherwise. Never throws. */
25
+ export function toolCardLines(b, cols = 100, now = Date.now()) {
26
+ const fn = b && b.card && Array.isArray(b.card.rows) && RENDERERS[b.tool];
27
+ if (!fn) return [];
28
+ try { return fn(b.card, { cols, now }); } catch { return []; }
29
+ }
30
+ export const cardTools = () => Object.keys(RENDERERS);
@@ -0,0 +1,41 @@
1
+ /**
2
+ * A failed tool's result_summary can arrive as the tool's raw output: `error: Exit code 1 { "success": false,
3
+ * "error": "Tool x not granted to this agent" }`. The tool line shows the plain message instead; the raw text is kept
4
+ * (rawSummary) and shown at /verbose full.
5
+ */
6
+ const UNESCAPE = { n: ' ', t: ' ', r: ' ', '"': '"', '\\': '\\', '/': '/' };
7
+
8
+ function messageOf(obj) {
9
+ if (!obj || typeof obj !== 'object') return null;
10
+ for (const k of ['error', 'message', 'reason', 'detail', 'msg']) {
11
+ const v = obj[k];
12
+ if (typeof v === 'string' && v.trim()) return v.trim();
13
+ if (v && typeof v === 'object') { const inner = messageOf(v); if (inner) return inner; }
14
+ }
15
+ return null;
16
+ }
17
+
18
+ /** -> the plain message, or null when the text is not a JSON-bearing error. */
19
+ export function plainToolError(summary) {
20
+ const raw = String(summary ?? '').trim();
21
+ const at = raw.indexOf('{');
22
+ if (at < 0) return null;
23
+ const before = raw.slice(0, at).trim();
24
+ if (before && !/^(error:?\s*)?(exit code \d+:?)?\s*$/i.test(before) && !/^(error|failed)\b[^{]{0,40}$/i.test(before)) return null; // prose before the brace is the message itself
25
+ let msg = null;
26
+ try { msg = messageOf(JSON.parse(raw.slice(at))); } catch { /* truncated JSON: read the field by hand */ }
27
+ if (!msg) {
28
+ const m = /"(?:error|message|reason|detail|msg)"\s*:\s*"((?:[^"\\]|\\.)*)"?/.exec(raw.slice(at));
29
+ if (m) msg = m[1].replace(/\\(.)/g, (_, c) => UNESCAPE[c] ?? c).trim();
30
+ }
31
+ if (!msg) return null;
32
+ msg = msg.replace(/^Tool\s+[\w.:-]+\s+(?:is\s+|was\s+)?/i, '').replace(/^Error:\s*/i, '').trim();
33
+ return msg || null;
34
+ }
35
+
36
+ /** The text the tool line shows: `error: <plain message>` for a JSON error, else the summary untouched; raw at verbose full. */
37
+ export function toolResultText(summary, verbose = 'on') {
38
+ if (summary == null || verbose === 'full') return summary;
39
+ const plain = plainToolError(summary);
40
+ return plain ? `error: ${plain}` : summary;
41
+ }