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.
- package/LICENSE +1 -1
- package/README.md +2 -0
- package/bin/quox.js +99 -126
- package/lib/api/endpoints/actions.js +15 -0
- package/lib/api/endpoints/admin.js +46 -0
- package/lib/api/endpoints/agent-schedules.js +26 -0
- package/lib/api/endpoints/agents.js +7 -0
- package/lib/api/endpoints/assistants.js +12 -0
- package/lib/api/endpoints/away.js +15 -0
- package/lib/api/endpoints/blog.js +16 -0
- package/lib/api/endpoints/chat.js +14 -0
- package/lib/api/endpoints/contactsq.js +6 -0
- package/lib/api/endpoints/conversations.js +14 -0
- package/lib/api/endpoints/devServerRegistry.js +25 -0
- package/lib/api/endpoints/engagement.js +26 -0
- package/lib/api/endpoints/evidence.js +20 -0
- package/lib/api/endpoints/executions.js +13 -0
- package/lib/api/endpoints/fetchAllowlist.js +27 -0
- package/lib/api/endpoints/files.js +14 -0
- package/lib/api/endpoints/hat.js +44 -0
- package/lib/api/endpoints/inbox.js +20 -0
- package/lib/api/endpoints/index.js +26 -0
- package/lib/api/endpoints/loops.js +26 -0
- package/lib/api/endpoints/memory.js +19 -0
- package/lib/api/endpoints/mind.js +13 -0
- package/lib/api/endpoints/mon.js +179 -0
- package/lib/api/endpoints/objectives.js +12 -0
- package/lib/api/endpoints/plugins.js +18 -0
- package/lib/api/endpoints/presence.js +33 -0
- package/lib/api/endpoints/quoxplan.js +7 -0
- package/lib/api/endpoints/quoxseo.js +12 -0
- package/lib/api/endpoints/router.js +17 -0
- package/lib/api/endpoints/safety.js +7 -0
- package/lib/api/endpoints/server.js +21 -0
- package/lib/api/endpoints/setup.js +6 -0
- package/lib/api/endpoints/skills.js +20 -0
- package/lib/api/endpoints/stepup.js +20 -0
- package/lib/api/endpoints/teams.js +2 -2
- package/lib/api/endpoints/triggers.js +26 -0
- package/lib/api/endpoints/v1.js +120 -0
- package/lib/api/endpoints/ward.js +7 -0
- package/lib/api/endpoints/workflows.js +7 -0
- package/lib/api/endpoints/x.js +8 -0
- package/lib/commands/actions.js +131 -0
- package/lib/commands/admin.js +16 -0
- package/lib/commands/agent.js +170 -27
- package/lib/commands/agentAttach.js +140 -13
- package/lib/commands/approvals.js +78 -1
- package/lib/commands/aside.js +24 -0
- package/lib/commands/ask.js +146 -0
- package/lib/commands/assistant.js +59 -4
- package/lib/commands/auth.js +8 -2
- package/lib/commands/backup.js +8 -4
- package/lib/commands/blog.js +110 -0
- package/lib/commands/brain.js +105 -14
- package/lib/commands/build.js +19 -5
- package/lib/commands/chat.js +180 -26
- package/lib/commands/chatCompanion.js +27 -0
- package/lib/commands/chatState.js +45 -0
- package/lib/commands/claude.js +35 -38
- package/lib/commands/contacts.js +134 -4
- package/lib/commands/contactsSendAt.js +21 -0
- package/lib/commands/conversation.js +35 -4
- package/lib/commands/dashboard.js +43 -0
- package/lib/commands/department.js +28 -7
- package/lib/commands/devResident.js +15 -3
- package/lib/commands/devSupervisor.js +13 -1
- package/lib/commands/discord.js +75 -39
- package/lib/commands/done.js +98 -28
- package/lib/commands/engagement.js +107 -6
- package/lib/commands/evidence.js +154 -0
- package/lib/commands/ext.js +23 -0
- package/lib/commands/fetchAllowlist.js +71 -0
- package/lib/commands/file.js +38 -1
- package/lib/commands/fleet.js +336 -4
- package/lib/commands/governance.js +12 -3
- package/lib/commands/gripe.js +44 -13
- package/lib/commands/hat.js +254 -9
- package/lib/commands/helpers.js +18 -1
- package/lib/commands/hyperion.js +58 -0
- package/lib/commands/image.js +68 -0
- package/lib/commands/inbox.js +74 -23
- package/lib/commands/kuma.js +1050 -0
- package/lib/commands/kumaSla.js +174 -0
- package/lib/commands/licence.js +52 -155
- package/lib/commands/loop.js +291 -0
- package/lib/commands/lucid.js +10 -4
- package/lib/commands/memory.js +85 -8
- package/lib/commands/mind.js +71 -0
- package/lib/commands/mon.js +69 -2
- package/lib/commands/monitoring.js +1 -1
- package/lib/commands/motion.js +2 -2
- package/lib/commands/nucleus.js +48 -17
- package/lib/commands/objective.js +59 -1
- package/lib/commands/offload.js +89 -0
- package/lib/commands/onboard.js +2 -1
- package/lib/commands/org.js +30 -3
- package/lib/commands/peek.js +28 -0
- package/lib/commands/plan.js +38 -0
- package/lib/commands/plugin.js +110 -0
- package/lib/commands/presence.js +89 -0
- package/lib/commands/provider.js +217 -0
- package/lib/commands/qlarity.js +19 -3
- package/lib/commands/quoxchat.js +13 -4
- package/lib/commands/quoxseo.js +69 -0
- package/lib/commands/reach.js +2 -1
- package/lib/commands/research.js +79 -0
- package/lib/commands/router.js +77 -0
- package/lib/commands/rows.js +367 -0
- package/lib/commands/run.js +82 -10
- package/lib/commands/scope.js +2 -2
- package/lib/commands/server.js +405 -0
- package/lib/commands/setup.js +33 -7
- package/lib/commands/shortcut.js +41 -0
- package/lib/commands/skills.js +68 -9
- package/lib/commands/slot.js +260 -7
- package/lib/commands/status.js +1 -1
- package/lib/commands/team.js +3 -1
- package/lib/commands/tips.js +23 -0
- package/lib/commands/tool.js +133 -45
- package/lib/commands/trace.js +12 -7
- package/lib/commands/trigger.js +95 -0
- package/lib/commands/update.js +46 -0
- package/lib/commands/vault.js +31 -8
- package/lib/commands/vm.js +237 -0
- package/lib/commands/voice.js +34 -3
- package/lib/commands/volt.js +45 -3
- package/lib/commands/walkthrough.js +32 -0
- package/lib/commands/watch.js +73 -21
- package/lib/commands/welcome.js +14 -0
- package/lib/commands/workflow.js +189 -32
- package/lib/commands/x.js +13 -4
- package/lib/commands/youtube.js +172 -0
- package/lib/core/actingAs.js +2 -2
- package/lib/core/actionItems.js +115 -0
- package/lib/core/activeSummary.js +186 -0
- package/lib/core/agentAlias.js +2 -0
- package/lib/core/agentFamilies.js +7 -0
- package/lib/core/aside.js +54 -0
- package/lib/core/askDecision.js +60 -0
- package/lib/core/auth.js +164 -13
- package/lib/core/callerSource.js +1 -1
- package/lib/core/cliBuild.js +62 -0
- package/lib/core/client.js +152 -35
- package/lib/core/commandRegistrars.js +130 -0
- package/lib/core/config.js +143 -25
- package/lib/core/customHats.js +168 -0
- package/lib/core/doneEngineFetch.js +8 -0
- package/lib/core/errors.js +329 -20
- package/lib/core/findingReconcile.js +90 -0
- package/lib/core/global-flags.js +105 -5
- package/lib/core/governedSession.js +1 -3
- package/lib/core/hyperion.js +102 -0
- package/lib/core/inboxResolution.js +65 -0
- package/lib/core/lazyCommands.js +4 -1
- package/lib/core/liveSessionsReport.js +1 -1
- package/lib/core/loopGlyph.js +97 -0
- package/lib/core/offload.js +533 -0
- package/lib/core/org-override.js +1 -1
- package/lib/core/orgResolve.js +70 -0
- package/lib/core/output.js +11 -11
- package/lib/core/peek.js +126 -0
- package/lib/core/profileUsers.js +51 -0
- package/lib/core/promptCancel.js +18 -0
- package/lib/core/repoPath.js +11 -0
- package/lib/core/residentSeed.js +3 -1
- package/lib/core/rootOptions.js +39 -0
- package/lib/core/router.js +168 -0
- package/lib/core/selfUpdate.js +188 -0
- package/lib/core/sessionHeartbeat.js +12 -8
- package/lib/core/sessionsOverview.js +64 -0
- package/lib/core/shortcut.js +83 -0
- package/lib/core/slotKeeper.js +248 -3
- package/lib/core/slotWake.js +120 -0
- package/lib/core/supervisorEngine.js +71 -2
- package/lib/core/version.js +1 -1
- package/lib/core/vm.js +366 -0
- package/lib/core/vmTui.js +86 -0
- package/lib/core/walkthrough.js +35 -0
- package/lib/dragon/companionView.js +34 -0
- package/lib/dragon/frames.js +118 -0
- package/lib/dragon/jobs.js +123 -0
- package/lib/dragon/renderWorker.js +15 -0
- package/lib/dragon/select.js +72 -0
- package/lib/evidence/bundleExport.js +47 -0
- package/lib/evidence/bundleVerify.js +77 -0
- package/lib/hyperion/deck.js +29 -0
- package/lib/lucid/LucidCard.jsx +68 -0
- package/lib/lucid/LucidIntro.jsx +45 -58
- package/lib/lucid/cards.js +86 -0
- package/lib/lucid/markdown.js +81 -0
- package/lib/lucid/motion.js +56 -0
- package/lib/quoxon/EXTENSIONS.md +23 -0
- package/lib/quoxon/activity.js +14 -2
- package/lib/quoxon/aocl.js +117 -0
- package/lib/quoxon/api.js +690 -25
- package/lib/quoxon/app.js +3586 -250
- package/lib/quoxon/approvalFacts.js +96 -0
- package/lib/quoxon/approvalSentence.js +44 -0
- package/lib/quoxon/approvalsView.js +384 -0
- package/lib/quoxon/asideView.js +40 -0
- package/lib/quoxon/ask.js +121 -0
- package/lib/quoxon/away.js +52 -0
- package/lib/quoxon/bang.js +224 -0
- package/lib/quoxon/bulkRisk.js +17 -0
- package/lib/quoxon/capabilities.js +13 -6
- package/lib/quoxon/cells.js +53 -0
- package/lib/quoxon/commandShort.js +124 -0
- package/lib/quoxon/commands.js +429 -39
- package/lib/quoxon/connect.js +210 -23
- package/lib/quoxon/connectCard.js +19 -2
- package/lib/quoxon/consent.js +86 -0
- package/lib/quoxon/copy.js +4 -3
- package/lib/quoxon/countdownLine.js +20 -0
- package/lib/quoxon/deck.js +30 -0
- package/lib/quoxon/demo.autoplay.js +29 -0
- package/lib/quoxon/demo.fixtures.js +1 -1
- package/lib/quoxon/demo.js +46 -3
- package/lib/quoxon/devObserver.js +20 -0
- package/lib/quoxon/dividerAocl.js +192 -0
- package/lib/quoxon/dividerRule.js +57 -0
- package/lib/quoxon/dragonCompanion.js +255 -0
- package/lib/quoxon/dragonModes.js +151 -0
- package/lib/quoxon/dragonMoments.js +82 -0
- package/lib/quoxon/dragonReads.js +68 -0
- package/lib/quoxon/dragonView.js +41 -0
- package/lib/quoxon/earlyInput.js +40 -0
- package/lib/quoxon/echo.js +15 -0
- package/lib/quoxon/egg.js +2 -2
- package/lib/quoxon/events.js +266 -26
- package/lib/quoxon/extensions.js +276 -0
- package/lib/quoxon/feedback.js +50 -0
- package/lib/quoxon/gallery.js +21 -7
- package/lib/quoxon/handoffOffer.js +78 -0
- package/lib/quoxon/hatSwitch.js +37 -0
- package/lib/quoxon/headless.js +161 -0
- package/lib/quoxon/hostUrls.js +41 -0
- package/lib/quoxon/inboxView.js +79 -0
- package/lib/quoxon/indicator.js +6 -2
- package/lib/quoxon/installCli.js +79 -0
- package/lib/quoxon/intent.js +12 -1
- package/lib/quoxon/keys.js +51 -8
- package/lib/quoxon/launcher.js +103 -0
- package/lib/quoxon/loopCard.js +456 -0
- package/lib/quoxon/loopCards.js +180 -0
- package/lib/quoxon/loopStream.js +87 -0
- package/lib/quoxon/main.js +101 -160
- package/lib/quoxon/markdown.js +45 -21
- package/lib/quoxon/markdownSettle.js +19 -0
- package/lib/quoxon/matrixDomains.json +46 -0
- package/lib/quoxon/mentions.js +90 -0
- package/lib/quoxon/mind.js +103 -0
- package/lib/quoxon/mode.js +46 -0
- package/lib/quoxon/model.js +132 -26
- package/lib/quoxon/modelPrefs.js +345 -0
- package/lib/quoxon/motion/clock.js +3 -2
- package/lib/quoxon/motion/level.js +91 -0
- package/lib/quoxon/mouse.js +222 -0
- package/lib/quoxon/noai.js +57 -0
- package/lib/quoxon/objectiveOffer.js +82 -0
- package/lib/quoxon/objects/away.js +72 -0
- package/lib/quoxon/objects/fetch.js +17 -5
- package/lib/quoxon/objects/fleet.js +149 -0
- package/lib/quoxon/objects/health.js +186 -0
- package/lib/quoxon/objects/index.js +46 -7
- package/lib/quoxon/objects/mirror.js +137 -0
- package/lib/quoxon/objects/model.js +33 -0
- package/lib/quoxon/objects/peek.js +17 -0
- package/lib/quoxon/objects/proof.js +12 -4
- package/lib/quoxon/objects/prove.js +130 -0
- package/lib/quoxon/objects/replay.js +125 -0
- package/lib/quoxon/objects/sitrep.js +58 -0
- package/lib/quoxon/objects/view.js +11 -0
- package/lib/quoxon/onboarding/copy.js +32 -4
- package/lib/quoxon/onboarding/credential.js +17 -4
- package/lib/quoxon/onboarding/dockerOffer.js +45 -0
- package/lib/quoxon/onboarding/failureCause.js +33 -0
- package/lib/quoxon/onboarding/finish.js +15 -0
- package/lib/quoxon/onboarding/index.js +34 -7
- package/lib/quoxon/onboarding/installRunner.js +64 -5
- package/lib/quoxon/onboarding/preflight.js +17 -3
- package/lib/quoxon/onboarding/probe.js +65 -0
- package/lib/quoxon/onboarding/shortcutOffer.js +28 -0
- package/lib/quoxon/onboarding/view.js +76 -19
- package/lib/quoxon/orgLabel.js +10 -0
- package/lib/quoxon/orgSwitch.js +59 -0
- package/lib/quoxon/ownBroadcast.js +22 -0
- package/lib/quoxon/palette.js +43 -0
- package/lib/quoxon/planSteps.js +51 -0
- package/lib/quoxon/print.js +12 -5
- package/lib/quoxon/profileSwitch.js +53 -0
- package/lib/quoxon/promptHistory.js +41 -0
- package/lib/quoxon/providers.js +48 -0
- package/lib/quoxon/qart.js +20 -51
- package/lib/quoxon/qdragon.js +410 -0
- package/lib/quoxon/qdragonFly.js +62 -0
- package/lib/quoxon/qdragonScenes.js +282 -0
- package/lib/quoxon/qeyes.js +213 -0
- package/lib/quoxon/qmotion.js +75 -37
- package/lib/quoxon/render.js +740 -124
- package/lib/quoxon/resend.js +72 -0
- package/lib/quoxon/roster.js +117 -17
- package/lib/quoxon/route.js +6 -5
- package/lib/quoxon/rows.js +954 -0
- package/lib/quoxon/rowsStore.js +80 -0
- package/lib/quoxon/screenContext.js +150 -0
- package/lib/quoxon/screenFrames.js +254 -0
- package/lib/quoxon/serverChip.js +42 -0
- package/lib/quoxon/session.js +182 -2
- package/lib/quoxon/sessionDead.js +17 -0
- package/lib/quoxon/signin.js +125 -0
- package/lib/quoxon/slash/agents.js +112 -0
- package/lib/quoxon/slash/argSources.js +65 -0
- package/lib/quoxon/slash/args.js +65 -0
- package/lib/quoxon/slash/automate.js +89 -0
- package/lib/quoxon/slash/broadcast.js +140 -0
- package/lib/quoxon/slash/broadcastRun.js +108 -0
- package/lib/quoxon/slash/card.js +30 -0
- package/lib/quoxon/slash/challenge.js +114 -0
- package/lib/quoxon/slash/cliCommand.js +94 -0
- package/lib/quoxon/slash/cliLeaves.json +1023 -0
- package/lib/quoxon/slash/compact.js +1 -1
- package/lib/quoxon/slash/confirm.js +30 -0
- package/lib/quoxon/slash/crystallize.js +69 -0
- package/lib/quoxon/slash/dashboard.js +136 -0
- package/lib/quoxon/slash/draft.js +54 -0
- package/lib/quoxon/slash/evidenceCmd.js +34 -0
- package/lib/quoxon/slash/hatWalk.js +206 -0
- package/lib/quoxon/slash/inverses.js +77 -0
- package/lib/quoxon/slash/killer.js +24 -0
- package/lib/quoxon/slash/killerApi.js +16 -0
- package/lib/quoxon/slash/ledger.js +13 -8
- package/lib/quoxon/slash/listView.js +3 -1
- package/lib/quoxon/slash/modelCmd.js +261 -0
- package/lib/quoxon/slash/palette.js +22 -8
- package/lib/quoxon/slash/paletteController.js +117 -7
- package/lib/quoxon/slash/panels.js +21 -6
- package/lib/quoxon/slash/picker.js +20 -2
- package/lib/quoxon/slash/qpeCmd.js +35 -0
- package/lib/quoxon/slash/restart.js +95 -13
- package/lib/quoxon/slash/resume.js +18 -1
- package/lib/quoxon/slash/rowCmd.js +157 -0
- package/lib/quoxon/slash/rowWalk.js +175 -0
- package/lib/quoxon/slash/settings.js +16 -6
- package/lib/quoxon/slash/spawn.js +177 -0
- package/lib/quoxon/slash/stepUp.js +274 -0
- package/lib/quoxon/slash/stepUpCard.js +60 -0
- package/lib/quoxon/slash/stepUpCmds.js +78 -0
- package/lib/quoxon/slash/todoCmd.js +122 -0
- package/lib/quoxon/slash/undoApi.js +38 -0
- package/lib/quoxon/slash/undoCmd.js +49 -0
- package/lib/quoxon/slash/undoWish.js +15 -0
- package/lib/quoxon/slash/watch.js +164 -0
- package/lib/quoxon/slash/wish.js +23 -0
- package/lib/quoxon/slash/wishCmd.js +32 -0
- package/lib/quoxon/slotChat.js +300 -0
- package/lib/quoxon/slotFormat.js +195 -0
- package/lib/quoxon/slotRun.js +131 -0
- package/lib/quoxon/sound/mapping.js +2 -1
- package/lib/quoxon/stray.js +22 -0
- package/lib/quoxon/suggestCommand.js +90 -0
- package/lib/quoxon/term.js +46 -11
- package/lib/quoxon/textBuffer.js +176 -2
- package/lib/quoxon/theme.js +58 -25
- package/lib/quoxon/tips.js +433 -0
- package/lib/quoxon/toolCards.js +30 -0
- package/lib/quoxon/toolError.js +41 -0
- package/lib/quoxon/transcript.js +42 -4
- package/lib/quoxon/web/qface.js +18 -10
- package/lib/quoxon/welcome.js +1 -1
- package/lib/quoxon/whatsnew.js +136 -0
- package/lib/quoxon/whatsnew.jsonl +134 -0
- package/lib/quoxon/windowOrg.js +123 -0
- package/lib/server/devServers.js +137 -0
- package/lib/server/diag.js +129 -0
- package/lib/server/doctor.js +71 -0
- package/lib/server/install.js +149 -0
- package/lib/server/pluginEnable.js +161 -0
- package/lib/server/skew.js +56 -0
- package/lib/server/snapshot.js +131 -0
- package/lib/server/status.js +128 -0
- package/lib/server/sys.js +65 -0
- package/lib/server/uninstall.js +63 -0
- package/lib/server/update.js +234 -0
- package/lib/toolOutputShape.js +315 -0
- package/lib/tui/views/HyperionDeck.jsx +79 -0
- package/lib/tui/views/LucidDeck.jsx +209 -217
- package/package.json +5 -1
- package/vendor/dragonlab/SOURCE +4 -0
- package/vendor/dragonlab/design/cipher/v1/cipher-master.svg +46 -0
- package/vendor/dragonlab/design/cipher/v2/character.mjs +49 -0
- package/vendor/dragonlab/design/cipher/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/cipher/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/drago/v1/character.mjs +19 -0
- package/vendor/dragonlab/design/drago/v1/drago-master.svg +56 -0
- package/vendor/dragonlab/design/drago/v2/character.mjs +87 -0
- package/vendor/dragonlab/design/drago/v4/character.mjs +67 -0
- package/vendor/dragonlab/design/drago/v4/profile.mjs +35 -0
- package/vendor/dragonlab/design/drago/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/drago/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/hyperion/v1/hyperion-master.svg +56 -0
- package/vendor/dragonlab/design/hyperion/v2/character.mjs +79 -0
- package/vendor/dragonlab/design/hyperion/v2/review.mjs +33 -0
- package/vendor/dragonlab/design/hyperion/v2/watch.mjs +56 -0
- package/vendor/dragonlab/design/lucid/recovery/arrival.mjs +22 -0
- package/vendor/dragonlab/design/lucid/recovery/character.mjs +106 -0
- package/vendor/dragonlab/design/lucid/recovery/layout.mjs +50 -0
- package/vendor/dragonlab/design/lucid/recovery/lucid-master.svg +78 -0
- package/vendor/dragonlab/design/lucid/recovery/motion.mjs +42 -0
- package/vendor/dragonlab/design/lucid/recovery/render.mjs +121 -0
- package/vendor/dragonlab/design/lucid/recovery/review.mjs +68 -0
- package/vendor/dragonlab/design/lucid/recovery/verify.mjs +84 -0
- package/vendor/dragonlab/design/lucid/recovery/watch.mjs +56 -0
- package/vendor/dragonlab/design/seer/v1/seer-master.svg +53 -0
- package/vendor/dragonlab/design/seer/v2/character.mjs +88 -0
- package/vendor/dragonlab/design/seer/v3/character.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/character.mjs +66 -0
- package/vendor/dragonlab/design/seer/v4/review.mjs +33 -0
- package/vendor/dragonlab/design/seer/v4/watch.mjs +56 -0
- package/vendor/dragonlab/design/stage3/companion-review.mjs +28 -0
- package/vendor/dragonlab/design/stage3/sentinel-art.js +39 -0
- package/vendor/dragonlab/design/stage3/sentinel-braille.js +216 -0
- package/vendor/dragonlab/design/stage3/sentinel-character.js +257 -0
- package/vendor/dragonlab/design/stage3/sentinel-icon.js +103 -0
- package/vendor/dragonlab/design/stage3/sentinel-warden-eyes.js +95 -0
- package/vendor/dragonlab/design/stage3/svg-coverage.js +148 -0
- package/vendor/dragonlab/factory/svg-rig.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/characters.mjs +88 -0
- package/vendor/dragonlab/integration/companions-v1/cipher.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/companion.mjs +63 -0
- package/vendor/dragonlab/integration/companions-v1/drago.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/hyperion.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/lucid.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/seer.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/sentinel.mjs +2 -0
- package/vendor/dragonlab/integration/companions-v1/view.mjs +28 -0
- package/vendor/dragonlab/integration/hyperion-v3/activity.mjs +76 -0
- package/vendor/dragonlab/lib/power-effects.js +294 -0
- package/vendor/dragonlab/lib/power-registry.js +74 -0
- package/vendor/dragonlab/vendor/quox-dragon/qart.js +348 -0
- package/vendor/dragonlab/vendor/quox-dragon/qeyes.js +213 -0
- package/lib/core/websiteSession.js +0 -148
package/lib/commands/brain.js
CHANGED
|
@@ -51,6 +51,18 @@ export function titleOverlap(a, b) {
|
|
|
51
51
|
for (const w of x) if (y.has(w)) hit++;
|
|
52
52
|
return hit / Math.min(x.size, y.size);
|
|
53
53
|
}
|
|
54
|
+
const NEGATIVE_WORDS = new Set(['disable', 'disabled', 'disabling', 'off', 'deny', 'denied', 'block', 'blocked', 'stop', 'stopped', 'remove', 'removed', 'drop', 'dropped', 'abandon', 'abandoned', 'reject', 'rejected', 'exclude', 'excluded', 'not', 'never', 'no', 'dont', 'cannot', 'cant', 'wont', 'without', 'revoke', 'revoked']);
|
|
55
|
+
/** Odd count of negating words = negative polarity ("not", "stop", "disable", "reject", ...). */
|
|
56
|
+
function negativeParity(t) {
|
|
57
|
+
const toks = String(t || '').toLowerCase().replace(/['\u2019]/g, '').split(/[^a-z0-9]+/).filter(Boolean);
|
|
58
|
+
let n = 0;
|
|
59
|
+
for (const w of toks) if (NEGATIVE_WORDS.has(w)) n++;
|
|
60
|
+
return n % 2;
|
|
61
|
+
}
|
|
62
|
+
/** True when two decision titles read as opposites (enable/disable, on/off, stop X, no longer X...). */
|
|
63
|
+
export function oppositePolarity(a, b) {
|
|
64
|
+
return negativeParity(a) !== negativeParity(b);
|
|
65
|
+
}
|
|
54
66
|
export const DECIDE_DUPE_THRESHOLD = 0.6;
|
|
55
67
|
export const DISTILL_CAP = 50;
|
|
56
68
|
|
|
@@ -136,6 +148,24 @@ export function scoreRetrievalEval(judgements, searches, k = 8) {
|
|
|
136
148
|
export function registerBrainCommands(program, ctx) {
|
|
137
149
|
const { client, auth } = ctx;
|
|
138
150
|
|
|
151
|
+
// quox#934: read/history/related go through the NotesQ API, which only knows
|
|
152
|
+
// editable NotesQ notes. A feed note (git_commit, email, ...) that `brain note`
|
|
153
|
+
// resolves came back as a bare 404. On a NotesQ 404, ask the brain itself;
|
|
154
|
+
// rethrow the original error when the brain does not know the note either.
|
|
155
|
+
const brainNoteOn404 = async (noteId, err) => {
|
|
156
|
+
if (!(err instanceof QuoxApiError) || err.status !== 404) throw err;
|
|
157
|
+
let note;
|
|
158
|
+
try {
|
|
159
|
+
const ep = resolveEndpoint('brain.note.get', { id: noteId });
|
|
160
|
+
note = await client.get(ep.path, { service: ep.service });
|
|
161
|
+
} catch {
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
164
|
+
if (!note || !note.id) throw err;
|
|
165
|
+
return note;
|
|
166
|
+
};
|
|
167
|
+
const feedReason = (n, what) => `${n.id} is a ${n.kind || 'feed'} note${n.source_type ? ` from the ${n.source_type} source` : ''}, not an editable NotesQ note, so it has no ${what}.`;
|
|
168
|
+
|
|
139
169
|
const brain = program
|
|
140
170
|
.command('brain')
|
|
141
171
|
.description('Query Quox Qortex (vaults, notes, semantic search)')
|
|
@@ -164,10 +194,16 @@ export function registerBrainCommands(program, ctx) {
|
|
|
164
194
|
console.log(JSON.stringify(data, null, 2));
|
|
165
195
|
return;
|
|
166
196
|
}
|
|
197
|
+
// quox#853: a keyword-only fallback fooled both the product and a
|
|
198
|
+
// verifier ("0 hits" read as "does not exist"). Say it in words.
|
|
199
|
+
const degradedNote = data.retrieval === 'fts' && data.degraded
|
|
200
|
+
? '⚠ Semantic search was unavailable for this query, so this is keyword-only: a missing or zero result does not prove the note is absent. Retry shortly.'
|
|
201
|
+
: '';
|
|
167
202
|
if (insufficient) {
|
|
168
203
|
const legLabel = data.retrieval ? ` · ${data.retrieval}` : '';
|
|
169
204
|
console.log(glyphLine('brain2', `search "${query.slice(0, 40)}"`, { result: `0 relevant hits${legLabel}`, ms: data.tookMs ?? '?' }));
|
|
170
205
|
console.log('⚠ Qortex judged the matches for this query insufficient (no relevant notes). The weak hits are withheld; use -o json to see them with the relevance details.');
|
|
206
|
+
if (degradedNote) console.log(degradedNote);
|
|
171
207
|
return;
|
|
172
208
|
}
|
|
173
209
|
// BRAIN2-AUDIT: sessions must SEE what Qortex recalled, not just a hit
|
|
@@ -176,12 +212,14 @@ export function registerBrainCommands(program, ctx) {
|
|
|
176
212
|
const results = data.results || [];
|
|
177
213
|
const legLabel = data.retrieval ? ` · ${data.retrieval}` : '';
|
|
178
214
|
console.log(glyphLine('brain2', `search "${query.slice(0, 40)}"`, { result: `${results.length} hits${legLabel}`, ms: data.tookMs ?? '?' }));
|
|
215
|
+
if (degradedNote) console.log(degradedNote);
|
|
179
216
|
if (results.length === 0) {
|
|
180
217
|
console.log(glyphLine('brain2', 'recall', { result: 'nothing on record for this query' }));
|
|
181
218
|
return;
|
|
182
219
|
}
|
|
183
220
|
for (const r of results) {
|
|
184
|
-
|
|
221
|
+
const miss = Array.isArray(r.missingEntities) && r.missingEntities.length ? ` · does not mention ${r.missingEntities.join(', ')}` : '';
|
|
222
|
+
console.log(`${brandName('🌌 QORTEX')} recall │ "${recallSnippet(r)}" · ${recallSource(r)} · ${r.title || 'untitled'} · note ${r.noteId || '?'}${miss}`);
|
|
185
223
|
}
|
|
186
224
|
return;
|
|
187
225
|
}
|
|
@@ -191,6 +229,7 @@ export function registerBrainCommands(program, ctx) {
|
|
|
191
229
|
if (program.opts().output !== 'json') {
|
|
192
230
|
const legLabel = data.retrieval ? ` · ${data.retrieval}` : '';
|
|
193
231
|
console.log(glyphLine('brain2', `search "${query.slice(0, 40)}"`, { result: `${(data.results || []).length} hits${legLabel}`, ms: data.tookMs ?? '?' }));
|
|
232
|
+
if (degradedNote) console.log(degradedNote);
|
|
194
233
|
}
|
|
195
234
|
formatOutput({
|
|
196
235
|
columns: ['title', 'kind', 'snippet', 'score'],
|
|
@@ -300,7 +339,7 @@ export function registerBrainCommands(program, ctx) {
|
|
|
300
339
|
if (!['private', 'org'].includes(options.scope)) throw new QuoxError('--scope must be private or org.', 2);
|
|
301
340
|
const prov = decisionProvenance();
|
|
302
341
|
const title = `Decision: ${what}`.slice(0, 200);
|
|
303
|
-
|
|
342
|
+
let markdown = decisionMarkdown({ what, why: options.why, rejected: options.rejected, prov });
|
|
304
343
|
const tags = ['decision', ...String(options.tags || '').split(',').map((t) => t.trim()).filter(Boolean)];
|
|
305
344
|
const json = program.opts().output === 'json';
|
|
306
345
|
if (options.dryRun) {
|
|
@@ -311,7 +350,15 @@ export function registerBrainCommands(program, ctx) {
|
|
|
311
350
|
const sp = resolveEndpoint('brain.search.list');
|
|
312
351
|
const found = await client.post(sp.path, { service: sp.service, body: { query: what, limit: 5 } });
|
|
313
352
|
const insufficient = found.insufficient === true || found.relevance?.insufficient === true;
|
|
314
|
-
const
|
|
353
|
+
const bare = (r) => String(r.title || '').replace(/^Decision:\s*/i, '');
|
|
354
|
+
const matches = insufficient ? [] : (found.results || []).filter((r) => titleOverlap(what, bare(r)) >= DECIDE_DUPE_THRESHOLD);
|
|
355
|
+
// An opposite-polarity match is a reversal of the earlier decision, never a duplicate of it.
|
|
356
|
+
const dupe = matches.find((r) => !oppositePolarity(what, bare(r)));
|
|
357
|
+
const reversed = dupe ? null : matches[0];
|
|
358
|
+
if (reversed) {
|
|
359
|
+
const ref = reversed.noteId || reversed.title;
|
|
360
|
+
if (ref) markdown = decisionMarkdown({ what, why: options.why, rejected: options.rejected, prov: { ...prov, supersedes: ref } });
|
|
361
|
+
}
|
|
315
362
|
if (dupe && !options.force) {
|
|
316
363
|
if (json) console.log(JSON.stringify({ saved: false, reason: 'existing_match', match: { noteId: dupe.noteId, title: dupe.title, kind: dupe.kind } }, null, 2));
|
|
317
364
|
else {
|
|
@@ -324,9 +371,10 @@ export function registerBrainCommands(program, ctx) {
|
|
|
324
371
|
}
|
|
325
372
|
const ep = resolveEndpoint('brain.capture.create');
|
|
326
373
|
const data = await client.post(ep.path, { service: ep.service, body: { title, text: markdown.replace(/^# .*\n\n/, ''), tags, scope: options.scope === 'org' ? 'org' : 'user' } });
|
|
327
|
-
if (json) { console.log(JSON.stringify(data, null, 2)); return; }
|
|
374
|
+
if (json) { console.log(JSON.stringify(reversed ? { ...data, reverses: { noteId: reversed.noteId, title: reversed.title } } : data, null, 2)); return; }
|
|
328
375
|
const verb = data.status === 'created' ? 'saved' : data.status === 'unchanged' ? 'already saved' : 'updated';
|
|
329
376
|
console.log(glyphLine('brain2', 'decide', { result: `${verb} · note ${String(data.noteId || '?').slice(0, 8)}` }));
|
|
377
|
+
if (reversed) console.log(` reverses ${reversed.title || 'untitled'} (note ${reversed.noteId || '?'})`);
|
|
330
378
|
});
|
|
331
379
|
|
|
332
380
|
brain
|
|
@@ -443,14 +491,14 @@ export function registerBrainCommands(program, ctx) {
|
|
|
443
491
|
formatDetail(data, { format });
|
|
444
492
|
return;
|
|
445
493
|
}
|
|
446
|
-
const verdict = (r) => (r.contradicted ? 'contradicted' : r.supported === true ? 'supported' : r.supported === false ? 'unsupported' : 'not measured');
|
|
494
|
+
const verdict = (r) => (r.fabricated || r.support_method === 'fabricated' ? 'fabricated' : r.support_method === 'unchecked' ? 'unchecked' : r.contradicted ? 'contradicted' : r.supported === true ? 'supported' : r.support_method === 'overlap' ? 'overlap' : r.supported === false ? 'unsupported' : 'not measured');
|
|
447
495
|
formatOutput({
|
|
448
496
|
columns: ['session', 'handle', 'note', 'span', 'rev', 'verdict', 'at'],
|
|
449
497
|
headers: ['Session', 'Handle', 'Note', 'Span', 'Rev', 'Verdict', 'At'],
|
|
450
498
|
rows: (data.records || []).map((r) => ({
|
|
451
499
|
session: r.session || '-',
|
|
452
500
|
handle: r.handle || '-',
|
|
453
|
-
note: r.note_id,
|
|
501
|
+
note: r.note_id || '-',
|
|
454
502
|
span: r.char_start != null && r.char_end != null ? `${r.char_start}-${r.char_end}` : '-',
|
|
455
503
|
rev: r.note_hash ? String(r.note_hash).slice(0, 8) : '-',
|
|
456
504
|
verdict: verdict(r),
|
|
@@ -509,7 +557,8 @@ export function registerBrainCommands(program, ctx) {
|
|
|
509
557
|
{ metric: 'answers after brain search', week: cw.answers ?? 0, total: ct.answers ?? 0 },
|
|
510
558
|
{ metric: 'answers citing a note', week: `${cw.grounded ?? 0} (${pct(cw.grounded ?? 0, cw.answers ?? 0)})`, total: `${ct.grounded ?? 0} (${pct(ct.grounded ?? 0, ct.answers ?? 0)})` },
|
|
511
559
|
...((cw.supportMeasured ?? 0) + (ct.supportMeasured ?? 0) > 0
|
|
512
|
-
? [{ metric: 'answers with supported citations (
|
|
560
|
+
? [{ metric: 'answers with supported citations (entailment)', week: `${cw.supportedAnswers ?? 0} (${pct(cw.supportedAnswers ?? 0, cw.supportMeasured ?? 0)})`, total: `${ct.supportedAnswers ?? 0} (${pct(ct.supportedAnswers ?? 0, ct.supportMeasured ?? 0)})` },
|
|
561
|
+
{ metric: 'answers with overlap only (not checked for meaning)', week: `${cw.overlapAnswers ?? 0}`, total: `${ct.overlapAnswers ?? 0}` }]
|
|
513
562
|
: []),
|
|
514
563
|
{ metric: 'notes cited / retrieved', week: `${cw.citedNotes ?? 0}/${cw.retrievedNotes ?? 0}`, total: `${ct.citedNotes ?? 0}/${ct.retrievedNotes ?? 0}` },
|
|
515
564
|
],
|
|
@@ -559,7 +608,13 @@ export function registerBrainCommands(program, ctx) {
|
|
|
559
608
|
.action(async (noteId, options) => {
|
|
560
609
|
auth.requireAuth();
|
|
561
610
|
const ep = resolveEndpoint('notesq.notes.get', { id: noteId });
|
|
562
|
-
|
|
611
|
+
let data;
|
|
612
|
+
try {
|
|
613
|
+
data = await client.get(ep.path, { service: ep.service });
|
|
614
|
+
} catch (e) {
|
|
615
|
+
const bn = await brainNoteOn404(noteId, e);
|
|
616
|
+
data = { note: bn, source: 'brain', editable: false };
|
|
617
|
+
}
|
|
563
618
|
const n = data.note || data;
|
|
564
619
|
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
565
620
|
if (options.raw) { console.log(n.body ?? n.markdown ?? ''); return; }
|
|
@@ -602,11 +657,22 @@ export function registerBrainCommands(program, ctx) {
|
|
|
602
657
|
.description('Delete a note (recorded as a revision, restorable in NotesQ). Requires --yes.')
|
|
603
658
|
.argument('<noteId>', 'note id')
|
|
604
659
|
.option('--yes', 'confirm deletion')
|
|
660
|
+
.option('--expected-hash <hash>', "the note's content_hash you read (see `quox brain read`); a stale value is a conflict (409) and nothing is deleted")
|
|
605
661
|
.action(async (noteId, options) => {
|
|
606
662
|
auth.requireAuth();
|
|
607
663
|
if (!options.yes) throw new QuoxError(`Refusing to delete ${noteId} without --yes.`, 2);
|
|
664
|
+
if (!options.expectedHash) throw new QuoxError(`Refusing to delete ${noteId} without --expected-hash (the content_hash from \`quox brain read ${noteId}\`), so a concurrent edit is never deleted.`, 2);
|
|
608
665
|
const ep = resolveEndpoint('notesq.notes.delete', { id: noteId });
|
|
609
|
-
|
|
666
|
+
let data;
|
|
667
|
+
try {
|
|
668
|
+
data = await client.delete(ep.path, { service: ep.service, query: { expected_hash: options.expectedHash } });
|
|
669
|
+
} catch (e) {
|
|
670
|
+
if (e instanceof QuoxApiError && e.status === 409) {
|
|
671
|
+
const h = e.body && (e.body.currentHash || e.body.current_hash);
|
|
672
|
+
throw new QuoxError(`Conflict (409): note ${noteId} changed since hash ${options.expectedHash} (current_hash ${h || 'unknown'}). Nothing was deleted. Re-run \`quox brain read ${noteId}\` and retry.`, 1);
|
|
673
|
+
}
|
|
674
|
+
throw e;
|
|
675
|
+
}
|
|
610
676
|
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
611
677
|
printSuccess(`Deleted note ${noteId}.`);
|
|
612
678
|
});
|
|
@@ -621,7 +687,14 @@ export function registerBrainCommands(program, ctx) {
|
|
|
621
687
|
.action(async (noteId, options) => {
|
|
622
688
|
auth.requireAuth();
|
|
623
689
|
const ep = resolveEndpoint('notesq.notes.related', { id: noteId });
|
|
624
|
-
|
|
690
|
+
let data;
|
|
691
|
+
try {
|
|
692
|
+
data = await client.get(ep.path, { service: ep.service, query: { limit: Math.min(Math.max(parseInt(options.limit, 10) || 8, 1), 20) } });
|
|
693
|
+
} catch (e) {
|
|
694
|
+
const bn = await brainNoteOn404(noteId, e);
|
|
695
|
+
data = { related: [], editable: false, reason: `${feedReason(bn, 'similarity links')} Use \`quox brain neighbours ${noteId}\` for its graph links.` };
|
|
696
|
+
if (program.opts().output !== 'json') console.log(data.reason);
|
|
697
|
+
}
|
|
625
698
|
const related = data.related || [];
|
|
626
699
|
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
627
700
|
console.log(glyphLine('brain2', `related ${noteId.slice(0, 8)}`, { result: `${related.length} notes` }));
|
|
@@ -657,11 +730,13 @@ export function registerBrainCommands(program, ctx) {
|
|
|
657
730
|
.description('Token-budgeted pack of the most relevant cited passages on a topic (what the Cortex brain_context tool returns)')
|
|
658
731
|
.argument('<query>', 'topic or task')
|
|
659
732
|
.option('--budget <n>', 'approximate token budget (200-4000)', '1200')
|
|
733
|
+
.option('--since <date>', 'only notes dated on or after this ISO date/datetime (e.g. 2026-10-05)')
|
|
734
|
+
.option('--until <date>', 'only notes dated on or before this ISO date/datetime (a date alone means the whole day)')
|
|
660
735
|
.action(async (query, options) => {
|
|
661
736
|
auth.requireAuth();
|
|
662
737
|
const tokenBudget = Math.min(Math.max(parseInt(options.budget, 10) || 1200, 200), 4000);
|
|
663
738
|
const ep = resolveEndpoint('brain.context.pack');
|
|
664
|
-
const data = await client.post(ep.path, { service: ep.service, body: { query, tokenBudget } });
|
|
739
|
+
const data = await client.post(ep.path, { service: ep.service, body: { query, tokenBudget, ...(options.since ? { since: options.since } : {}), ...(options.until ? { until: options.until } : {}) } });
|
|
665
740
|
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
666
741
|
const passages = data.passages || [];
|
|
667
742
|
const insufficient = data.insufficient === true || data.relevance?.insufficient === true || passages.length === 0;
|
|
@@ -693,7 +768,7 @@ export function registerBrainCommands(program, ctx) {
|
|
|
693
768
|
if (!data || data.success === false) {
|
|
694
769
|
throw new QuoxError(`brain_check failed: ${(data && (data.error || data.output)) || 'unknown error'}`, 1);
|
|
695
770
|
}
|
|
696
|
-
const mark = { supported: '✔', contradicted: '✖', unknown: '◐' }[data.verdict] || '';
|
|
771
|
+
const mark = { supported: '✔', contradicted: '✖', overlap: '◐', unknown: '◐' }[data.verdict] || '';
|
|
697
772
|
console.log(glyphLine('brain2', `check "${claim.slice(0, 40)}"`, { result: `${data.verdict} ${mark}`.trim() }));
|
|
698
773
|
const ev = data.evidence || [];
|
|
699
774
|
for (const e of ev) {
|
|
@@ -749,7 +824,14 @@ export function registerBrainCommands(program, ctx) {
|
|
|
749
824
|
return;
|
|
750
825
|
}
|
|
751
826
|
const ep = resolveEndpoint('notesq.notes.revisions', { id: noteId });
|
|
752
|
-
|
|
827
|
+
let data;
|
|
828
|
+
try {
|
|
829
|
+
data = await client.get(ep.path, { service: ep.service });
|
|
830
|
+
} catch (e) {
|
|
831
|
+
const bn = await brainNoteOn404(noteId, e);
|
|
832
|
+
data = { revisions: [], editable: false, reason: feedReason(bn, 'revision history') };
|
|
833
|
+
if (program.opts().output !== 'json') { console.log(data.reason); return; }
|
|
834
|
+
}
|
|
753
835
|
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
754
836
|
formatOutput({ columns: ['rev', 'action', 'author_kind', 'created_at', 'content_hash'], headers: ['Rev', 'Action', 'Author', 'When', 'Hash'], rows: data.revisions || [] }, { format: program.opts().output });
|
|
755
837
|
});
|
|
@@ -770,7 +852,16 @@ export function registerBrainCommands(program, ctx) {
|
|
|
770
852
|
throw new QuoxError(`Conflict: note ${noteId} changed since hash ${options.expectedHash} (current_hash ${current || 'unknown'}). Nothing was restored. Re-run \`quox brain read ${noteId}\` and try again.`, 1);
|
|
771
853
|
}
|
|
772
854
|
const ep = resolveEndpoint('notesq.notes.restore', { id: noteId });
|
|
773
|
-
|
|
855
|
+
let data;
|
|
856
|
+
try {
|
|
857
|
+
data = await client.post(ep.path, { service: ep.service, body: { rev, expected_hash: options.expectedHash } });
|
|
858
|
+
} catch (e) {
|
|
859
|
+
if (e instanceof QuoxApiError && e.status === 409) {
|
|
860
|
+
const h = e.body && (e.body.currentHash || e.body.current_hash);
|
|
861
|
+
throw new QuoxError(`Conflict (409): note ${noteId} changed since hash ${options.expectedHash} (current_hash ${h || 'unknown'}). Nothing was restored. Re-run \`quox brain read ${noteId}\` and try again.`, 1);
|
|
862
|
+
}
|
|
863
|
+
throw e;
|
|
864
|
+
}
|
|
774
865
|
if (program.opts().output === 'json') { console.log(JSON.stringify(data, null, 2)); return; }
|
|
775
866
|
printSuccess(data.unchanged ? `Note ${noteId} already matches revision ${rev}.` : `Restored note ${noteId} to revision ${rev} (new rev ${data.revision?.rev ?? '?'}).`);
|
|
776
867
|
});
|
package/lib/commands/build.js
CHANGED
|
@@ -33,10 +33,26 @@ export function registerBuildCommands(program, ctx) {
|
|
|
33
33
|
// own parsed opts.
|
|
34
34
|
const groupOpt = (options, key) => options[key] ?? build.opts()[key];
|
|
35
35
|
|
|
36
|
+
// An org with exactly one dev server needs no --host: use it and say so. With several, name them;
|
|
37
|
+
// with none, say so (proof cases code/reach-build-status / -targets exited 1 on a one-server org).
|
|
38
|
+
async function resolveHost(options) {
|
|
39
|
+
const given = groupOpt(options, 'host');
|
|
40
|
+
if (given) return given;
|
|
41
|
+
const listEp = resolveEndpoint('v1.dev.servers.list');
|
|
42
|
+
const list = await client.get(listEp.path, { service: listEp.service });
|
|
43
|
+
const ids = (Array.isArray(list?.servers) ? list.servers : []).map((s) => s.host_id).filter(Boolean);
|
|
44
|
+
if (ids.length === 1) {
|
|
45
|
+
printWarning(`No --host given; using the only dev server: ${ids[0]}`);
|
|
46
|
+
return ids[0];
|
|
47
|
+
}
|
|
48
|
+
if (ids.length === 0) throw new Error('No dev server is registered for this org (register one with `quox dev enable <hostId>`).');
|
|
49
|
+
throw new Error(`--host <hostId> is required: this org has ${ids.length} dev servers (${ids.join(', ')}).`);
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
async function dispatch(target, options) {
|
|
37
53
|
auth.requireAuth();
|
|
38
54
|
options = {
|
|
39
|
-
host:
|
|
55
|
+
host: await resolveHost(options),
|
|
40
56
|
stream: groupOpt(options, 'stream'),
|
|
41
57
|
phase: groupOpt(options, 'phase'),
|
|
42
58
|
msg: groupOpt(options, 'msg'),
|
|
@@ -76,8 +92,7 @@ export function registerBuildCommands(program, ctx) {
|
|
|
76
92
|
.option('--host <hostId>', 'dev server host_id')
|
|
77
93
|
.action(async (options) => {
|
|
78
94
|
auth.requireAuth();
|
|
79
|
-
const host =
|
|
80
|
-
if (!host) throw new Error('--host <hostId> is required');
|
|
95
|
+
const host = await resolveHost(options);
|
|
81
96
|
const endpoint = resolveEndpoint('v1.dev.build.status', { hostId: host });
|
|
82
97
|
const data = await client.get(endpoint.path, { service: endpoint.service });
|
|
83
98
|
formatDetail(data, { format: program.opts().output, title: `Build status: ${host}` });
|
|
@@ -89,8 +104,7 @@ export function registerBuildCommands(program, ctx) {
|
|
|
89
104
|
.option('--host <hostId>', 'dev server host_id')
|
|
90
105
|
.action(async (options) => {
|
|
91
106
|
auth.requireAuth();
|
|
92
|
-
const host =
|
|
93
|
-
if (!host) throw new Error('--host <hostId> is required');
|
|
107
|
+
const host = await resolveHost(options);
|
|
94
108
|
const endpoint = resolveEndpoint('v1.dev.build.targets', { hostId: host });
|
|
95
109
|
const data = await client.get(endpoint.path, { service: endpoint.service });
|
|
96
110
|
formatOutput(data.targets || [], {
|
package/lib/commands/chat.js
CHANGED
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { formatDetail } from '../core/output.js';
|
|
7
7
|
import { resolveEndpoint } from '../api/endpoints.js';
|
|
8
|
+
import { existsSync } from 'node:fs';
|
|
9
|
+
import { lastConversation, saveLastConversation } from './chatState.js';
|
|
10
|
+
import { loadChatCompanion } from './chatCompanion.js';
|
|
11
|
+
import { companionPlan, motionPolicy, VENDORED_DRAGONLAB } from '../dragon/select.js';
|
|
8
12
|
|
|
9
13
|
/**
|
|
10
14
|
* Build the POST body for /chat/stream (quox#782). The prompt and context
|
|
@@ -26,6 +30,46 @@ export function buildStreamQuery({ queryId, message, agent, sessionId, context }
|
|
|
26
30
|
return body;
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* TUI-DIVIDER: `quox chat --trace` prints the turn's protocol events as they arrive, so the
|
|
35
|
+
* per-layer AOCL record (`aocl.layer`: id, name, result, ms) is visible in the CLI before any
|
|
36
|
+
* UI animates it. Returns { text, tone } or null for an event --trace does not show.
|
|
37
|
+
* tone: ok | warn | fail | accent (no grey: every tone is a real colour).
|
|
38
|
+
*/
|
|
39
|
+
const RESULT_TONE = { pass: 'ok', gated: 'warn', blocked: 'fail', bypass: 'accent' };
|
|
40
|
+
const fmtTraceMs = (ms) => {
|
|
41
|
+
const n = Number(ms);
|
|
42
|
+
if (!Number.isFinite(n)) return '';
|
|
43
|
+
return n < 10 ? `${Math.round(n * 10) / 10} ms` : `${Math.round(n)} ms`;
|
|
44
|
+
};
|
|
45
|
+
export function formatTraceEvent(ev) {
|
|
46
|
+
if (!ev || typeof ev !== 'object') return null;
|
|
47
|
+
switch (ev.type) {
|
|
48
|
+
case 'turn.started':
|
|
49
|
+
return { text: `▸ turn.started ${[ev.provider, ev.model].filter(Boolean).join(' ')}`, tone: 'accent' };
|
|
50
|
+
case 'aocl.layer': {
|
|
51
|
+
const result = String(ev.result || 'pass');
|
|
52
|
+
const line = `▤ aocl.layer ${String(ev.id || '?').padEnd(4)}${String(ev.name || '').padEnd(10)}${result.padEnd(8)}${fmtTraceMs(ev.ms).padStart(8)} ${ev.phase || ''}${ev.reason ? ` ${ev.reason}` : ''}`;
|
|
53
|
+
return { text: line, tone: RESULT_TONE[result] || 'accent' };
|
|
54
|
+
}
|
|
55
|
+
case 'aocl.summary':
|
|
56
|
+
return { text: `▤ aocl.summary ${ev.accounted ?? '?'}/${ev.total ?? '?'} accounted · ${ev.passed ?? 0} passed · ${ev.bypassed ?? 0} bypassed · ${ev.rejected ?? 0} rejected${ev.ms != null ? ` · ${fmtTraceMs(ev.ms)}` : ''}`, tone: ev.rejected ? 'fail' : 'ok' };
|
|
57
|
+
case 'aocl.verdict':
|
|
58
|
+
return { text: `▤ aocl.verdict ${ev.layer || 'L8'} ${ev.verdict}${ev.reason ? `: ${ev.reason}` : ''}`, tone: ev.verdict === 'blocked' ? 'fail' : 'warn' };
|
|
59
|
+
case 'trace':
|
|
60
|
+
return { text: `· trace ${ev.protocol || ''} ${ev.label || ''}${ev.detail ? ` ${ev.detail}` : ''}`, tone: 'accent' };
|
|
61
|
+
case 'turn.finished':
|
|
62
|
+
return { text: `▸ turn.finished ${ev.status || ''}${ev.usage?.elapsed_ms != null ? ` · ${ev.usage.elapsed_ms} ms` : ''}`, tone: ev.status === 'completed' ? 'ok' : 'fail' };
|
|
63
|
+
default:
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const TONE_PAINT = { ok: chalk.green, warn: chalk.yellow, fail: chalk.red, accent: chalk.cyan };
|
|
68
|
+
function printTrace(ev) {
|
|
69
|
+
const f = formatTraceEvent(ev);
|
|
70
|
+
if (f) process.stderr.write((TONE_PAINT[f.tone] || chalk.cyan)(f.text) + '\n');
|
|
71
|
+
}
|
|
72
|
+
|
|
29
73
|
function generateQueryId() {
|
|
30
74
|
return `CLI_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
31
75
|
}
|
|
@@ -34,7 +78,7 @@ function generateQueryId() {
|
|
|
34
78
|
* Consume a chat SSE stream, printing chunks as they arrive.
|
|
35
79
|
* Returns { text, sessionId, agentUsed } once the stream completes.
|
|
36
80
|
*/
|
|
37
|
-
export async function consumeStream(client, { message, agent, sessionId, context }, { onAgentLabel } = {}) {
|
|
81
|
+
export async function consumeStream(client, { message, agent, sessionId, context }, { onAgentLabel, trace = false, companionPath, companionDisplay } = {}) {
|
|
38
82
|
const queryId = generateQueryId();
|
|
39
83
|
const body = buildStreamQuery({ queryId, message, agent, sessionId, context });
|
|
40
84
|
|
|
@@ -50,7 +94,25 @@ export async function consumeStream(client, { message, agent, sessionId, context
|
|
|
50
94
|
onAgentLabel?.(agentUsed);
|
|
51
95
|
};
|
|
52
96
|
|
|
53
|
-
|
|
97
|
+
const companion = await loadChatCompanion(companionPath, { display: companionDisplay });
|
|
98
|
+
const write = text => companion ? companion.write(text) : process.stdout.write(text);
|
|
99
|
+
// Native terminal status follows legacy `done`; observe it when opted in.
|
|
100
|
+
let iter;
|
|
101
|
+
try {
|
|
102
|
+
iter = client.stream(resolveEndpoint('chat.stream').path, { service: resolveEndpoint('chat.stream').service, body, untilClose: Boolean(trace || companion) })[Symbol.asyncIterator]();
|
|
103
|
+
while (true) {
|
|
104
|
+
let step;
|
|
105
|
+
try {
|
|
106
|
+
step = await iter.next();
|
|
107
|
+
} catch (err) {
|
|
108
|
+
// A refusal before the stream opened carries the layers it really evaluated.
|
|
109
|
+
if (trace && Array.isArray(err?.body?.aocl_layers)) { companion?.suspend(); for (const l of err.body.aocl_layers) printTrace({ type: 'aocl.layer', ...l }); }
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
if (step.done) break;
|
|
113
|
+
const event = step.value;
|
|
114
|
+
companion?.event(event);
|
|
115
|
+
if (trace) { companion?.suspend(); printTrace(event); }
|
|
54
116
|
switch (event.type) {
|
|
55
117
|
case 'connected':
|
|
56
118
|
break;
|
|
@@ -58,9 +120,9 @@ export async function consumeStream(client, { message, agent, sessionId, context
|
|
|
58
120
|
case 'thinking': {
|
|
59
121
|
const stage = event.stage || '';
|
|
60
122
|
if (stage === 'tool_call' || stage.includes('tool')) {
|
|
61
|
-
|
|
123
|
+
write(chalk.dim(` ⚙ using tool: ${event.message}`) + '\n');
|
|
62
124
|
} else if (process.argv.includes('--verbose') || process.argv.includes('-v')) {
|
|
63
|
-
|
|
125
|
+
write(chalk.dim(` … ${event.message}`) + '\n');
|
|
64
126
|
}
|
|
65
127
|
break;
|
|
66
128
|
}
|
|
@@ -68,19 +130,19 @@ export async function consumeStream(client, { message, agent, sessionId, context
|
|
|
68
130
|
case 'chunk':
|
|
69
131
|
printLabel();
|
|
70
132
|
sawChunk = true;
|
|
71
|
-
|
|
133
|
+
write(event.content || '');
|
|
72
134
|
text += event.content || '';
|
|
73
135
|
break;
|
|
74
136
|
|
|
75
137
|
case 'done':
|
|
76
138
|
if (!sawChunk && event.full_response) {
|
|
77
139
|
printLabel();
|
|
78
|
-
|
|
140
|
+
write(event.full_response);
|
|
79
141
|
text = event.full_response;
|
|
80
142
|
}
|
|
81
143
|
if (event.session_id) resultSessionId = event.session_id;
|
|
82
144
|
if (event.agent_used) agentUsed = event.agent_used;
|
|
83
|
-
|
|
145
|
+
write('\n');
|
|
84
146
|
break;
|
|
85
147
|
|
|
86
148
|
case 'error':
|
|
@@ -90,28 +152,99 @@ export async function consumeStream(client, { message, agent, sessionId, context
|
|
|
90
152
|
break;
|
|
91
153
|
}
|
|
92
154
|
}
|
|
155
|
+
} catch (err) {
|
|
156
|
+
companion?.event({ type: 'error' });
|
|
157
|
+
throw err;
|
|
158
|
+
} finally {
|
|
159
|
+
await companion?.close();
|
|
160
|
+
try { await iter?.return?.(); } catch { /* cleanup must not mask a stream error */ }
|
|
161
|
+
}
|
|
93
162
|
|
|
94
163
|
return { text, sessionId: resultSessionId, agentUsed };
|
|
95
164
|
}
|
|
96
165
|
|
|
97
|
-
/**
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
166
|
+
/** The words for a failed turn: the server's plain message first, the bare error code only as a fallback. */
|
|
167
|
+
export function turnFailureText(result, httpStatus) {
|
|
168
|
+
return result?.message || result?.error || `chat turn failed (HTTP ${httpStatus})`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Poll a server-side chat turn until it is done. The turn keeps running on the server whatever this
|
|
175
|
+
* client does, so giving up early would hide a result (and any objects it created) from the user.
|
|
176
|
+
*/
|
|
177
|
+
async function awaitTurn(client, turnId) {
|
|
178
|
+
const capMs = Number(process.env.QUOX_CHAT_TURN_TIMEOUT_MS) || 20 * 60 * 1000;
|
|
179
|
+
const fixed = Number(process.env.QUOX_CHAT_POLL_MS) || 0;
|
|
180
|
+
const started = Date.now();
|
|
181
|
+
let wait = fixed || 1500;
|
|
182
|
+
let failures = 0;
|
|
183
|
+
const ep = resolveEndpoint('chat.turn.get', { id: turnId });
|
|
184
|
+
while (true) {
|
|
185
|
+
let t;
|
|
186
|
+
try {
|
|
187
|
+
t = await client.get(ep.path, { service: ep.service });
|
|
188
|
+
failures = 0;
|
|
189
|
+
} catch (err) {
|
|
190
|
+
if (err?.status === 404 || /not.found/i.test(err?.message || '')) {
|
|
191
|
+
throw new Error(`The server no longer has chat turn ${turnId} (expired, or the collector restarted). Check \`quox conversation list\` to see whether it finished.`);
|
|
192
|
+
}
|
|
193
|
+
if (++failures >= 5) throw err;
|
|
194
|
+
t = { status: 'running' };
|
|
195
|
+
}
|
|
196
|
+
if (t.status === 'done') {
|
|
197
|
+
if (t.http_status >= 400) throw new Error(turnFailureText(t.result, t.http_status));
|
|
198
|
+
return t.result || {};
|
|
199
|
+
}
|
|
200
|
+
if (t.status === 'error') throw new Error(`chat turn ${turnId} failed on the server: ${t.error || 'unknown error'}`);
|
|
201
|
+
if (Date.now() - started > capMs) {
|
|
202
|
+
throw new Error(`Chat turn ${turnId} is still running after ${Math.round(capMs / 1000)}s. It has not been lost: fetch it with \`quox chat --resume ${turnId}\`.`);
|
|
203
|
+
}
|
|
204
|
+
await sleep(wait);
|
|
205
|
+
if (!fixed) wait = Math.min(wait + 500, 5000);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** One-shot (non-streaming) chat via POST /chat/claude, kept server side as a pollable turn. */
|
|
210
|
+
async function sendOneShot(client, { message, agent, sessionId, resumeTurn }) {
|
|
211
|
+
let data;
|
|
212
|
+
if (resumeTurn) {
|
|
213
|
+
data = await awaitTurn(client, resumeTurn);
|
|
214
|
+
} else {
|
|
215
|
+
const body = { prompt: message };
|
|
216
|
+
if (agent) body.selected_agent = agent;
|
|
217
|
+
if (sessionId) body.session_id = sessionId;
|
|
102
218
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
219
|
+
// Prefer: respond-async returns 202 {turn_id} at once, so no client timeout can orphan a
|
|
220
|
+
// multi-tool turn. An older collector ignores it and answers synchronously (handled below).
|
|
221
|
+
data = await client.post(resolveEndpoint('chat.claude').path, {
|
|
222
|
+
service: resolveEndpoint('chat.claude').service,
|
|
223
|
+
body,
|
|
224
|
+
headers: { Prefer: 'respond-async' },
|
|
225
|
+
timeout: 30000,
|
|
226
|
+
});
|
|
227
|
+
if (data && data.turn_id && data.status === 'running') data = await awaitTurn(client, data.turn_id);
|
|
228
|
+
}
|
|
108
229
|
|
|
109
230
|
const response = data.result || data.response || data.text || JSON.stringify(data);
|
|
110
231
|
return { data, response, sessionId: data.session_id || sessionId };
|
|
111
232
|
}
|
|
112
233
|
|
|
234
|
+
/** Explicit --dragon-module wins; otherwise pick the dragon for the attached agent (null path = ordinary chat). */
|
|
235
|
+
export function chooseCompanion({ options, config, env = process.env, isTTY = Boolean(process.stdout.isTTY), exists = existsSync }) {
|
|
236
|
+
const policy = motionPolicy(env);
|
|
237
|
+
if (options.dragonModule) return { companionPath: options.dragonModule, companionDisplay: { still: policy.still, mono: policy.mono } };
|
|
238
|
+
let settings = {};
|
|
239
|
+
try { settings = config?.getActiveProfile?.()?.quoxon || {}; } catch { /* no profile settings */ }
|
|
240
|
+
const e = options.dragon ? { ...env, QUOX_DRAGON_COMPANION: options.dragon } : env;
|
|
241
|
+
const plan = companionPlan({ agent: options.agent, settings, env: e, isTTY, exists, bundled: VENDORED_DRAGONLAB });
|
|
242
|
+
if (!plan.enabled) return { companionPath: undefined, companionDisplay: undefined };
|
|
243
|
+
return { companionPath: plan.file, companionDisplay: { still: plan.still, mono: plan.mono, persist: plan.persist } };
|
|
244
|
+
}
|
|
245
|
+
|
|
113
246
|
export function registerChatCommands(program, ctx) {
|
|
114
|
-
const { client, auth } = ctx;
|
|
247
|
+
const { client, auth, config } = ctx;
|
|
115
248
|
|
|
116
249
|
const chat = program
|
|
117
250
|
.command('chat')
|
|
@@ -120,12 +253,25 @@ export function registerChatCommands(program, ctx) {
|
|
|
120
253
|
.option('--agent <name>', 'route to a specific agent (e.g. sentinel, cipher, nova)')
|
|
121
254
|
.option('--no-stream', 'disable streaming output (one-shot request/response)')
|
|
122
255
|
.option('--conversation <id>', 'continue a conversation (session id)')
|
|
256
|
+
.option('--continue', 'continue the last conversation for this profile and directory')
|
|
257
|
+
.option('--resume <turn>', 'fetch a chat turn that was still running (its id is printed if a turn outlives the wait)')
|
|
258
|
+
.option('--trace', 'print the turn\'s protocol events (per-layer AOCL, traces, summary) to stderr as they arrive')
|
|
259
|
+
.option('--dragon <mode>', 'dragon companion for dragon-backed agents: on, greeting or off (default from /dragon companion; art is bundled; QUOX_DRAGONLAB overrides it)')
|
|
260
|
+
.option('--dragon-module <path>', 'opt in to a trusted local dragon companion module (streaming, single-message mode)')
|
|
123
261
|
.action(async (message, options) => {
|
|
124
262
|
auth.requireAuth();
|
|
125
263
|
const streaming = options.stream !== false;
|
|
264
|
+
const profileName = (() => { try { return config?.getActiveProfileName?.(); } catch { return undefined; } })();
|
|
265
|
+
const noteConversation = (id) => {
|
|
266
|
+
if (!id) return;
|
|
267
|
+
saveLastConversation(profileName, process.cwd(), id);
|
|
268
|
+
if (program.opts().output !== 'json') process.stderr.write(`conversation: ${id}\n`);
|
|
269
|
+
};
|
|
270
|
+
if (!options.conversation && options.continue) options.conversation = lastConversation(profileName, process.cwd());
|
|
271
|
+
if (options.dragonModule && (!message || !streaming)) throw new Error('--dragon-module requires a single message with streaming enabled.');
|
|
126
272
|
|
|
127
|
-
// Interactive mode if no message
|
|
128
|
-
if (!message) {
|
|
273
|
+
// Interactive mode if no message (a --resume fetch is not interactive)
|
|
274
|
+
if (!message && !options.resume) {
|
|
129
275
|
const { default: inquirer } = await import('inquirer');
|
|
130
276
|
let currentAgent = options.agent || 'quox';
|
|
131
277
|
let sessionId = options.conversation;
|
|
@@ -161,8 +307,9 @@ export function registerChatCommands(program, ctx) {
|
|
|
161
307
|
sessionId,
|
|
162
308
|
}, {
|
|
163
309
|
onAgentLabel: (agentUsed) => process.stdout.write(chalk.green(`${(agentUsed || currentAgent).toUpperCase()}: `)),
|
|
310
|
+
trace: Boolean(options.trace),
|
|
164
311
|
});
|
|
165
|
-
if (result.sessionId) sessionId = result.sessionId;
|
|
312
|
+
if (result.sessionId) { sessionId = result.sessionId; saveLastConversation(profileName, process.cwd(), sessionId); }
|
|
166
313
|
console.log();
|
|
167
314
|
} else {
|
|
168
315
|
const { response, sessionId: newSessionId } = await sendOneShot(client, {
|
|
@@ -170,7 +317,7 @@ export function registerChatCommands(program, ctx) {
|
|
|
170
317
|
agent: currentAgent,
|
|
171
318
|
sessionId,
|
|
172
319
|
});
|
|
173
|
-
if (newSessionId) sessionId = newSessionId;
|
|
320
|
+
if (newSessionId) { sessionId = newSessionId; saveLastConversation(profileName, process.cwd(), sessionId); }
|
|
174
321
|
console.log(chalk.green(`${currentAgent.toUpperCase()}:`), response);
|
|
175
322
|
console.log();
|
|
176
323
|
}
|
|
@@ -182,15 +329,19 @@ export function registerChatCommands(program, ctx) {
|
|
|
182
329
|
}
|
|
183
330
|
|
|
184
331
|
// Single message mode
|
|
185
|
-
if (streaming) {
|
|
332
|
+
if (streaming && !options.resume) {
|
|
333
|
+
const companion = chooseCompanion({ options, config });
|
|
186
334
|
try {
|
|
187
|
-
await consumeStream(client, {
|
|
335
|
+
const result = await consumeStream(client, {
|
|
188
336
|
message,
|
|
189
337
|
agent: options.agent,
|
|
190
338
|
sessionId: options.conversation,
|
|
191
339
|
}, {
|
|
192
340
|
onAgentLabel: () => {}, // no label prefix in single-shot streaming; just print tokens
|
|
341
|
+
trace: Boolean(options.trace),
|
|
342
|
+
...companion,
|
|
193
343
|
});
|
|
344
|
+
noteConversation(result?.sessionId);
|
|
194
345
|
} catch (err) {
|
|
195
346
|
console.error(chalk.red(`Error: ${err.message}`));
|
|
196
347
|
process.exitCode = 1;
|
|
@@ -202,15 +353,17 @@ export function registerChatCommands(program, ctx) {
|
|
|
202
353
|
const spinner = ora('Thinking...').start();
|
|
203
354
|
|
|
204
355
|
try {
|
|
205
|
-
const { data, response } = await sendOneShot(client, {
|
|
356
|
+
const { data, response, sessionId: convId } = await sendOneShot(client, {
|
|
206
357
|
message,
|
|
207
358
|
agent: options.agent,
|
|
208
359
|
sessionId: options.conversation,
|
|
360
|
+
resumeTurn: options.resume,
|
|
209
361
|
});
|
|
210
362
|
spinner.stop();
|
|
363
|
+
noteConversation(convId);
|
|
211
364
|
|
|
212
365
|
if (program.opts().output === 'json') {
|
|
213
|
-
console.log(JSON.stringify(data, null, 2));
|
|
366
|
+
console.log(JSON.stringify({ ...data, conversation_id: convId || null }, null, 2));
|
|
214
367
|
} else {
|
|
215
368
|
console.log(response);
|
|
216
369
|
|
|
@@ -238,3 +391,4 @@ export function registerChatCommands(program, ctx) {
|
|
|
238
391
|
});
|
|
239
392
|
});
|
|
240
393
|
}
|
|
394
|
+
|