elanous 0.2.12 → 0.2.14
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-6e7e0377c1db0bdc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
- package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9906-482c6619b3fdda20.js → 8060-cc008cc6fedfa818.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-7b2b4f378354869a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-1512d91c7491ed62.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-a627db94ec6904a0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-fed69f9759d248d0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-a67e11ba962337b5.js → page-15374909d8f1aebd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-012a742f81218b01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-99effd9d893a43dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-938de57ae0315e46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-629e1ede482ec6eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-793fa536b3a6ef37.js → page-2ce5471926761fdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-27e99b4e54a849d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-32cde713be47cddb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e64caff084f7ce21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-db07eaffba0df7e3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-86f91034bd5cabe5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-ce4d8a1215ce53a3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/{page-e297866051595d81.js → page-274703bc41b71333.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-3b7e821dc492bdfd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2f810b18d89ecba6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +1 -0
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +23 -23
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +22 -22
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/loops/index.html +1 -0
- package/apps/pwa/out/loops/index.txt +30 -0
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/examples/teams/personas/backend.yaml +4 -0
- package/examples/teams/personas/pm.yaml +4 -0
- package/examples/teams/personas/qa.yaml +4 -0
- package/examples/teams/small-product.yaml +20 -0
- package/graphs/lecture/lecture-note.yaml +28 -0
- package/graphs/lecture/recipes.yaml +17 -0
- package/graphs/orchestrator/orchestrator.yaml +59 -0
- package/graphs/orchestrator/recipes.yaml +18 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -1
- package/graphs/rhythm/daily.yaml +38 -0
- package/graphs/rhythm/recipes.yaml +18 -0
- package/graphs/rhythm/weekly.yaml +39 -0
- package/graphs/video/recipes.yaml +3 -0
- package/graphs/video/youtube-summary.yaml +17 -0
- package/package.json +2 -1
- package/scripts/audit-test-state-writes.ts +72 -17
- package/scripts/brand/check.ts +186 -0
- package/scripts/context-hooks/emit.ts +36 -14
- package/scripts/coord-channel-watch.sh +1 -0
- package/scripts/coord-post.sh +11 -1
- package/scripts/discord-seat-probe.ts +144 -0
- package/scripts/docs-cli-check.ts +23 -6
- package/scripts/expose-rubric-check.ts +118 -0
- package/scripts/expose-rubric-suggest.ts +163 -0
- package/scripts/heal/run-role.ts +14 -0
- package/scripts/hq/hq-standby.sh +65 -0
- package/scripts/hq/standby-promote.ts +173 -0
- package/scripts/hq/standby-role.ts +59 -0
- package/scripts/hq/standby-snapshot.ts +226 -0
- package/scripts/hq/standby-verify.ts +223 -0
- package/scripts/lecture-note/node.ts +256 -0
- package/scripts/lessons/handbook-page.ts +85 -0
- package/scripts/release-loop/gate-node.ts +62 -15
- package/scripts/release-loop/ops-upgrade-node.ts +17 -5
- package/scripts/release-loop/publish-node.ts +14 -1
- package/scripts/release-loop/unattended-release.ts +23 -1
- package/scripts/release-loop/vault-note-node.ts +196 -0
- package/scripts/release-story/draft.ts +172 -0
- package/scripts/rhythm/daily.ts +383 -0
- package/scripts/rhythm/weekly.ts +294 -0
- package/scripts/skills/public-check.ts +169 -0
- package/scripts/test-deterministic.ts +57 -11
- package/scripts/test-diet/effectiveness.ts +195 -0
- package/scripts/test-diet/lib.ts +16 -8
- package/scripts/test-diet/node.ts +77 -9
- package/scripts/ux/demo-rehearsal.ts +2 -2
- package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
- package/scripts/video/youtube-summary.ts +133 -0
- package/skills/diagram-master/SKILL.md +42 -49
- package/skills/diagram-master/references/pyproject.toml +4 -1
- package/skills/diagram-master/references/routing-guide.md +4 -2
- package/skills/grill-me/SKILL.md +1 -0
- package/skills/omni-crawl/SKILL.md +16 -16
- package/skills/omni-crawl/package-lock.json +1 -544
- package/skills/omni-crawl/package.json +3 -4
- package/skills/omni-crawl/scripts/main.ts +13 -13
- package/skills/omni-crawl/scripts/monitor.ts +8 -8
- package/skills/omni-crawl/src/apify.ts +13 -2
- package/skills/omni-crawl/src/capture.ts +6 -0
- package/skills/omni-crawl/src/registry.ts +2 -2
- package/skills/omni-crawl/src/stdout-json.ts +4 -0
- package/skills/omni-digest/SKILL.md +10 -11
- package/skills/omni-digest/package-lock.json +0 -545
- package/skills/omni-digest/package.json +2 -5
- package/skills/omni-digest/scripts/main.ts +3 -3
- package/skills/omni-digest/src/delegate.ts +5 -5
- package/skills/omni-digest/src/fetch.ts +2 -2
- package/skills/omni-digest/src/media.ts +25 -11
- package/skills/omni-digest/src/visual.ts +3 -2
- package/skills/omni-market/scripts/main.ts +1 -1
- package/skills/omni-market/scripts/stdout-json.ts +4 -0
- package/skills/youtube-master/SKILL.md +13 -13
- package/skills/youtube-master/package-lock.json +0 -545
- package/skills/youtube-master/package.json +2 -5
- package/skills/youtube-master/scripts/main.ts +2 -2
- package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
- package/skills/youtube-master/src/util.ts +30 -7
- package/src/agent/identity-env.ts +11 -0
- package/src/agent-mission/driver.ts +15 -10
- package/src/agent-mission/duo.ts +94 -0
- package/src/agent-mission/pty-decision.ts +1 -1
- package/src/autopilot/build/llm-conflict-merge.ts +45 -8
- package/src/autopilot/build/merge-intent.ts +53 -0
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/autopilot/track-agent.ts +4 -1
- package/src/boot/daemon-prompt-request.ts +3 -0
- package/src/boot/daemon-prompt-turn.ts +6 -2
- package/src/boot/daemon-runtime.ts +48 -16
- package/src/chat/index.ts +2 -0
- package/src/claims/claims-ledger.ts +232 -0
- package/src/claims/claims-render.ts +157 -0
- package/src/cli/claims-cli.ts +94 -0
- package/src/cli/decisions-cli.ts +84 -20
- package/src/cli/docs-cli.ts +2 -0
- package/src/cli/flow-cli.ts +11 -0
- package/src/cli/freeze-cli.ts +45 -0
- package/src/cli/lesson-cli.ts +108 -0
- package/src/cli/logs-cli.ts +104 -2
- package/src/cli/logs-model-input.ts +131 -0
- package/src/cli/outputs-cli.ts +92 -0
- package/src/cli/persona-cli.ts +29 -0
- package/src/cli/pod-cli.ts +10 -3
- package/src/cli/pr-cli.ts +44 -2
- package/src/cli/private-ledger-commands.ts +54 -4
- package/src/cli/pty-drive-verdict.ts +11 -2
- package/src/cli/release-cli.ts +248 -45
- package/src/cli/rfc-status.ts +91 -0
- package/src/connectors/linear.ts +5 -3
- package/src/context-bus/condense.ts +189 -0
- package/src/context-bus/context-day.ts +82 -0
- package/src/context-bus/context-now-public.ts +86 -0
- package/src/context-bus/context-now-surfaces.ts +70 -4
- package/src/context-bus/context-now.ts +4 -3
- package/src/context-bus/long-term-memory.ts +201 -24
- package/src/context-bus/nightly-condense.ts +1 -1
- package/src/dashboard/index.ts +75 -22
- package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
- package/src/dashboard/slash-runtime/loops-table.ts +77 -0
- package/src/decisions/decision-cards.ts +13 -7
- package/src/decisions/decision-ledger.ts +83 -18
- package/src/discord/sprint21-runtime.ts +16 -0
- package/src/discord-self-message.ts +8 -0
- package/src/discord-slash-wire.ts +31 -2
- package/src/domains/outbound-alert.ts +1 -1
- package/src/elanous-config-dir.ts +29 -4
- package/src/execution-loop/launch-gate.ts +29 -14
- package/src/flow/cards.ts +239 -0
- package/src/graph-kinds/registry.ts +2 -2
- package/src/graph-runner/graph-approval-card.ts +2 -2
- package/src/graph-runner/graph-grow.ts +64 -11
- package/src/graph-runner/graph-tick.ts +13 -2
- package/src/graph-runner/runner.ts +111 -22
- package/src/harness/harness-cli-command.ts +178 -27
- package/src/harness/harness-incidents.ts +43 -12
- package/src/harness/harness-pod-dispatch.ts +2 -0
- package/src/harness/harness-queue-child.ts +37 -0
- package/src/harness/harness-queue.ts +494 -0
- package/src/harness/helper-scan.ts +139 -0
- package/src/harness/model-input-observation.ts +37 -0
- package/src/hooks/github-shadow.ts +72 -2
- package/src/hooks/receiver.ts +4 -2
- package/src/hq/hq.ts +318 -0
- package/src/hq/lease.ts +210 -0
- package/src/hq/ledger-write-target.ts +41 -0
- package/src/hq/promote.ts +301 -0
- package/src/index.ts +281 -27
- package/src/intake-plane/discord-seat-work.ts +13 -6
- package/src/intake-plane/linear-wish.ts +72 -0
- package/src/intake-plane/persona-answer.ts +5 -0
- package/src/intake-plane/telegram-seat-work.ts +13 -6
- package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
- package/src/intake-plane/wish-card.ts +53 -2
- package/src/intake-plane/wish-folder.ts +5 -2
- package/src/intake-plane/wish-reply.ts +72 -0
- package/src/lessons/lesson-import.ts +106 -0
- package/src/lessons/lesson-ledger.ts +206 -0
- package/src/loops/activity.ts +203 -0
- package/src/loops/budget.ts +32 -0
- package/src/loops/checker.ts +180 -0
- package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
- package/src/loops/loop-cli.ts +72 -6
- package/src/loops/neighbors.ts +40 -0
- package/src/loops/observe.ts +59 -0
- package/src/loops/orchestrator/author-depth.ts +164 -0
- package/src/loops/orchestrator/author-input-check.ts +77 -0
- package/src/loops/orchestrator/author-ledger.ts +230 -0
- package/src/loops/orchestrator/finish-rate.ts +175 -0
- package/src/loops/orchestrator/overlap.ts +180 -0
- package/src/loops/orchestrator/resync-shadow.ts +227 -0
- package/src/loops/orchestrator/tick.ts +302 -0
- package/src/loops/orchestrator/traffic.ts +165 -0
- package/src/loops/package/verify.ts +125 -0
- package/src/loops/registry.ts +454 -9
- package/src/loops/status-rows.ts +118 -0
- package/src/loops/verdict.ts +34 -0
- package/src/maturity/bot-slash-catalog.ts +66 -0
- package/src/maturity/feature-maturity.ts +8 -5
- package/src/memory.ts +37 -4
- package/src/nexus/api/card-followup-route.ts +118 -0
- package/src/nexus/api/dashboard.ts +6 -1
- package/src/nexus/api/decisions-route.ts +68 -0
- package/src/nexus/api/http-server.ts +65 -2
- package/src/nexus/api/merge-approvals-gate.ts +1 -1
- package/src/nexus/api/personas.ts +2 -1
- package/src/nexus/api/schedules-read.ts +5 -1
- package/src/nexus/api/skill-exec-route.ts +97 -0
- package/src/nexus/api/task-cards-api.ts +44 -5
- package/src/nexus/api/workflows.ts +2 -1
- package/src/persona/loader.ts +18 -0
- package/src/persona/persona-todo.ts +14 -9
- package/src/persona/presets.ts +2 -1
- package/src/persona/team.ts +105 -0
- package/src/persona/types.ts +4 -0
- package/src/pod-lease/host-lease.ts +130 -20
- package/src/release-loop/auto-start.ts +48 -0
- package/src/release-loop/checklist.ts +13 -4
- package/src/release-loop/feature-store.ts +28 -16
- package/src/release-loop/landed-but-yellow.ts +12 -0
- package/src/release-loop/landing-freeze.ts +121 -0
- package/src/release-loop/placement.ts +242 -0
- package/src/release-loop/release-schedule.ts +3 -1
- package/src/seat-dispatch/ceo-commands.ts +2 -2
- package/src/seat-dispatch/seat-ask.ts +73 -15
- package/src/seat-dispatch/seat-questions.ts +58 -0
- package/src/seat-loop/seat-loop.ts +495 -46
- package/src/seat-loop/seat-pack.ts +127 -0
- package/src/self-dev/ask-launch-flow.ts +3 -0
- package/src/self-dev/dev-pipeline.ts +6 -2
- package/src/self-dev/draft-sweep.ts +76 -0
- package/src/self-dev/run-store.ts +73 -1
- package/src/self-dev/run-supervisor.ts +8 -0
- package/src/self-dev/self-orchestrate-runtime.ts +5 -1
- package/src/self-implement/budget-gate.ts +29 -0
- package/src/self-implement/expose-gate.ts +47 -0
- package/src/self-implement/frozen-merges.ts +294 -0
- package/src/self-implement/gate-cli.ts +10 -4
- package/src/self-implement/gate-scope.ts +46 -9
- package/src/self-implement/goal-archive.ts +222 -0
- package/src/self-implement/goal-lookup.ts +147 -0
- package/src/self-implement/heal-intake.ts +79 -0
- package/src/self-implement/host-regate.ts +25 -2
- package/src/self-implement/l8-merge-queue.ts +157 -1
- package/src/self-implement/orchestrator.ts +114 -10
- package/src/self-implement/revert-guard.ts +62 -0
- package/src/self-implement/run-ledger.ts +1 -1
- package/src/self-implement/seams.ts +28 -8
- package/src/session/index.ts +2 -0
- package/src/steward/launch.ts +5 -1
- package/src/steward/readiness.ts +125 -0
- package/src/steward/rescue.ts +75 -0
- package/src/steward/triage.ts +48 -9
- package/src/task-cards/card-cli.ts +86 -0
- package/src/task-cards/card-store.ts +10 -6
- package/src/task-orchestrator/surfaces/pod-lease.ts +23 -8
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +112 -18
- package/src/telegram-commands.ts +27 -3
- package/src/telegram-dispatch-forward.ts +1 -1
- package/src/telegram-fabric-plan.ts +90 -0
- package/src/telegram-loops-command.ts +44 -0
- package/src/telegram-project-command.ts +10 -4
- package/src/telegram.ts +5 -0
- package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
- package/src/user-config.ts +204 -16
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/executor.ts +4 -0
- package/src/workflow-runtime/node-catalog.ts +13 -0
- package/src/workflow-runtime/nodes/knowledge.ts +38 -0
- package/src/workflow-runtime/schema.ts +17 -1
- package/src/workflow-runtime/storage.ts +31 -13
- package/src/workflow-runtime/types.ts +13 -1
- package/apps/pwa/out/_next/static/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +0 -25
- package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
- package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-5c05b0da4bc1392c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
- /package/apps/pwa/out/_next/static/{-6XrAoD3eU7NyMHAE4xPS → x6mjI6vqOkVVu0jFuDkz1}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type LoopCronVerdict = 'alive' | 'late' | 'failed' | 'off';
|
|
2
|
+
|
|
3
|
+
/** Shared display inputs for a graph loop or cron job. Fixed intervals use
|
|
4
|
+
* milliseconds; calendar cron schedules supply the second actual due instant. */
|
|
5
|
+
export interface LoopCronVerdictInput {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
lastRunAt: string | null;
|
|
8
|
+
lastStatus: string | null;
|
|
9
|
+
intervalMs: number | null;
|
|
10
|
+
/** Second scheduled execution after the last run, for calendar-based cron schedules. */
|
|
11
|
+
secondDueAt?: string | null;
|
|
12
|
+
/** Cron-aware daemon state (src/domains/schedule-state.ts). Callers pass it for cron schedules only —
|
|
13
|
+
* a fixed interval keeps its own «2× interval» rule (review must-fix: stale must not override an interval). */
|
|
14
|
+
scheduleState?: 'live' | 'firing' | 'stale' | 'off';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Pure health classification; a failed last execution takes precedence over age.
|
|
18
|
+
* Without a valid last run and a cadence/due instant, lateness needs the daemon stale signal. */
|
|
19
|
+
export function loopCronVerdict(entry: LoopCronVerdictInput, nowMs: number): LoopCronVerdict {
|
|
20
|
+
if (!entry.enabled || entry.scheduleState === 'off') return 'off';
|
|
21
|
+
if (entry.lastStatus === 'failed' || entry.lastStatus === 'error' || entry.lastStatus === 'failure' ||
|
|
22
|
+
entry.lastStatus === 'abandoned' || entry.lastStatus === 'expired') return 'failed';
|
|
23
|
+
if (entry.scheduleState === 'stale') return 'late';
|
|
24
|
+
const lastRunMs = entry.lastRunAt === null ? NaN : Date.parse(entry.lastRunAt);
|
|
25
|
+
if (entry.secondDueAt !== undefined) {
|
|
26
|
+
const secondDueMs = entry.secondDueAt === null ? NaN : Date.parse(entry.secondDueAt);
|
|
27
|
+
return Number.isFinite(lastRunMs) && Number.isFinite(nowMs) &&
|
|
28
|
+
Number.isFinite(secondDueMs) && secondDueMs > lastRunMs && nowMs > secondDueMs ? 'late' : 'alive';
|
|
29
|
+
}
|
|
30
|
+
if (Number.isFinite(nowMs) && Number.isFinite(lastRunMs) &&
|
|
31
|
+
entry.intervalMs !== null && Number.isFinite(entry.intervalMs) && entry.intervalMs > 0 &&
|
|
32
|
+
nowMs - lastRunMs > 2 * entry.intervalMs) return 'late';
|
|
33
|
+
return 'alive';
|
|
34
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { SlashCommand } from '../chat/index.js';
|
|
2
|
+
import type { Maturity } from './feature-maturity.js';
|
|
3
|
+
import type { BotSurface } from './bot-command-maturity.js';
|
|
4
|
+
|
|
5
|
+
export interface BotSlashCatalogEntry {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
supported: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface BotSlashCatalog {
|
|
12
|
+
commands: BotSlashCatalogEntry[];
|
|
13
|
+
/** Null means this name is already handled, or is not in the catalog. */
|
|
14
|
+
unsupportedReply(name: string): string | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The menu is a projection of registered core commands, not a second list of invented names.
|
|
18
|
+
* A bot's actual handlers take precedence over maturity metadata: a grade describes
|
|
19
|
+
* visibility, never whether an existing command can be dispatched. */
|
|
20
|
+
export function buildBotSlashCatalog(options: {
|
|
21
|
+
surface: BotSurface;
|
|
22
|
+
coreCommands: readonly Pick<SlashCommand, 'name' | 'description'>[];
|
|
23
|
+
maturity: {
|
|
24
|
+
tuiSlash: Readonly<Record<string, Maturity>>;
|
|
25
|
+
telegramCommand: Readonly<Record<string, Maturity>>;
|
|
26
|
+
discordCommand: Readonly<Record<string, Maturity>>;
|
|
27
|
+
};
|
|
28
|
+
handledCommands: readonly { name: string; description: string }[];
|
|
29
|
+
}): BotSlashCatalog {
|
|
30
|
+
const { surface, coreCommands, maturity, handledCommands } = options;
|
|
31
|
+
const grades = surface === 'telegram' ? maturity.telegramCommand : maturity.discordCommand;
|
|
32
|
+
const validName = surface === 'telegram' ? /^[a-z0-9_]{1,32}$/ : /^[a-z0-9_-]{1,32}$/;
|
|
33
|
+
const commands: BotSlashCatalogEntry[] = [];
|
|
34
|
+
const seen = new Set<string>();
|
|
35
|
+
for (const command of handledCommands) {
|
|
36
|
+
if (seen.has(command.name)) continue;
|
|
37
|
+
commands.push({ name: command.name, description: command.description, supported: true });
|
|
38
|
+
seen.add(command.name);
|
|
39
|
+
}
|
|
40
|
+
const limit = surface === 'telegram' ? 100 : 25;
|
|
41
|
+
const candidateNames = new Set<string>();
|
|
42
|
+
const candidates = coreCommands.filter((command) => {
|
|
43
|
+
const name = command.name;
|
|
44
|
+
const grade = Object.hasOwn(grades, name) ? grades[name] : undefined;
|
|
45
|
+
if (seen.has(name) || candidateNames.has(name) || !validName.test(name) || grade === undefined || grade === 'broken' || grade === 'system') return false;
|
|
46
|
+
candidateNames.add(name);
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
if (commands.length + candidates.length > limit) {
|
|
50
|
+
throw new Error(`${surface} command catalog exceeds ${limit} slots`);
|
|
51
|
+
}
|
|
52
|
+
for (const command of candidates) {
|
|
53
|
+
const name = command.name;
|
|
54
|
+
const description = command.description.replace(/\s+/g, ' ').trim();
|
|
55
|
+
const suffix = ` (${surface === 'telegram' ? '텔레그램' : '디스코드'} 미지원)`;
|
|
56
|
+
commands.push({ name, description: `${description.slice(0, 100 - suffix.length)}${suffix}`, supported: false });
|
|
57
|
+
seen.add(name);
|
|
58
|
+
}
|
|
59
|
+
const unsupported = new Set(commands.filter((command) => !command.supported).map((command) => command.name));
|
|
60
|
+
return {
|
|
61
|
+
commands,
|
|
62
|
+
unsupportedReply(name) {
|
|
63
|
+
return unsupported.has(name) ? `/${name}은(는) ${surface === 'telegram' ? '텔레그램' : '디스코드'}에서 아직 지원되지 않습니다. TUI에서 /${name}을(를) 사용하세요.` : null;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -41,6 +41,7 @@ export const FEATURE_MATURITY = {
|
|
|
41
41
|
'/design-check': { pwa: 'tool' },
|
|
42
42
|
'/control': { pwa: 'tool' },
|
|
43
43
|
'/scheduler': { pwa: 'ops' },
|
|
44
|
+
'/loops': { pwa: 'beta' },
|
|
44
45
|
'/ops/release': { pwa: 'ops' },
|
|
45
46
|
'/ops/seats': { pwa: 'ops' },
|
|
46
47
|
'/ops/checklist': { pwa: 'ops' },
|
|
@@ -57,11 +58,11 @@ export const FEATURE_MATURITY = {
|
|
|
57
58
|
pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta',
|
|
58
59
|
where: 'stable', shadow: 'beta', llm: 'beta', storage: 'beta', leader: 'beta',
|
|
59
60
|
pod: 'beta', browser: 'beta', questions: 'beta', usage: 'stable', live: 'beta',
|
|
60
|
-
research: 'beta', release: 'beta', seat: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
|
|
61
|
+
research: 'beta', release: 'beta', freeze: 'beta', flow: 'beta', seat: 'beta', hq: 'beta', steward: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
|
|
61
62
|
control: 'beta', resources: 'beta', hooks: 'beta', setup: 'stable', start: 'stable',
|
|
62
63
|
grounding: 'beta', graph: 'beta', loop: 'stable', card: 'stable', 'launch-head': 'beta',
|
|
63
64
|
a2a: 'beta', plugin: 'stable', make: 'beta', skills: 'beta', connect: 'beta', import: 'beta',
|
|
64
|
-
persona: 'beta', 'team-set': 'beta', market: 'beta', directive: 'stable', connector: 'beta', storyboard: 'beta',
|
|
65
|
+
persona: 'beta', team: 'beta', 'team-set': 'beta', market: 'beta', directive: 'stable', connector: 'beta', storyboard: 'beta',
|
|
65
66
|
python: 'beta', 'self-update': 'stable', 'measure-fabric-arc-ab': 'beta', mcp: 'beta',
|
|
66
67
|
relay: 'beta', 'telegram-test': 'beta', telegram: 'beta', 'discord-test': 'beta',
|
|
67
68
|
sync: 'beta', status: 'beta', git: 'beta', gh: 'beta', repro: 'system',
|
|
@@ -71,7 +72,7 @@ export const FEATURE_MATURITY = {
|
|
|
71
72
|
provider: 'stable', 'provider:set': 'beta', 'provider:restore': 'beta',
|
|
72
73
|
'provider:rotate': 'beta', 'provider:use': 'beta', 'status-bar': 'beta',
|
|
73
74
|
schedule: 'beta', intake: 'beta', logs: 'stable', ad: 'beta', docs: 'beta',
|
|
74
|
-
decisions: 'beta', directives: 'beta', fleet: 'beta', ops: 'stable', publish: 'beta',
|
|
75
|
+
decisions: 'beta', directives: 'beta', claims: 'beta', lesson: 'beta', fleet: 'beta', ops: 'stable', outputs: 'beta', publish: 'beta',
|
|
75
76
|
env: 'beta', 'agent-mission': 'beta', dev: 'beta', keys: 'beta', finance: 'beta',
|
|
76
77
|
config: 'stable', tasks: 'stable', labels: 'beta', msg: 'beta', ask: 'stable',
|
|
77
78
|
onboarding: 'beta', login: 'stable', session: 'beta', chat: 'stable', repl: 'stable',
|
|
@@ -110,6 +111,8 @@ export const FEATURE_MATURITY = {
|
|
|
110
111
|
plugin: 'tool',
|
|
111
112
|
widget: 'tool',
|
|
112
113
|
log: 'tool',
|
|
114
|
+
loops: 'beta',
|
|
115
|
+
persona: 'beta', // G3 b — 대화별 페르소나(GOAL-BENCH B7 #23836)
|
|
113
116
|
memory: 'beta',
|
|
114
117
|
export: 'stable',
|
|
115
118
|
delta: 'tool',
|
|
@@ -153,7 +156,7 @@ export const FEATURE_MATURITY = {
|
|
|
153
156
|
ping: 'stable', provider: 'stable', sessions: 'stable', fork: 'stable', resume: 'stable',
|
|
154
157
|
decisions: 'stable', work: 'stable', cancel: 'stable',
|
|
155
158
|
skills: 'beta', skill: 'beta', digest: 'beta', intake: 'beta', ad: 'beta',
|
|
156
|
-
taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', project: 'beta', wish: 'beta',
|
|
159
|
+
taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', project: 'beta', wish: 'beta',
|
|
157
160
|
brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool', local: 'tool',
|
|
158
161
|
harness: 'ops',
|
|
159
162
|
// Existing commands outside the CXO four-grade menu list are owner-only.
|
|
@@ -162,7 +165,7 @@ export const FEATURE_MATURITY = {
|
|
|
162
165
|
},
|
|
163
166
|
discordCommand: {
|
|
164
167
|
status: 'stable', sessions: 'stable', new: 'stable', fork: 'stable',
|
|
165
|
-
persona: 'beta', poll: 'beta', attach: 'beta',
|
|
168
|
+
persona: 'beta', poll: 'beta', attach: 'beta', now: 'beta',
|
|
166
169
|
brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool',
|
|
167
170
|
relay: 'ops', showroom: 'ops', bots: 'ops', bot: 'ops', screen: 'ops', chart: 'ops',
|
|
168
171
|
routines: 'ops', botsay: 'ops', 'voice-join': 'ops', 'voice-leave': 'ops', 'voice-status': 'ops',
|
package/src/memory.ts
CHANGED
|
@@ -63,6 +63,16 @@ export function writeFileDurable(path: string, data: string, fsync: (fd: number)
|
|
|
63
63
|
|
|
64
64
|
export type MemoryType = 'user' | 'feedback' | 'project' | 'reference';
|
|
65
65
|
|
|
66
|
+
/** Context-condense metadata travels with the existing curated memory file, not a parallel store. */
|
|
67
|
+
export interface ContextMemoryCard {
|
|
68
|
+
project: string;
|
|
69
|
+
seat: string;
|
|
70
|
+
topic: string;
|
|
71
|
+
source: string;
|
|
72
|
+
updatedAt: string;
|
|
73
|
+
status: 'active' | 'retired';
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
export interface MemoryEntry {
|
|
67
77
|
id: string; // uuid v4
|
|
68
78
|
filename: string; // "<uuid>-<slug>.md"
|
|
@@ -89,6 +99,7 @@ export interface MemoryEntry {
|
|
|
89
99
|
pinned: boolean;
|
|
90
100
|
/** Optional pinned-memory freshness window; absent memories retain their original injection text. */
|
|
91
101
|
staleAfterMinutes?: number;
|
|
102
|
+
contextCard?: ContextMemoryCard;
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
// ── Paths ────────────────────────────────────────────────────────────
|
|
@@ -192,6 +203,7 @@ export interface SaveMemoryOpts {
|
|
|
192
203
|
staleAfterMinutes?: number;
|
|
193
204
|
/** Override the generated id (tests). */
|
|
194
205
|
id?: string;
|
|
206
|
+
contextCard?: ContextMemoryCard;
|
|
195
207
|
}
|
|
196
208
|
|
|
197
209
|
export function saveMemory(opts: SaveMemoryOpts, root: string = memoryRoot()): MemoryEntry {
|
|
@@ -207,6 +219,7 @@ export function saveMemory(opts: SaveMemoryOpts, root: string = memoryRoot()): M
|
|
|
207
219
|
let existingPriority: number | undefined;
|
|
208
220
|
let existingPinned: boolean | undefined;
|
|
209
221
|
let existingStaleAfterMinutes: number | undefined;
|
|
222
|
+
let existingContextCard: ContextMemoryCard | undefined;
|
|
210
223
|
if (existsSync(path)) {
|
|
211
224
|
try {
|
|
212
225
|
const parsed = parseFrontmatter(readFileSync(path, 'utf-8'));
|
|
@@ -214,6 +227,7 @@ export function saveMemory(opts: SaveMemoryOpts, root: string = memoryRoot()): M
|
|
|
214
227
|
if (parsed.fm.priority) existingPriority = parseInt(parsed.fm.priority, 10) || 0;
|
|
215
228
|
if (parsed.fm.pinned) existingPinned = parsed.fm.pinned === 'true';
|
|
216
229
|
existingStaleAfterMinutes = parseStaleAfterMinutes(parsed.fm['stale-after-minutes']);
|
|
230
|
+
existingContextCard = parseContextCard(parsed.fm['context-card']);
|
|
217
231
|
} catch { /* keep new createdAt */ }
|
|
218
232
|
}
|
|
219
233
|
const priority = opts.priority ?? existingPriority ?? 0;
|
|
@@ -229,6 +243,8 @@ export function saveMemory(opts: SaveMemoryOpts, root: string = memoryRoot()): M
|
|
|
229
243
|
if (priority > 0) frontmatter.priority = String(Math.floor(priority));
|
|
230
244
|
if (pinned) frontmatter.pinned = 'true';
|
|
231
245
|
if (staleAfterMinutes !== undefined) frontmatter['stale-after-minutes'] = String(staleAfterMinutes);
|
|
246
|
+
const contextCard = opts.contextCard ?? existingContextCard;
|
|
247
|
+
if (contextCard) frontmatter['context-card'] = JSON.stringify(contextCard);
|
|
232
248
|
const raw = renderFrontmatter(frontmatter, opts.body);
|
|
233
249
|
writeFileDurable(path, raw);
|
|
234
250
|
|
|
@@ -248,9 +264,23 @@ export function saveMemory(opts: SaveMemoryOpts, root: string = memoryRoot()): M
|
|
|
248
264
|
priority,
|
|
249
265
|
pinned,
|
|
250
266
|
staleAfterMinutes,
|
|
267
|
+
...(contextCard ? { contextCard } : {}),
|
|
251
268
|
};
|
|
252
269
|
}
|
|
253
270
|
|
|
271
|
+
function parseContextCard(raw: string | undefined): ContextMemoryCard | undefined {
|
|
272
|
+
if (!raw) return undefined;
|
|
273
|
+
try {
|
|
274
|
+
const card: unknown = JSON.parse(raw);
|
|
275
|
+
if (card && typeof card === 'object' && !Array.isArray(card)) {
|
|
276
|
+
const c = card as Record<string, unknown>;
|
|
277
|
+
if (['project', 'seat', 'topic', 'source', 'updatedAt'].every(key => typeof c[key] === 'string')
|
|
278
|
+
&& (c.status === 'active' || c.status === 'retired')) return c as unknown as ContextMemoryCard;
|
|
279
|
+
}
|
|
280
|
+
} catch { /* malformed metadata is not a context memory */ }
|
|
281
|
+
return undefined;
|
|
282
|
+
}
|
|
283
|
+
|
|
254
284
|
/** Parse one memory file. Returns null on read/parse error. */
|
|
255
285
|
function parseMemoryFile(path: string): MemoryEntry | null {
|
|
256
286
|
if (!existsSync(path)) return null;
|
|
@@ -261,6 +291,7 @@ function parseMemoryFile(path: string): MemoryEntry | null {
|
|
|
261
291
|
const basename = path.split(/[\\/]/).pop() ?? '';
|
|
262
292
|
const match = basename.match(/^([0-9a-f-]{36})-(.+)\.md$/i);
|
|
263
293
|
const id = match ? match[1] : createHash('sha1').update(basename).digest('hex').slice(0, 36);
|
|
294
|
+
const contextCard = parseContextCard(fm['context-card']);
|
|
264
295
|
return {
|
|
265
296
|
id,
|
|
266
297
|
filename: basename,
|
|
@@ -274,6 +305,7 @@ function parseMemoryFile(path: string): MemoryEntry | null {
|
|
|
274
305
|
priority: fm.priority ? (parseInt(fm.priority, 10) || 0) : 0,
|
|
275
306
|
pinned: fm.pinned === 'true',
|
|
276
307
|
staleAfterMinutes: parseStaleAfterMinutes(fm['stale-after-minutes']),
|
|
308
|
+
...(contextCard ? { contextCard } : {}),
|
|
277
309
|
};
|
|
278
310
|
}
|
|
279
311
|
|
|
@@ -364,6 +396,7 @@ export function searchMemories(
|
|
|
364
396
|
const entries = listMemories({ type: opts.type }, root);
|
|
365
397
|
const hits: SearchHit[] = [];
|
|
366
398
|
for (const entry of entries) {
|
|
399
|
+
if (entry.contextCard?.status === 'retired') continue;
|
|
367
400
|
// 정확-토큰 매칭(2026-07-19) — 이전 substring `includes(kw)` 는 "ref"가 "reference"·
|
|
368
401
|
// "prefer" 안에까지 걸려 오차용(false-recall)을 냈다(openclaw tool-search.ts 이식:
|
|
369
402
|
// tokenize→Set.has 전체토큰 동등). name/description 은 토큰집합 정확매칭, body 는
|
|
@@ -415,7 +448,7 @@ export function rebuildIndex(root: string = memoryRoot()): void {
|
|
|
415
448
|
const byType: Record<MemoryType, MemoryEntry[]> = {
|
|
416
449
|
user: [], feedback: [], project: [], reference: [],
|
|
417
450
|
};
|
|
418
|
-
for (const e of entries) byType[e.type].push(e);
|
|
451
|
+
for (const e of entries) if (e.contextCard?.status !== 'retired') byType[e.type].push(e);
|
|
419
452
|
|
|
420
453
|
const lines: string[] = [];
|
|
421
454
|
lines.push('# elanous memory index');
|
|
@@ -515,7 +548,7 @@ export function buildMemoryInjection(
|
|
|
515
548
|
|
|
516
549
|
// 📌 pinned 기억 — 키워드 무관 항상 주입(relevance gate bypass · Letta pinned block /
|
|
517
550
|
// claude-code MEMORY.md tier 동형). priority 높은 순. core/safety/ref-location 사실용.
|
|
518
|
-
const pinnedMems = listMemories({}, root).filter(m => m.pinned).sort((a, b) => b.priority - a.priority);
|
|
551
|
+
const pinnedMems = listMemories({}, root).filter(m => m.pinned && m.contextCard?.status !== 'retired').sort((a, b) => b.priority - a.priority);
|
|
519
552
|
const pinnedParts: string[] = [];
|
|
520
553
|
for (const e of pinnedMems) {
|
|
521
554
|
const chunk = pinnedChunk(e);
|
|
@@ -678,7 +711,7 @@ export async function buildMemoryInjectionLLM(
|
|
|
678
711
|
}
|
|
679
712
|
|
|
680
713
|
// pinned 항상주입(키워드/판정 무관).
|
|
681
|
-
const pinnedMems = listMemories({}, root).filter((m) => m.pinned).sort((a, b) => b.priority - a.priority);
|
|
714
|
+
const pinnedMems = listMemories({}, root).filter((m) => m.pinned && m.contextCard?.status !== 'retired').sort((a, b) => b.priority - a.priority);
|
|
682
715
|
const pinnedParts: string[] = [];
|
|
683
716
|
for (const e of pinnedMems) {
|
|
684
717
|
const chunk = pinnedChunk(e);
|
|
@@ -693,7 +726,7 @@ export async function buildMemoryInjectionLLM(
|
|
|
693
726
|
let judged: MemoryEntry[] = [];
|
|
694
727
|
if (!lowContext) {
|
|
695
728
|
const candidates = listMemories({}, root)
|
|
696
|
-
.filter((m) => !m.pinned && !injected.includes(m.id))
|
|
729
|
+
.filter((m) => !m.pinned && m.contextCard?.status !== 'retired' && !injected.includes(m.id))
|
|
697
730
|
.slice(0, opts.maxCandidates ?? 60);
|
|
698
731
|
if (candidates.length > 0) {
|
|
699
732
|
try {
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
4
|
+
import { defaultAttachmentBaseDir, resolveAttachmentPath } from '../../boot/attachment-store.js';
|
|
5
|
+
import { detectCard, NotACardError, runCardFollowup } from '../../card-followup/core.js';
|
|
6
|
+
import { debug } from '../../debug/log.js';
|
|
7
|
+
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
8
|
+
import { localOcrText } from '../../telegram-card-followup.js';
|
|
9
|
+
import { jsonResponse } from './json-response.js';
|
|
10
|
+
|
|
11
|
+
export const CARD_FOLLOWUP_PATH = '/v1/card-followup';
|
|
12
|
+
|
|
13
|
+
type CardJob = { id: string; status: 'judging' | 'running' | 'done' | 'not-card' | 'failed'; updatedAt: number; replies?: [string, string, string]; error?: string };
|
|
14
|
+
type CardDeps = {
|
|
15
|
+
rootDir?: string;
|
|
16
|
+
resolveAttachment?: typeof resolveAttachmentPath;
|
|
17
|
+
ocr?: typeof localOcrText;
|
|
18
|
+
detect?: typeof detectCard;
|
|
19
|
+
run?: typeof runCardFollowup;
|
|
20
|
+
now?: () => number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function withinAttachmentStore(path: string): boolean {
|
|
24
|
+
try {
|
|
25
|
+
const relativePath = relative(realpathSync(defaultAttachmentBaseDir()), realpathSync(path));
|
|
26
|
+
return relativePath !== '' && relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);
|
|
27
|
+
} catch { return false; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The in-memory set identifies only work launched by this daemon; disk is the pollable ledger. */
|
|
31
|
+
export function createCardFollowupJobs({
|
|
32
|
+
rootDir = effectiveInstanceRoot(),
|
|
33
|
+
resolveAttachment = resolveAttachmentPath,
|
|
34
|
+
ocr = localOcrText,
|
|
35
|
+
detect = detectCard,
|
|
36
|
+
run = runCardFollowup,
|
|
37
|
+
now = Date.now,
|
|
38
|
+
}: CardDeps = {}) {
|
|
39
|
+
const base = join(rootDir, 'graph-runs', 'card-followup');
|
|
40
|
+
const active = new Set<string>();
|
|
41
|
+
const unavailable = new Set<string>();
|
|
42
|
+
const file = (id: string) => join(base, `${id}.pwa.json`);
|
|
43
|
+
const save = (job: CardJob) => {
|
|
44
|
+
mkdirSync(base, { recursive: true, mode: 0o700 });
|
|
45
|
+
chmodSync(base, 0o700);
|
|
46
|
+
writeFileSync(file(job.id), JSON.stringify(job), { mode: 0o600 });
|
|
47
|
+
chmodSync(file(job.id), 0o600);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
async post(req: Request): Promise<Response> {
|
|
52
|
+
let body: unknown;
|
|
53
|
+
try { body = await req.json(); } catch { body = null; }
|
|
54
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'bad_request' }, 400);
|
|
55
|
+
const { attachmentId, context } = body as Record<string, unknown>;
|
|
56
|
+
if (context !== undefined && typeof context !== 'string') return jsonResponse({ error: 'bad_request' }, 400);
|
|
57
|
+
const imagePath = typeof attachmentId === 'string' ? resolveAttachment(attachmentId) : null;
|
|
58
|
+
if (!imagePath || (resolveAttachment === resolveAttachmentPath && !withinAttachmentStore(imagePath))) {
|
|
59
|
+
return jsonResponse({ error: 'attachment-not-found' }, 400);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const id = randomUUID();
|
|
63
|
+
const update = (status: CardJob['status'], fields: Partial<Pick<CardJob, 'replies' | 'error'>> = {}) =>
|
|
64
|
+
save({ id, status, updatedAt: now(), ...fields });
|
|
65
|
+
update('judging');
|
|
66
|
+
active.add(id);
|
|
67
|
+
debug.log('card-followup.pwa', 'started', { id });
|
|
68
|
+
// Start OCR on the next event-loop turn so the acceptance response precedes the work.
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
void (async () => {
|
|
71
|
+
try {
|
|
72
|
+
const ocrText = await ocr(imagePath);
|
|
73
|
+
if (detect({ ocrText }).decision !== 'card') {
|
|
74
|
+
update('not-card');
|
|
75
|
+
debug.log('card-followup.pwa', 'not-card', { id });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
update('running');
|
|
79
|
+
const result = await run({ imagePath, ocrText, rootDir, runId: id, ...(context === undefined ? {} : { context }) });
|
|
80
|
+
update('done', { replies: result.replies });
|
|
81
|
+
debug.log('card-followup.pwa', 'done', { id });
|
|
82
|
+
} catch (cause) {
|
|
83
|
+
const notCard = cause instanceof NotACardError;
|
|
84
|
+
// OCR·그래프 예외 문구엔 명함 원문이 섞일 수 있다 — 원장·응답엔 고정 코드만 싣는다(리뷰 must-fix).
|
|
85
|
+
try {
|
|
86
|
+
update(notCard ? 'not-card' : 'failed', notCard ? {} : { error: 'card-followup-failed' });
|
|
87
|
+
debug.log('card-followup.pwa', notCard ? 'not-card' : 'failed', { id });
|
|
88
|
+
} catch {
|
|
89
|
+
unavailable.add(id);
|
|
90
|
+
debug.log('card-followup.pwa', 'failed', { id });
|
|
91
|
+
}
|
|
92
|
+
} finally {
|
|
93
|
+
active.delete(id);
|
|
94
|
+
}
|
|
95
|
+
})().catch(() => {
|
|
96
|
+
unavailable.add(id);
|
|
97
|
+
active.delete(id);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
return jsonResponse({ id }, 202);
|
|
101
|
+
},
|
|
102
|
+
get(id: string): Response {
|
|
103
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) return jsonResponse({ error: 'not-found' }, 404);
|
|
104
|
+
if (unavailable.has(id)) return jsonResponse({ id, status: 'failed', error: 'card-followup-failed' });
|
|
105
|
+
let job: CardJob;
|
|
106
|
+
try { job = JSON.parse(readFileSync(file(id), 'utf8')) as CardJob; }
|
|
107
|
+
catch { return jsonResponse({ error: 'not-found' }, 404); }
|
|
108
|
+
if (job.id !== id) return jsonResponse({ error: 'not-found' }, 404);
|
|
109
|
+
if ((job.status === 'judging' || job.status === 'running') && !active.has(id)) {
|
|
110
|
+
return jsonResponse({ id, status: 'failed', error: 'daemon-restarted' });
|
|
111
|
+
}
|
|
112
|
+
const { status, replies, error } = job;
|
|
113
|
+
return jsonResponse({ id, status, ...(replies ? { replies } : {}), ...(error ? { error } : {}) });
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type CardFollowupJobs = ReturnType<typeof createCardFollowupJobs>;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// GET /v1/dashboard/timeline — 신호 타임라인 (?hours=48&floor=6)
|
|
5
5
|
// GET /v1/dashboard/heatmap — 자산×국가×섹터 매력도 + US 섹터 + 뉴스 분포
|
|
6
6
|
// GET /v1/dashboard/digs — 디깅 리포트 피드 (?limit=20)
|
|
7
|
+
// GET /v1/dashboard/loop-activity — 루프 활동 그래프 (?since=24h)
|
|
7
8
|
//
|
|
8
9
|
// 전부 read-only · 캐시/로컬 SQLite 만 (dashboard-data.ts) — 라이브 fetch 없음.
|
|
9
10
|
// PWA 폴링(60s) 대상. reflection.ts 패턴(CORS·checkAuth seam) 상속.
|
|
@@ -16,12 +17,15 @@ import {
|
|
|
16
17
|
} from '../../domains/dashboard-data.js';
|
|
17
18
|
import { readLiveSnapshot } from '../../domains/market-live.js';
|
|
18
19
|
import { marketSessions } from '../../domains/finance.js';
|
|
20
|
+
import { loopActivity } from '../../loops/activity.js';
|
|
19
21
|
|
|
20
22
|
export interface DashboardRouteOpts {
|
|
21
23
|
/** Auth check seam. */
|
|
22
24
|
checkAuth?: (req: Request) => boolean;
|
|
23
25
|
/** Test seam — 실제 detached 수집 spawn 대체. */
|
|
24
26
|
spawnCollector?: () => void;
|
|
27
|
+
/** Read-only activity provider for isolated route tests. */
|
|
28
|
+
loopActivity?: typeof loopActivity;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
const CORS_HEADERS: Record<string, string> = {
|
|
@@ -43,7 +47,7 @@ export function parseDashboardPath(pathname: string): string | null {
|
|
|
43
47
|
const m = /^\/v1\/dashboard\/([^/]+)$/.exec(pathname);
|
|
44
48
|
if (!m) return null;
|
|
45
49
|
const seg = decodeURIComponent(m[1]!);
|
|
46
|
-
return ['summary', 'timeline', 'heatmap', 'digs', 'schedules', 'ontology', 'backtest', 'loops', 'ops'].includes(seg) ? seg : null;
|
|
50
|
+
return ['summary', 'timeline', 'heatmap', 'digs', 'schedules', 'ontology', 'backtest', 'loops', 'loop-activity', 'ops'].includes(seg) ? seg : null;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/** POST /v1/dashboard/refresh-live — 온디맨드 라이브 재수집 트리거 (refresh 버튼 설계 ·
|
|
@@ -99,6 +103,7 @@ export function handleDashboard(req: Request, section: string, opts: DashboardRo
|
|
|
99
103
|
case 'ontology': return jsonResponse({ ok: true, ontology: dashboardOntology() }, 200);
|
|
100
104
|
case 'backtest': return jsonResponse({ ok: true, backtest: dashboardBacktest() }, 200);
|
|
101
105
|
case 'loops': return jsonResponse({ ok: true, loops: dashboardLoops() }, 200);
|
|
106
|
+
case 'loop-activity': return jsonResponse({ ok: true, activity: (opts.loopActivity ?? loopActivity)({ since: q.get('since') ?? '24h' }) }, 200);
|
|
102
107
|
case 'ops': {
|
|
103
108
|
const mid = q.get('mission');
|
|
104
109
|
if (mid) return jsonResponse({ ok: true, missionDetail: dashboardOpsMission(mid) }, 200);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { debug } from '../../debug/log.js';
|
|
2
|
+
import { DecisionLedger, type DecisionEntry } from '../../decisions/decision-ledger.js';
|
|
3
|
+
import { jsonResponse } from './json-response.js';
|
|
4
|
+
|
|
5
|
+
export const DECISIONS_PATH = '/v1/decisions';
|
|
6
|
+
|
|
7
|
+
export interface DecisionsRouteDeps {
|
|
8
|
+
authorize?: (request: Request) => boolean;
|
|
9
|
+
ledger?: Pick<DecisionLedger, 'list' | 'decide'>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function handleDecisions(req: Request, deps: DecisionsRouteDeps = {}): Promise<Response> {
|
|
13
|
+
const pathname = new URL(req.url).pathname;
|
|
14
|
+
if (pathname !== DECISIONS_PATH && !/^\/v1\/decisions\/[^/]+\/decide$/.test(pathname)) return jsonResponse({ error: 'not-found' }, 404);
|
|
15
|
+
if (!deps.authorize?.(req)) {
|
|
16
|
+
debug.log('decisions.api', 'rejected', { reason: 'unauthorized' });
|
|
17
|
+
return jsonResponse({ error: 'unauthorized' }, 401);
|
|
18
|
+
}
|
|
19
|
+
const ledger = deps.ledger ?? new DecisionLedger();
|
|
20
|
+
if (pathname === DECISIONS_PATH) {
|
|
21
|
+
if (req.method !== 'GET') return jsonResponse({ error: 'method-not-allowed' }, 405);
|
|
22
|
+
if (new URL(req.url).searchParams.get('status') !== 'open') {
|
|
23
|
+
debug.log('decisions.api', 'rejected', { reason: 'invalid-status' });
|
|
24
|
+
return jsonResponse({ error: 'bad_request' }, 400);
|
|
25
|
+
}
|
|
26
|
+
const decisions = ledger.list({ status: 'open' }).map(entry => ({
|
|
27
|
+
id: entry.id,
|
|
28
|
+
title: entry.title,
|
|
29
|
+
situation: entry.scqa.s.slice(0, 200),
|
|
30
|
+
options: entry.options.map(option => ({ id: option.key, label: option.label })),
|
|
31
|
+
recommendation: entry.recommendation,
|
|
32
|
+
raisedAt: entry.raisedAt,
|
|
33
|
+
dueAt: entry.dueAt,
|
|
34
|
+
}));
|
|
35
|
+
debug.log('decisions.api', 'listed', { count: decisions.length });
|
|
36
|
+
return jsonResponse({ decisions });
|
|
37
|
+
}
|
|
38
|
+
if (req.method !== 'POST') return jsonResponse({ error: 'method-not-allowed' }, 405);
|
|
39
|
+
let id: string;
|
|
40
|
+
try { id = decodeURIComponent(pathname.slice(DECISIONS_PATH.length + 1, -'/decide'.length)); }
|
|
41
|
+
catch {
|
|
42
|
+
debug.log('decisions.api', 'rejected', { reason: 'invalid-id' });
|
|
43
|
+
return jsonResponse({ error: 'not-found' }, 404);
|
|
44
|
+
}
|
|
45
|
+
let body: unknown;
|
|
46
|
+
try { body = await req.json(); } catch { body = null; }
|
|
47
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)
|
|
48
|
+
|| !('choice' in body) || typeof body.choice !== 'string' || !body.choice
|
|
49
|
+
|| ('note' in body && body.note !== undefined && typeof body.note !== 'string')) {
|
|
50
|
+
debug.log('decisions.api', 'rejected', { id, reason: 'invalid-body' });
|
|
51
|
+
return jsonResponse({ error: 'bad_request' }, 400);
|
|
52
|
+
}
|
|
53
|
+
const { choice, note } = body as { choice: string; note?: string };
|
|
54
|
+
try {
|
|
55
|
+
const entry: DecisionEntry = ledger.decide(id, choice, { kind: 'human' }, note);
|
|
56
|
+
debug.log('decisions.api', 'decided', { id });
|
|
57
|
+
return jsonResponse(entry);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const message = error instanceof Error ? error.message : '';
|
|
60
|
+
const status = message.startsWith('decision not found:') ? 404
|
|
61
|
+
: message.startsWith('decision already closed:') ? 409
|
|
62
|
+
: message.startsWith('option not found:') || message.startsWith('note ') ? 400 : 0;
|
|
63
|
+
if (!status) throw error;
|
|
64
|
+
const reason = status === 404 ? 'not-found' : status === 409 ? 'already-decided' : 'bad_request';
|
|
65
|
+
debug.log('decisions.api', 'rejected', { id, reason });
|
|
66
|
+
return jsonResponse({ error: reason }, status);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -22,6 +22,9 @@ import { spawnSync } from 'node:child_process';
|
|
|
22
22
|
import { isAbsolute } from 'node:path';
|
|
23
23
|
import { createProject, listProjects } from '../../project/project-store.js';
|
|
24
24
|
import { handleOutputsGet, type OutputsDeps } from './outputs-route.js';
|
|
25
|
+
import { DECISIONS_PATH, handleDecisions, type DecisionsRouteDeps } from './decisions-route.js';
|
|
26
|
+
import { CARD_FOLLOWUP_PATH, createCardFollowupJobs, type CardFollowupJobs } from './card-followup-route.js';
|
|
27
|
+
import { SKILL_EXEC_PATH, createSkillExecJobs, type SkillExecJobs } from './skill-exec-route.js';
|
|
25
28
|
import { compareTokenConstTime } from '../../acp/transport/auth.js';
|
|
26
29
|
import { ensureAuthToken } from '../../auth/acp-token.js';
|
|
27
30
|
import type { NexusState } from '../state/state.js';
|
|
@@ -181,7 +184,9 @@ import { handleChannelBotsGet, handleChannelBotSet } from './setup-channel-bot.j
|
|
|
181
184
|
import { handleLlmRoutePredict } from './llm-route-predict.js';
|
|
182
185
|
import type { MissionRouter } from '../../llm/mission-router.js';
|
|
183
186
|
import { handleContextFetchUrl } from './context-url.js';
|
|
184
|
-
import { contextNow } from '../../context-bus/context-now.js';
|
|
187
|
+
import { contextNow, type ContextNowDeps } from '../../context-bus/context-now.js';
|
|
188
|
+
import type { ContextNowAudience } from '../../context-bus/context-now-surfaces.js';
|
|
189
|
+
import { renderVoiceNow } from '../../context-bus/context-now-surfaces.js';
|
|
185
190
|
import type { IntentPredictionService } from '../../intent-prediction/index.js';
|
|
186
191
|
import type { NotificationActionLoopback } from '../../web-push/notification-action-loopback.js';
|
|
187
192
|
import { handlePlatforms } from './platforms.js';
|
|
@@ -472,6 +477,9 @@ export interface NexusHttpServerOpts {
|
|
|
472
477
|
/** Optional installer state root for isolated API consumers and tests. */
|
|
473
478
|
pluginStateRoot?: string;
|
|
474
479
|
outputs?: OutputsDeps;
|
|
480
|
+
decisions?: Pick<DecisionsRouteDeps, 'ledger'>;
|
|
481
|
+
cardFollowup?: CardFollowupJobs;
|
|
482
|
+
skillExec?: SkillExecJobs;
|
|
475
483
|
execRequests?: ExecRequestRunner;
|
|
476
484
|
seatRequests?: SeatRequestsDeps;
|
|
477
485
|
consultRequests?: ConsultRequestsDeps;
|
|
@@ -480,6 +488,8 @@ export interface NexusHttpServerOpts {
|
|
|
480
488
|
eventBus: NexusEventBus;
|
|
481
489
|
/** Test seam for the read-only usage collector; production uses the cached handler. */
|
|
482
490
|
usageHandler?: () => Promise<Response>;
|
|
491
|
+
/** Test seam for the context-now ledgers; production reads the live ledgers. */
|
|
492
|
+
contextNowDeps?: ContextNowDeps;
|
|
483
493
|
/** Supervisor handle — required for the write API (PR ι). When omitted
|
|
484
494
|
* (skipSupervisor mode), mutation routes return 503. */
|
|
485
495
|
supervisor?: Supervisor;
|
|
@@ -852,6 +862,16 @@ function execRequestRunner(): ExecRequestRunner {
|
|
|
852
862
|
return execRequestRunnerInstance;
|
|
853
863
|
}
|
|
854
864
|
|
|
865
|
+
let cardFollowupJobs: CardFollowupJobs | undefined;
|
|
866
|
+
function defaultCardFollowupJobs(): CardFollowupJobs {
|
|
867
|
+
return cardFollowupJobs ??= createCardFollowupJobs();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
let skillExecJobs: SkillExecJobs | undefined;
|
|
871
|
+
function defaultSkillExecJobs(): SkillExecJobs {
|
|
872
|
+
return skillExecJobs ??= createSkillExecJobs();
|
|
873
|
+
}
|
|
874
|
+
|
|
855
875
|
export async function routeRequest(
|
|
856
876
|
req: Request,
|
|
857
877
|
opts: NexusHttpServerOpts,
|
|
@@ -1338,6 +1358,23 @@ export async function routeRequest(
|
|
|
1338
1358
|
return method === 'GET' ? handleProjectsGet() : handleProjectsPost(req);
|
|
1339
1359
|
}
|
|
1340
1360
|
|
|
1361
|
+
// Polling must remain outside the non-GET dispatch below.
|
|
1362
|
+
if (method === 'GET' && pathname.startsWith(`${SKILL_EXEC_PATH}/`) && !pathname.slice(SKILL_EXEC_PATH.length + 1).includes('/')) {
|
|
1363
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1364
|
+
return (opts.skillExec ?? defaultSkillExecJobs()).get(pathname.slice(SKILL_EXEC_PATH.length + 1));
|
|
1365
|
+
}
|
|
1366
|
+
if (method === 'GET' && pathname.startsWith(`${CARD_FOLLOWUP_PATH}/`) && !pathname.slice(CARD_FOLLOWUP_PATH.length + 1).includes('/')) {
|
|
1367
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1368
|
+
return (opts.cardFollowup ?? defaultCardFollowupJobs()).get(pathname.slice(CARD_FOLLOWUP_PATH.length + 1));
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
if (method === 'GET' && pathname === DECISIONS_PATH) {
|
|
1372
|
+
return handleDecisions(req, {
|
|
1373
|
+
authorize: request => !!opts.metaApi && checkAuth(request, opts.metaApi),
|
|
1374
|
+
...opts.decisions,
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1341
1378
|
if (method === 'GET' && pathname === '/v1/outputs') {
|
|
1342
1379
|
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1343
1380
|
return handleOutputsGet(req, opts.outputs);
|
|
@@ -1371,6 +1408,20 @@ export async function routeRequest(
|
|
|
1371
1408
|
// Templates POST handled here too (PR κ).
|
|
1372
1409
|
// Config / secrets PUT/POST/DELETE here too (PR μ).
|
|
1373
1410
|
if (method !== 'GET') {
|
|
1411
|
+
if (method === 'POST' && pathname === SKILL_EXEC_PATH) {
|
|
1412
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1413
|
+
return (opts.skillExec ?? defaultSkillExecJobs()).post(req);
|
|
1414
|
+
}
|
|
1415
|
+
if (method === 'POST' && pathname === CARD_FOLLOWUP_PATH) {
|
|
1416
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1417
|
+
return (opts.cardFollowup ?? defaultCardFollowupJobs()).post(req);
|
|
1418
|
+
}
|
|
1419
|
+
if (method === 'POST' && /^\/v1\/decisions\/[^/]+\/decide$/.test(pathname)) {
|
|
1420
|
+
return handleDecisions(req, {
|
|
1421
|
+
authorize: request => !!opts.metaApi && checkAuth(request, opts.metaApi),
|
|
1422
|
+
...opts.decisions,
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1374
1425
|
if (method === 'POST' && pathname === '/v1/plugins/install') {
|
|
1375
1426
|
if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1376
1427
|
return handlePluginsInstall(req, opts.metaApi, opts.pluginStateRoot);
|
|
@@ -2832,7 +2883,19 @@ export async function routeRequest(
|
|
|
2832
2883
|
}
|
|
2833
2884
|
if (pathname === '/v1/context/now') {
|
|
2834
2885
|
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
2835
|
-
|
|
2886
|
+
const audiences = url.searchParams.getAll('audience');
|
|
2887
|
+
if (audiences.length > 1) return jsonResponse({ error: 'invalid_audience' }, 400);
|
|
2888
|
+
const requested = audiences[0];
|
|
2889
|
+
if (requested !== undefined && requested !== 'operator' && requested !== 'user' && requested !== 'public-demo') {
|
|
2890
|
+
return jsonResponse({ error: 'invalid_audience' }, 400);
|
|
2891
|
+
}
|
|
2892
|
+
const forced = getUserConfig().nexus?.demoMode === true;
|
|
2893
|
+
const audience: ContextNowAudience = forced ? 'public-demo' : requested ?? 'operator';
|
|
2894
|
+
const answer = contextNow({ audience, ...(url.searchParams.has('topic') ? { topic: url.searchParams.get('topic') ?? '' } : {}) }, opts.contextNowDeps);
|
|
2895
|
+
debug.log('context.now', 'served', { audience, hidden: answer.hiddenCount ?? 0, forced });
|
|
2896
|
+
return jsonResponse(url.searchParams.get('format') === 'voice'
|
|
2897
|
+
? { text: renderVoiceNow(answer, audience), audience, hiddenCount: answer.hiddenCount ?? 0 }
|
|
2898
|
+
: { ...answer, audience, hiddenCount: answer.hiddenCount ?? 0 });
|
|
2836
2899
|
}
|
|
2837
2900
|
// §6.3 (2026-05-09) — context URL fetch (showroom).
|
|
2838
2901
|
// POST /v1/context/fetch-url
|