elanous 0.2.16 → 0.2.18
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/P6YTVgzuZz5oTZhY4Din_/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{1307-b46d45b705e8c9ae.js → 1307-30dff22c9e1f3073.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-4f936cdb30322aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4554-5761612531d0146b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-a11f37254196b659.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6158.9c0449a9a8d0064b.js → 6158.af2e93b7adff709b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8401-8615a660c84cdb03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-269e4464cc4b491c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d92f7ee3994e84c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-3cb52e5ea8fc2fa5.js → page-4733c967b56a4277.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-d22fd849cc22f104.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-c1e20d7d338713f1.js → page-bb742f995c6769e7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-482c5dbde699947c.js → page-02b073a2ac793b83.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-60c3da708f9a0ccc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8a9b3594bc23289b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-a0eb02638b1e6f31.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-a0e2a6d1bfb52fc5.js → page-308111abadf35670.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-6eaf7bac37830c21.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/{page-c53598cfce6156f2.js → page-5d97b2901e0d1ea6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-13a63953f53c6a23.js → page-c39554c321576982.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-5d3c8429d04f3831.js → page-8c3d8e63ae2e1c98.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-3193bdc0bbab047a.js → page-9b72a07d424701c6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-bd5d586429167fea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-6107ca8ddd5e3bdd.js → page-a572f58cd3ca5864.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-59b7834d497c2255.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-be5ea965a07fbc41.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-c130b63862437814.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-fa244bd146a00ae8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-44d56d6391965a6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-4167348049166852.js → page-83a700d4c754d802.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{not-found-de52e8fdf9095f7a.js → not-found-7b3170f374f77b9d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-5f23d0c66a3b4793.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-ca3952cd78a83ca2.js → page-a3c58f5c72a5f3f9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-a2c4625596f8d69c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-0e9099531d239f11.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-e8e1ecaea7cd71d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-a1b7ffed710a93e0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-1ebfe049c640e171.js → page-1467047104141297.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-2da6f9b8a38c6978.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-102310930791197a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-f331e38fefc00393.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-6ed0b73bae8351d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-abe8e3b74bea1ffc.js → page-44d5f767491692bf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-43336055451f6e37.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-d28573d6792b4275.js → page-3cc47dca0922ab9a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-a77ee355a2304ff0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-d751de5f8062ff5e.js → page-b17cd46cf0e9b8cf.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-fe1759f94003b32b.js → page-61553851aa606146.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/{page-d64c984e855c8c68.js → page-a1f585e154e61c59.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-7bf54c9bb9274af8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-9afdee66b820c76e.js → page-5150ef17b5383a57.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b9d8ce70792c1672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a5a3de231f2cc7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-3544f154c87b43ee.js → page-134da468538baf48.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-31ac4afa2dcd915c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-539cde99554be20f.js → webpack-c2782b330447ffb6.js} +1 -1
- package/apps/pwa/out/_next/static/css/7fa358fec7da7c47.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/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/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/decisions/index.html +1 -0
- package/apps/pwa/out/decisions/index.txt +30 -0
- 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/loops/index.html +1 -1
- package/apps/pwa/out/loops/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/graphs/intake/intake-daily.yaml +9 -2
- package/graphs/intake/news-sources.yaml +4 -0
- package/graphs/intake/recipes.yaml +6 -1
- package/package.json +2 -1
- package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
- package/samples/workflows/templates/chat-driven-research.yaml +35 -0
- package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
- package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
- package/samples/workflows/templates/manual-quickstart.yaml +21 -0
- package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
- package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
- package/scripts/__fixtures__/intake-news.xml +7 -0
- package/scripts/ci-public-export-import-gate.ts +7 -2
- package/scripts/ci-typecheck-changed.ts +21 -6
- package/scripts/discord-seat-probe.ts +3 -2
- package/scripts/docs/generate-cli-anatomy.ts +162 -0
- package/scripts/goals/authoring-metrics.ts +73 -0
- package/scripts/intake-news.ts +169 -0
- package/scripts/landing-heal/must-fix-match.ts +7 -7
- package/scripts/landing-heal/run-mission.ts +3 -2
- package/scripts/public-export-test-run.ts +37 -2
- package/scripts/release-loop/checklist-gate-node.ts +13 -11
- package/scripts/release-loop/cut-branch.ts +2 -1
- package/scripts/release-loop/cutoff.ts +4 -2
- package/scripts/release-loop/docs-land-node.ts +77 -11
- package/scripts/release-loop/docs-node.ts +1 -1
- package/scripts/release-loop/gate-node.ts +99 -14
- package/scripts/release-loop/node-verdict.ts +2 -1
- package/scripts/release-loop/npm-publish-node.ts +11 -7
- package/scripts/release-loop/ops-upgrade-node.ts +8 -1
- package/scripts/release-loop/preflight.ts +246 -0
- package/scripts/release-loop/publish-node.ts +10 -3
- package/scripts/release-loop/release-docs.ts +134 -0
- package/scripts/release-loop/release-readiness.ts +11 -6
- package/scripts/release-loop/release-version.ts +48 -0
- package/scripts/release-loop/resume-release.ts +115 -0
- package/scripts/release-loop/unattended-release.ts +43 -7
- package/scripts/release-loop/version-node.ts +149 -41
- package/scripts/release-story/draft.ts +42 -14
- package/scripts/rhythm/daily.ts +25 -4
- package/scripts/skills/public-check.ts +62 -12
- package/scripts/test-deterministic.ts +51 -33
- package/scripts/test-diet/effectiveness.ts +62 -1
- package/scripts/test-diet/lib.ts +1 -1
- package/scripts/test-diet/node.ts +8 -1
- package/src/agent-mission/driver.ts +53 -10
- package/src/agent-mission/review-loop.ts +4 -3
- package/src/agent-substrate/pr-comment-meta.ts +20 -0
- package/src/autopilot/build/llm-conflict-merge.ts +28 -8
- package/src/autopilot/track-agent.ts +5 -3
- package/src/briefing/brief-items.ts +86 -17
- package/src/cli/autopilot-cli.ts +86 -1
- package/src/cli/brief-cli.ts +36 -2
- package/src/cli/decisions-cli.ts +4 -1
- package/src/cli/freeze-cli.ts +3 -2
- package/src/cli/intake-cli.ts +74 -2
- package/src/cli/log-event-names.ts +1 -0
- package/src/cli/notify-cli.ts +75 -0
- package/src/cli/pr-cli.ts +12 -1
- package/src/cli/provider-cli.ts +24 -0
- package/src/cli/release-cli.ts +101 -17
- package/src/cli/self-update-canary.ts +46 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/tasks-cli.ts +309 -3
- package/src/context-bus/condense.ts +19 -3
- package/src/context-bus/context-day.ts +77 -24
- package/src/context-bus/context-first.ts +33 -0
- package/src/context-card/index.ts +45 -0
- package/src/context-card/task.ts +76 -0
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -0
- package/src/decisions/decision-ledger.ts +10 -1
- package/src/discord-self-message.ts +20 -2
- package/src/discord-slash-wire.ts +1 -0
- package/src/domains/outbound-alert.ts +200 -27
- package/src/domains/outbound-log-probe.ts +16 -1
- package/src/domains/release-tool.ts +2 -2
- package/src/execution-loop/gates.ts +11 -2
- package/src/flow-loop/split.ts +8 -4
- package/src/git-fs/worktree.ts +23 -3
- package/src/harness/harness-cli-command.ts +151 -11
- package/src/harness/harness-pod-dispatch.ts +11 -0
- package/src/harness/harness-queue.ts +466 -32
- package/src/harness/helper-scan.ts +10 -3
- package/src/harness/nested-elanous-policy.ts +23 -4
- package/src/hq/fence-wrapper.ts +123 -0
- package/src/hq/hq.ts +8 -0
- package/src/hq/seats.ts +369 -0
- package/src/index.ts +190 -23
- package/src/intake-plane/adopt-gate.ts +49 -0
- package/src/intake-plane/check.ts +3 -0
- package/src/intake-plane/collect-github.ts +57 -25
- package/src/intake-plane/digest.ts +39 -4
- package/src/intake-plane/discord-seat-work.ts +8 -2
- package/src/intake-plane/requirement-funnel.ts +316 -0
- package/src/knowledge/know-find.ts +10 -1
- package/src/loops/budget.ts +36 -0
- package/src/loops/orchestrator/finish-rate.ts +73 -1
- package/src/loops/orchestrator/grid-place.ts +11 -0
- package/src/loops/orchestrator/tick.ts +295 -20
- package/src/maturity/feature-maturity.ts +3 -2
- package/src/nexus/api/decisions-route.ts +11 -1
- package/src/nexus/api/grid.ts +61 -0
- package/src/nexus/api/http-server.ts +19 -1
- package/src/nexus/api/loop-edges.ts +69 -13
- package/src/nexus/api/ops-seats-sources.ts +2 -2
- package/src/nexus/api/task-cards-api.ts +19 -0
- package/src/nexus/api/workflow-templates.ts +3 -0
- package/src/oauth/codex.ts +120 -2
- package/src/onboarding/first-chat-gate.ts +29 -0
- package/src/release-loop/checklist.ts +104 -6
- package/src/release-loop/feature-store.ts +10 -9
- package/src/release-loop/landing-freeze.ts +24 -14
- package/src/release-loop/placement.ts +13 -3
- package/src/release-loop/rubric.ts +80 -0
- package/src/seat-dispatch/seat-ask.ts +79 -16
- package/src/seat-loop/seat-loop.ts +107 -37
- package/src/self-dev/ask-launch-flow.ts +50 -10
- package/src/self-dev/ask-path-candidates.ts +68 -0
- package/src/self-dev/dev-pipeline.ts +4 -0
- package/src/self-dev/draft-triage-rules.ts +1 -1
- package/src/self-dev/orchestrate-cli.ts +22 -0
- package/src/self-dev/round-pr-supersede.ts +244 -0
- package/src/self-dev/run-store.ts +5 -1
- package/src/self-dev/run-supervisor.ts +21 -1
- package/src/self-dev/supervisor-verdict-edges.ts +46 -0
- package/src/self-implement/follow-up-goals.ts +2 -2
- package/src/self-implement/frozen-merges.ts +25 -20
- package/src/self-implement/gate-remote.ts +427 -0
- package/src/self-implement/goal-author-clarification.ts +81 -0
- package/src/self-implement/goal-author-cli.ts +33 -5
- package/src/self-implement/goal-author.ts +93 -12
- package/src/self-implement/goal-clarification-escalation.ts +37 -1
- package/src/self-implement/goal-document-write.ts +28 -0
- package/src/self-implement/heal-intake.ts +4 -0
- package/src/self-implement/host-regate.ts +3 -1
- package/src/self-implement/l8-merge-queue.ts +29 -5
- package/src/self-implement/orchestrator.ts +78 -13
- package/src/self-implement/prior-draft-findings.ts +4 -3
- package/src/skills/skill-feedback.ts +76 -0
- package/src/task-agent/actions.ts +151 -0
- package/src/task-agent/judge.ts +122 -0
- package/src/task-agent/mission.ts +423 -0
- package/src/task-agent/shadow.ts +90 -0
- package/src/task-agent/task-hand.ts +235 -0
- package/src/task-cards/card-store.ts +4 -1
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +43 -4
- package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
- package/src/task-orchestrator/surfaces/pod-pool.ts +81 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +225 -35
- package/src/telegram-commands.ts +1 -1
- package/src/telegram-loops-command.ts +22 -15
- package/src/telegram.ts +5 -20
- package/src/user-config.ts +86 -4
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9247-29d889b809288e65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-8c2d9526164f5d09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-dce686e582c23937.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-1a61c7ca1ceb3ae7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-51f811fa119308b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-5b042736aa113756.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-57924393e3e4e539.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0f5cd57ca928f42b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-59f03fda76c03d22.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1ea64acadd704ace.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +0 -1
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{rv9cierV0jSOvDBm1g0XB → P6YTVgzuZz5oTZhY4Din_}/_ssgManifest.js +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync } from 'node:fs';
|
|
1
|
+
import { closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync } from 'node:fs';
|
|
2
2
|
import { dlopen, FFIType } from 'bun:ffi';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { listMemories, memoryRoot, saveMemory, type MemoryEntry } from '../memory.js';
|
|
5
5
|
import { DecisionLedger } from '../decisions/decision-ledger.js';
|
|
6
|
+
import { debug } from '../debug/log.js';
|
|
6
7
|
import { condenseContextWindowWithReport, readCondenseEvents, type CondenseFunnel, type CondenseSkipped, type MemoryDeps, type MemoryItem, type MemorySource, type MemorySummary } from './long-term-memory.js';
|
|
7
8
|
|
|
8
9
|
const summarize = async (source: MemorySource): Promise<MemorySummary> =>
|
|
@@ -51,6 +52,7 @@ export function renderCondenseMarkdown(report: Pick<CondenseReport, 'since' | 'u
|
|
|
51
52
|
|
|
52
53
|
type CondenseStore = {
|
|
53
54
|
list(): MemoryEntry[];
|
|
55
|
+
hasMemoryDirectory?(): boolean;
|
|
54
56
|
save(input: Parameters<typeof saveMemory>[0]): MemoryEntry;
|
|
55
57
|
raise(input: Parameters<DecisionLedger['raiseOnce']>[0], ref: string): string;
|
|
56
58
|
withLock<T>(action: () => T): T;
|
|
@@ -96,6 +98,7 @@ function defaultStore(root?: string): CondenseStore {
|
|
|
96
98
|
const ledger = new DecisionLedger(root ? { stateDir: root } : {});
|
|
97
99
|
return {
|
|
98
100
|
list: () => listMemories({}, memoryDir),
|
|
101
|
+
hasMemoryDirectory: () => existsSync(memoryDir),
|
|
99
102
|
save: input => saveMemory(input, memoryDir),
|
|
100
103
|
raise: (input, ref) => ledger.raiseOnce(input, ref).id,
|
|
101
104
|
withLock: action => withMemoryLock(memoryDir, action),
|
|
@@ -118,10 +121,22 @@ function newestActive(entries: readonly MemoryEntry[]): MemoryEntry | undefined
|
|
|
118
121
|
}
|
|
119
122
|
|
|
120
123
|
/** Apply candidate cards through the existing curated memory and decision ledger paths. */
|
|
121
|
-
export function applyCondensedCards(cards: readonly MemoryItem[], store: CondenseStore): NonNullable<CondenseReport['applied']> {
|
|
124
|
+
export function applyCondensedCards(cards: readonly MemoryItem[], store: CondenseStore, now: Date = new Date()): NonNullable<CondenseReport['applied']> {
|
|
122
125
|
const decisions: string[] = [];
|
|
123
126
|
let written = 0;
|
|
124
127
|
let retired = 0;
|
|
128
|
+
const cutoff = now.getTime() - 30 * 24 * 3_600_000;
|
|
129
|
+
const retireAged = () => {
|
|
130
|
+
let agedRetired = 0;
|
|
131
|
+
for (const old of store.list()) {
|
|
132
|
+
if (old.contextCard?.status !== 'active' || !(Date.parse(old.contextCard.updatedAt) < cutoff)) continue;
|
|
133
|
+
store.save({ type: old.type, name: old.name, description: old.description, body: old.body, id: old.id,
|
|
134
|
+
pinned: false, priority: old.priority, contextCard: { ...old.contextCard, status: 'retired' } });
|
|
135
|
+
agedRetired++;
|
|
136
|
+
}
|
|
137
|
+
retired += agedRetired;
|
|
138
|
+
debug.log('context.condense', 'aged-out', { retired: agedRetired });
|
|
139
|
+
};
|
|
125
140
|
for (const card of cards) {
|
|
126
141
|
if (card.conflict) {
|
|
127
142
|
const ref = `context-condense:${JSON.stringify([card.project, card.seat, card.topic, card.source, card.updatedAt])}`;
|
|
@@ -162,6 +177,7 @@ export function applyCondensedCards(cards: readonly MemoryItem[], store: Condens
|
|
|
162
177
|
};
|
|
163
178
|
store.withLock(applyTopic);
|
|
164
179
|
}
|
|
180
|
+
if (store.hasMemoryDirectory?.() !== false) store.withLock(retireAged);
|
|
165
181
|
return { written, retired, decisions };
|
|
166
182
|
}
|
|
167
183
|
|
|
@@ -184,6 +200,6 @@ export async function runContextCondense(options: {
|
|
|
184
200
|
const conflicts = cards.filter(card => card.conflict !== undefined).length;
|
|
185
201
|
const report: CondenseReport = { since, until, cards, conflicts, skipped, folded, droppedHarnessChild, funnel,
|
|
186
202
|
markdown: renderCondenseMarkdown({ since, until, cards, conflicts, skipped, droppedHarnessChild, funnel }) };
|
|
187
|
-
if (options.apply === true) report.applied = applyCondensedCards(cards, store ?? defaultStore(options.root));
|
|
203
|
+
if (options.apply === true) report.applied = applyCondensedCards(cards, store ?? defaultStore(options.root), now);
|
|
188
204
|
return report;
|
|
189
205
|
}
|
|
@@ -12,6 +12,16 @@ export interface ContextDayRow {
|
|
|
12
12
|
status: '들어옴' | '안 들어옴';
|
|
13
13
|
}
|
|
14
14
|
export interface ContextDayReport { since: string; until: string; rows: ContextDayRow[] }
|
|
15
|
+
export interface ContextDayTimelineEvent {
|
|
16
|
+
at: string;
|
|
17
|
+
source: ContextDaySource;
|
|
18
|
+
kind: ContextDayKind | 'other';
|
|
19
|
+
summary: string;
|
|
20
|
+
link: string | null;
|
|
21
|
+
}
|
|
22
|
+
export interface ContextDayTimelineReport { since: string; until: string; events: ContextDayTimelineEvent[] }
|
|
23
|
+
interface JournalEvent { surface: string; kind: string; ts: string; refs: string | null; summary: string | null }
|
|
24
|
+
type DayDeps = { now?: () => Date; dbPath?: string; seat?: ContextDaySource };
|
|
15
25
|
|
|
16
26
|
/** Reject malformed windows instead of silently changing the time range being judged. */
|
|
17
27
|
export function contextDayHours(since: string): number {
|
|
@@ -34,43 +44,86 @@ function kindFor(surface: string, kind: string): ContextDayKind | null {
|
|
|
34
44
|
return null;
|
|
35
45
|
}
|
|
36
46
|
|
|
47
|
+
function sourceFor(event: JournalEvent): { source: ContextDaySource; refs: { url?: unknown; source?: unknown } } | null {
|
|
48
|
+
let refs: { seat?: unknown; origin?: unknown; url?: unknown; source?: unknown };
|
|
49
|
+
try { refs = JSON.parse(event.refs ?? '{}') as typeof refs; } catch { return null; }
|
|
50
|
+
if (!refs || typeof refs !== 'object') return null;
|
|
51
|
+
const source = event.surface === 'context:external' ? refs.origin : refs.seat;
|
|
52
|
+
if (typeof source !== 'string' || !CONTEXT_DAY_SOURCES.some(item => item === source)) return null;
|
|
53
|
+
const isExternal = CONTEXT_DAY_SOURCES.indexOf(source as ContextDaySource) >= 4;
|
|
54
|
+
// Do not attribute an external hook to a seat (or a seat event to an external origin).
|
|
55
|
+
if ((event.surface === 'context:external') !== isExternal) return null;
|
|
56
|
+
return { source: source as ContextDaySource, refs };
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
/** Read only the context journal, not recallEvents (which mutates recall counts) or the writable DB opener. */
|
|
38
|
-
|
|
60
|
+
function readDay(since: string, deps: DayDeps): { since: string; until: string; events: JournalEvent[] } {
|
|
39
61
|
const hours = contextDayHours(since);
|
|
40
62
|
const now = (deps.now ?? (() => new Date()))();
|
|
41
63
|
const until = now.toISOString();
|
|
42
64
|
const from = new Date(now.getTime() - hours * 3_600_000).toISOString();
|
|
43
|
-
const rows: ContextDayRow[] = CONTEXT_DAY_SOURCES.map(source => ({
|
|
44
|
-
source, counts: { 'task-claimed': 0, done: 0, asked: 0, 'guidance-changed': 0 }, lastAt: null, status: '안 들어옴',
|
|
45
|
-
}));
|
|
46
|
-
const bySource = new Map(rows.map(row => [row.source, row]));
|
|
47
65
|
const path = deps.dbPath ?? surfaceEventsDbPath();
|
|
48
|
-
if (!existsSync(path)) return { since: from, until,
|
|
66
|
+
if (!existsSync(path)) return { since: from, until, events: [] };
|
|
49
67
|
const db = new Database(path, { readonly: true });
|
|
50
68
|
try {
|
|
51
|
-
const events = db.prepare(`SELECT surface, kind, ts, refs FROM events
|
|
69
|
+
const events = db.prepare(`SELECT surface, kind, ts, refs, summary FROM events
|
|
52
70
|
WHERE surface IN ('coord:channel', 'context:session', 'context:external')
|
|
53
|
-
AND direction='outbound' AND ts>=? AND ts<=? ORDER BY ts ASC`).all(from, until) as
|
|
54
|
-
|
|
55
|
-
}>;
|
|
56
|
-
for (const event of events) {
|
|
57
|
-
let refs: { seat?: unknown; origin?: unknown };
|
|
58
|
-
try { refs = JSON.parse(event.refs ?? '{}') as typeof refs; } catch { continue; }
|
|
59
|
-
const source = event.surface === 'context:external' ? refs.origin : refs.seat;
|
|
60
|
-
if (typeof source !== 'string') continue;
|
|
61
|
-
const row = bySource.get(source as ContextDaySource);
|
|
62
|
-
if (!row) continue;
|
|
63
|
-
// Do not attribute an external hook to a seat (or a seat event to an external origin).
|
|
64
|
-
if ((event.surface === 'context:external') !== (rows.indexOf(row) >= 4)) continue;
|
|
65
|
-
row.lastAt = event.ts;
|
|
66
|
-
row.status = '들어옴';
|
|
67
|
-
const kind = kindFor(event.surface, event.kind);
|
|
68
|
-
if (kind) row.counts[kind]++;
|
|
69
|
-
}
|
|
71
|
+
AND direction='outbound' AND ts>=? AND ts<=? ORDER BY ts ASC`).all(from, until) as JournalEvent[];
|
|
72
|
+
return { since: from, until, events };
|
|
70
73
|
} finally { db.close(); }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function contextDay(since = '24h', deps: { now?: () => Date; dbPath?: string } = {}): ContextDayReport {
|
|
77
|
+
const { since: from, until, events } = readDay(since, deps);
|
|
78
|
+
const rows: ContextDayRow[] = CONTEXT_DAY_SOURCES.map(source => ({
|
|
79
|
+
source, counts: { 'task-claimed': 0, done: 0, asked: 0, 'guidance-changed': 0 }, lastAt: null, status: '안 들어옴',
|
|
80
|
+
}));
|
|
81
|
+
const bySource = new Map(rows.map(row => [row.source, row]));
|
|
82
|
+
for (const event of events) {
|
|
83
|
+
const attribution = sourceFor(event);
|
|
84
|
+
if (!attribution) continue;
|
|
85
|
+
const row = bySource.get(attribution.source)!;
|
|
86
|
+
row.lastAt = event.ts;
|
|
87
|
+
row.status = '들어옴';
|
|
88
|
+
const kind = kindFor(event.surface, event.kind);
|
|
89
|
+
if (kind) row.counts[kind]++;
|
|
90
|
+
}
|
|
71
91
|
return { since: from, until, rows };
|
|
72
92
|
}
|
|
73
93
|
|
|
94
|
+
export function contextDayTimeline(since = '24h', deps: DayDeps = {}): ContextDayTimelineReport {
|
|
95
|
+
if (deps.seat !== undefined && !CONTEXT_DAY_SOURCES.some(source => source === deps.seat)) {
|
|
96
|
+
throw new Error(`unknown --seat: ${deps.seat}`);
|
|
97
|
+
}
|
|
98
|
+
const { since: from, until, events } = readDay(since, deps);
|
|
99
|
+
const timeline: ContextDayTimelineEvent[] = [];
|
|
100
|
+
for (const event of events) {
|
|
101
|
+
const attribution = sourceFor(event);
|
|
102
|
+
if (!attribution || (deps.seat && attribution.source !== deps.seat)) continue;
|
|
103
|
+
const { url, source } = attribution.refs;
|
|
104
|
+
const link = [url, source].find(value => typeof value === 'string' && /^(?:https?:\/\/|elanous:\/\/)[^\s]+$/.test(value));
|
|
105
|
+
timeline.push({ at: event.ts, source: attribution.source, kind: kindFor(event.surface, event.kind) ?? 'other',
|
|
106
|
+
summary: event.summary ?? '', link: typeof link === 'string' ? link : null });
|
|
107
|
+
}
|
|
108
|
+
return { since: from, until, events: timeline };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const kstFormatter = new Intl.DateTimeFormat('en-CA', {
|
|
112
|
+
timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hourCycle: 'h23',
|
|
113
|
+
});
|
|
114
|
+
function kst(at: string): string {
|
|
115
|
+
const parts = Object.fromEntries(kstFormatter.formatToParts(new Date(at)).map(part => [part.type, part.value]));
|
|
116
|
+
return `${parts.year}-${parts.month}-${parts.day} ${parts.hour}:${parts.minute} KST`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function renderContextDayTimeline(report: ContextDayTimelineReport, dbPath = surfaceEventsDbPath()): string {
|
|
120
|
+
const lines = [`맥락 사건 ${kst(report.since)} ~ ${kst(report.until)}`];
|
|
121
|
+
if (!report.events.length) lines.push(`이 창에 사건 0건 — 원장 경로: ${dbPath}`);
|
|
122
|
+
else lines.push(...report.events.map(event => `${kst(event.at).slice(11)} · ${event.source} · ${event.kind} · ${event.summary.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]+/g, ' ')} · ${event.link ?? '—'}`));
|
|
123
|
+
lines.push(`${report.events.length}건 · 출처 ${new Set(report.events.map(event => event.source)).size}곳`);
|
|
124
|
+
return lines.join('\n');
|
|
125
|
+
}
|
|
126
|
+
|
|
74
127
|
export function renderContextDay(report: ContextDayReport): string {
|
|
75
128
|
return [
|
|
76
129
|
`맥락 이벤트 ${report.since} ~ ${report.until} (UTC)`,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ContextNowAnswer, ContextNowDeps } from './context-now.js';
|
|
2
|
+
|
|
3
|
+
const CONTEXT_FIRST_GAP_MS = 6 * 60 * 60 * 1000;
|
|
4
|
+
const CONTEXT_UNREADABLE = '맥락 못 읽음';
|
|
5
|
+
|
|
6
|
+
/** Track every allowed utterance, consuming a pending summary only on a normal turn. */
|
|
7
|
+
export function createContextFirstGate({ now, gapMs = CONTEXT_FIRST_GAP_MS }: { now: () => number; gapMs?: number }) {
|
|
8
|
+
const lastUtteranceAt = new Map<string, number>();
|
|
9
|
+
const pending = new Set<string>();
|
|
10
|
+
return {
|
|
11
|
+
note(key: string): void {
|
|
12
|
+
const at = now();
|
|
13
|
+
const previous = lastUtteranceAt.get(key);
|
|
14
|
+
lastUtteranceAt.set(key, at);
|
|
15
|
+
if (previous === undefined || at - previous >= gapMs) pending.add(key);
|
|
16
|
+
},
|
|
17
|
+
take(key: string): boolean {
|
|
18
|
+
return pending.delete(key);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function renderContextFirst(
|
|
24
|
+
read: (options: {}, deps?: ContextNowDeps) => ContextNowAnswer,
|
|
25
|
+
deps: ContextNowDeps | undefined,
|
|
26
|
+
render: (answer: ContextNowAnswer) => string,
|
|
27
|
+
): string {
|
|
28
|
+
try {
|
|
29
|
+
return render(read({}, deps));
|
|
30
|
+
} catch {
|
|
31
|
+
return CONTEXT_UNREADABLE;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** A rebuildable view of the ledger and code, never an authoritative record. */
|
|
2
|
+
export interface ContextCard {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly kind: 'task' | 'goal' | 'run' | 'pr' | 'cell' | 'doc';
|
|
5
|
+
readonly conclusion: string;
|
|
6
|
+
readonly why: string;
|
|
7
|
+
readonly verdict: string;
|
|
8
|
+
readonly remaining: readonly string[];
|
|
9
|
+
readonly pointers: readonly string[];
|
|
10
|
+
readonly source: string;
|
|
11
|
+
readonly updatedAt: string;
|
|
12
|
+
readonly supersedes?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Serialized code points, including JSON escaping; callers may supply a smaller screen budget. */
|
|
16
|
+
export const CONTEXT_CARD_SCREEN_CHARS = 1_600;
|
|
17
|
+
const length = (text: string): number => [...text].length;
|
|
18
|
+
|
|
19
|
+
/** Preserve the decision and provenance; fold only the two expandable lists. Never mutate the card. */
|
|
20
|
+
export function serializeContextCard(card: ContextCard, maxChars = CONTEXT_CARD_SCREEN_CHARS): string {
|
|
21
|
+
if (!Number.isSafeInteger(maxChars) || maxChars < 1) throw new RangeError('maxChars must be a positive safe integer');
|
|
22
|
+
if (/[\r\n\u2028\u2029]/u.test(card.conclusion)) throw new Error('conclusion must be one line');
|
|
23
|
+
const remaining = [...card.remaining];
|
|
24
|
+
const pointers = [...card.pointers];
|
|
25
|
+
const render = () => JSON.stringify({
|
|
26
|
+
id: card.id, kind: card.kind, conclusion: card.conclusion, why: card.why,
|
|
27
|
+
verdict: card.verdict, remaining: remaining.length < card.remaining.length
|
|
28
|
+
? [...remaining, `외 ${card.remaining.length - remaining.length}개`] : remaining,
|
|
29
|
+
pointers: pointers.length < card.pointers.length
|
|
30
|
+
? [...pointers, `외 ${card.pointers.length - pointers.length}개`] : pointers,
|
|
31
|
+
source: card.source, updatedAt: card.updatedAt,
|
|
32
|
+
...(card.supersedes === undefined ? {} : { supersedes: card.supersedes }),
|
|
33
|
+
});
|
|
34
|
+
let text = render();
|
|
35
|
+
while (length(text) > maxChars && (remaining.length || pointers.length)) {
|
|
36
|
+
// Remove the larger last entry first, then break ties by preferring remaining.
|
|
37
|
+
const remainingCost = remaining.length ? length(JSON.stringify(remaining.at(-1))) : -1;
|
|
38
|
+
const pointerCost = pointers.length ? length(JSON.stringify(pointers.at(-1))) : -1;
|
|
39
|
+
if (remainingCost >= pointerCost) remaining.pop();
|
|
40
|
+
else pointers.pop();
|
|
41
|
+
text = render();
|
|
42
|
+
}
|
|
43
|
+
if (length(text) > maxChars) throw new RangeError('card core and omission counts exceed maxChars');
|
|
44
|
+
return text;
|
|
45
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { RunLedgerEntry } from '../self-implement/run-ledger.js';
|
|
2
|
+
import type { ContextCard } from './index.js';
|
|
3
|
+
|
|
4
|
+
/** An injected, read-only view of the JSONL run ledger. No writer or store mutation is available. */
|
|
5
|
+
export interface TaskLedgerReader {
|
|
6
|
+
readRuns(): readonly { readonly runId: string; readonly entries: readonly RunLedgerEntry[] }[];
|
|
7
|
+
/** Returns the verbatim original ask from the goal document named by start.goalFile. */
|
|
8
|
+
readOriginalAsk?(goalFile: string): string | null;
|
|
9
|
+
/** Returns must-fix text from the review artifact named by reviewed.artifactPath. */
|
|
10
|
+
readReviewMustFix?(artifactPath: string): readonly string[] | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TaskContextCard extends ContextCard {
|
|
14
|
+
readonly kind: 'task';
|
|
15
|
+
readonly originalAsk: string;
|
|
16
|
+
readonly authoredGoal: { readonly path: string; readonly sha256: string } | null;
|
|
17
|
+
readonly runIds: readonly string[];
|
|
18
|
+
readonly mustFix: readonly string[];
|
|
19
|
+
readonly decisionCards: readonly string[];
|
|
20
|
+
readonly nextAction: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const text = (value: unknown): string | null => typeof value === 'string' && value.trim() ? value : null;
|
|
24
|
+
const strings = (value: unknown): string[] | null => Array.isArray(value) && value.every((item) => typeof item === 'string') ? [...value] : null;
|
|
25
|
+
|
|
26
|
+
/** Produce a task card from source events, not from the previous card or wall-clock time. */
|
|
27
|
+
export function buildTaskCard(taskId: string, reader: TaskLedgerReader): TaskContextCard {
|
|
28
|
+
const runs = reader.readRuns().filter((run) => run.entries.some((entry) => entry.event === 'start' && entry.goalId === taskId))
|
|
29
|
+
.map((run) => ({ runId: run.runId, entries: run.entries.filter((entry) => entry.runId === run.runId && (entry.goalId === undefined || entry.goalId === taskId)) }))
|
|
30
|
+
.sort((a, b) => a.runId < b.runId ? -1 : a.runId > b.runId ? 1 : 0);
|
|
31
|
+
if (!runs.length) throw new Error(`no run-ledger start for task: ${taskId}`);
|
|
32
|
+
const events = runs.flatMap(({ entries }) => entries).sort((a, b) =>
|
|
33
|
+
(a.timestamp ?? '').localeCompare(b.timestamp ?? '') || a.runId.localeCompare(b.runId));
|
|
34
|
+
const start = events.find((event) => event.event === 'start')!;
|
|
35
|
+
const goalFile = text(start.data.goalFile);
|
|
36
|
+
const originalAsk = text(start.data.originalAsk)
|
|
37
|
+
?? (goalFile ? reader.readOriginalAsk?.(goalFile) ?? null : null);
|
|
38
|
+
if (!originalAsk) throw new Error(`original ask unavailable for task: ${taskId}`);
|
|
39
|
+
|
|
40
|
+
const authored = [...events].reverse().find((event) => event.event === 'goal-authored'
|
|
41
|
+
&& text(event.data.path) !== null && /^[a-f0-9]{64}$/i.test(text(event.data.sha256) ?? ''));
|
|
42
|
+
const authoredGoal = authored ? { path: text(authored.data.path)!, sha256: text(authored.data.sha256)! } : null;
|
|
43
|
+
const reviews = events.filter((event) => event.event === 'reviewed' && event.data.reviewed === true);
|
|
44
|
+
const review = reviews.at(-1);
|
|
45
|
+
const artifactPath = review ? text(review.data.artifactPath) : null;
|
|
46
|
+
const mustFix = review
|
|
47
|
+
? strings(review.data.mustFix) ?? (artifactPath ? reader.readReviewMustFix?.(artifactPath) ?? null : null)
|
|
48
|
+
: [];
|
|
49
|
+
if (mustFix === null && review?.data.mustFix !== 0) throw new Error(`review must-fix unavailable for task: ${taskId}`);
|
|
50
|
+
const findings = mustFix ?? [];
|
|
51
|
+
const decisionCards = events.filter((event) => event.event === 'decision-card')
|
|
52
|
+
.flatMap((event) => {
|
|
53
|
+
const cardId = text(event.data.cardId);
|
|
54
|
+
return cardId ? [cardId] : [];
|
|
55
|
+
});
|
|
56
|
+
const nextAction = [...events].reverse().map((event) => text(event.data.nextAction)).find((value): value is string => value !== null)
|
|
57
|
+
?? findings[0] ?? null;
|
|
58
|
+
if (!nextAction) throw new Error(`next action unavailable for task: ${taskId}`);
|
|
59
|
+
const updatedAt = events.at(-1)?.timestamp;
|
|
60
|
+
if (!updatedAt) throw new Error(`timestamp unavailable for task: ${taskId}`);
|
|
61
|
+
const pointers = [
|
|
62
|
+
...(goalFile ? [goalFile] : []),
|
|
63
|
+
...(authoredGoal ? [`${authoredGoal.path}#sha256=${authoredGoal.sha256}`] : []),
|
|
64
|
+
...runs.map(({ runId }) => `run:${runId}`),
|
|
65
|
+
...(artifactPath ? [artifactPath] : []),
|
|
66
|
+
...decisionCards,
|
|
67
|
+
];
|
|
68
|
+
return {
|
|
69
|
+
id: `task:${taskId}`, kind: 'task', conclusion: nextAction,
|
|
70
|
+
why: findings.length ? `최근 리뷰 must-fix ${findings.length}개` : '원문과 런 원장에 따른 다음 행동',
|
|
71
|
+
verdict: text(review?.data.verdict) ?? '리뷰 판정 없음',
|
|
72
|
+
remaining: [...findings], pointers,
|
|
73
|
+
source: `run-ledger:${runs.map(({ runId }) => runId).join(',')}`, updatedAt,
|
|
74
|
+
originalAsk, authoredGoal, runIds: runs.map(({ runId }) => runId), mustFix: [...findings], decisionCards, nextAction,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -2442,6 +2442,8 @@ export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps): SlashComm
|
|
|
2442
2442
|
cwd: () => process.cwd(),
|
|
2443
2443
|
now: () => Date.now(),
|
|
2444
2444
|
isInteractive: () => ctx.surfaceUx !== undefined,
|
|
2445
|
+
selfResolveClarification: (await import('../../self-implement/goal-author-clarification.js')).defaultGoalAuthorSelfResolve,
|
|
2446
|
+
writeGoalDocument: (await import('../../self-implement/goal-document-write.js')).writeGoalDocumentAtomic,
|
|
2445
2447
|
buildPreflightDeps: askIo.buildAskPreflightDeps,
|
|
2446
2448
|
priorBlockSamples: () => askIo.priorBlockSamplesFrom(rows),
|
|
2447
2449
|
recentAuthoringSamples: () => askIo.recentAuthoringSamplesFrom(rows),
|
|
@@ -93,7 +93,16 @@ function validate(input: RaiseInput, historical = false): RaiseInput {
|
|
|
93
93
|
const c = scqaField(input.scqa.c, 'c');
|
|
94
94
|
const q = input.scqa.q?.trim() ? scqaField(input.scqa.q, 'q') : undefined;
|
|
95
95
|
const a = input.scqa.a?.trim() ? scqaField(input.scqa.a, 'a') : undefined;
|
|
96
|
-
if (q && q === c) throw new Error('SCQA Q repeats C — leave Q empty');
|
|
96
|
+
if (q && q === c && input.raisedBy.agent !== 'seat-loop') throw new Error('SCQA Q repeats C — leave Q empty');
|
|
97
|
+
if (input.raisedBy.agent === 'seat-loop') {
|
|
98
|
+
if (!q || !a || !input.pendingQuestion?.trim()) throw new Error('seat-loop cards require --q, --a and --pending-question');
|
|
99
|
+
if ('skipped' in input.recommendation && !(['money', 'security', 'secret'].includes(input.category)
|
|
100
|
+
|| (['publish', 'irreversible'].includes(input.category)
|
|
101
|
+
&& /공개|게시|발행|출시|publish|release/i.test(`${title} ${input.pendingQuestion} ${input.recommendation.reason}`)
|
|
102
|
+
&& /되돌릴 수 없|철회 불가|복구 불가|irreversib|cannot (?:undo|retract)/i.test(`${input.pendingQuestion} ${input.recommendation.reason}`)))) {
|
|
103
|
+
throw new Error('seat-loop recommendation may only be skipped for money, security or irreversible publication');
|
|
104
|
+
}
|
|
105
|
+
}
|
|
97
106
|
if (!Array.isArray(input.options) || (input.options.length < 2 && !(historical && input.options.length === 0))) throw new Error('at least two options required');
|
|
98
107
|
const options = input.options.map(o => ({ key: single(o.key, 'option key'), label: safe(single(o.label, 'option label')), consequence: safe(single(o.consequence, 'option consequence')) }));
|
|
99
108
|
if (options.some(o => !/^[a-z]$/.test(o.key)) || new Set(options.map(o => o.key)).size !== options.length) throw new Error('option keys must be unique lowercase letters');
|
|
@@ -65,7 +65,9 @@ import { detectCard, runCardFollowup, NotACardError } from './card-followup/core
|
|
|
65
65
|
import { localOcrText } from './telegram-card-followup.js';
|
|
66
66
|
import type { runGraph } from './graph-runner/runner.js';
|
|
67
67
|
import { readSlashContextNow, renderTelegramNow } from './context-bus/context-now-surfaces.js';
|
|
68
|
-
import type
|
|
68
|
+
import { contextNow, type ContextNowDeps } from './context-bus/context-now.js';
|
|
69
|
+
import { createContextFirstGate, renderContextFirst } from './context-bus/context-first.js';
|
|
70
|
+
import { telegramLoopsStatus, type LoopsSources } from './telegram-loops-command.js';
|
|
69
71
|
import { botUnavailableSlashReply, deriveTuiSlashAvailability, type TuiSlashAvailability } from './maturity/tui-slash-availability.js';
|
|
70
72
|
|
|
71
73
|
const SLASH_FOCUS_TURNS_DEFAULT = 8;
|
|
@@ -143,6 +145,9 @@ export interface DiscordSelfMessageDeps {
|
|
|
143
145
|
log?: (msg: string) => void;
|
|
144
146
|
cardFollowupDeps?: { ocrText?: (path: string) => Promise<string | null>; runGraph?: typeof runGraph; rootDir?: () => string };
|
|
145
147
|
nowDeps?: ContextNowDeps;
|
|
148
|
+
/** Override the clock for the context-first gate in isolated message-path tests. */
|
|
149
|
+
contextFirstNow?: () => number;
|
|
150
|
+
loopsSources?: LoopsSources;
|
|
146
151
|
/** Override the TUI slash projection for isolated message-path tests. */
|
|
147
152
|
tuiSlashAvailability?: readonly TuiSlashAvailability[];
|
|
148
153
|
}
|
|
@@ -151,6 +156,15 @@ export interface DiscordSelfMessageDeps {
|
|
|
151
156
|
export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessageHandler {
|
|
152
157
|
const log = deps.log ?? ((): void => {});
|
|
153
158
|
const cfg = deps.userConfig;
|
|
159
|
+
const contextFirstGate = createContextFirstGate({ now: deps.contextFirstNow ?? Date.now });
|
|
160
|
+
const maybeSendContextFirst = async (channelId: string): Promise<void> => {
|
|
161
|
+
if (!contextFirstGate.take(channelId) || cfg.discord?.contextFirst === false) return;
|
|
162
|
+
try {
|
|
163
|
+
await deps.getBot()?.sendMessage(channelId, renderContextFirst(contextNow, deps.nowDeps, renderTelegramNow));
|
|
164
|
+
} catch (err) {
|
|
165
|
+
log(`discord context-first failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
154
168
|
// C2+ (2026-07-12 dogfood): 디스코드 첨부는 메시지 단위라 "사진 먼저,
|
|
155
169
|
// !cdx 명령은 다음 메시지" 패턴에서 위임 턴이 빈손이 된다. 채널별로
|
|
156
170
|
// 마지막 첨부를 기억해 두고, 첨부 없는 위임 명령이 2분 내 같은
|
|
@@ -359,6 +373,7 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
|
|
|
359
373
|
|
|
360
374
|
return async (ctx, streamer) => {
|
|
361
375
|
if (deps.channelScope && ctx.channelId !== deps.channelScope) return undefined;
|
|
376
|
+
contextFirstGate.note(ctx.channelId);
|
|
362
377
|
// Voice text commands pass the guild gate for the voice adapter's
|
|
363
378
|
// sake — never route them into the LLM turn (legacy behavior).
|
|
364
379
|
if (/^\/voice-(join|leave|status)\b/.test(ctx.text.trim())) return undefined;
|
|
@@ -367,6 +382,8 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
|
|
|
367
382
|
const args = nowCommand[1]?.trim().split(/\s+/).filter(Boolean) ?? [];
|
|
368
383
|
return renderTelegramNow(readSlashContextNow(args, deps.nowDeps));
|
|
369
384
|
}
|
|
385
|
+
if (/^[/!]loops$/.test(ctx.text.trim())) return telegramLoopsStatus([], deps.loopsSources);
|
|
386
|
+
if (/^[/!]loops\s+\S/.test(ctx.text.trim())) return telegramLoopsStatus(['extra'], deps.loopsSources);
|
|
370
387
|
if (!ctx.text.trim() && ctx.attachments.length === 1 && ctx.attachments[0]!.contentType?.startsWith('image/')) {
|
|
371
388
|
const photo = ctx.attachments[0]!;
|
|
372
389
|
if (detectCard({ width: photo.width, height: photo.height, ocrText: null }).decision !== 'skip') {
|
|
@@ -408,6 +425,8 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
|
|
|
408
425
|
const unavailable = botUnavailableSlashReply(entry, 'discord');
|
|
409
426
|
if (unavailable) return unavailable;
|
|
410
427
|
}
|
|
428
|
+
const cmd = parseDiscordAcpCommand(ctx.text);
|
|
429
|
+
if (!cmd && !/^[!/]/.test(ctx.text.trim())) await maybeSendContextFirst(ctx.channelId);
|
|
411
430
|
log(`◀ ${ctx.userName ?? ctx.userId}: ${ctx.text.slice(0, 80)}${ctx.attachments.length ? ` (+첨부 ${ctx.attachments.length})` : ''}`);
|
|
412
431
|
debug.log('discord.self.turn', 'inbound', { channelId: ctx.channelId, chars: ctx.text.length, attachments: ctx.attachments.length });
|
|
413
432
|
// C2+ — 첨부 기억(사진만 먼저 올리는 패턴 지원). 명령 여부와 무관.
|
|
@@ -416,7 +435,6 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
|
|
|
416
435
|
const delegationKey = delegationChatKey('dc', ctx.channelId);
|
|
417
436
|
|
|
418
437
|
// 1) Explicit text commands — /cc·/cdx·/gem delegate, /brain exits.
|
|
419
|
-
const cmd = parseDiscordAcpCommand(ctx.text);
|
|
420
438
|
if (cmd?.kind === 'brain') {
|
|
421
439
|
clearActiveDelegation(delegationKey);
|
|
422
440
|
return '🧠 브레인 모드로 복귀했습니다 (active delegation 해제).';
|
|
@@ -48,6 +48,7 @@ export const ELANOUS_SLASH_COMMANDS: readonly SlashCommandSchema[] = [
|
|
|
48
48
|
{ name: 'now', description: '지금 판·칸·결정·자리·최근 맥락 보기', options: [
|
|
49
49
|
{ name: 'topic', description: '좁혀 볼 주제 (선택)', type: 3, required: false },
|
|
50
50
|
] },
|
|
51
|
+
{ name: 'loops', description: '루프·크론 현황 (늦음·실패 먼저)' },
|
|
51
52
|
{ name: 'sessions', description: '바인딩된 세션 목록' },
|
|
52
53
|
{ name: 'new', description: '새 세션으로 시작 (이전 대화 보존)' },
|
|
53
54
|
{ name: 'attach', description: '이 채널을 다른 세션에 연결', options: [
|