elanous 0.2.11 → 0.2.13
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/{1394-556de6d71bbdb0fb.js → 1394-931c9827067b2276.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{3992-2e522a8ac5f2afde.js → 3992-fe5c06d6b31f8b25.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8605-ea5bee9dfae512c7.js → 8605-9da4678f8a310d18.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-6a061bd97f0b9398.js → page-e49b9b76c565a0b1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-7c97dcdfe2e37a60.js → page-4c1dbb21861beab2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-5721537ca8a3598c.js → page-f6bc66b919b2ea7d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-b654240a572e4c48.js → page-60603c1e056c9b25.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-b9750abc897508c3.js → page-a0ea8f5a2341ee6b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-c8b1ccda6ba7ae98.js → page-31012264da8a341f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-d6b1d156fb95e74b.js → layout-50309aeb0bdd554a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/{page-9688f5b4f1ff950f.js → page-0696ee3fac9fe56c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-35414e0a007b4082.js → page-6ddd2109b7fc3a80.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/{page-e001823c35c9dd67.js → page-7a2bf830038a2264.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-371423d1a573d64b.js → page-1f327f18694fabca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-4b764b125339ac17.js → page-e5cef2fe6e4028df.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-cdf8e4b01067bcf1.js → page-cf596128d2b420ba.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/{page-3a24072b35470157.js → page-f672536f14f0969f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-d37cb0d52f9b6bd5.js → page-29a709616df9b050.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/{page-686da0df9bf23e5c.js → page-fee842d7c2151129.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-4dc614f3ce043e63.js → page-78b40a344ed22f24.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-d02c782fc792e2af.js → page-a011dd5b0bff29c5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/{page-239c17b911b84e72.js → page-d92e32d51ab5157e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/share/{page-dc6a79fb003f746c.js → page-5f21aed6932ef375.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-0fffb84cafdc9da6.js → page-1e1a53946804debf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-1218753c82d585f6.js → page-a1833cd43d832601.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-4b5935e67531efb5.js → page-63d14a4d8fe1e1c1.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/{page-3285bc87caf1eaa5.js → page-2a511d320f762e06.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/{page-ad553b0e06505ecd.js → page-1c3e03f88dac1e21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-fca096e3e860306f.js → page-c4d5289c2ea97a89.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-9ebfa78f0d9885c3.js → page-b91f8952deaeaf27.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-3dc5f21f7e39622d.js → page-71b43a2588489987.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +1 -0
- package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +14 -14
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +14 -14
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +14 -14
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +14 -14
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +14 -14
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +14 -14
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +14 -14
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +14 -14
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +14 -14
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +14 -14
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +14 -14
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +14 -14
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +14 -14
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +14 -14
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +14 -14
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +14 -14
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +14 -14
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +14 -14
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +14 -14
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +14 -14
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +14 -14
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +14 -14
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +14 -14
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +14 -14
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +15 -15
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +14 -14
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +14 -14
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +14 -14
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +14 -14
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +14 -14
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +14 -14
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +14 -14
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +14 -14
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +14 -14
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +14 -14
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +14 -14
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +14 -14
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +14 -14
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +14 -14
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +14 -14
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +14 -14
- 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/context/nightly-condense.yaml +21 -0
- package/graphs/context/recipes.yaml +12 -0
- package/graphs/demo/inside-seed.yaml +20 -0
- package/graphs/demo/recipes.yaml +12 -0
- package/graphs/lecture/lecture-note.yaml +28 -0
- package/graphs/lecture/recipes.yaml +17 -0
- package/package.json +2 -1
- package/plugins/card-followup/examples/sample-card.png +0 -0
- package/scripts/context-hooks/emit.ts +36 -14
- package/scripts/coord-channel-watch.sh +1 -0
- package/scripts/discord-seat-probe.ts +144 -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 +219 -0
- package/scripts/lecture-note/node.ts +256 -0
- package/scripts/lib/telegram-inject-args.ts +31 -0
- package/scripts/measure-pod-memory-by-goal.ts +130 -0
- package/scripts/release-loop/auto-approve-node.ts +1 -1
- package/scripts/release-loop/checklist-gate-node.ts +2 -1
- package/scripts/release-loop/gate-node.ts +61 -17
- package/scripts/release-loop/ops-upgrade-node.ts +59 -6
- package/scripts/release-loop/publish-node.ts +4 -1
- package/scripts/release-loop/version-node.ts +13 -3
- package/scripts/telegram-inject.ts +14 -6
- package/scripts/test-deterministic.ts +53 -3
- package/scripts/test-diet/remeasure-list.ts +112 -0
- package/scripts/ux/demo-rehearsal.ts +17 -13
- package/scripts/ux/demo-seed.ts +108 -0
- package/scripts/ux/lib/rehearsal-verdict.ts +12 -5
- 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/src/agent/identity-env.ts +11 -0
- package/src/agent-mission/driver.ts +150 -23
- package/src/agent-mission/duo.ts +94 -0
- package/src/agent-mission/pty-decision.ts +2 -2
- package/src/autopilot/pty-control-loop.ts +4 -1
- 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 +1 -0
- package/src/cli/decisions-cli.ts +51 -19
- package/src/cli/outputs-cli.ts +92 -0
- package/src/cli/persona-cli.ts +45 -13
- package/src/cli/pod-cli.ts +27 -3
- package/src/cli/pod-memory-advice.ts +35 -0
- package/src/cli/pr-cli.ts +15 -1
- package/src/cli/release-cli.ts +135 -15
- package/src/context-bus/condense.ts +194 -0
- package/src/context-bus/context-day.ts +82 -0
- package/src/context-bus/context-now-surfaces.ts +32 -2
- package/src/context-bus/context-now.ts +2 -2
- package/src/context-bus/coord-events.ts +2 -2
- package/src/context-bus/long-term-memory.ts +118 -22
- package/src/context-bus/nightly-condense.ts +153 -0
- package/src/context-bus/session-events.ts +35 -0
- package/src/dashboard/dashboard-status-lines.ts +2 -0
- package/src/dashboard/index.ts +8 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +24 -0
- package/src/decisions/decision-cards.ts +13 -7
- package/src/decisions/decision-ledger.ts +43 -17
- package/src/domains/outbound-alert.ts +99 -31
- package/src/domains/outbound-log-probe.ts +8 -0
- package/src/execution-loop/launch-gate.ts +17 -8
- package/src/field-note/telegram.ts +142 -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-llm.ts +23 -13
- package/src/graph-runner/graph-grow.ts +69 -13
- package/src/graph-runner/graph-tick.ts +9 -2
- package/src/graph-runner/runner.ts +92 -24
- package/src/harness/harness-cli-command.ts +165 -17
- package/src/harness/harness-queue-child.ts +37 -0
- package/src/harness/harness-queue.ts +306 -0
- package/src/hooks/consult-intake.ts +153 -0
- package/src/hooks/github-shadow.ts +72 -2
- package/src/hooks/receiver.ts +82 -3
- 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/index.ts +232 -16
- package/src/intake-plane/discord-seat-work.ts +36 -3
- package/src/intake-plane/linear-wish.ts +44 -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-card.ts +33 -0
- package/src/llm/difficulty.ts +1 -1
- package/src/loops/checker.ts +178 -0
- package/src/loops/loop-cli.ts +37 -3
- package/src/loops/observe.ts +49 -0
- package/src/loops/orchestrator/overlap.ts +141 -0
- package/src/loops/orchestrator/traffic.ts +150 -0
- package/src/loops/package/verify.ts +125 -0
- package/src/loops/verdict.ts +22 -0
- package/src/maturity/feature-maturity.ts +5 -4
- package/src/memory.ts +37 -4
- package/src/nexus/api/card-followup-route.ts +118 -0
- package/src/nexus/api/decisions-route.ts +68 -0
- package/src/nexus/api/http-server.ts +78 -8
- package/src/nexus/api/outbound-report.ts +40 -7
- package/src/nexus/api/personas.ts +97 -54
- package/src/nexus/api/skill-exec-route.ts +97 -0
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/workflow-history.ts +88 -0
- package/src/nexus/api/workflows.ts +52 -0
- package/src/nexus/outbound/delivery-ledger.ts +17 -8
- package/src/nexus/outbound/router.ts +8 -3
- package/src/persona/loader.ts +18 -0
- package/src/persona/persona-todo.ts +14 -9
- package/src/persona/presets.ts +10 -2
- package/src/persona/team.ts +105 -0
- package/src/persona/types.ts +4 -0
- package/src/plugins/maker/market-bundle.ts +2 -1
- package/src/pod-lease/host-lease.ts +130 -20
- package/src/release-loop/checklist.ts +15 -3
- package/src/release-loop/feature-store.ts +24 -10
- package/src/release-loop/landed-but-yellow.ts +60 -0
- package/src/seat-dispatch/seat-ask.ts +57 -12
- package/src/seat-loop/seat-loop.ts +253 -18
- package/src/seat-loop/seat-pack.ts +127 -0
- package/src/self-dev/ask-launch-flow.ts +3 -0
- package/src/self-dev/draft-sweep.ts +44 -10
- package/src/self-dev/draft-triage-rules.ts +54 -2
- package/src/self-implement/budget-gate.ts +29 -12
- package/src/self-implement/gate-candidate-ranking.ts +60 -0
- package/src/self-implement/gate-scope.ts +9 -8
- package/src/self-implement/headless-elanous-driver.ts +7 -2
- package/src/self-implement/l8-merge-queue.ts +157 -1
- package/src/self-implement/orchestrator.ts +15 -0
- package/src/self-implement/test-seams.ts +1 -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 +78 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -10
- package/src/task-orchestrator/surfaces/pod-lease.ts +18 -6
- package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +46 -8
- package/src/telegram-commands.ts +21 -0
- package/src/telegram-dispatch-forward.ts +1 -1
- package/src/telegram-fabric-plan.ts +90 -0
- package/src/telegram-project-command.ts +84 -4
- package/src/telegram.ts +37 -5
- package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
- package/src/user-config.ts +101 -13
- 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/types.ts +13 -1
- package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +0 -1
- package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-b12b6d2f8d2d1aea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +0 -1
- package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +0 -1
- /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_buildManifest.js +0 -0
- /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_ssgManifest.js +0 -0
|
@@ -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';
|
|
@@ -335,7 +338,7 @@ import {
|
|
|
335
338
|
handleTasksList,
|
|
336
339
|
handleTaskDetail,
|
|
337
340
|
} from './tasks-scheduler.js';
|
|
338
|
-
import { handleTaskCardsGet } from './task-cards-api.js';
|
|
341
|
+
import { handleTaskCardsGet, handleTaskCardsWishPost } from './task-cards-api.js';
|
|
339
342
|
import { handleTaskCreatePost, handleTaskApprovePost } from './tasks-create.js';
|
|
340
343
|
import { matchIngestAuthorization } from './ingest-token.js';
|
|
341
344
|
import {
|
|
@@ -344,6 +347,8 @@ import {
|
|
|
344
347
|
handleWorkflowApprovalsPending,
|
|
345
348
|
handleWorkflowDelete,
|
|
346
349
|
handleWorkflowGet,
|
|
350
|
+
handleWorkflowHistoryList,
|
|
351
|
+
handleWorkflowHistoryVersionGet,
|
|
347
352
|
handleWorkflowPut,
|
|
348
353
|
handleWorkflowRunGet,
|
|
349
354
|
handleWorkflowRunsList,
|
|
@@ -470,6 +475,9 @@ export interface NexusHttpServerOpts {
|
|
|
470
475
|
/** Optional installer state root for isolated API consumers and tests. */
|
|
471
476
|
pluginStateRoot?: string;
|
|
472
477
|
outputs?: OutputsDeps;
|
|
478
|
+
decisions?: Pick<DecisionsRouteDeps, 'ledger'>;
|
|
479
|
+
cardFollowup?: CardFollowupJobs;
|
|
480
|
+
skillExec?: SkillExecJobs;
|
|
473
481
|
execRequests?: ExecRequestRunner;
|
|
474
482
|
seatRequests?: SeatRequestsDeps;
|
|
475
483
|
consultRequests?: ConsultRequestsDeps;
|
|
@@ -850,6 +858,16 @@ function execRequestRunner(): ExecRequestRunner {
|
|
|
850
858
|
return execRequestRunnerInstance;
|
|
851
859
|
}
|
|
852
860
|
|
|
861
|
+
let cardFollowupJobs: CardFollowupJobs | undefined;
|
|
862
|
+
function defaultCardFollowupJobs(): CardFollowupJobs {
|
|
863
|
+
return cardFollowupJobs ??= createCardFollowupJobs();
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
let skillExecJobs: SkillExecJobs | undefined;
|
|
867
|
+
function defaultSkillExecJobs(): SkillExecJobs {
|
|
868
|
+
return skillExecJobs ??= createSkillExecJobs();
|
|
869
|
+
}
|
|
870
|
+
|
|
853
871
|
export async function routeRequest(
|
|
854
872
|
req: Request,
|
|
855
873
|
opts: NexusHttpServerOpts,
|
|
@@ -1336,6 +1354,23 @@ export async function routeRequest(
|
|
|
1336
1354
|
return method === 'GET' ? handleProjectsGet() : handleProjectsPost(req);
|
|
1337
1355
|
}
|
|
1338
1356
|
|
|
1357
|
+
// Polling must remain outside the non-GET dispatch below.
|
|
1358
|
+
if (method === 'GET' && pathname.startsWith(`${SKILL_EXEC_PATH}/`) && !pathname.slice(SKILL_EXEC_PATH.length + 1).includes('/')) {
|
|
1359
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1360
|
+
return (opts.skillExec ?? defaultSkillExecJobs()).get(pathname.slice(SKILL_EXEC_PATH.length + 1));
|
|
1361
|
+
}
|
|
1362
|
+
if (method === 'GET' && pathname.startsWith(`${CARD_FOLLOWUP_PATH}/`) && !pathname.slice(CARD_FOLLOWUP_PATH.length + 1).includes('/')) {
|
|
1363
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1364
|
+
return (opts.cardFollowup ?? defaultCardFollowupJobs()).get(pathname.slice(CARD_FOLLOWUP_PATH.length + 1));
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
if (method === 'GET' && pathname === DECISIONS_PATH) {
|
|
1368
|
+
return handleDecisions(req, {
|
|
1369
|
+
authorize: request => !!opts.metaApi && checkAuth(request, opts.metaApi),
|
|
1370
|
+
...opts.decisions,
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1339
1374
|
if (method === 'GET' && pathname === '/v1/outputs') {
|
|
1340
1375
|
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1341
1376
|
return handleOutputsGet(req, opts.outputs);
|
|
@@ -1360,10 +1395,29 @@ export async function routeRequest(
|
|
|
1360
1395
|
return handleSkillProblems(req, opts.metaApi);
|
|
1361
1396
|
}
|
|
1362
1397
|
|
|
1398
|
+
if (method === 'GET' && pathname === '/v1/persona-presets') {
|
|
1399
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1400
|
+
return dispatchPersonaRoute(req, pathname) as Promise<Response>;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1363
1403
|
// Mutation routes (PR ι) — POST/PATCH/DELETE on /v1/nexus/tabs[/:id[/action]].
|
|
1364
1404
|
// Templates POST handled here too (PR κ).
|
|
1365
1405
|
// Config / secrets PUT/POST/DELETE here too (PR μ).
|
|
1366
1406
|
if (method !== 'GET') {
|
|
1407
|
+
if (method === 'POST' && pathname === SKILL_EXEC_PATH) {
|
|
1408
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1409
|
+
return (opts.skillExec ?? defaultSkillExecJobs()).post(req);
|
|
1410
|
+
}
|
|
1411
|
+
if (method === 'POST' && pathname === CARD_FOLLOWUP_PATH) {
|
|
1412
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1413
|
+
return (opts.cardFollowup ?? defaultCardFollowupJobs()).post(req);
|
|
1414
|
+
}
|
|
1415
|
+
if (method === 'POST' && /^\/v1\/decisions\/[^/]+\/decide$/.test(pathname)) {
|
|
1416
|
+
return handleDecisions(req, {
|
|
1417
|
+
authorize: request => !!opts.metaApi && checkAuth(request, opts.metaApi),
|
|
1418
|
+
...opts.decisions,
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1367
1421
|
if (method === 'POST' && pathname === '/v1/plugins/install') {
|
|
1368
1422
|
if (!opts.metaApi) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1369
1423
|
return handlePluginsInstall(req, opts.metaApi, opts.pluginStateRoot);
|
|
@@ -1389,6 +1443,10 @@ export async function routeRequest(
|
|
|
1389
1443
|
const mutated = await handleGraphsMutation(pathname, req);
|
|
1390
1444
|
if (mutated) return mutated;
|
|
1391
1445
|
}
|
|
1446
|
+
if (method === 'POST' && pathname === '/v1/task-cards/wish') {
|
|
1447
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1448
|
+
return handleTaskCardsWishPost(req);
|
|
1449
|
+
}
|
|
1392
1450
|
if (method === 'POST' && pathname === '/v1/tasks') {
|
|
1393
1451
|
if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
|
|
1394
1452
|
return handleTaskCreatePost(req, opts.metaApi);
|
|
@@ -1645,10 +1703,10 @@ export async function routeRequest(
|
|
|
1645
1703
|
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1646
1704
|
return pathname === '/v1/setup/child-llm' ? handleChildLlmSet(req) : handleAnswerPrioritySet(req);
|
|
1647
1705
|
}
|
|
1648
|
-
//
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1706
|
+
// Persona creation and editing require owner auth before parsing a write body.
|
|
1707
|
+
if ((method === 'POST' && pathname === '/v1/personas')
|
|
1708
|
+
|| (method === 'PATCH' && pathname.startsWith('/v1/personas/'))) {
|
|
1709
|
+
if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
1652
1710
|
const result = await dispatchPersonaRoute(req, pathname);
|
|
1653
1711
|
if (result) return result;
|
|
1654
1712
|
}
|
|
@@ -2645,7 +2703,7 @@ export async function routeRequest(
|
|
|
2645
2703
|
catch { return jsonResponse({ error: 'bad_request' }, 400); }
|
|
2646
2704
|
return handleScheduleDetail(req, id, opts.metaApi);
|
|
2647
2705
|
}
|
|
2648
|
-
if (pathname === '/v1/task-cards' || /^\/v1\/task-cards\/[^/]+$/.test(pathname)) {
|
|
2706
|
+
if (method === 'GET' && (pathname === '/v1/task-cards' || /^\/v1\/task-cards\/[^/]+$/.test(pathname))) {
|
|
2649
2707
|
return handleTaskCardsGet(pathname);
|
|
2650
2708
|
}
|
|
2651
2709
|
if (pathname === '/v1/tasks') {
|
|
@@ -2738,11 +2796,23 @@ export async function routeRequest(
|
|
|
2738
2796
|
}
|
|
2739
2797
|
if (pathname.startsWith('/v1/workflows/')) {
|
|
2740
2798
|
if (!opts.metaApi) return jsonResponse({ error: 'meta-api-not-wired' }, 503);
|
|
2741
|
-
const
|
|
2799
|
+
const segments = pathname.slice('/v1/workflows/'.length).split('/');
|
|
2800
|
+
let wfName: string;
|
|
2801
|
+
try { wfName = decodeURIComponent(segments[0] ?? ''); }
|
|
2802
|
+
catch { return jsonResponse({ error: 'bad_request', reason: 'invalid workflow name' }, 400); }
|
|
2742
2803
|
if (!wfName || wfName.includes('/') || wfName.includes('\\') || wfName.includes('..')) {
|
|
2743
2804
|
return jsonResponse({ error: 'bad_request', reason: 'invalid workflow name' }, 400);
|
|
2744
2805
|
}
|
|
2745
|
-
|
|
2806
|
+
if (segments.length === 2 && segments[1] === 'history') {
|
|
2807
|
+
return handleWorkflowHistoryList(req, wfName, opts.metaApi);
|
|
2808
|
+
}
|
|
2809
|
+
if (segments.length === 3 && segments[1] === 'history') {
|
|
2810
|
+
let id: string;
|
|
2811
|
+
try { id = decodeURIComponent(segments[2] ?? ''); }
|
|
2812
|
+
catch { return jsonResponse({ error: 'bad_request', reason: 'invalid workflow history version id' }, 400); }
|
|
2813
|
+
return handleWorkflowHistoryVersionGet(req, wfName, id, opts.metaApi);
|
|
2814
|
+
}
|
|
2815
|
+
if (segments.length === 1) return handleWorkflowGet(req, wfName, opts.metaApi);
|
|
2746
2816
|
}
|
|
2747
2817
|
if (pathname.startsWith('/v1/nexus/templates/')) {
|
|
2748
2818
|
const name = pathname.slice('/v1/nexus/templates/'.length);
|
|
@@ -19,6 +19,9 @@ import { checkAuth, type MetaApiOpts } from './meta-api.js';
|
|
|
19
19
|
import { routeOutbound } from '../outbound/router.js';
|
|
20
20
|
import { getUserConfig } from '../../user-config.js';
|
|
21
21
|
import { openSurfaceEventsDb, recordEvent } from '../../domains/surface-events.js';
|
|
22
|
+
import { debug } from '../../debug/log.js';
|
|
23
|
+
import { resolveReportTarget } from '../../telegram-report.js';
|
|
24
|
+
import type { RouteResult, RouterDeps } from '../outbound/router.js';
|
|
22
25
|
|
|
23
26
|
/** 크로스서피스 기억(P0.5) — /v1/outbound = ALL outbound 단일 수렴점이므로 여기서
|
|
24
27
|
* 원장 기록하면 sendOutbound 클라 + 직접 POST(morning-report·external) 전부 포착.
|
|
@@ -31,6 +34,21 @@ function recordOutboundEvent(text: string, kind: string): void {
|
|
|
31
34
|
} catch { /* 발송 원장 기록 실패는 무시 */ }
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
function observeDaemonSend(cfg: ReturnType<typeof getUserConfig>, text: string, kind: string, result: RouteResult, source: string): void {
|
|
38
|
+
if (result.suppressed) return; // 라우터가 suppressedBy 만 싣는 억제 관측을 남긴다.
|
|
39
|
+
try {
|
|
40
|
+
const target = result.channels.some(c => c.type === 'telegram') ? resolveReportTarget(cfg, kind) : null;
|
|
41
|
+
const id = target?.botToken.split(':', 1)[0];
|
|
42
|
+
const bot = id && /^\d+$/.test(id) ? `telegram:${id}` : target ? 'telegram:configured' : 'none';
|
|
43
|
+
debug.log('outbound.send', result.delivered ? 'sent' : 'failed', {
|
|
44
|
+
kind: /^[a-zA-Z0-9_-]{1,80}$/.test(kind) ? kind : 'unknown',
|
|
45
|
+
source: /^[a-zA-Z0-9_./-]{1,160}$/.test(source) ? source : 'unknown',
|
|
46
|
+
chars: text.length, bot, path: 'daemon',
|
|
47
|
+
channels: result.channels.map(c => ({ type: c.type, ok: c.ok })),
|
|
48
|
+
});
|
|
49
|
+
} catch { /* observation must not change the send result */ }
|
|
50
|
+
}
|
|
51
|
+
|
|
34
52
|
interface OutboundBody {
|
|
35
53
|
text?: string;
|
|
36
54
|
markdown?: boolean;
|
|
@@ -45,14 +63,16 @@ function json(body: unknown, status: number): Response {
|
|
|
45
63
|
}
|
|
46
64
|
|
|
47
65
|
/** The daemon's own sendOutbound path (OB8) — what POST /v1/outbound does, without the HTTP self-call. */
|
|
48
|
-
export async function routeOutboundInProcess(text: string, kind: string): Promise<boolean> {
|
|
49
|
-
const
|
|
50
|
-
|
|
66
|
+
export async function routeOutboundInProcess(text: string, kind: string, source = 'daemon-in-process', deferFailureObservation = false): Promise<boolean> {
|
|
67
|
+
const cfg = getUserConfig();
|
|
68
|
+
const result = await routeOutbound(cfg, { text, markdown: false, kind });
|
|
69
|
+
if (result.delivered || !deferFailureObservation) observeDaemonSend(cfg, text, kind, result, source);
|
|
70
|
+
if (result.delivered && !result.suppressed) recordOutboundEvent(text, kind);
|
|
51
71
|
return result.delivered;
|
|
52
72
|
}
|
|
53
73
|
|
|
54
74
|
/** POST /v1/outbound — fan one message out to the routed channels. */
|
|
55
|
-
export async function handleOutboundReport(req: Request, metaApi: MetaApiOpts): Promise<Response> {
|
|
75
|
+
export async function handleOutboundReport(req: Request, metaApi: MetaApiOpts, routerDeps: RouterDeps = {}): Promise<Response> {
|
|
56
76
|
if (!checkAuth(req, metaApi)) return json({ error: 'unauthorized' }, 401);
|
|
57
77
|
|
|
58
78
|
let body: OutboundBody;
|
|
@@ -68,16 +88,29 @@ export async function handleOutboundReport(req: Request, metaApi: MetaApiOpts):
|
|
|
68
88
|
// Router is per-channel fail-soft; `delivered` = any channel ok.
|
|
69
89
|
// Producers (outbound-alert.ts deliver()) gate their direct-telegram
|
|
70
90
|
// fallback on this top-level boolean — keep it stable.
|
|
71
|
-
const result = await routeOutbound(cfg, { text, markdown: body.markdown ?? true, kind });
|
|
72
|
-
|
|
91
|
+
const result = await routeOutbound(cfg, { text, markdown: body.markdown ?? true, kind }, routerDeps);
|
|
92
|
+
const clientFallback = req.headers.get('X-Elanous-Client-Fallback') === 'direct';
|
|
93
|
+
const source = req.headers.get('X-Elanous-Outbound-Source') ?? 'daemon-http';
|
|
94
|
+
if (result.delivered || !clientFallback) observeDaemonSend(cfg, text, kind, result, source);
|
|
95
|
+
if (result.delivered && !result.suppressed) recordOutboundEvent(text, kind); // 크로스서피스 기억 원장(P0.5)
|
|
73
96
|
return json({
|
|
74
97
|
delivered: result.delivered,
|
|
75
98
|
// Legacy single-channel field (pre-R6 consumers) + full breakdown.
|
|
76
|
-
channel: result.
|
|
99
|
+
channel: result.suppressed ? 'suppressed' : (result.channels.find(c => c.ok)?.type ?? 'none'),
|
|
77
100
|
channels: result.channels,
|
|
101
|
+
...(result.suppressed ? { suppressed: true, suppressedBy: result.suppressedBy } : {}),
|
|
102
|
+
...(!result.suppressed && result.channels.length === 0 ? { reason: 'no-channel' } : {}),
|
|
78
103
|
kind,
|
|
79
104
|
}, result.delivered ? 200 : 503);
|
|
80
105
|
} catch (e) {
|
|
106
|
+
if (req.headers.get('X-Elanous-Client-Fallback') !== 'direct') {
|
|
107
|
+
try {
|
|
108
|
+
debug.log('outbound.send', 'failed', {
|
|
109
|
+
kind: /^[a-zA-Z0-9_-]{1,80}$/.test(kind) ? kind : 'unknown',
|
|
110
|
+
source: 'daemon-http', chars: text.length, bot: 'unknown', path: 'daemon',
|
|
111
|
+
});
|
|
112
|
+
} catch { /* observation must not change the response */ }
|
|
113
|
+
}
|
|
81
114
|
return json({ error: e instanceof Error ? e.message : String(e) }, 502);
|
|
82
115
|
}
|
|
83
116
|
}
|
|
@@ -1,27 +1,14 @@
|
|
|
1
|
-
// NEXUS · personas REST handlers
|
|
2
|
-
//
|
|
3
|
-
// Endpoints
|
|
4
|
-
// GET /v1/personas — list all loaded personas
|
|
5
|
-
// GET /v1/personas/:personaId — single persona profile
|
|
6
|
-
//
|
|
7
|
-
// Architecture: read-only thin wrapper over the global PersonaRegistry
|
|
8
|
-
// (`src/persona/global-registry.ts`). Disk yaml is the source of truth;
|
|
9
|
-
// editing requires text editor + `elanous persona reload` (or fs.watch
|
|
10
|
-
// auto-reload). PWA Showroom consumes this for the per-panel persona
|
|
11
|
-
// picker (§6.4 Q2 = REST read-only).
|
|
12
|
-
//
|
|
13
|
-
// PLAN: 내부 문서 `PLAN-cv-3-showroom-mvp-2026-05-07` §6.4
|
|
14
|
-
// Cross-ref: src/persona/global-registry.ts (singleton accessor)
|
|
15
|
-
// src/persona/types.ts (PersonaProfile shape)
|
|
16
|
-
|
|
17
|
-
import { homedir } from 'node:os';
|
|
18
|
-
import { join } from 'node:path';
|
|
1
|
+
// NEXUS · personas REST handlers. Presets clone into the state persona store;
|
|
2
|
+
// writes reload the global registry so subsequent reads see the saved profile.
|
|
19
3
|
|
|
20
4
|
import {
|
|
21
5
|
awaitGlobalPersonaLoad,
|
|
22
6
|
getGlobalPersonaRegistry,
|
|
23
7
|
reloadGlobalPersonaRegistry,
|
|
8
|
+
resolveStatePersonaDir,
|
|
24
9
|
} from '../../persona/global-registry.js';
|
|
10
|
+
import { clonePreset, editPersona, loadPresets } from '../../persona/presets.js';
|
|
11
|
+
import { resolvePersonaAddress } from '../../intake-plane/persona-answer.js';
|
|
25
12
|
import type { PersonaRegistryEvent } from '../../persona/registry.js';
|
|
26
13
|
import type { PersonaProfile } from '../../persona/types.js';
|
|
27
14
|
import {
|
|
@@ -113,7 +100,7 @@ export async function handlePersonaGet(
|
|
|
113
100
|
}
|
|
114
101
|
await awaitGlobalPersonaLoad();
|
|
115
102
|
const registry = getGlobalPersonaRegistry();
|
|
116
|
-
const persona =
|
|
103
|
+
const persona = resolvePersonaAddress(personaId, registry);
|
|
117
104
|
if (!persona) return notFound(`persona ${personaId} not found`);
|
|
118
105
|
return jsonResponse({ persona: toWire(persona) }, 200);
|
|
119
106
|
}
|
|
@@ -193,22 +180,41 @@ export function handlePersonasEvents(
|
|
|
193
180
|
});
|
|
194
181
|
}
|
|
195
182
|
|
|
196
|
-
/**
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (env && env.length > 0) return env;
|
|
201
|
-
return join(homedir(), '.elanous', 'personas');
|
|
183
|
+
/** GET /v1/persona-presets — shipped preset catalog in index order. */
|
|
184
|
+
export function handlePersonaPresets(req: Request, opts: PersonaRouteOpts = {}): Response {
|
|
185
|
+
if (opts.checkAuth && !opts.checkAuth(req)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
186
|
+
return jsonResponse({ presets: loadPresets() }, 200);
|
|
202
187
|
}
|
|
203
188
|
|
|
204
|
-
/**
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
189
|
+
/** POST /v1/personas — clone a shipped preset into the user's persona store. */
|
|
190
|
+
export async function handlePersonaCreate(req: Request, opts: PersonaRouteOpts = {}): Promise<Response> {
|
|
191
|
+
if (opts.checkAuth && !opts.checkAuth(req)) return jsonResponse({ error: 'unauthorized' }, 401);
|
|
192
|
+
let body: unknown;
|
|
193
|
+
try { body = await req.json(); }
|
|
194
|
+
catch { return jsonResponse({ error: 'invalid-json' }, 400); }
|
|
195
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'invalid-body' }, 400);
|
|
196
|
+
const { preset, name } = body as Record<string, unknown>;
|
|
197
|
+
if (typeof preset !== 'string' || !preset.trim() || typeof name !== 'string' || !name.trim()) {
|
|
198
|
+
return jsonResponse({ error: 'preset-and-name-required' }, 400);
|
|
199
|
+
}
|
|
200
|
+
await awaitGlobalPersonaLoad();
|
|
201
|
+
if (!loadPresets().some((p) => p.personaId === preset)) return notFound(`preset ${preset} not found`);
|
|
202
|
+
try {
|
|
203
|
+
const created = clonePreset(preset, name.trim(), { dir: resolveStatePersonaDir() });
|
|
204
|
+
await reloadGlobalPersonaRegistry();
|
|
205
|
+
const persona = getGlobalPersonaRegistry().get(created.personaId);
|
|
206
|
+
if (!persona) return notFound(`persona ${created.personaId} not found after reload`);
|
|
207
|
+
return jsonResponse({ persona: toWire(persona) }, 201);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (error instanceof Error && error.message.startsWith('persona name already exists:')) {
|
|
210
|
+
return jsonResponse({ error: 'persona-name-conflict' }, 409);
|
|
211
|
+
}
|
|
212
|
+
return jsonResponse({ error: 'create-failed' }, 500);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** PATCH /v1/personas/:personaId — description-only edits retain the
|
|
217
|
+
* existing surgical YAML behavior; multi-field edits use the persona store. */
|
|
212
218
|
export async function handlePersonaPatch(
|
|
213
219
|
req: Request,
|
|
214
220
|
personaId: string,
|
|
@@ -217,17 +223,23 @@ export async function handlePersonaPatch(
|
|
|
217
223
|
if (opts.checkAuth && !opts.checkAuth(req)) {
|
|
218
224
|
return jsonResponse({ error: 'unauthorized' }, 401);
|
|
219
225
|
}
|
|
220
|
-
let
|
|
221
|
-
try {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return jsonResponse({ error: 'invalid-
|
|
226
|
+
let parsed: unknown;
|
|
227
|
+
try { parsed = await req.json(); }
|
|
228
|
+
catch { return jsonResponse({ error: 'invalid-json' }, 400); }
|
|
229
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
230
|
+
return jsonResponse({ error: 'invalid-body' }, 400);
|
|
225
231
|
}
|
|
226
|
-
|
|
227
|
-
|
|
232
|
+
const body = parsed as Record<string, unknown>;
|
|
233
|
+
const fields = Object.keys(body);
|
|
234
|
+
if (!fields.length) return jsonResponse({ error: 'description-required' }, 400);
|
|
235
|
+
if (fields.some((field) => !['displayName', 'description', 'systemPrompt'].includes(field))) {
|
|
236
|
+
return jsonResponse({ error: 'invalid-persona-field' }, 400);
|
|
228
237
|
}
|
|
229
|
-
if (
|
|
230
|
-
|
|
238
|
+
if (fields.some((field) => typeof body[field] !== 'string')
|
|
239
|
+
|| (body.displayName !== undefined && !(body.displayName as string).trim())) {
|
|
240
|
+
return jsonResponse({ error: 'invalid-persona-field' }, 400);
|
|
241
|
+
}
|
|
242
|
+
if (typeof body.description === 'string' && body.description.length > MAX_PERSONA_DESCRIPTION_LENGTH * 4) {
|
|
231
243
|
return jsonResponse({ error: 'description-too-long' }, 400);
|
|
232
244
|
}
|
|
233
245
|
|
|
@@ -237,14 +249,37 @@ export async function handlePersonaPatch(
|
|
|
237
249
|
return notFound(`persona ${personaId} not found`);
|
|
238
250
|
}
|
|
239
251
|
|
|
240
|
-
const dir =
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
252
|
+
const dir = resolveStatePersonaDir();
|
|
253
|
+
if (fields.length === 1 && typeof body.description === 'string') {
|
|
254
|
+
const result = updatePersonaDescription(dir, personaId, body.description);
|
|
255
|
+
if (!result.ok) {
|
|
256
|
+
const status = result.reason === 'file-not-found' ? 404 : 400;
|
|
257
|
+
return jsonResponse({ error: 'patch-failed', reason: result.reason }, status);
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
const edits: Record<string, unknown> = {};
|
|
261
|
+
for (const field of fields) edits[field] = body[field];
|
|
262
|
+
if (typeof edits.description === 'string') {
|
|
263
|
+
const description = edits.description.trim();
|
|
264
|
+
edits.description = description;
|
|
265
|
+
if (description.length > MAX_PERSONA_DESCRIPTION_LENGTH) {
|
|
266
|
+
return jsonResponse({ error: 'description-too-long' }, 400);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
editPersona(personaId, edits, { dir });
|
|
271
|
+
} catch (error) {
|
|
272
|
+
if (error instanceof Error && error.message.startsWith('persona not found:')) {
|
|
273
|
+
return notFound(`persona ${personaId} not found in state store`);
|
|
274
|
+
}
|
|
275
|
+
if (error instanceof Error && error.message.startsWith('persona name already exists:')) {
|
|
276
|
+
return jsonResponse({ error: 'persona-name-conflict' }, 409);
|
|
277
|
+
}
|
|
278
|
+
return jsonResponse({ error: 'patch-failed', reason: error instanceof Error ? error.message : 'invalid-persona' }, 400);
|
|
279
|
+
}
|
|
245
280
|
}
|
|
246
281
|
|
|
247
|
-
// Reload so the in-memory registry reflects the
|
|
282
|
+
// Reload so the in-memory registry reflects the saved fields on the
|
|
248
283
|
// next GET. fs.watch would auto-reload but is async + race-y vs the
|
|
249
284
|
// immediate echo below.
|
|
250
285
|
await reloadGlobalPersonaRegistry();
|
|
@@ -255,7 +290,7 @@ export async function handlePersonaPatch(
|
|
|
255
290
|
return jsonResponse({ persona: toWire(reloaded) }, 200);
|
|
256
291
|
}
|
|
257
292
|
|
|
258
|
-
/** Combined dispatcher — `/v1/personas[/:id]` + `/v1/personas/events`.
|
|
293
|
+
/** Combined dispatcher — `/v1/persona-presets` + `/v1/personas[/:id]` + `/v1/personas/events`.
|
|
259
294
|
* Returns null when the pathname doesn't match so the caller chains. */
|
|
260
295
|
export async function dispatchPersonaRoute(
|
|
261
296
|
req: Request,
|
|
@@ -263,11 +298,14 @@ export async function dispatchPersonaRoute(
|
|
|
263
298
|
opts: PersonaRouteOpts = {},
|
|
264
299
|
): Promise<Response | null> {
|
|
265
300
|
const method = req.method.toUpperCase();
|
|
301
|
+
if (pathname === '/v1/persona-presets') {
|
|
302
|
+
if (method !== 'GET') return jsonResponse({ error: 'method not allowed' }, 405);
|
|
303
|
+
return handlePersonaPresets(req, opts);
|
|
304
|
+
}
|
|
266
305
|
if (pathname === '/v1/personas') {
|
|
267
|
-
if (method
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
return handlePersonasList(req, opts);
|
|
306
|
+
if (method === 'GET') return handlePersonasList(req, opts);
|
|
307
|
+
if (method === 'POST') return handlePersonaCreate(req, opts);
|
|
308
|
+
return jsonResponse({ error: 'method not allowed' }, 405);
|
|
271
309
|
}
|
|
272
310
|
// SSE — must come BEFORE the single-persona regex so that the
|
|
273
311
|
// literal "events" segment isn't misread as a personaId.
|
|
@@ -279,8 +317,13 @@ export async function dispatchPersonaRoute(
|
|
|
279
317
|
}
|
|
280
318
|
const match = pathname.match(/^\/v1\/personas\/([^/]+)$/);
|
|
281
319
|
if (!match) return null;
|
|
282
|
-
|
|
320
|
+
let personaId: string;
|
|
321
|
+
try { personaId = decodeURIComponent(match[1]!); }
|
|
322
|
+
catch { return jsonResponse({ error: 'invalid-persona-id' }, 400); }
|
|
283
323
|
if (method === 'PATCH') {
|
|
324
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/.test(personaId)) {
|
|
325
|
+
return jsonResponse({ error: 'invalid-persona-id' }, 400);
|
|
326
|
+
}
|
|
284
327
|
return handlePersonaPatch(req, personaId, opts);
|
|
285
328
|
}
|
|
286
329
|
if (method !== 'GET') {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { debug } from '../../debug/log.js';
|
|
5
|
+
import { effectiveInstanceRoot } from '../../instance/resolve.js';
|
|
6
|
+
import { dispatchSkillExec } from '../../tool-runtime/skill-exec-runtime.js';
|
|
7
|
+
import { jsonResponse } from './json-response.js';
|
|
8
|
+
|
|
9
|
+
export const SKILL_EXEC_PATH = '/v1/skills/exec';
|
|
10
|
+
|
|
11
|
+
type SkillJob = {
|
|
12
|
+
id: string;
|
|
13
|
+
skill: string;
|
|
14
|
+
status: 'running' | 'done' | 'rejected' | 'failed';
|
|
15
|
+
updatedAt: number;
|
|
16
|
+
ok?: boolean;
|
|
17
|
+
output?: string;
|
|
18
|
+
reason?: 'not-allowlisted';
|
|
19
|
+
error?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type SkillExecDeps = {
|
|
23
|
+
rootDir?: string;
|
|
24
|
+
exec?: typeof dispatchSkillExec;
|
|
25
|
+
now?: () => number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function createSkillExecJobs({ rootDir = effectiveInstanceRoot(), exec = dispatchSkillExec, now = Date.now }: SkillExecDeps = {}) {
|
|
29
|
+
const base = join(rootDir, 'skill-exec');
|
|
30
|
+
const active = new Set<string>();
|
|
31
|
+
const unavailable = new Set<string>();
|
|
32
|
+
const file = (id: string) => join(base, `${id}.json`);
|
|
33
|
+
const save = (job: SkillJob) => {
|
|
34
|
+
mkdirSync(base, { recursive: true, mode: 0o700 });
|
|
35
|
+
chmodSync(base, 0o700);
|
|
36
|
+
writeFileSync(file(job.id), JSON.stringify(job), { mode: 0o600 });
|
|
37
|
+
chmodSync(file(job.id), 0o600);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
async post(req: Request): Promise<Response> {
|
|
42
|
+
let body: unknown;
|
|
43
|
+
try { body = await req.json(); } catch { body = null; }
|
|
44
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'bad_request' }, 400);
|
|
45
|
+
const { skill, task } = body as Record<string, unknown>;
|
|
46
|
+
if (typeof skill !== 'string' || !skill.trim() || typeof task !== 'string' || !task.trim() || task.length > 4000) {
|
|
47
|
+
return jsonResponse({ error: 'bad_request' }, 400);
|
|
48
|
+
}
|
|
49
|
+
const id = randomUUID();
|
|
50
|
+
const update = (status: SkillJob['status'], fields: Partial<Pick<SkillJob, 'ok' | 'output' | 'reason' | 'error'>> = {}) =>
|
|
51
|
+
save({ id, skill, status, updatedAt: now(), ...fields });
|
|
52
|
+
update('running');
|
|
53
|
+
active.add(id);
|
|
54
|
+
debug.log('skill-exec.pwa', 'started', { id, skill });
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
void (async () => {
|
|
57
|
+
try {
|
|
58
|
+
const result = await exec({ skill, task });
|
|
59
|
+
if (!result.ok && result.output.startsWith(`자동 실행 대상 아님: ${skill}.`)) {
|
|
60
|
+
update('rejected', { reason: 'not-allowlisted' });
|
|
61
|
+
debug.log('skill-exec.pwa', 'rejected', { id, skill });
|
|
62
|
+
} else {
|
|
63
|
+
update('done', { ok: result.ok, output: result.output.slice(0, 20_000) });
|
|
64
|
+
debug.log('skill-exec.pwa', 'done', { id, skill });
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
try {
|
|
68
|
+
update('failed', { error: 'skill-exec-failed' });
|
|
69
|
+
} catch {
|
|
70
|
+
unavailable.add(id);
|
|
71
|
+
}
|
|
72
|
+
debug.log('skill-exec.pwa', 'failed', { id, skill });
|
|
73
|
+
} finally {
|
|
74
|
+
active.delete(id);
|
|
75
|
+
}
|
|
76
|
+
})().catch(() => {
|
|
77
|
+
unavailable.add(id);
|
|
78
|
+
active.delete(id);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
return jsonResponse({ id }, 202);
|
|
82
|
+
},
|
|
83
|
+
get(id: string): Response {
|
|
84
|
+
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);
|
|
85
|
+
if (unavailable.has(id)) return jsonResponse({ id, status: 'failed', error: 'skill-exec-failed' });
|
|
86
|
+
let job: SkillJob;
|
|
87
|
+
try { job = JSON.parse(readFileSync(file(id), 'utf8')) as SkillJob; }
|
|
88
|
+
catch { return jsonResponse({ error: 'not-found' }, 404); }
|
|
89
|
+
if (job.id !== id) return jsonResponse({ error: 'not-found' }, 404);
|
|
90
|
+
if (job.status === 'running' && !active.has(id)) return jsonResponse({ id, status: 'failed', error: 'daemon-restarted' });
|
|
91
|
+
const { status, ok, output, reason, error } = job;
|
|
92
|
+
return jsonResponse({ id, status, ...(ok !== undefined ? { ok } : {}), ...(output !== undefined ? { output } : {}), ...(reason ? { reason } : {}), ...(error ? { error } : {}) });
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export type SkillExecJobs = ReturnType<typeof createSkillExecJobs>;
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { CardStore } from '../../task-cards/card-store.js';
|
|
2
|
+
import { createWishCard } from '../../intake-plane/wish-card.js';
|
|
2
3
|
import { jsonResponse } from './json-response.js';
|
|
3
4
|
|
|
5
|
+
export async function handleTaskCardsWishPost(req: Request, root?: string): Promise<Response> {
|
|
6
|
+
let body: unknown;
|
|
7
|
+
try { body = await req.json(); }
|
|
8
|
+
catch { return jsonResponse({ error: 'bad_request' }, 400); }
|
|
9
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'bad_request' }, 400);
|
|
10
|
+
const { text, ref } = body as Record<string, unknown>;
|
|
11
|
+
if (typeof text !== 'string' || !text.trim() || typeof ref !== 'string' || !ref.trim()) {
|
|
12
|
+
return jsonResponse({ error: 'bad_request' }, 400);
|
|
13
|
+
}
|
|
14
|
+
const store = new CardStore(root);
|
|
15
|
+
try {
|
|
16
|
+
const card = createWishCard({ text, source: 'pwa', ref }, store);
|
|
17
|
+
return jsonResponse(card, card.created ? 201 : 200);
|
|
18
|
+
} finally {
|
|
19
|
+
store.close();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
4
23
|
/** Read-only card API; route registration and authorization belong to the HTTP server. */
|
|
5
24
|
export function handleTaskCardsGet(pathname: string, root?: string): Response {
|
|
6
25
|
if (pathname !== '/v1/task-cards' && !/^\/v1\/task-cards\/[^/]+$/.test(pathname)) {
|