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/quoxon/events.js
CHANGED
|
@@ -11,9 +11,16 @@
|
|
|
11
11
|
* turn : null | { id, status: running|interrupting, startedAt, stage, activity }
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { approvalSentence } from './approvalSentence.js';
|
|
15
|
+
import { dividerStep } from './dividerAocl.js';
|
|
16
|
+
import { approvalFacts } from './approvalFacts.js';
|
|
17
|
+
import { planLines } from './planSteps.js';
|
|
18
|
+
import { isConsentError, consentErrorText } from './consent.js';
|
|
19
|
+
import { splitSettled } from './markdownSettle.js'; // not markdown.js: that loads React + Ink (QUOXON-LATENCY)
|
|
15
20
|
import { nextActivity, actionActivity } from './activity.js';
|
|
16
21
|
import { normUsage, turnTokens, fmtTokens, fmtCost } from './indicator.js';
|
|
22
|
+
import { isLayerTrace, normaliseSummary, summaryFromTraces, verdictNote } from './aocl.js';
|
|
23
|
+
import { toolResultText } from './toolError.js';
|
|
17
24
|
|
|
18
25
|
export const TOOL_STATUSES = ['ok', 'failed', 'denied', 'effect_unknown'];
|
|
19
26
|
export const APPROVAL_ARM_MS = 300;
|
|
@@ -23,10 +30,14 @@ export const HELD_KEY_MS = 1500;
|
|
|
23
30
|
export const initialState = (over = {}) => ({
|
|
24
31
|
history: [],
|
|
25
32
|
blocks: [],
|
|
33
|
+
asks: [], // ASK-SETTLE: open y/N/c ask cards (live, never scrollback) until answered/expired/closed
|
|
26
34
|
turn: null,
|
|
27
35
|
ctx: { agent: null, model: null, provider: null, endpoint: null, hat: null, conversationId: null },
|
|
28
36
|
pending: [], // client-side queue (Tab)
|
|
29
37
|
steering: [], // steer messages not yet accepted
|
|
38
|
+
lastTurnId: null, // the newest turn begun here: a card from an older turn is stale
|
|
39
|
+
lastAocl: null, // QUOXON-AOCL: the last turn's normalised aocl.summary (for /aocl)
|
|
40
|
+
waiting: [], // approvals raised by ANOTHER conversation/session: a notice only, never a focused card (answered via /approvals)
|
|
30
41
|
approvals: [], // approval cards waiting for a human, first is shown; survive the turn that raised them
|
|
31
42
|
running: [], // approved from this terminal, result arrives asynchronously (polled)
|
|
32
43
|
inbox: [], // last /approvals listing
|
|
@@ -46,21 +57,51 @@ export const initialState = (over = {}) => ({
|
|
|
46
57
|
* streams keep using `chunk`. Returns { append, st } where st is { v1, lastChunk }.
|
|
47
58
|
*/
|
|
48
59
|
export function textGate(st, ev) {
|
|
49
|
-
const g = st || { v1: false, lastChunk: null };
|
|
60
|
+
const g = st || { v1: false, lastChunk: null, acc: '' };
|
|
61
|
+
const acc = g.acc || '';
|
|
62
|
+
const add = (c) => (acc + c).slice(-200000);
|
|
50
63
|
if (ev.type === 'turn.started') return { append: false, st: { ...g, v1: true } };
|
|
51
64
|
if (ev.type === 'chunk') {
|
|
52
|
-
|
|
53
|
-
return { append:
|
|
65
|
+
const c = ev.content;
|
|
66
|
+
if (!c) return { append: false, st: g };
|
|
67
|
+
// RESTORE-TUI: on a v1 stream a chunk is skipped only when it mirrors text already shown (the full text so far,
|
|
68
|
+
// or the delta it repeats). A chunk carrying NEW text is still answer text (it was dropped since dae720283).
|
|
69
|
+
if (g.v1 && acc.endsWith(c)) return { append: false, st: g };
|
|
70
|
+
return { append: true, st: { ...g, lastChunk: c, acc: add(c) } };
|
|
54
71
|
}
|
|
55
72
|
if (ev.type === 'text.delta') {
|
|
56
|
-
const
|
|
57
|
-
|
|
73
|
+
const c = ev.content;
|
|
74
|
+
const mirrored = g.lastChunk != null && g.lastChunk === c;
|
|
75
|
+
const append = Boolean(c) && !mirrored;
|
|
76
|
+
return { append, st: { v1: true, lastChunk: null, acc: append ? add(c) : acc } };
|
|
58
77
|
}
|
|
59
78
|
return { append: false, st: g };
|
|
60
79
|
}
|
|
61
80
|
|
|
62
81
|
const tms = (v) => { const t = typeof v === 'number' ? v : Date.parse(v); return Number.isFinite(t) ? t : null; };
|
|
63
82
|
|
|
83
|
+
const short = (v, n = 120) => { const t = typeof v === 'string' ? v : (v == null ? '' : JSON.stringify(v)); return t.length > n ? t.slice(0, n - 1) + '…' : t; };
|
|
84
|
+
const argText = (a) => a && typeof a === 'object' && !Array.isArray(a) && (a.argv || a.command) ? argText(a.argv ?? a.command) : Array.isArray(a) ? a.join(' ') : (a && typeof a === 'object' ? Object.entries(a).filter(([, v]) => v != null && typeof v !== 'object').slice(0, 4).map(([k, v]) => `${k}=${v}`).join(' ') : (a == null ? '' : String(a)));
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Describe ANY inbox approval from what the item carries: the tool or leaf, the command or key args, the target
|
|
88
|
+
* (explicit target, else the org it acts on), and who asked and when. Never invents; empty fields are null.
|
|
89
|
+
*/
|
|
90
|
+
export function describeInboxItem(it) {
|
|
91
|
+
const p = it?.payload || {};
|
|
92
|
+
const c = p.approval_context || {};
|
|
93
|
+
const m = c.method || {};
|
|
94
|
+
const ob = c.on_behalf_of || {};
|
|
95
|
+
const w = p.what && typeof p.what === 'object' ? p.what : {}; // APPROVAL-WHAT: the server's own action/target/summary wins
|
|
96
|
+
const tool = w.action || m.tool || c.tool || c.leaf || p.tool || p.leaf || null;
|
|
97
|
+
const argv = m.argv ?? m.args ?? c.argv ?? c.args ?? p.argv ?? p.args ?? m.command ?? c.command ?? p.command ?? null;
|
|
98
|
+
const cmd = short([m.leaf || c.leaf || p.leaf, argText(argv)].filter(Boolean).join(' ').trim() || null);
|
|
99
|
+
const target = w.target || m.target || c.target || p.target || cmd || w.target_none || c.target_org || c.org_name || p.org_name || c.org_id || p.org_id || null;
|
|
100
|
+
const by = ob.agent || ob.agent_id || ob.session_name || ob.session || c.requested_by?.agent_label || c.source_label || p.source || it?.source || null;
|
|
101
|
+
const what = w.summary ? String(w.summary) : (it?.title ? short(String(it.title), 160) : null);
|
|
102
|
+
return { tool, target: target ? short(target, 200) : null, title: what, by: by ? String(by) : null, at: it?.created_at ?? it?.createdAt ?? null };
|
|
103
|
+
}
|
|
104
|
+
|
|
64
105
|
/**
|
|
65
106
|
* A pending inbox approval found by polling -> approval card, or null when it does not qualify.
|
|
66
107
|
* Qualifies when created after `since` (the turn start, else app start), or raised on behalf of this
|
|
@@ -68,19 +109,80 @@ const tms = (v) => { const t = typeof v === 'number' ? v : Date.parse(v); return
|
|
|
68
109
|
*/
|
|
69
110
|
export function cardFromInboxItem(it, { since = 0, userId = null, conversationId = null, now = 0 } = {}) {
|
|
70
111
|
if (!it || !it.id) return null;
|
|
112
|
+
if (it.payload?.type === 'decision') return null; // quox ask: answered with /answer N key from /approvals, never a y/n key card
|
|
71
113
|
const c = it.payload?.approval_context || {};
|
|
72
114
|
const m = c.method || {};
|
|
73
115
|
const created = tms(it.created_at ?? it.createdAt);
|
|
74
116
|
const mine = Boolean(userId && c.on_behalf_of?.user_id && String(c.on_behalf_of.user_id) === String(userId));
|
|
75
117
|
const here = Boolean(conversationId && c.on_behalf_of?.conversation_id === conversationId);
|
|
76
118
|
if (!(created != null && created >= since) && !mine && !here) return null;
|
|
119
|
+
// QUOXON-REVIEW: collector tool approvals mint the inbox item with aee_correlation_id = the approval id;
|
|
120
|
+
// without it the card had no id, polling was skipped and every terminal approve read "timeout".
|
|
121
|
+
const toolApproval = it.payload?.type === 'native_tool_approval';
|
|
122
|
+
const approvalId = it.payload?.approval_id || it.approval_id || it.approvalId
|
|
123
|
+
|| (toolApproval ? (it.aee_correlation_id || it.aeeCorrelationId || null) : null) || null;
|
|
124
|
+
const ob = c.on_behalf_of || {};
|
|
125
|
+
const d = describeInboxItem(it);
|
|
126
|
+
const tool = d.tool || String(it.title || it.id);
|
|
77
127
|
return {
|
|
78
|
-
callId: `inbox:${it.id}`, inboxItemId: it.id, approvalId
|
|
79
|
-
tool:
|
|
80
|
-
url: it.dashboard_url || null, shownAt: now, undescribable: !
|
|
128
|
+
callId: `inbox:${it.id}`, inboxItemId: it.id, approvalId,
|
|
129
|
+
tool, target: d.target, what: d.title, askedBy: d.by, askedAt: d.at, scope: null, risk: c.risk || null,
|
|
130
|
+
url: it.dashboard_url || null, shownAt: now, undescribable: !d.tool && !d.target && !it._plan,
|
|
131
|
+
plan: it._plan ? { lines: planLines(it._plan.steps), loaded: it._plan.loaded !== false, orgId: it._plan.orgId || null, orgName: it._plan.orgName || null } : null, // the plan's steps + the item's own org
|
|
132
|
+
facts: approvalFacts(it), // APPROVAL-WWWH: the exact command, hosts, chain, reason, time and read-only class
|
|
133
|
+
// only an item that names THIS conversation is ours; anything else came from another session and must not take the keyboard
|
|
134
|
+
foreign: !here,
|
|
135
|
+
origin: String(ob.agent || ob.agent_id || ob.session_name || ob.session || (ob.conversation_id ? `conversation ${String(ob.conversation_id).slice(0, 8)}` : approvalSourceLabel(it))),
|
|
136
|
+
privileged: isPrivileged({ tool, risk: c.risk, flag: c.privileged === true || c.elevated === true || it.payload?.privileged === true, title: it.title })
|
|
81
137
|
};
|
|
82
138
|
}
|
|
83
139
|
|
|
140
|
+
/** Where an inbox approval came from: its source label, else the payload source, else its type. */
|
|
141
|
+
export function approvalSourceLabel(it) {
|
|
142
|
+
const c = it?.payload?.approval_context || {};
|
|
143
|
+
return String(c.source_label || it?.payload?.source || it?.source || (c.requested_by && c.requested_by.agent_label) || it?.payload?.type || 'plugin or workflow');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* QUOXON-FW7: every open inbox approval, grouped for /approvals. 'this conversation' (names this conversation,
|
|
148
|
+
* or already a card here), 'other sessions' (raised on behalf of another conversation or agent), and
|
|
149
|
+
* 'plugins and workflows' (everything else, labelled by source). Empty groups are dropped.
|
|
150
|
+
*/
|
|
151
|
+
export function groupApprovals(items, { conversationId = null, cardIds = [] } = {}) {
|
|
152
|
+
const g = { 'decisions asked of you': [], 'this conversation': [], 'other sessions': [], 'plugins and workflows': [] };
|
|
153
|
+
for (const item of items || []) {
|
|
154
|
+
if (item?.payload?.type === 'decision') { g['decisions asked of you'].push({ item, label: String(item.payload.session || 'session') }); continue; } // quox ask
|
|
155
|
+
const ob = item?.payload?.approval_context?.on_behalf_of || {};
|
|
156
|
+
const label = approvalSourceLabel(item);
|
|
157
|
+
// A card that names a conversation belongs here only when it is THIS one (a card shown in this window for another
|
|
158
|
+
// conversation, e.g. a privileged hat.switch, is still "other sessions"). No conversation named: a card here counts.
|
|
159
|
+
const mine = ob.conversation_id ? Boolean(conversationId) && ob.conversation_id === conversationId : cardIds.includes(item.id);
|
|
160
|
+
if (mine) g['this conversation'].push({ item, label });
|
|
161
|
+
else if (ob.conversation_id || ob.agent || ob.agent_id || ob.session_name || ob.session) g['other sessions'].push({ item, label: String(ob.session_name || ob.session || ob.agent || ob.agent_id || `conversation ${String(ob.conversation_id).slice(0, 8)}`) });
|
|
162
|
+
else g['plugins and workflows'].push({ item, label });
|
|
163
|
+
}
|
|
164
|
+
return Object.entries(g).filter(([, v]) => v.length).map(([group, v]) => ({ group, items: v }));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* QLOOP-LIVE: which approval card the keyboard goes to first. Cards about a loop THIS window is watching lead, then the rest of
|
|
169
|
+
* this conversation, then other sessions' items (stable inside each rank). The loop id appears in the card's target / description.
|
|
170
|
+
*/
|
|
171
|
+
export function orderApprovals(cards, mineIds = []) {
|
|
172
|
+
const ids = (mineIds || []).filter(Boolean).map(String);
|
|
173
|
+
const rank = (c) => (ids.length && ids.some((id) => [c.target, c.what, c.facts?.where].some((t) => t != null && String(t).includes(id))) ? 0 : c.foreign ? 2 : 1);
|
|
174
|
+
return cards.map((c, i) => ({ c, i, r: rank(c) })).sort((a, b) => a.r - b.r || a.i - b.i).map((x) => x.c);
|
|
175
|
+
}
|
|
176
|
+
/** Cards raised by other sessions (the "other sessions: N" tail). */
|
|
177
|
+
export const otherSessionsCount = (cards) => (cards || []).filter((c) => c.foreign).length;
|
|
178
|
+
|
|
179
|
+
/** Privileged/elevated grants (hat switches and anything the server marks privileged or elevated) need an explicit confirm. */
|
|
180
|
+
export function isPrivileged({ tool = '', risk = null, flag = false, title = '' } = {}) {
|
|
181
|
+
if (flag === true) return true;
|
|
182
|
+
if (String(tool) === 'hat.switch' || /^hat[ .]switch/i.test(String(title))) return true;
|
|
183
|
+
return /privileged|elevated|critical/i.test(String(risk || ''));
|
|
184
|
+
}
|
|
185
|
+
|
|
84
186
|
let uid = 0;
|
|
85
187
|
const nid = () => ++uid;
|
|
86
188
|
|
|
@@ -101,6 +203,11 @@ function flush(s, force = false) {
|
|
|
101
203
|
return out.length || blocks.length !== s.blocks.length ? { ...s, history: [...s.history, ...out], blocks } : s;
|
|
102
204
|
}
|
|
103
205
|
|
|
206
|
+
/** Fold the turn's VOLT tool count and list into its AOCL summary (footer count, /aocl list). */
|
|
207
|
+
function withTools(sum, turn) {
|
|
208
|
+
const t = turn?.toolsOffer;
|
|
209
|
+
return t && t.offered != null ? { ...sum, toolsOffered: t.offered, toolsTotal: t.total, toolsList: t.list } : sum;
|
|
210
|
+
}
|
|
104
211
|
function note(s, text, tone = 'dim') {
|
|
105
212
|
return { ...s, history: [...s.history, { id: nid(), kind: 'note', text, tone }] };
|
|
106
213
|
}
|
|
@@ -133,6 +240,14 @@ function appendText(s00, content) {
|
|
|
133
240
|
return addBlock(s, { kind: 'text', text: content });
|
|
134
241
|
}
|
|
135
242
|
|
|
243
|
+
/** QUOXON-FW3: who a delegation targets. Server fields first; older servers only put agent_id=<id> in the summary. */
|
|
244
|
+
export function delegateInfo(ev) {
|
|
245
|
+
if (ev.tool !== 'delegate_to_agent') return {};
|
|
246
|
+
const id = ev.agent_id || (String(ev.summary || '').match(/\bagent_id=(\S+)/) || [])[1] || null;
|
|
247
|
+
if (!id) return {};
|
|
248
|
+
return { agentId: id, agentName: ev.agent_name || id, ...(ev.task_gist ? { taskGist: ev.task_gist } : {}) };
|
|
249
|
+
}
|
|
250
|
+
|
|
136
251
|
function upsertTool(s, ev, patch) {
|
|
137
252
|
const i = s.blocks.findIndex(b => b.kind === 'tool' && b.callId === ev.call_id);
|
|
138
253
|
if (i >= 0) {
|
|
@@ -172,7 +287,8 @@ export function summarizeTurn(s, status, now) {
|
|
|
172
287
|
// Real server usage when it arrived; otherwise the estimate from streamed text; nothing when neither exists.
|
|
173
288
|
const tk = turnTokens(t);
|
|
174
289
|
if (tk > 0) parts.push(`${fmtTokens(tk)} tokens`);
|
|
175
|
-
|
|
290
|
+
// A labelled estimate (cost_source, e.g. the claude CLI's own figure) is never shown as this turn's price; /cost explains it.
|
|
291
|
+
const cost = t?.usage?.costSource || t?.viaCli ? null : fmtCost(t?.usage?.cost ?? normUsage(null, t?.doneCost).cost);
|
|
176
292
|
if (cost) parts.push(cost);
|
|
177
293
|
if (status === 'interrupted' || status === 'failed') {
|
|
178
294
|
if (t?.tools) parts.push(`${plural(finished, 'tool')} finished`);
|
|
@@ -215,15 +331,49 @@ function endTurn(s, status, now) {
|
|
|
215
331
|
function reduceInner(s, a) {
|
|
216
332
|
switch (a.type) {
|
|
217
333
|
case 'user': // a submitted prompt
|
|
218
|
-
return { ...s, history: [...s.history, { id: nid(), kind: 'user', text: a.text, queued: !!a.queued }] };
|
|
334
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'user', text: a.text, queued: !!a.queued, ...(a.echo ? { echo: true } : {}) }] };
|
|
219
335
|
case 'turn.begin':
|
|
220
|
-
return { ...s, turn: { id: a.turnId, status: 'running', startedAt: a.now, stage: null, activity: null, tools: 0, finishedTools: 0, approvals: 0, receipts: [], chars: 0, usage: null }, blocks: [], autoDrain: false };
|
|
336
|
+
return { ...s, lastTurnId: a.turnId, turn: { id: a.turnId, status: 'running', startedAt: a.now, stage: null, activity: null, tools: 0, finishedTools: 0, approvals: 0, receipts: [], chars: 0, usage: null }, blocks: [], autoDrain: false };
|
|
221
337
|
case 'note':
|
|
222
338
|
return note(s, a.text, a.tone);
|
|
339
|
+
// ASK-SETTLE (owner 2026-10-17): an open ask is a live card; settling it removes the card and leaves one line.
|
|
340
|
+
case 'ask.open': { // the same ask shown again (after a chat about it) replaces its card, never a second one
|
|
341
|
+
const asks = (s.asks || []).filter(x => x.text !== a.text);
|
|
342
|
+
return { ...s, asks: [...asks, { id: a.id, text: a.text, at: a.at ?? null }] };
|
|
343
|
+
}
|
|
344
|
+
case 'ask.settle': {
|
|
345
|
+
const asks = s.asks || [];
|
|
346
|
+
if (!asks.some(x => x.id === a.id)) return s;
|
|
347
|
+
if (a.silent) return { ...s, asks: asks.filter(x => x.id !== a.id) }; // a walkthrough card that ended normally: no leftover line
|
|
348
|
+
return note({ ...s, asks: asks.filter(x => x.id !== a.id) }, a.text, a.tone);
|
|
349
|
+
}
|
|
350
|
+
case 'slotmsg.settle': { // SLOT-RUN: an answered/expired run request collapses to one line in place
|
|
351
|
+
let hit = false;
|
|
352
|
+
const history = s.history.map(it => {
|
|
353
|
+
if (it.kind !== 'slotmsg' || !it.line?.run || it.line.run.askId !== a.askId || it.line.run.settled) return it;
|
|
354
|
+
hit = true;
|
|
355
|
+
return { ...it, line: { ...it.line, run: { ...it.line.run, settled: a.text, settledTone: a.tone } } };
|
|
356
|
+
});
|
|
357
|
+
return hit ? { ...s, history } : s;
|
|
358
|
+
}
|
|
359
|
+
case 'bang.out': // QUOXON-FW8: command output, shown as is (not note-painted); the transcript records only the action name
|
|
360
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'bang', text: a.text }] };
|
|
361
|
+
case 'slotmsg': // QUOXON-MESH: a slot chat line (slotChat.js slotLine), incoming or sent
|
|
362
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'slotmsg', line: a.line }] };
|
|
363
|
+
case 'loopcard': // QLOOP-LIVE: a live loop card; the card object is mutated in place by loopCards.js
|
|
364
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'loopcard', card: a.card }] };
|
|
365
|
+
case 'tip': // a "did you know" tip (tips.js); text + the command to try
|
|
366
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'tip', text: a.text, cmd: a.cmd }] };
|
|
367
|
+
case 'bcard': // QUOXON-BROADCAST: the sender's framed delivery card (slash/broadcast.js broadcastCard), pure data
|
|
368
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'bcard', card: a.card }] };
|
|
369
|
+
case 'whatsnew': // the startup "What's new" box (whatsnew.js): [{ title, cmd }] + how many more
|
|
370
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'whatsnew', items: a.items, more: a.more, head: a.head, pad: a.pad }] };
|
|
223
371
|
case 'notify':
|
|
224
372
|
return { ...s, notify: { ...s.notify, ...a.patch } };
|
|
225
373
|
case 'welcome':
|
|
226
374
|
return { ...s, history: [...s.history, { id: nid(), kind: 'welcome', version: a.version }] };
|
|
375
|
+
case 'introShow': // /welcome: the first screen again (Q art + the greeting text), settled in history
|
|
376
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'intro', text: a.text, cols: a.cols }] };
|
|
227
377
|
case 'help':
|
|
228
378
|
return { ...s, history: [...s.history, { id: nid(), kind: 'help', model: a.model }] };
|
|
229
379
|
case 'object': // inline object view (objects/): pure data, rebuilt at render width
|
|
@@ -232,6 +382,8 @@ function reduceInner(s, a) {
|
|
|
232
382
|
return flush(addBlock(s, { kind: 'error', text: a.text }), false);
|
|
233
383
|
case 'verbose':
|
|
234
384
|
return { ...s, verbose: a.mode };
|
|
385
|
+
case 'aocl.table': // /aocl: the last turn's per-layer table, on demand
|
|
386
|
+
return { ...s, history: [...s.history, { id: nid(), kind: 'aocl', table: true, sum: s.lastAocl || null }] };
|
|
235
387
|
case 'history.add': // restored turns (/resume): ordinary history items, plus their answers for /copy
|
|
236
388
|
return { ...s, history: [...s.history, ...(a.items || []).map(i => ({ id: nid(), ...i }))], answers: [...(s.answers || []), ...(a.answers || [])].slice(-20) };
|
|
237
389
|
case 'history.reset': // /clear: a fresh screen; nothing on the server is touched
|
|
@@ -273,20 +425,47 @@ function reduceInner(s, a) {
|
|
|
273
425
|
}
|
|
274
426
|
case 'interrupt.requested':
|
|
275
427
|
return s.turn ? { ...s, turn: { ...s.turn, status: 'interrupting' } } : s;
|
|
428
|
+
case 'turn.deferred': // QUOXON-FW3: the send never reached the server; end the turn quietly (no failure summary, nothing ran)
|
|
429
|
+
return { ...s, turn: null, blocks: [], autoDrain: false };
|
|
276
430
|
case 'stopped.local': { // SSE aborted by us because the server has no interrupt route
|
|
277
431
|
let n = note(s, 'Stopped locally; work already sent may still finish. Verify before retrying.', 'warn');
|
|
278
432
|
return endTurn(n, 'interrupted', a.now);
|
|
279
433
|
}
|
|
280
434
|
case 'approval.found': { // pending approval seen by polling (covers provider paths that emit no approval.requested)
|
|
281
435
|
const card = cardFromInboxItem(a.item, { since: s.turn?.startedAt ?? a.since ?? 0, userId: a.userId, conversationId: s.ctx.conversationId, now: a.now || 0 });
|
|
436
|
+
if (card && card.approvalId) { // an event card raised without an inbox id learns it from the poll (else y answers a null id: 404)
|
|
437
|
+
const j = s.approvals.findIndex(x => !x.inboxItemId && x.approvalId === card.approvalId);
|
|
438
|
+
if (j >= 0) { const approvals = s.approvals.slice(); approvals[j] = { ...approvals[j], inboxItemId: card.inboxItemId, url: approvals[j].url || card.url, facts: approvals[j].facts || card.facts }; return { ...s, approvals, seenInbox: { ...s.seenInbox, [card.inboxItemId]: true } }; }
|
|
439
|
+
}
|
|
440
|
+
if (card && card.approvalId) { // an event stub that could not be described learns its description from the inbox item
|
|
441
|
+
const j = s.approvals.findIndex(x => x.undescribable && (x.inboxItemId === card.inboxItemId || x.approvalId === card.approvalId));
|
|
442
|
+
if (j >= 0 && !card.undescribable) { const approvals = s.approvals.slice(); approvals[j] = { ...approvals[j], tool: card.tool, target: card.target, what: card.what, askedBy: card.askedBy, askedAt: card.askedAt, facts: card.facts, undescribable: false }; return { ...s, approvals }; }
|
|
443
|
+
}
|
|
282
444
|
if (!card || s.seenInbox[card.inboxItemId] || s.approvals.some(x => x.inboxItemId === card.inboxItemId || (card.approvalId && x.approvalId === card.approvalId))) return s;
|
|
283
|
-
|
|
445
|
+
if (card.foreign) { // another conversation's card: a compact notice, never the focused card
|
|
446
|
+
if (s.waiting.some(x => x.inboxItemId === card.inboxItemId)) return s;
|
|
447
|
+
return { ...s, waiting: [...s.waiting, card] };
|
|
448
|
+
}
|
|
449
|
+
return { ...s, approvals: [...s.approvals, { ...card, turnId: s.turn?.id ?? null }], seenInbox: { ...s.seenInbox, [card.inboxItemId]: true }, turn: s.turn ? { ...s.turn, approvals: s.turn.approvals + 1 } : s.turn };
|
|
450
|
+
}
|
|
451
|
+
case 'approval.gone': { // the server says it no longer knows this approval (404): someone already handled it
|
|
452
|
+
const hit = x => x.callId === a.callId || (a.inboxItemId && x.inboxItemId === a.inboxItemId);
|
|
453
|
+
const had = s.approvals.some(hit) || s.waiting.some(hit);
|
|
454
|
+
const n = { ...s, inbox: (s.inbox || []).filter(x => x.id !== a.inboxItemId), approvals: s.approvals.filter(x => !hit(x)), waiting: s.waiting.filter(x => !hit(x)), seenInbox: a.inboxItemId ? { ...s.seenInbox, [a.inboxItemId]: true } : s.seenInbox };
|
|
455
|
+
return had ? note(n, 'approval already handled', 'accent') : n;
|
|
284
456
|
}
|
|
285
457
|
case 'approval.dismiss':
|
|
286
458
|
return { ...s, approvals: s.approvals.filter(x => x.callId !== a.callId) };
|
|
287
459
|
case 'approval.sent': { // the human answered from this terminal
|
|
288
|
-
const rest = { ...s, approvals: s.approvals.filter(x => x.callId !== a.callId) };
|
|
289
|
-
if (a.
|
|
460
|
+
const rest = { ...s, inbox: (s.inbox || []).filter(x => x.id !== a.inboxItemId), seenInbox: a.inboxItemId ? { ...s.seenInbox, [a.inboxItemId]: true } : s.seenInbox, approvals: s.approvals.filter(x => x.callId !== a.callId && !(a.inboxItemId && x.inboxItemId === a.inboxItemId)), waiting: s.waiting.filter(x => x.callId !== a.callId && x.inboxItemId !== a.callId) };
|
|
461
|
+
if (a.batch) return rest; // /approve N prints its own one-line summary
|
|
462
|
+
if (a.quiet && a.action === 'approve') return rest;
|
|
463
|
+
if (a.action !== 'approve') return note(rest, approvalSentence(a, false), 'warn');
|
|
464
|
+
if (a.foreign) return note(rest, approvalSentence(a, true), 'ok');
|
|
465
|
+
if (a.tool === 'hat.switch') { // applied server-side by the inbox approve; nothing to wait for
|
|
466
|
+
const to = String(a.target || '').split('\u2192').pop().replace(/\(privileged\)/, '').trim() || 'the new hat';
|
|
467
|
+
return note(rest, `Hat switched to ${to}. Ask again and I'll carry on.`, 'ok');
|
|
468
|
+
}
|
|
290
469
|
const run = { callId: a.callId, approvalId: a.approvalId || null, tool: a.tool, target: a.target };
|
|
291
470
|
return note({ ...rest, running: [...rest.running.filter(x => x.callId !== a.callId), run] },
|
|
292
471
|
`approved; running (result arrives asynchronously)${a.tool ? `: ${a.tool}${a.target ? ` on ${a.target}` : ''}` : ''}`, 'ok');
|
|
@@ -298,12 +477,15 @@ function reduceInner(s, a) {
|
|
|
298
477
|
if (a.outcome === 'done') return note(n, `result: ${what}: ${a.text || 'completed'}`, 'ok');
|
|
299
478
|
if (a.outcome === 'failed') return note(n, `failed: ${what}: ${a.text || 'error'}`, 'warn');
|
|
300
479
|
if (a.outcome === 'denied') return note(n, `denied: ${what}`, 'warn');
|
|
301
|
-
|
|
480
|
+
// FW4: never silent and never a guess. Say what is unknown and where the authoritative answer is.
|
|
481
|
+
const check = run && run.approvalId ? `quox approvals result ${run.approvalId} (or /approvals)` : '/approvals (no approval id was returned, so it cannot be tracked from here)';
|
|
482
|
+
return note(n, `result not known yet: ${what}; approved, but the server has not said whether it ran. It may still be running or may have run. Check: ${check}`, 'warn');
|
|
302
483
|
}
|
|
303
484
|
case 'inbox.sync': { // open approval ids from a successful inbox refresh
|
|
304
485
|
const ids = new Set(a.ids);
|
|
305
486
|
const approvals = s.approvals.filter(x => !x.inboxItemId || ids.has(x.inboxItemId) || (a.now || 0) - x.shownAt < 5000);
|
|
306
|
-
|
|
487
|
+
const waiting = s.waiting.filter(x => ids.has(x.inboxItemId));
|
|
488
|
+
return approvals.length === s.approvals.length && waiting.length === s.waiting.length ? s : { ...s, approvals, waiting };
|
|
307
489
|
}
|
|
308
490
|
case 'event':
|
|
309
491
|
return reduceEvent(s, a.ev, a.now || 0);
|
|
@@ -315,6 +497,14 @@ function reduceInner(s, a) {
|
|
|
315
497
|
/** The first card is armed from the moment it is actually SHOWN, not from when it was received. */
|
|
316
498
|
export function reduce(s, a) {
|
|
317
499
|
let n = reduceInner(s, a);
|
|
500
|
+
// TUI-DIVIDER: the bottom-rule AOCL strip, moved only by real turn events (dividerAocl.js).
|
|
501
|
+
if (a.type === 'turn.begin' || a.type === 'event') {
|
|
502
|
+
const dv = dividerStep(s.divider ?? null, a, a.now || 0);
|
|
503
|
+
if (dv !== (n.divider ?? null)) n = { ...n, divider: dv };
|
|
504
|
+
}
|
|
505
|
+
// A turn that ends WITHOUT a turn.finished event (stopped.local, turn.deferred, a /clear cancel) must not leave the
|
|
506
|
+
// strip open: endedAt stayed null, dividerLive stayed true, and the dot train + scrambled label looped on a dead turn.
|
|
507
|
+
if (n.divider && n.divider.endedAt == null && !n.turn) n = { ...n, divider: null };
|
|
318
508
|
// The honest activity word: only real events move it (see activity.js).
|
|
319
509
|
if (n.turn) {
|
|
320
510
|
const act = a.type === 'event' ? nextActivity(s.turn?.act, a.ev, a.now || 0, { hasText: Boolean(n.turn.chars) })
|
|
@@ -331,12 +521,15 @@ export function reduce(s, a) {
|
|
|
331
521
|
function reduceEvent(s, ev, now) {
|
|
332
522
|
switch (ev.type) {
|
|
333
523
|
case 'connected': return s;
|
|
334
|
-
//
|
|
524
|
+
// background.status is not in events v1 yet; handled so the line works the moment the server sends it (RESTORE-TUI: 4fbd3178c dropped it).
|
|
525
|
+
case 'background.status': return { ...s, background: Math.max(0, Number(ev.pending) || 0) };
|
|
335
526
|
case 'turn.started': {
|
|
336
527
|
const ctx = {
|
|
337
528
|
agent: ev.agent ?? s.ctx.agent, model: ev.model ?? s.ctx.model, provider: ev.provider ?? s.ctx.provider,
|
|
338
529
|
// B3: the host a self-hosted provider ran at; cleared when the server names none.
|
|
339
530
|
endpoint: ev.provider ? (ev.endpoint ?? null) : (s.ctx.endpoint ?? null),
|
|
531
|
+
// QUOXON-PARITY: which Claude path ran (subscription CLI vs API key); cleared like endpoint.
|
|
532
|
+
auth: ev.provider ? (ev.auth ?? null) : (s.ctx.auth ?? null),
|
|
340
533
|
hat: ev.hat ?? s.ctx.hat, conversationId: ev.conversation_id ?? s.ctx.conversationId
|
|
341
534
|
};
|
|
342
535
|
// The model THIS turn actually used, as the server reported it (never the profile's default).
|
|
@@ -378,13 +571,15 @@ function reduceEvent(s, ev, now) {
|
|
|
378
571
|
case 'replay.gap':
|
|
379
572
|
return addBlock(s, { kind: 'note', text: `Events ${ev.from} to ${ev.to} of this turn were not kept on the server (size cap); the rest follows.`, tone: 'warn' });
|
|
380
573
|
case 'tool.started':
|
|
381
|
-
return upsertTool(s, ev, { status: 'running', summary: ev.summary || ev.tool, startedAt: now });
|
|
574
|
+
return upsertTool(s, ev, { status: 'running', summary: ev.summary || ev.tool, startedAt: now, ...delegateInfo(ev) });
|
|
382
575
|
case 'tool.finished': {
|
|
383
576
|
const status = TOOL_STATUSES.includes(ev.status) ? ev.status : 'unknown'; // never green for a status we do not know
|
|
384
577
|
const first = !s.doneCalls[ev.call_id] && ['ok', 'failed', 'denied'].includes(status);
|
|
385
578
|
let withTool = upsertTool(s, ev, {
|
|
386
|
-
status, resultSummary: ev.result_summary, durationMs: ev.duration_ms,
|
|
387
|
-
envelopeId: ev.envelope_id, receipt: ev.receipt || null
|
|
579
|
+
status, resultSummary: toolResultText(ev.result_summary, s.verbose), rawSummary: ev.result_summary, durationMs: ev.duration_ms,
|
|
580
|
+
envelopeId: ev.envelope_id, receipt: ev.receipt || null,
|
|
581
|
+
...(ev.card && typeof ev.card === 'object' && Array.isArray(ev.card.rows) ? { card: ev.card } : {}), // HITL-INBOX-BAR: a list answer's rows (toolCards.js)
|
|
582
|
+
...(Number.isFinite(ev.tools_used) ? { toolsUsed: ev.tools_used } : {})
|
|
388
583
|
});
|
|
389
584
|
if (first && withTool.turn) withTool = { ...withTool, turn: { ...withTool.turn, finishedTools: withTool.turn.finishedTools + 1 } };
|
|
390
585
|
if (ev.receipt && ev.receipt.verified === true && withTool.turn && ev.receipt.ward_seq != null) {
|
|
@@ -396,7 +591,9 @@ function reduceEvent(s, ev, now) {
|
|
|
396
591
|
const card = {
|
|
397
592
|
callId: ev.call_id, inboxItemId: ev.inbox_item_id, approvalId: ev.approval_id, tool: ev.tool,
|
|
398
593
|
target: ev.target, scope: ev.scope, risk: ev.risk, url: ev.dashboard_url, shownAt: now,
|
|
399
|
-
undescribable: ev.describable === false || !ev.target
|
|
594
|
+
undescribable: ev.describable === false || !ev.target,
|
|
595
|
+
turnId: s.turn?.id ?? ev.turn_id ?? null, // the turn this card arrived in (a stream event is always this conversation)
|
|
596
|
+
privileged: isPrivileged({ tool: ev.tool, risk: ev.risk, flag: ev.privileged === true || ev.elevated === true })
|
|
400
597
|
};
|
|
401
598
|
if (s.approvals.some(x => x.callId === card.callId)) return s;
|
|
402
599
|
const dup = s.approvals.findIndex(x => (card.inboxItemId && x.inboxItemId === card.inboxItemId) || (card.approvalId && x.approvalId === card.approvalId));
|
|
@@ -404,13 +601,15 @@ function reduceEvent(s, ev, now) {
|
|
|
404
601
|
const approvals = s.approvals.slice(); approvals[dup] = { ...approvals[dup], callId: card.callId };
|
|
405
602
|
return { ...s, approvals };
|
|
406
603
|
}
|
|
604
|
+
const wi = card.inboxItemId ? s.waiting.findIndex(x => x.inboxItemId === card.inboxItemId) : -1;
|
|
605
|
+
if (wi >= 0) s = { ...s, waiting: s.waiting.filter((_, i) => i !== wi) }; // the stream says it is ours after all
|
|
407
606
|
if (card.inboxItemId && s.seenInbox[card.inboxItemId]) return s; // already shown and answered
|
|
408
607
|
return { ...s, seenInbox: card.inboxItemId ? { ...s.seenInbox, [card.inboxItemId]: true } : s.seenInbox, approvals: [...s.approvals, card], turn: s.turn ? { ...s.turn, approvals: s.turn.approvals + 1 } : s.turn };
|
|
409
608
|
}
|
|
410
609
|
case 'approval.resolved': {
|
|
411
610
|
const card = s.approvals.find(x => x.callId === ev.call_id);
|
|
412
611
|
const n = { ...s, approvals: s.approvals.filter(x => x.callId !== ev.call_id) };
|
|
413
|
-
return card || ev.decision ? note(n, `approval ${ev.decision || 'resolved'}${card ? `: ${card.tool} on ${card.target}` : ''}`, ev.decision === 'approved' ? 'ok' : 'warn') : n;
|
|
612
|
+
return card || ev.decision ? note(n, `${ev.decision === 'approved' ? '✔ approved by you' : `approval ${ev.decision || 'resolved'}`}${card ? `: ${card.tool} on ${card.target}` : ''}`, ev.decision === 'approved' ? 'ok' : 'warn') : n;
|
|
414
613
|
}
|
|
415
614
|
case 'steer.accepted': {
|
|
416
615
|
const i = s.steering.findIndex(x => x.text === ev.text);
|
|
@@ -418,10 +617,27 @@ function reduceEvent(s, ev, now) {
|
|
|
418
617
|
return addBlock({ ...s, steering }, { kind: 'note', text: `steered: ${ev.text}`, tone: 'accent' });
|
|
419
618
|
}
|
|
420
619
|
case 'trace':
|
|
620
|
+
// QUOXON-AOCL: per-layer AOCL rows fold into ONE line per turn (aocl.summary); /aocl shows the table.
|
|
621
|
+
if (ev.protocol === 'volt' && /tools offered/i.test(ev.label || '')) { // kept for the footer count and /aocl, shown only in verbose
|
|
622
|
+
const m = String(ev.label).match(/(\d+)\s+of\s+(\d+)/);
|
|
623
|
+
const list = String(ev.detail || '');
|
|
624
|
+
const toolsOffered = m ? Number(m[1]) : (list ? list.split(/,\s*/).filter(Boolean).length : null);
|
|
625
|
+
if (s.turn) s = { ...s, turn: { ...s.turn, toolsOffer: { offered: toolsOffered, total: m ? Number(m[2]) : null, list } } };
|
|
626
|
+
}
|
|
627
|
+
if (isLayerTrace(ev)) return s.turn ? { ...s, turn: { ...s.turn, aoclTraces: [...(s.turn.aoclTraces || []), { label: ev.label, detail: ev.detail }] } } : s;
|
|
421
628
|
if (s.verbose === 'off') return s;
|
|
422
629
|
return addBlock(s, { kind: 'trace', protocol: ev.protocol, label: ev.label, ref: ev.ref, detail: ev.detail });
|
|
630
|
+
case 'aocl.summary': {
|
|
631
|
+
const sum = withTools(normaliseSummary(ev), s.turn);
|
|
632
|
+
const n = { ...s, lastAocl: sum, turn: s.turn ? { ...s.turn, aoclSummary: true } : s.turn };
|
|
633
|
+
return addBlock(n, { kind: 'aocl', sum }); // the one-line footer shows at every verbosity
|
|
634
|
+
}
|
|
635
|
+
case 'aocl.verdict': {
|
|
636
|
+
const v = verdictNote(ev);
|
|
637
|
+
return v.tone === 'error' ? addBlock(s, { kind: 'error', text: v.text }) : addBlock(s, { kind: 'note', text: v.text, tone: v.tone });
|
|
638
|
+
}
|
|
423
639
|
case 'error': {
|
|
424
|
-
const n = addBlock(s, { kind: 'error', text: String(ev.error || 'stream error') });
|
|
640
|
+
const n = addBlock(s, { kind: 'error', text: isConsentError(ev) ? consentErrorText(ev) : String(ev.error || 'stream error') });
|
|
425
641
|
return n;
|
|
426
642
|
}
|
|
427
643
|
case 'turn.finished': case 'done': {
|
|
@@ -435,8 +651,16 @@ function reduceEvent(s, ev, now) {
|
|
|
435
651
|
// `done` only carries text/session; turn.finished (sent just after it) ends the turn. A legacy
|
|
436
652
|
// server never sends turn.finished: the app synthesises one when the stream closes.
|
|
437
653
|
// usage: `done` may carry cost_usd; turn.finished carries `usage` (input/output tokens, maybe cost_usd).
|
|
438
|
-
|
|
439
|
-
|
|
654
|
+
// cli_session_id on `done` = the claude CLI path: its cost is that CLI's own estimate even from a server that
|
|
655
|
+
// does not label it yet (cost_source 'claude-cli', R3 D5).
|
|
656
|
+
if (ev.type === 'done') return { ...n, turn: { ...n.turn, doneCost: ev.cost_usd, viaCli: Boolean(ev.cli_session_id) } };
|
|
657
|
+
// An older server sends per-layer AOCL traces and no aocl.summary: fold them into the one line here.
|
|
658
|
+
if (!n.turn.aoclSummary && n.turn.aoclTraces?.length) {
|
|
659
|
+
const sum = withTools(summaryFromTraces(n.turn.aoclTraces), n.turn);
|
|
660
|
+
n = { ...n, lastAocl: sum };
|
|
661
|
+
n = addBlock(n, { kind: 'aocl', sum });
|
|
662
|
+
}
|
|
663
|
+
n = { ...n, turn: { ...n.turn, usage: normUsage(n.turn.viaCli && ev.usage && !ev.usage.cost_source ? { ...ev.usage, cost_source: 'claude-cli' } : ev.usage, n.turn.doneCost) } };
|
|
440
664
|
return endTurn(n, ev.status || 'completed', now);
|
|
441
665
|
}
|
|
442
666
|
default:
|
|
@@ -456,3 +680,19 @@ export function approvalKeyAllowed({ now, shownAt, lastKeyAt, heldKey = null, ch
|
|
|
456
680
|
if (heldKey != null && ch === heldKey && (lastKeyAt == null || now - lastKeyAt < HELD_KEY_MS)) return false;
|
|
457
681
|
return true;
|
|
458
682
|
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* What an approval is for, without repeating who asked: titles often start "<who> wants to: ..." (the source label
|
|
686
|
+
* already says who), and a generic "Approval required" title falls back to the item's summary or description.
|
|
687
|
+
*/
|
|
688
|
+
export function approvalWhat(it) {
|
|
689
|
+
const w = it?.payload?.what;
|
|
690
|
+
if (w && typeof w === 'object' && typeof w.summary === 'string' && w.summary.trim()) { // APPROVAL-WHAT: the server says what will happen
|
|
691
|
+
const s = w.summary.trim(); const tg = typeof w.target === 'string' ? w.target.trim() : '';
|
|
692
|
+
return tg && !s.includes(tg) ? `${s} (target: ${tg})` : (!tg && w.target_none ? `${s} (${String(w.target_none).trim()})` : s);
|
|
693
|
+
}
|
|
694
|
+
let t = String(it?.title || it?.id || '').replace(/^[^:]{0,80}\bwants to:\s*/i, '').trim();
|
|
695
|
+
const more = it?.summary || it?.description || it?.detail || '';
|
|
696
|
+
if ((!t || /^approval (?:required|needed)\b/i.test(t)) && more) t = String(more).trim();
|
|
697
|
+
return t || String(it?.id || 'approval');
|
|
698
|
+
}
|