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
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LUCID object cards: pure views for the deck, in the Quoxon object-card shape ({ box, rows } of
|
|
3
|
+
* { text, role } segments, see quoxon/objects/model.js). An area is a tile card; a Needs-you item is a card
|
|
4
|
+
* that opens in place into who / what / why / when and the actions. Nothing here reads a clock or does I/O.
|
|
5
|
+
*
|
|
6
|
+
* Roles are theme roles; the renderer (LucidCard.jsx) also understands 'sel' (the selected action bar).
|
|
7
|
+
* Text is WRAPPED to the card, never clipped, except for a single action line and the command line.
|
|
8
|
+
*/
|
|
9
|
+
import { seg, fit, padRow } from '../quoxon/objects/model.js';
|
|
10
|
+
import { wrap, whenText, ageOf, GLYPH, clip } from './fmt.js';
|
|
11
|
+
|
|
12
|
+
export const URGENCY_ROLE = { now: 'error', today: 'warn', 'this week': 'sky', whenever: 'muted', fyi: 'muted' };
|
|
13
|
+
export const URGENCY_LABEL = { now: 'NOW', today: 'TODAY', 'this week': 'THIS WEEK', whenever: 'WHENEVER', fyi: 'FYI' };
|
|
14
|
+
export const STATE_ROLE = { ok: 'ok', attention: 'warn', unknown: 'muted', blind: 'error' };
|
|
15
|
+
export const STATE_WORD = { ok: 'ok', attention: 'attention', unknown: 'no signal', blind: 'blind' };
|
|
16
|
+
|
|
17
|
+
const nm = (x) => (typeof x === 'string' ? x : (x && (x.label || x.name || x.L0 || x.id)) || '');
|
|
18
|
+
const LW = 8; // label column of the open card
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* An area tile. d: { area, label, state, L0, last, n (needs), active (filter on), selected, focused,
|
|
22
|
+
* expanded, now }. A tile is one status row; the selected tile in a focused pane opens to its sentence.
|
|
23
|
+
*/
|
|
24
|
+
export function lucidArea(d, cols) {
|
|
25
|
+
const w = Math.max(18, cols), inner = w - 4;
|
|
26
|
+
const all = d.area == null;
|
|
27
|
+
const glyph = all ? '◉' : (GLYPH[d.state] || '?');
|
|
28
|
+
const role = all ? 'sky' : (STATE_ROLE[d.state] || 'muted');
|
|
29
|
+
const right = `${d.active ? '● ' : ''}${d.n ? d.n : '·'}`;
|
|
30
|
+
const rows = [];
|
|
31
|
+
const word = all ? 'everything' : (STATE_WORD[d.state] || 'unknown');
|
|
32
|
+
const age = d.last && d.last.ts != null && d.now != null ? `last ${ageOf(d.last.ts, d.now)}` : '';
|
|
33
|
+
const line = age && [...`${word} · ${age}`].length <= inner ? `${word} · ${age}` : word;
|
|
34
|
+
rows.push([seg(line, d.selected ? 'value' : role === 'ok' ? 'muted' : role)]);
|
|
35
|
+
if (d.expanded && d.L0) for (const ln of wrap(d.L0, inner)) rows.push([seg(ln, 'value')]);
|
|
36
|
+
const mark = d.selected ? '▶ ' : '';
|
|
37
|
+
return {
|
|
38
|
+
box: {
|
|
39
|
+
width: w, title: `${mark}${glyph} ${fit(d.label || d.area || 'All areas', Math.max(4, inner - 8))}`, titleRole: role,
|
|
40
|
+
right, rightRole: d.n ? 'warn' : 'muted', border: d.selected ? 'sky' : 'faint', sel: d.selected ? (d.focused ? 'focus' : 'dim') : null,
|
|
41
|
+
},
|
|
42
|
+
rows,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* A Needs-you card. d: { item, areaLabel, isNew, selected, focused, open, mSel, now }.
|
|
48
|
+
* Collapsed: the wrapped sentence. Open: the sentence, who/what/why/when, affects, then the actions with
|
|
49
|
+
* the highlighted one as a bar and its exact command beneath.
|
|
50
|
+
*/
|
|
51
|
+
export function lucidNeed(d, cols) {
|
|
52
|
+
const it = d.item, w = Math.max(24, cols), inner = w - 4;
|
|
53
|
+
const urg = it.urgency || 'whenever';
|
|
54
|
+
const a = it.L1 || {};
|
|
55
|
+
const ms = (it.L2 && it.L2.measures) || [];
|
|
56
|
+
const mark = d.selected ? '▶ ' : '';
|
|
57
|
+
const rows = [];
|
|
58
|
+
const text = (d.isNew ? '● ' : '') + (it.L0 || '');
|
|
59
|
+
wrap(text, inner).forEach((ln, i) => rows.push([seg(ln, d.isNew ? 'ok' : d.selected ? 'value' : 'text', d.selected)]));
|
|
60
|
+
if (d.open) {
|
|
61
|
+
const detail = (lbl, v) => wrap(v, inner - LW).forEach((ln, i) => rows.push([seg(i ? ' '.repeat(LW) : lbl.padEnd(LW), i ? 'muted' : 'label'), seg(ln, 'text')]));
|
|
62
|
+
for (const [lbl, v] of [['Who', a.who], ['What', a.what], ['Why', a.why], ['When', whenText(a.when, d.now)]]) if (v) detail(lbl, v);
|
|
63
|
+
if ((a.affected || []).length) rows.push([seg('Affects'.padEnd(LW), 'label'), seg(clip(a.affected.map(nm).filter(Boolean).join(', '), inner - LW), 'muted')]);
|
|
64
|
+
rows.push(ms.length ? [seg('What you can do', 'key', true)] : [seg('No actions to offer here. Esc closes.', 'muted')]);
|
|
65
|
+
ms.forEach((m, mi) => {
|
|
66
|
+
const on = mi === d.mSel;
|
|
67
|
+
const risk = ` ${m.risk}`;
|
|
68
|
+
const label = fit(m.say || m.label, Math.max(8, inner - 2 - risk.length));
|
|
69
|
+
if (on) rows.push([seg(`❯ ${label}${risk}`.padEnd(inner, ' '), 'sel', true)]);
|
|
70
|
+
else rows.push([seg(' ', 'muted'), seg(label, 'text'), seg(risk, m.risk === 'dangerous' ? 'error' : m.risk === 'gated' ? 'warn' : 'muted')]);
|
|
71
|
+
});
|
|
72
|
+
const cmd = ms[d.mSel] && ms[d.mSel].command;
|
|
73
|
+
if (cmd) rows.push([seg(' $ ', 'muted'), seg(clip(cmd, inner - 4), 'code')]);
|
|
74
|
+
else if (ms.some((m) => m.command)) rows.push([seg(' ', 'muted')]);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
box: {
|
|
78
|
+
width: w, title: `${mark}${URGENCY_LABEL[urg] || String(urg).toUpperCase()} · ${it.kind || 'item'}`, titleRole: URGENCY_ROLE[urg] || 'muted',
|
|
79
|
+
right: d.areaLabel || '', rightRole: 'muted', border: d.selected ? 'sky' : (urg === 'now' ? 'error' : 'faint'), sel: d.selected ? (d.focused ? 'focus' : 'dim') : null,
|
|
80
|
+
},
|
|
81
|
+
rows,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const cardHeight = (view) => view.rows.length + 2;
|
|
86
|
+
export { padRow };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sitrep as chat Markdown (Discord, Matrix, slots): short, scannable, under one Discord message.
|
|
3
|
+
* Owner 2026-10-17: "Format your sitrep" after the spoken paragraph was posted to Discord as a wall of text.
|
|
4
|
+
* Pure: data in, string out. No colour codes, no box drawing (chat clients mangle both).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const URGENCY = { now: '🔴', today: '🟠', this_week: '🟡', week: '🟡', whenever: '⚪', fyi: '⚪' };
|
|
8
|
+
const STATE = { attention: '⚠️', ok: '✅', partial: '🟡', unknown: '❔', blind: '⛔' };
|
|
9
|
+
export const MAX_CHARS = 1900; // Discord's limit is 2000 per message
|
|
10
|
+
|
|
11
|
+
const clip = (s, n) => {
|
|
12
|
+
const t = String(s || '').replace(/\s+/g, ' ').trim();
|
|
13
|
+
return t.length > n ? `${t.slice(0, n - 1)}…` : t;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function age(ms) {
|
|
17
|
+
if (!(ms > 0)) return '';
|
|
18
|
+
const m = Math.floor(ms / 60000);
|
|
19
|
+
if (m < 60) return `${m}m`;
|
|
20
|
+
const h = Math.floor(m / 60);
|
|
21
|
+
if (h < 48) return `${h}h`;
|
|
22
|
+
return `${Math.floor(h / 24)}d`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function clockLondon(now) {
|
|
26
|
+
try {
|
|
27
|
+
return new Intl.DateTimeFormat('en-GB', { timeZone: 'Europe/London', hour: '2-digit', minute: '2-digit', timeZoneName: 'short' }).format(new Date(now));
|
|
28
|
+
} catch { return new Date(now).toISOString().slice(11, 16) + ' UTC'; }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** opts: { now, maxNeeds (default 6), maxChars (default MAX_CHARS) } */
|
|
32
|
+
export function renderSitrepMarkdown(data, opts = {}) {
|
|
33
|
+
const sit = (data && data.situation) || data || {};
|
|
34
|
+
const now = opts.now ?? Date.now();
|
|
35
|
+
const maxNeeds = opts.maxNeeds ?? 6;
|
|
36
|
+
const maxChars = opts.maxChars ?? MAX_CHARS;
|
|
37
|
+
const cov = sit.coverage || (sit.cannot_see && sit.cannot_see.coverage) || { live: 0, expected: 0 };
|
|
38
|
+
const missing = sit.coverage && Array.isArray(sit.coverage.missing) ? sit.coverage.missing : [];
|
|
39
|
+
const needs = Array.isArray(sit.needs_you) ? sit.needs_you : [];
|
|
40
|
+
const low = needs.filter((n) => n.urgency === 'whenever' || n.urgency === 'fyi').length;
|
|
41
|
+
|
|
42
|
+
const out = [];
|
|
43
|
+
out.push(`**◉ LUCID sitrep** · ${clockLondon(now)} · ${cov.live}/${cov.expected} sources`);
|
|
44
|
+
out.push('');
|
|
45
|
+
if (!needs.length) {
|
|
46
|
+
out.push('**Needs you:** nothing right now.');
|
|
47
|
+
} else {
|
|
48
|
+
out.push(`**Needs you (${needs.length}${low ? `, ${low} low priority` : ''})**`);
|
|
49
|
+
needs.slice(0, maxNeeds).forEach((n, i) => {
|
|
50
|
+
const w = n.L1 && n.L1.when && n.L1.when.waited_ms;
|
|
51
|
+
const tail = [n.area, age(w)].filter(Boolean).join(', ');
|
|
52
|
+
out.push(`${i + 1}. ${URGENCY[n.urgency] || '•'} ${clip(n.L0, 90)}${tail ? ` _(${tail})_` : ''}`);
|
|
53
|
+
});
|
|
54
|
+
if (needs.length > maxNeeds) out.push(`…and ${needs.length - maxNeeds} more: \`quox sitrep\``);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const areas = Array.isArray(sit.areas) ? sit.areas : [];
|
|
58
|
+
if (areas.length) {
|
|
59
|
+
out.push('');
|
|
60
|
+
out.push('**At a glance**');
|
|
61
|
+
for (const a of areas) out.push(`${STATE[a.state] || '•'} **${a.label || a.area}**: ${clip(a.L0, 80)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const dau = Array.isArray(sit.doesnt_add_up) ? sit.doesnt_add_up : [];
|
|
65
|
+
if (dau.length) {
|
|
66
|
+
out.push('');
|
|
67
|
+
out.push(`**Doesn't add up (${dau.length}):** ${clip(dau[0].L0, 90)}${dau.length > 1 ? ' …' : ''}`);
|
|
68
|
+
}
|
|
69
|
+
out.push('');
|
|
70
|
+
out.push(missing.length ? `**Can't see:** ${missing.join(', ')}` : `**Can't see:** nothing, every source is reporting.`);
|
|
71
|
+
|
|
72
|
+
// Stay inside one chat message: drop "at a glance" rows from the bottom, then needs, never the header.
|
|
73
|
+
let text = out.join('\n');
|
|
74
|
+
while (text.length > maxChars && out.length > 3) {
|
|
75
|
+
const i = out.findLastIndex((l) => /^(✅|⚠️|🟡|❔|⛔|•) \*\*/.test(l));
|
|
76
|
+
if (i < 0) break;
|
|
77
|
+
out.splice(i, 1);
|
|
78
|
+
text = out.join('\n');
|
|
79
|
+
}
|
|
80
|
+
return text.length > maxChars ? `${text.slice(0, maxChars - 1)}…` : text;
|
|
81
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LUCID deck motion: pure helpers for the deck's ONE clock (quoxon/motion/clock.js).
|
|
3
|
+
*
|
|
4
|
+
* Idle is still. The deck declares a demand each render; the clock keeps one timer for the soonest demand and
|
|
5
|
+
* none at all when nothing asks. Frames (~12 fps) are demanded ONLY while something is visibly moving: a fetch
|
|
6
|
+
* in flight (the Q works), the Q settling after a state change (a short window, then still colour), or a
|
|
7
|
+
* card dropping in. Everything else is a one-shot wake (toast expiry, ghost expiry, the minute tick).
|
|
8
|
+
*
|
|
9
|
+
* LUCID state -> Q state (qglyph.js): needs you = waiting, fetching = working, unreachable = failing,
|
|
10
|
+
* quiet = resting. Failing wins over working so an outage never reads as busy.
|
|
11
|
+
*/
|
|
12
|
+
import { FRAME_MS } from '../quoxon/motion/clock.js';
|
|
13
|
+
|
|
14
|
+
export { FRAME_MS };
|
|
15
|
+
/** After a state change the Q animates this long, then holds a still colour (no timer, no writes). */
|
|
16
|
+
export const SETTLE_MS = 2400;
|
|
17
|
+
/** A card dropping in is revealing for this long from its start (rows decode, then a short afterglow). */
|
|
18
|
+
export const REVEAL_MS = 1500;
|
|
19
|
+
/** Cards of one load start this far apart so the board fills top to bottom. */
|
|
20
|
+
export const STAGGER_MS = 70;
|
|
21
|
+
|
|
22
|
+
export function glyphStateOf({ err = null, fetching = false, needs = 0 } = {}) {
|
|
23
|
+
if (err) return 'failing';
|
|
24
|
+
if (fetching) return 'working';
|
|
25
|
+
return needs > 0 ? 'waiting' : 'resting';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Is the Q moving at `now`? Working moves while it lasts; every other state settles after SETTLE_MS. */
|
|
29
|
+
export function glyphAnimating({ state, since, now, motion }) {
|
|
30
|
+
if (!motion) return false;
|
|
31
|
+
if (state === 'working') return true;
|
|
32
|
+
return since != null && now - since < SETTLE_MS;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Seconds into a card's reveal, or Infinity when settled (or motion is off). A future start is negative. */
|
|
36
|
+
export function revealT(startAt, now, motion) {
|
|
37
|
+
if (!motion || startAt == null) return Infinity;
|
|
38
|
+
const ms = now - startAt;
|
|
39
|
+
return ms >= REVEAL_MS ? Infinity : ms / 1000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The demands for this render. frames: something is moving right now. at: epoch ms of one-shot wakes
|
|
44
|
+
* (past or null entries are ignored). Without motion there are no frame demands at all.
|
|
45
|
+
*/
|
|
46
|
+
export function deckDemands({ motion = false, frames = false, at = [], now = Date.now() } = {}) {
|
|
47
|
+
const d = [];
|
|
48
|
+
if (motion && frames) d.push({ every: FRAME_MS });
|
|
49
|
+
let soonest = null;
|
|
50
|
+
for (const a of at) if (Number.isFinite(a) && a > now && (soonest == null || a < soonest)) soonest = a;
|
|
51
|
+
if (soonest != null) d.push({ at: soonest });
|
|
52
|
+
return d;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Next wall-clock minute boundary for a displayed time (the injected clock, so tests stay deterministic). */
|
|
56
|
+
export const nextMinuteAt = (wall, shown) => wall + (60000 - (shown % 60000));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# quoxon extensions
|
|
2
|
+
|
|
3
|
+
Optional npm packages that add a surface to quoxon (a MIDI pad deck, a status bar). Core ships none and knows none by name.
|
|
4
|
+
|
|
5
|
+
**Loading.** Nothing loads unless the package is installed AND listed in `~/.quox/extensions.json`
|
|
6
|
+
(`quox ext add|remove|list <pkg>`; `/ext list|on|off <name>` inside quoxon). Install with `npm i -g <pkg>`.
|
|
7
|
+
Resolution order: env `QUOX_EXT_<NAME>_MODULE` (name upper-cased, non-alphanumerics to `_`), a local `import(name)`,
|
|
8
|
+
then the global npm root (`dirname(execPath)/../lib/node_modules` first, then `npm root -g` once, cached).
|
|
9
|
+
`/ext list` names where each was found; a miss lists where it looked.
|
|
10
|
+
|
|
11
|
+
**Module.** Default export (or the module itself):
|
|
12
|
+
`{ name, version, commands?: [{ name: '/deck', help, run(args, ctx) }], create({ emit, log, settings }) -> { update(state), close() } }`.
|
|
13
|
+
Its slash commands exist only while it is loaded. `run` may return a string or `{ text, tone }`.
|
|
14
|
+
|
|
15
|
+
**State** (`update(state)`, deduped, at most 1/s):
|
|
16
|
+
`{ org:{id,name}, approvals:{count, items:[{n,id,risk:'low'|'high',title,agent,options?:[{key,label}]}]}, health:{<name>:'ok'|'warn'|'down'},
|
|
17
|
+
loops:[{id,title,turn,max,status}], fleet:{total,online,offline,warn,hosts:[{name,state:'ok'|'warn'|'down'}] (max 16)},
|
|
18
|
+
evidence:{chain:'ok'|'broken'|'unknown', checked_at}, sessions:{active}, event?:{kind,text,level} }`.
|
|
19
|
+
`fleet` comes from a 30 s poll of the host list while an extension is loaded. `evidence.chain` is `unknown` until quoxon holds a chain status.
|
|
20
|
+
|
|
21
|
+
**emit.** `{type:'open_approval', n}` opens approval n; `{type:'pick', n, option}` only fills the composer with the
|
|
22
|
+
answer command (the user presses Enter, high risk still needs the step-up PIN); `{type:'alert_ack'}` clears the event.
|
|
23
|
+
An extension can never resolve an approval. emit is ignored once the extension is unloaded.
|
package/lib/quoxon/activity.js
CHANGED
|
@@ -37,6 +37,13 @@ const TOOL_TABLE = [
|
|
|
37
37
|
[/(^|_)(list|get|status)$|^contacts_/, 'Looking up'],
|
|
38
38
|
];
|
|
39
39
|
|
|
40
|
+
/** QUOXON-FW3: "Delegating <Agent> · <task gist>" instead of an anonymous delegate_to_agent. */
|
|
41
|
+
function delegateTarget(ev) {
|
|
42
|
+
const id = ev.agent_id || (String(ev.summary || '').match(/\bagent_id=(\S+)/) || [])[1] || '';
|
|
43
|
+
const name = ev.agent_name || id;
|
|
44
|
+
return { ...ev, target: [name, ev.task_gist].filter(Boolean).join(' \u00b7 ') };
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
/** { word, detail } for a tool call. Unknown tools are "Working" with the tool name, never a guess. */
|
|
41
48
|
export function toolWord(tool, ev = {}) {
|
|
42
49
|
const name = String(tool || '').toLowerCase();
|
|
@@ -46,7 +53,7 @@ export function toolWord(tool, ev = {}) {
|
|
|
46
53
|
return { word: 'Working', detail: name || null };
|
|
47
54
|
}
|
|
48
55
|
|
|
49
|
-
const mk = (word, detail = null, now = 0, tone = 'work', flash = null) => ({ word, detail: detail ? clipD(detail) : null, tone, flash, since: now });
|
|
56
|
+
const mk = (word, detail = null, now = 0, tone = 'work', flash = null, tool = null) => ({ word, detail: detail ? clipD(detail) : null, tone, flash, since: now, ...(tool ? { tool: clipD(tool, 40) } : {}) });
|
|
50
57
|
export const initialActivity = (now = 0) => mk('Sending', null, now); // the request was dispatched: true from turn.begin
|
|
51
58
|
|
|
52
59
|
/** Trace lines from the governance chain. Labels as the server sends them. */
|
|
@@ -82,7 +89,12 @@ export function nextActivity(prev, ev, now = 0, ctx = {}) {
|
|
|
82
89
|
return p;
|
|
83
90
|
}
|
|
84
91
|
case 'trace': { const t = traceWord(ev); return t ? mk(t[0], t[1], now) : p; }
|
|
85
|
-
case 'tool.started': { const t = toolWord(ev.tool, ev); return mk(t.word, t.detail, now); }
|
|
92
|
+
case 'tool.started': { const t = toolWord(ev.tool, ev.tool === 'delegate_to_agent' ? delegateTarget(ev) : ev); return mk(t.word, t.detail, now, 'work', null, ev.tool || null); }
|
|
93
|
+
case 'delegation.progress': { // QUOXON 2026-10-17: a specialist's real tool call, forwarded by the server
|
|
94
|
+
if (!ev.tool) return p;
|
|
95
|
+
const who = ev.agent_name || ev.agent_id || 'specialist';
|
|
96
|
+
return mk('Delegating', `${who} \u00b7 ${ev.tool}${ev.detail ? ` (${ev.detail})` : ''}`, now, 'work', null, ev.tool);
|
|
97
|
+
}
|
|
86
98
|
case 'tool.finished': return ev.status === 'failed' ? { ...p, flash: now } : p; // keep the word, flash the Q only
|
|
87
99
|
case 'approval.requested': return mk('Waiting', 'for your approval', now, 'wait');
|
|
88
100
|
case 'approval.resolved': return mk('Resuming', null, now);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QUOXON-AOCL: one compact AOCL line per turn, and the full per-layer table on demand (/aocl).
|
|
3
|
+
*
|
|
4
|
+
* The server sends ONE `aocl.summary` per streamed turn ({ layers:[{id,outcome,ms,reason}],
|
|
5
|
+
* total, accounted, passed, bypassed, rejected, rejected_at, ms, verdict? }) and an
|
|
6
|
+
* `aocl.verdict` when L8 flags text that was already on screen. Older servers send only
|
|
7
|
+
* per-layer `trace` rows (protocol aocl, label "L1 identity complete"); those are folded
|
|
8
|
+
* into the same line so nothing is lost, and the line says how many layers were reported.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const LAYER_NAMES = {
|
|
12
|
+
L0: 'ingress', L1: 'identity', L2: 'router', L3: 'policy', L4: 'planner', L5: 'context',
|
|
13
|
+
L6: 'shape', L7: 'delegate', L8: 'verify', L9: 'assemble', L10: 'audit'
|
|
14
|
+
};
|
|
15
|
+
const ORDER = Object.keys(LAYER_NAMES);
|
|
16
|
+
const OUTCOME_WORD = { complete: 'passed', bypass: 'bypassed', reject: 'rejected', not_run: 'not run' };
|
|
17
|
+
const OUTCOME_GLYPH = { complete: '✔', bypass: '○', reject: '✖', not_run: '?' };
|
|
18
|
+
|
|
19
|
+
/** A per-layer AOCL trace row (folded into the one-line summary instead of a row per layer). */
|
|
20
|
+
export const isLayerTrace = (ev) => ev?.protocol === 'aocl' && /^L(?:10|[0-9])\b/.test(String(ev.label || ''));
|
|
21
|
+
|
|
22
|
+
const fmtMs = (ms) => {
|
|
23
|
+
const n = Number(ms);
|
|
24
|
+
if (!Number.isFinite(n)) return '';
|
|
25
|
+
return n < 10 ? `${Math.round(n * 10) / 10} ms` : n < 1000 ? `${Math.round(n)} ms` : `${(n / 1000).toFixed(1)} s`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** Normalise a server summary (tolerant of missing fields). */
|
|
29
|
+
export function normaliseSummary(ev) {
|
|
30
|
+
const layers = Array.isArray(ev?.layers) ? ev.layers.filter(l => l && l.id).map(l => ({ id: String(l.id), outcome: String(l.outcome || 'not_run'), ms: Number(l.ms) || 0, ...(l.reason ? { reason: String(l.reason) } : {}) })) : [];
|
|
31
|
+
const count = (o) => layers.filter(l => l.outcome === o).length;
|
|
32
|
+
const rejected = layers.filter(l => l.outcome === 'reject');
|
|
33
|
+
return {
|
|
34
|
+
layers,
|
|
35
|
+
total: Number(ev?.total) || 11,
|
|
36
|
+
accounted: Number.isFinite(Number(ev?.accounted)) ? Number(ev.accounted) : layers.filter(l => l.outcome !== 'not_run').length,
|
|
37
|
+
passed: Number.isFinite(Number(ev?.passed)) ? Number(ev.passed) : count('complete'),
|
|
38
|
+
bypassed: Number.isFinite(Number(ev?.bypassed)) ? Number(ev.bypassed) : count('bypass'),
|
|
39
|
+
rejected: Number.isFinite(Number(ev?.rejected)) ? Number(ev.rejected) : rejected.length,
|
|
40
|
+
rejectedAt: ev?.rejected_at || (rejected[0] && rejected[0].id) || null,
|
|
41
|
+
ms: Number.isFinite(Number(ev?.ms)) ? Number(ev.ms) : null,
|
|
42
|
+
verdict: ev?.verdict || null,
|
|
43
|
+
legacy: Boolean(ev?.legacy)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Build a summary from legacy per-layer trace rows (a server without aocl.summary). */
|
|
48
|
+
export function summaryFromTraces(traces) {
|
|
49
|
+
const layers = [];
|
|
50
|
+
for (const t of traces || []) {
|
|
51
|
+
const m = String(t.label || '').match(/^(L(?:10|[0-9]))\b.*?\b(complete|bypass|reject)\b/);
|
|
52
|
+
if (!m || layers.some(l => l.id === m[1])) continue;
|
|
53
|
+
const ms = String(t.detail || '').match(/(\d+(?:\.\d+)?)\s*ms/);
|
|
54
|
+
layers.push({ id: m[1], outcome: m[2], ms: ms ? Number(ms[1]) : 0 });
|
|
55
|
+
}
|
|
56
|
+
layers.sort((a, b) => ORDER.indexOf(a.id) - ORDER.indexOf(b.id));
|
|
57
|
+
const ms = layers.reduce((n, l) => n + l.ms, 0);
|
|
58
|
+
return normaliseSummary({ layers, total: 11, accounted: layers.length, ms, legacy: true });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The one line: "AOCL 11/11 ✔ · 9 passed · 2 bypassed · 34 ms" (the caller draws the ▤ icon).
|
|
63
|
+
* -> { segs: [{ t, tone }] } where tone is 'ok' | 'fail' | 'warn' | 'text'.
|
|
64
|
+
*/
|
|
65
|
+
export function aoclLineSegs(sum) {
|
|
66
|
+
const s = sum || normaliseSummary({});
|
|
67
|
+
const segs = [{ t: `${s.accounted}/${s.total} `, tone: 'text' }];
|
|
68
|
+
if (s.rejected > 0) {
|
|
69
|
+
const rl = s.layers.find(l => l.id === s.rejectedAt);
|
|
70
|
+
segs.push({ t: '✖', tone: 'fail' }, { t: ` ${[s.rejectedAt || 'a layer', LAYER_NAMES[s.rejectedAt]].filter(Boolean).join(' ')} rejected${rl && rl.reason ? `: ${rl.reason}` : ''}`, tone: 'fail' });
|
|
71
|
+
} else if (s.accounted < s.total) {
|
|
72
|
+
segs.push({ t: '⚠', tone: 'warn' }, { t: ` only ${s.accounted} of ${s.total} reported`, tone: 'warn' });
|
|
73
|
+
} else {
|
|
74
|
+
segs.push({ t: '✔', tone: 'ok' });
|
|
75
|
+
}
|
|
76
|
+
segs.push({ t: ` · ${s.passed} passed`, tone: 'text' });
|
|
77
|
+
if (s.bypassed) segs.push({ t: ` · ${s.bypassed} bypassed`, tone: 'text' });
|
|
78
|
+
if (s.verdict && s.verdict.verdict === 'warned') segs.push({ t: ` · ${s.verdict.layer || 'L8'} warned`, tone: 'warn' });
|
|
79
|
+
if (s.toolsOffered != null) segs.push({ t: ` · ${s.toolsOffered} tool${s.toolsOffered === 1 ? '' : 's'}`, tone: 'text' });
|
|
80
|
+
if (s.ms != null) segs.push({ t: ` · ${fmtMs(s.ms)}`, tone: 'text' });
|
|
81
|
+
if (s.legacy) segs.push({ t: ' · server sent no summary', tone: 'warn' });
|
|
82
|
+
return { segs };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const aoclLineText = (sum) => 'AOCL ' + aoclLineSegs(sum).segs.map(x => x.t).join('');
|
|
86
|
+
|
|
87
|
+
/** The /aocl table: [text, tone] rows, one per layer, for the last turn. */
|
|
88
|
+
export function aoclTableLines(sum) {
|
|
89
|
+
if (!sum) return [['No AOCL record yet. Send a message, then /aocl shows every layer of that turn.', 'warn']];
|
|
90
|
+
const out = [[`▤ AOCL last turn │ ${aoclLineText(sum).slice(5)}`, sum.rejected ? 'error' : 'accent']];
|
|
91
|
+
const byId = Object.fromEntries(sum.layers.map(l => [l.id, l]));
|
|
92
|
+
for (const id of ORDER) {
|
|
93
|
+
const l = byId[id] || { id, outcome: 'not_run', ms: 0 };
|
|
94
|
+
const word = OUTCOME_WORD[l.outcome] || l.outcome;
|
|
95
|
+
const line = `${OUTCOME_GLYPH[l.outcome] || '·'} ${id.padEnd(4)}${LAYER_NAMES[id].padEnd(10)}${word.padEnd(10)}${fmtMs(l.ms).padStart(8)}${l.reason ? ` ${l.reason}` : ''}`;
|
|
96
|
+
out.push([line, l.outcome === 'reject' ? 'error' : l.outcome === 'not_run' ? 'warn' : l.outcome === 'complete' ? 'ok' : 'accent']);
|
|
97
|
+
}
|
|
98
|
+
if (sum.toolsOffered != null) { // the VOLT "tools offered" list, on demand instead of opening every turn
|
|
99
|
+
out.push([`tools offered to the model: ${sum.toolsOffered}${sum.toolsTotal != null ? ` of ${sum.toolsTotal}` : ''}`, 'accent']);
|
|
100
|
+
let row = ' ';
|
|
101
|
+
for (const t of String(sum.toolsList || '').split(/,\s*/).filter(Boolean)) {
|
|
102
|
+
if (row.length + t.length + 2 > 76 && row.trim()) { out.push([row.replace(/,?\s*$/, ''), 'text']); row = ' '; }
|
|
103
|
+
row += t + ', ';
|
|
104
|
+
}
|
|
105
|
+
if (row.trim()) out.push([row.replace(/,?\s*$/, ''), 'text']);
|
|
106
|
+
}
|
|
107
|
+
if (sum.verdict) out.push([`${sum.verdict.layer || 'L8'} ${sum.verdict.verdict}: ${sum.verdict.reason || ''}${sum.verdict.delivered ? ' (the text was already shown)' : ''}`, sum.verdict.verdict === 'blocked' ? 'error' : 'warn']);
|
|
108
|
+
if (sum.legacy) out.push(['This server sent per-layer traces only, no aocl.summary: layers it did not report are shown as not run.', 'warn']);
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The note shown when L8 flags an answer that is already on screen. */
|
|
113
|
+
export function verdictNote(v) {
|
|
114
|
+
const reason = v?.reason ? `: ${v.reason}` : '';
|
|
115
|
+
if (v?.verdict === 'blocked') return { text: `✖ AOCL ${v.layer || 'L8'} blocked this answer after it was shown${reason}. Do not act on it.`, tone: 'error' };
|
|
116
|
+
return { text: `⚠ AOCL ${v?.layer || 'L8'} flagged this answer${reason}.`, tone: 'warn' };
|
|
117
|
+
}
|