elanous 0.2.14 → 0.2.16
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/{1307-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
- package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
- package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
- package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
- package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +15 -15
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +15 -15
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +15 -15
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +15 -15
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +15 -15
- package/apps/pwa/out/ceo/index.html +1 -0
- package/apps/pwa/out/ceo/index.txt +33 -0
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +15 -15
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +15 -15
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +15 -15
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +15 -15
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +15 -15
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +15 -15
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +15 -15
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +15 -15
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +15 -15
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +15 -15
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +15 -15
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +15 -15
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +15 -15
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +15 -15
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +15 -15
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +15 -15
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +15 -15
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +15 -15
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +15 -15
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +15 -15
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +16 -16
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +15 -15
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +15 -15
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +15 -15
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +15 -15
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +15 -15
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +15 -15
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +15 -15
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +15 -15
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +15 -15
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +15 -15
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +15 -15
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +15 -15
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +15 -15
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +15 -15
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +15 -15
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +15 -15
- package/graphs/doc-gen/doc-gen.yaml +24 -0
- package/graphs/doc-gen/recipes.yaml +3 -0
- package/graphs/heal/heal-loop.yaml +1 -1
- package/graphs/implement-loop.yaml +1 -1
- package/graphs/orchestrator/orchestrator.yaml +7 -0
- package/graphs/orchestrator/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +3 -0
- package/graphs/release/release-loop.yaml +4 -2
- package/graphs/steward/recipes.yaml +2 -1
- package/package.json +1 -1
- package/scripts/brand/check.ts +7 -5
- package/scripts/ci-public-export-import-gate.ts +154 -0
- package/scripts/ci-test-interference-gate.ts +5 -1
- package/scripts/ci-typecheck-changed.ts +82 -31
- package/scripts/docs/doc-rot.ts +164 -0
- package/scripts/docs/generate-graph-anatomy.ts +437 -0
- package/scripts/docs-cli-check.ts +4 -4
- package/scripts/expose-rubric-suggest.ts +18 -12
- package/scripts/goals/goal-census.ts +343 -0
- package/scripts/lecture-note/node.ts +31 -11
- package/scripts/lessons/handbook-page.ts +30 -3
- package/scripts/marketing/generate-ax-brief.ts +140 -0
- package/scripts/marketing/generate-feature-map.ts +118 -0
- package/scripts/measure/author-section-contribution.ts +84 -0
- package/scripts/release-loop/checklist-gate-node.ts +53 -14
- package/scripts/release-loop/cut-branch.ts +43 -13
- package/scripts/release-loop/gate-node.ts +82 -14
- package/scripts/release-loop/vault-note-node.ts +257 -8
- package/scripts/release-story/draft.ts +57 -9
- package/scripts/rhythm/daily.ts +263 -76
- package/scripts/rhythm/monthly.ts +97 -0
- package/scripts/rhythm/weekly.ts +30 -9
- package/scripts/smart-merge/resync-shadow.ts +147 -0
- package/src/acp/client.ts +17 -2
- package/src/agent-env/profile.ts +161 -0
- package/src/agent-mission/driver.ts +51 -1
- package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
- package/src/agent-mission/supervisor-metrics.ts +218 -0
- package/src/agent-substrate/pr-reviewer.ts +2 -0
- package/src/ask-user-question/pending-questions.ts +5 -0
- package/src/auth/github-app-token.ts +112 -13
- package/src/autopilot/build/llm-conflict-merge.ts +32 -6
- package/src/autopilot/track-agent-shadow.ts +2 -1
- package/src/briefing/brief-items.ts +276 -0
- package/src/budget/usage-outcomes.ts +204 -0
- package/src/claims/claims-render.ts +14 -5
- package/src/cli/agent-env-cli.ts +21 -0
- package/src/cli/brief-cli.ts +73 -0
- package/src/cli/decisions-cli.ts +9 -0
- package/src/cli/doctor-cli.ts +23 -1
- package/src/cli/doctor-node-modules.ts +147 -0
- package/src/cli/intake-cli.ts +46 -17
- package/src/cli/know-cli.ts +20 -0
- package/src/cli/logs-cli.ts +43 -1
- package/src/cli/pod-cli.ts +2 -0
- package/src/cli/pr-cli.ts +80 -12
- package/src/cli/release-cli.ts +66 -29
- package/src/cli/seat-requests-cli.ts +52 -0
- package/src/cli/self-update.ts +200 -1
- package/src/cli/usage-cli.ts +26 -0
- package/src/context-bus/context-now-public.ts +16 -6
- package/src/context-bus/context-now.ts +3 -1
- package/src/context-bus/coord-events.ts +6 -4
- package/src/decisions/decision-cards.ts +33 -7
- package/src/decisions/proact-meter.ts +286 -0
- package/src/decisions/proact1-lite.ts +86 -0
- package/src/decisions/web-push-decision-cards.ts +1 -1
- package/src/discord-self-message.ts +9 -0
- package/src/domains/alert-audit.ts +271 -0
- package/src/domains/core-tools.ts +4 -3
- package/src/domains/ops-facts-tool.ts +31 -1
- package/src/flow-loop/split.ts +24 -0
- package/src/harness/harness-cli-command.ts +147 -17
- package/src/harness/harness-incidents.ts +2 -0
- package/src/harness/harness-pod-dispatch.ts +67 -8
- package/src/harness/harness-queue.ts +380 -50
- package/src/harness/harness-seams.ts +13 -3
- package/src/harness/helper-repair.ts +218 -0
- package/src/harness/helper-scan.ts +27 -4
- package/src/harness/launch-stamp.ts +236 -0
- package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
- package/src/harness/ledger-write-guard.ts +35 -0
- package/src/harness/nested-elanous-policy.ts +114 -0
- package/src/harness/pod-memory-policy.ts +15 -0
- package/src/harness/repository-name.ts +55 -0
- package/src/hq/fence-audit.ts +90 -0
- package/src/hq/hq.ts +70 -6
- package/src/hq/lease.ts +9 -7
- package/src/hq/move-plan.ts +126 -0
- package/src/hq/promote.ts +31 -0
- package/src/hq/transfer.ts +185 -0
- package/src/index.ts +189 -13
- package/src/intake-plane/check.ts +63 -0
- package/src/intake-plane/digest.ts +65 -21
- package/src/intake-plane/front-classifier.ts +6 -0
- package/src/intake-plane/items.ts +3 -2
- package/src/intake-plane/lens.ts +177 -0
- package/src/intake-plane/route.ts +145 -1
- package/src/knowledge/know-find.ts +105 -0
- package/src/llm.ts +75 -19
- package/src/loops/budget.ts +36 -5
- package/src/loops/checker.ts +2 -2
- package/src/loops/orchestrator/author-depth.ts +65 -9
- package/src/loops/orchestrator/author-input-check.ts +8 -3
- package/src/loops/orchestrator/author-ledger.ts +3 -1
- package/src/loops/orchestrator/finish-rate.ts +14 -3
- package/src/loops/orchestrator/seat-share.ts +56 -0
- package/src/loops/orchestrator/tick.ts +107 -29
- package/src/loops/orchestrator/traffic.ts +117 -11
- package/src/loops/registry.ts +142 -61
- package/src/maturity/bot-slash-catalog.ts +11 -2
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/maturity/tui-slash-availability.ts +40 -0
- package/src/mss/logging/log-store.ts +35 -4
- package/src/nexus/api/http-server.ts +28 -14
- package/src/nexus/api/loop-edges.ts +173 -0
- package/src/nexus/api/schedules-read.ts +13 -0
- package/src/nexus/api/seat-requests.ts +79 -56
- package/src/oauth/overload-failover-launch.ts +75 -0
- package/src/oauth/overload-failover.ts +137 -0
- package/src/prompt-enhance/enhance.ts +6 -1
- package/src/release-loop/auto-start.ts +6 -3
- package/src/release-loop/checklist.ts +19 -5
- package/src/release-loop/feature-store.ts +59 -31
- package/src/release-loop/merged-pr-checklist.ts +125 -0
- package/src/release-loop/placement.ts +98 -29
- package/src/seat-dispatch/seat-ask.ts +33 -6
- package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
- package/src/seat-dispatch/seat-request-ledger.ts +133 -0
- package/src/seat-loop/seat-loop.ts +215 -12
- package/src/self-dev/ask-launch-flow.ts +22 -1
- package/src/self-dev/authored-goal-trace.ts +64 -0
- package/src/self-dev/dev-cli.ts +19 -1
- package/src/self-dev/dev-pipeline.ts +14 -9
- package/src/self-dev/draft-sweep.ts +90 -10
- package/src/self-dev/draft-triage-rules.ts +25 -8
- package/src/self-dev/entrance-registry.ts +10 -6
- package/src/self-dev/old-door.ts +73 -0
- package/src/self-dev/orchestrate.ts +15 -1
- package/src/self-dev/review-provider-fallback.ts +6 -1
- package/src/self-dev/run-supervisor.ts +49 -2
- package/src/self-implement/app-api-budget.ts +166 -0
- package/src/self-implement/budget-gate.ts +7 -5
- package/src/self-implement/follow-up-goals.ts +289 -0
- package/src/self-implement/frozen-merges.ts +3 -2
- package/src/self-implement/goal-author.ts +50 -6
- package/src/self-implement/goal-clarification-escalation.ts +5 -0
- package/src/self-implement/heal-intake.ts +120 -2
- package/src/self-implement/heal-triage.ts +44 -0
- package/src/self-implement/host-regate.ts +9 -2
- package/src/self-implement/l8-merge-queue.ts +132 -0
- package/src/self-implement/orchestrator.ts +325 -47
- package/src/self-implement/pipeline-shape.ts +1 -1
- package/src/self-implement/revert-guard.ts +42 -7
- package/src/self-implement/run-ledger.ts +6 -0
- package/src/self-implement/run-status-mapping.ts +2 -0
- package/src/self-implement/seams.ts +45 -16
- package/src/self-implement/self-implement-cli.ts +1 -0
- package/src/self-implement/speed-claims.ts +338 -0
- package/src/session-runtime/index.ts +2 -0
- package/src/session-runtime/retry-api.ts +87 -0
- package/src/session-runtime/retry-policy.ts +107 -6
- package/src/steward/triage.ts +74 -3
- package/src/steward/working-backwards.ts +20 -2
- package/src/task-cards/card-cli.ts +25 -0
- package/src/task-cards/card-store.ts +5 -3
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
- package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
- package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
- package/src/task-orchestrator/surfaces/pod-pool.ts +70 -9
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
- package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
- package/src/telegram-commands.ts +5 -0
- package/src/telegram.ts +72 -4
- package/src/user-config.ts +64 -7
- package/src/version/packed-revision.json +1 -1
- package/src/workflow-runtime/nodes/bash.ts +9 -1
- package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
- package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
- package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
|
@@ -2,21 +2,30 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { checkBrand } from '../../scripts/brand/check.js';
|
|
5
|
+
import { debug } from '../debug/log.js';
|
|
5
6
|
import type { ContextFact, ContextNowAnswer } from './context-now.js';
|
|
6
7
|
|
|
7
8
|
const internalItem = (count: number) => `내부 항목 ${count}개`;
|
|
8
9
|
const seatNames: Record<string, string> = { OP: '운영', TC: '기술', MK: '마케팅', UX: '사용자 경험' };
|
|
9
10
|
|
|
10
11
|
/** Public copy only: no raw ledger identifiers or private sources in the rendered answer. */
|
|
11
|
-
export function filterPublicDemoContext(answer: ContextNowAnswer): ContextNowAnswer {
|
|
12
|
+
export function filterPublicDemoContext(answer: ContextNowAnswer, brandCheck: typeof checkBrand = checkBrand): ContextNowAnswer {
|
|
12
13
|
const dir = mkdtempSync(join(tmpdir(), 'context-now-public-'));
|
|
13
14
|
const file = join(dir, 'text.txt');
|
|
14
15
|
try {
|
|
16
|
+
let rulesMissing = brandCheck('public-docs', []).missing;
|
|
17
|
+
const logMissing = () => debug.log('context.now', 'brand-rules-missing', { audience: 'public-demo' });
|
|
18
|
+
if (rulesMissing) logMissing();
|
|
15
19
|
const flagged = (texts: string[]): Set<number> => {
|
|
20
|
+
if (rulesMissing) return new Set(texts.map((_, index) => index));
|
|
16
21
|
if (!texts.length) return new Set();
|
|
17
22
|
writeFileSync(file, texts.map(text => text.replace(/[\r\n]+/g, ' ')).join('\n') + '\n');
|
|
18
|
-
const result =
|
|
19
|
-
if (result.missing)
|
|
23
|
+
const result = brandCheck('public-docs', [file]);
|
|
24
|
+
if (result.missing) {
|
|
25
|
+
rulesMissing = true;
|
|
26
|
+
logMissing();
|
|
27
|
+
return new Set(texts.map((_, index) => index));
|
|
28
|
+
}
|
|
20
29
|
return new Set(result.findings.map(finding => finding.line - 1));
|
|
21
30
|
};
|
|
22
31
|
const publicText = (value: string): string => value
|
|
@@ -60,8 +69,9 @@ export function filterPublicDemoContext(answer: ContextNowAnswer): ContextNowAns
|
|
|
60
69
|
status: safe(fact.status, '진행 중'), source: source(fact.source) });
|
|
61
70
|
} else if (fact.kind === 'seat') {
|
|
62
71
|
facts.push({ ...fact, seat: safe(fact.seat, '공개 담당'), status: safe(fact.status, '진행 중'),
|
|
63
|
-
id: fact.id ? (privateIds.has(fact.id) ? '내부 항목' : safe(fact.id, '공개 항목')) : null,
|
|
64
|
-
title: fact.title ? (privateIds.has(fact.id ?? '') ? '내부 항목' : safe(fact.title, '공개 항목')
|
|
72
|
+
id: fact.id ? (rulesMissing || privateIds.has(fact.id) ? '내부 항목' : safe(fact.id, '공개 항목')) : null,
|
|
73
|
+
title: fact.title === null ? null : rulesMissing ? internalItem(1) : privateIds.has(fact.id ?? '') ? '내부 항목' : safe(fact.title, '공개 항목'),
|
|
74
|
+
source: source(fact.source) });
|
|
65
75
|
} else facts.push({ ...fact, version: safe(fact.version, '공개 판'), source: source(fact.source) });
|
|
66
76
|
}
|
|
67
77
|
for (const kind of ['cell', 'decision'] as const) {
|
|
@@ -78,7 +88,7 @@ export function filterPublicDemoContext(answer: ContextNowAnswer): ContextNowAns
|
|
|
78
88
|
events: answer.events.map(event => ({ ...event, kind: safe(event.kind, '소식'),
|
|
79
89
|
summary: publicField(event.summary, '공개 소식'), source: source(event.source) })),
|
|
80
90
|
guide: answer.guide.map(text => publicField(text, '공개 안내')),
|
|
81
|
-
hiddenCount: collapsed.cell + collapsed.decision,
|
|
91
|
+
hiddenCount: collapsed.cell + collapsed.decision + (rulesMissing ? answer.facts.filter(fact => fact.kind === 'seat' && fact.title !== null).length : 0),
|
|
82
92
|
};
|
|
83
93
|
} finally {
|
|
84
94
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { checkBrand } from '../../scripts/brand/check.js';
|
|
2
3
|
import { debug, redactSecretText } from '../debug/log.js';
|
|
3
4
|
import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
|
|
4
5
|
import { listChecklist, devVersion, type Checklist } from '../release-loop/checklist.js';
|
|
@@ -22,6 +23,7 @@ export interface ContextNowDeps {
|
|
|
22
23
|
decisions?: () => DecisionEntry[];
|
|
23
24
|
seatEntries?: (now: Date) => Array<{ entry: SeatEntry; source: string }>;
|
|
24
25
|
events?: (since: string) => CoordEvent[];
|
|
26
|
+
brandCheck?: typeof checkBrand;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
function todaySeatEntries(now: Date): Array<{ entry: SeatEntry; source: string }> {
|
|
@@ -101,5 +103,5 @@ export function contextNow(options: { topic?: string; limit?: number; audience?:
|
|
|
101
103
|
.map(event => `📌 ${event.summary} — ${event.source}`);
|
|
102
104
|
const answer = { at, topic, facts: boundedFacts, events: selectedEvents, guide };
|
|
103
105
|
debug.log('context.now', 'answer', { topic, facts: answer.facts, events: answer.events });
|
|
104
|
-
return options.audience === 'public-demo' ? filterPublicDemoContext(answer) : answer;
|
|
106
|
+
return options.audience === 'public-demo' ? filterPublicDemoContext(answer, deps.brandCheck) : answer;
|
|
105
107
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
2
|
import { readFileSync, existsSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { debug, redactSecretText } from '../debug/log.js';
|
|
@@ -99,12 +99,14 @@ export function recordCoordEvent(input: CoordEventInput, deps: { db?: Database }
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/** Read-only chronological list; a missing store is an empty ledger, not a newly created one. */
|
|
102
|
-
export function listCoordEvents(opts: { since: string; seat?: string }, deps: { db?: Database } = {}): CoordEvent[] {
|
|
102
|
+
export function listCoordEvents(opts: { since: string; seat?: string; channelOnly?: boolean }, deps: { db?: Database } = {}): CoordEvent[] {
|
|
103
103
|
if (!deps.db && !existsSync(surfaceEventsDbPath())) return [];
|
|
104
|
-
const db = deps.db ??
|
|
104
|
+
const db = deps.db ?? (opts.channelOnly
|
|
105
|
+
? new Database(surfaceEventsDbPath(), { readonly: true, strict: true })
|
|
106
|
+
: openSurfaceEventsDb());
|
|
105
107
|
try {
|
|
106
108
|
const rows = db.prepare(`SELECT id, ts, text, summary, kind, refs FROM events
|
|
107
|
-
WHERE surface IN ('coord:channel', 'context:external', 'context:session') AND direction='outbound' AND ts>=?
|
|
109
|
+
WHERE surface ${opts.channelOnly ? "= 'coord:channel'" : "IN ('coord:channel', 'context:external', 'context:session')"} AND direction='outbound' AND ts>=?
|
|
108
110
|
${opts.seat ? "AND json_extract(refs,'$.seat')=?" : ''} ORDER BY ts ASC, rowid ASC`)
|
|
109
111
|
.all(...(opts.seat ? [opts.since, opts.seat] : [opts.since])) as Array<{
|
|
110
112
|
id: string; ts: string; text: string; summary: string; kind: string; refs: string;
|
|
@@ -28,6 +28,7 @@ export interface CardTransport {
|
|
|
28
28
|
const IRREVERSIBLE = new Set(['money', 'publish', 'security', 'irreversible', 'secret']);
|
|
29
29
|
export const DECISION_DATA_PREFIX = 'dec:';
|
|
30
30
|
const REMIND_BEFORE_MS = 2 * 3600_000;
|
|
31
|
+
const WEBPUSH_MAX_SEND_ATTEMPTS = 3;
|
|
31
32
|
|
|
32
33
|
export function isIrreversible(entry: Pick<DecisionEntry, 'category'>): boolean {
|
|
33
34
|
return IRREVERSIBLE.has(entry.category);
|
|
@@ -99,7 +100,8 @@ interface CardState {
|
|
|
99
100
|
/** First start of this platform's service — decisions raised before it are not pushed (no burst of old items at
|
|
100
101
|
* deploy); they stay reachable through `/decisions`. */
|
|
101
102
|
since?: string;
|
|
102
|
-
cards: Record<string, { refs: CardRef[]; closed?: boolean; remindedAt?: string; note?: string }>;
|
|
103
|
+
cards: Record<string, { refs: CardRef[]; closed?: boolean; remindedAt?: string; note?: string; attempts?: number; noSubscribers?: boolean }>;
|
|
104
|
+
testOriginQuestions?: Record<string, true>;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
export interface DecisionCardServiceOptions {
|
|
@@ -165,16 +167,27 @@ export class DecisionCardService {
|
|
|
165
167
|
for (const record of pending.questions) {
|
|
166
168
|
for (const question of record.questions) {
|
|
167
169
|
const data = { questionId: record.id, runId: record.runId };
|
|
170
|
+
const logSkip = (reason: string): void => debug.log('hitl.card-bridge', 'skipped', { ...data, reason });
|
|
171
|
+
// Path is not provenance: operational goals can live under the OS temporary directory too.
|
|
172
|
+
if (record.testOrigin === true) {
|
|
173
|
+
state.testOriginQuestions ??= {};
|
|
174
|
+
if (!state.testOriginQuestions[record.id]) {
|
|
175
|
+
state.testOriginQuestions[record.id] = true;
|
|
176
|
+
dirty = true;
|
|
177
|
+
debug.log('hitl.card-bridge', 'skipped', { ...data, reason: 'test-origin' });
|
|
178
|
+
}
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
168
181
|
if (record.expiresAt && record.expiresAt <= this.now().toISOString()) {
|
|
169
|
-
|
|
182
|
+
logSkip('expired');
|
|
170
183
|
continue;
|
|
171
184
|
}
|
|
172
185
|
if (question.impact !== 'high' && question.impact !== 'critical') {
|
|
173
|
-
|
|
186
|
+
logSkip('impact');
|
|
174
187
|
continue;
|
|
175
188
|
}
|
|
176
189
|
if (record.questions.length !== 1 || question.options.length < 2 || question.options.length > 4) {
|
|
177
|
-
|
|
190
|
+
logSkip('options');
|
|
178
191
|
continue;
|
|
179
192
|
}
|
|
180
193
|
if (seen.has(record.id)) {
|
|
@@ -200,7 +213,7 @@ export class DecisionCardService {
|
|
|
200
213
|
all.unshift(entry);
|
|
201
214
|
debug.log('hitl.card-bridge', 'raised', { ...data, decisionId: entry.id });
|
|
202
215
|
} catch (error) {
|
|
203
|
-
|
|
216
|
+
logSkip(error instanceof Error ? error.name : 'unknown');
|
|
204
217
|
}
|
|
205
218
|
}
|
|
206
219
|
}
|
|
@@ -208,13 +221,26 @@ export class DecisionCardService {
|
|
|
208
221
|
const chats = await this.opts.transport.ownerChats().catch(() => [] as string[]);
|
|
209
222
|
for (const e of all) {
|
|
210
223
|
const card = state.cards[e.id];
|
|
211
|
-
if (e.status === 'open' && !card
|
|
224
|
+
if (e.status === 'open' && (!card || (platform === 'webpush' && !card.refs.length && (card.attempts ?? 0) < WEBPUSH_MAX_SEND_ATTEMPTS))
|
|
225
|
+
&& e.options.length >= 2 && e.raisedAt !== undefined && e.raisedAt >= state.since) {
|
|
226
|
+
if (platform === 'webpush' && !chats.length) {
|
|
227
|
+
if (!card?.noSubscribers) {
|
|
228
|
+
state.cards[e.id] = { ...(card ?? { refs: [] }), noSubscribers: true };
|
|
229
|
+
dirty = true;
|
|
230
|
+
debug.log('decisions.webpush', 'no-subscribers', { id: e.id });
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
212
234
|
const refs: CardRef[] = [];
|
|
213
235
|
for (const chat of chats) {
|
|
214
236
|
const ref = await this.opts.transport.send(chat, renderCard(e)).catch(() => null);
|
|
215
237
|
if (ref) refs.push(ref);
|
|
216
238
|
}
|
|
217
|
-
if (
|
|
239
|
+
if (platform === 'webpush') {
|
|
240
|
+
state.cards[e.id] = { ...(card ?? { refs: [] }), refs, attempts: (card?.attempts ?? 0) + 1 };
|
|
241
|
+
dirty = true;
|
|
242
|
+
} else if (refs.length) state.cards[e.id] = { refs };
|
|
243
|
+
if (refs.length) sent++;
|
|
218
244
|
debug.log('decisions.telegram', 'card-sent', { platform, id: e.id, category: e.category, chats: chats.length, delivered: refs.length });
|
|
219
245
|
continue;
|
|
220
246
|
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { DecisionLedger, type DecisionEntry, type SeatDecisionRecord } from './decision-ledger.js';
|
|
4
|
+
import { createRequire } from 'node:module';
|
|
5
|
+
import { listCoordEvents, type CoordEvent } from '../context-bus/coord-events.js';
|
|
6
|
+
import { effectiveInstanceRoot } from '../instance/resolve.js';
|
|
7
|
+
import { elanousStateRoot } from '../autopilot/state-paths.js';
|
|
8
|
+
|
|
9
|
+
/** Directive row fields this meter reads — kept local because `src/directives/directive-index` is excluded from the public export. */
|
|
10
|
+
export interface DirectiveRow { ts: string; text: string; source_file: string; line_no: number }
|
|
11
|
+
|
|
12
|
+
/** Loads the private directive index at run time only, so public builds (no index) bundle cleanly and report `directivesError`. */
|
|
13
|
+
function openDirectiveIndex(stateDir?: string): { search(q: string, f: { since?: string; until?: string; limit?: number }): DirectiveRow[]; close(): void } {
|
|
14
|
+
const spec = ['..', 'directives', 'directive-index.js'].join('/');
|
|
15
|
+
const mod = createRequire(import.meta.url)(spec) as { DirectiveIndex: new (o: { stateDir?: string }) => ReturnType<typeof openDirectiveIndex> };
|
|
16
|
+
return new mod.DirectiveIndex(stateDir ? { stateDir } : {});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** KST calendar day, matching the seat-loop ledger file name. */
|
|
20
|
+
export function proactKstDay(at: Date | string): string {
|
|
21
|
+
const date = typeof at === 'string' ? new Date(at) : at;
|
|
22
|
+
if (!Number.isFinite(date.getTime())) throw new Error('valid timestamp required');
|
|
23
|
+
return new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit' }).format(date);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function proactWindow(now: Date, days = 7): { since: string; until: string; days: string[] } {
|
|
27
|
+
if (!Number.isFinite(now.getTime()) || !Number.isInteger(days) || days < 1) throw new Error('valid window required');
|
|
28
|
+
const today = proactKstDay(now);
|
|
29
|
+
const todayStart = Date.parse(`${today}T00:00:00+09:00`);
|
|
30
|
+
const list: string[] = [];
|
|
31
|
+
for (let i = days - 1; i >= 0; i--) list.push(proactKstDay(new Date(todayStart - i * 86_400_000)));
|
|
32
|
+
return { since: new Date(todayStart - (days - 1) * 86_400_000).toISOString(), until: now.toISOString(), days: list };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ProactKind = 'proposal' | 'alert' | 'action';
|
|
36
|
+
export type ProactSource = 'decision' | 'directive' | 'coord' | 'loop';
|
|
37
|
+
export interface ProactSample {
|
|
38
|
+
readonly day: string;
|
|
39
|
+
readonly bucket: 'offered' | 'adopted' | 'asked';
|
|
40
|
+
readonly source: ProactSource;
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly at: string;
|
|
43
|
+
readonly title: string;
|
|
44
|
+
/** Where the original row lives, so a count can be opened again. */
|
|
45
|
+
readonly link: string;
|
|
46
|
+
}
|
|
47
|
+
export interface ProactDay {
|
|
48
|
+
readonly day: string;
|
|
49
|
+
/** System offered a proposal, alert, or action before the owner asked. */
|
|
50
|
+
readonly offered: number;
|
|
51
|
+
/** Of those, the ones the owner (or a delegated seat) adopted. */
|
|
52
|
+
readonly adopted: number;
|
|
53
|
+
/** Problems that stayed invisible until the owner asked first. */
|
|
54
|
+
readonly asked: number;
|
|
55
|
+
}
|
|
56
|
+
export interface ProactSourceStatus { readonly read: boolean; readonly reason?: string }
|
|
57
|
+
export interface ProactMeter {
|
|
58
|
+
readonly since: string;
|
|
59
|
+
readonly until: string;
|
|
60
|
+
readonly days: readonly ProactDay[];
|
|
61
|
+
readonly samples: readonly ProactSample[];
|
|
62
|
+
readonly sources: Readonly<Record<ProactSource, ProactSourceStatus>>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ProactMeterInput {
|
|
66
|
+
decisions?: readonly DecisionEntry[];
|
|
67
|
+
seatDecisions?: readonly SeatDecisionRecord[];
|
|
68
|
+
directives?: readonly DirectiveRow[];
|
|
69
|
+
coord?: readonly CoordEvent[];
|
|
70
|
+
loops?: readonly ProactLoopRow[];
|
|
71
|
+
decisionsError?: string;
|
|
72
|
+
directivesError?: string;
|
|
73
|
+
coordError?: string;
|
|
74
|
+
loopsError?: string;
|
|
75
|
+
}
|
|
76
|
+
export interface ProactLoopRow {
|
|
77
|
+
readonly seat: string;
|
|
78
|
+
readonly at: string;
|
|
79
|
+
readonly status: string;
|
|
80
|
+
readonly action?: string;
|
|
81
|
+
readonly id?: string;
|
|
82
|
+
readonly title?: string;
|
|
83
|
+
readonly file: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const ALERT = /경보|경고|장애|실패|막힘|alert|fail|blocked|stall/i;
|
|
87
|
+
const ACTION = /조치|실행|발사|launch|queued|launched/i;
|
|
88
|
+
const ASKED = /왜|문제|안 되|안돼|고장|버그|실패|막혀|뭐가|무슨 일|확인해/;
|
|
89
|
+
|
|
90
|
+
export function proactKind(text: string, action?: string): ProactKind {
|
|
91
|
+
if (action === 'decision' || action === 'harness' || action === 'seat-question' || ACTION.test(text)) return 'action';
|
|
92
|
+
if (ALERT.test(text)) return 'alert';
|
|
93
|
+
return 'proposal';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function sample(day: string, bucket: ProactSample['bucket'], source: ProactSource, id: string, at: string, title: string, link: string): ProactSample {
|
|
97
|
+
return { day, bucket, source, id, at, title: title.replace(/\s+/g, ' ').trim().slice(0, 160), link };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Count one row once. Adoption is a property of an offered row, never a second offered row. */
|
|
101
|
+
export function measureProact(input: ProactMeterInput, now: Date, days = 7): ProactMeter {
|
|
102
|
+
const window = proactWindow(now, days);
|
|
103
|
+
const inWindow = (at: string | undefined): at is string => !!at && at >= window.since && at <= window.until;
|
|
104
|
+
const offered: ProactSample[] = [];
|
|
105
|
+
const adopted: ProactSample[] = [];
|
|
106
|
+
const asked: ProactSample[] = [];
|
|
107
|
+
|
|
108
|
+
if (!input.decisionsError) {
|
|
109
|
+
for (const card of input.decisions ?? []) {
|
|
110
|
+
const at = card.raisedAt ?? card.importedAt;
|
|
111
|
+
if (!inWindow(at)) continue;
|
|
112
|
+
const day = proactKstDay(at);
|
|
113
|
+
const link = `decisions://${card.id}`;
|
|
114
|
+
const system = card.raisedBy.agent !== 'human' && card.raisedBy.agent !== 'owner';
|
|
115
|
+
if (system) {
|
|
116
|
+
offered.push(sample(day, 'offered', 'decision', card.id, at, card.title, link));
|
|
117
|
+
const decidedAt = card.decidedAt;
|
|
118
|
+
const adoptedDay = decidedAt ? proactKstDay(decidedAt) : '';
|
|
119
|
+
if (card.status === 'decided' && decidedAt && decidedAt <= window.until) {
|
|
120
|
+
adopted.push(sample(adoptedDay, 'adopted', 'decision', card.id, decidedAt, card.title, link));
|
|
121
|
+
}
|
|
122
|
+
} else if (ASKED.test(`${card.title} ${card.scqa.c}`)) {
|
|
123
|
+
asked.push(sample(day, 'asked', 'decision', card.id, at, card.title, link));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const seat of input.seatDecisions ?? []) {
|
|
127
|
+
if (!inWindow(seat.recordedAt)) continue;
|
|
128
|
+
const at = seat.decidedAt && seat.decidedAt <= window.until ? seat.decidedAt : seat.recordedAt;
|
|
129
|
+
const adoptedDay = proactKstDay(at);
|
|
130
|
+
const link = `decisions://${seat.id}`;
|
|
131
|
+
offered.push(sample(proactKstDay(seat.recordedAt), 'offered', 'decision', seat.id, seat.recordedAt, seat.title, link));
|
|
132
|
+
if (window.days.includes(adoptedDay)) adopted.push(sample(adoptedDay, 'adopted', 'decision', seat.id, at, seat.title, link));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!input.directivesError) {
|
|
137
|
+
for (const row of input.directives ?? []) {
|
|
138
|
+
if (!inWindow(row.ts) || !ASKED.test(row.text)) continue;
|
|
139
|
+
asked.push(sample(proactKstDay(row.ts), 'asked', 'directive', `${row.source_file}:${row.line_no}`, row.ts, row.text, `directives://${row.source_file}#L${row.line_no}`));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!input.coordError) {
|
|
144
|
+
for (const event of input.coord ?? []) {
|
|
145
|
+
if (!inWindow(event.at)) continue;
|
|
146
|
+
const day = proactKstDay(event.at);
|
|
147
|
+
const link = event.refs.url ?? `coord://${event.id}`;
|
|
148
|
+
const text = `${event.summary} ${event.kind}`;
|
|
149
|
+
const systemOffered = event.refs.seat && event.refs.seat !== 'CEO' && (event.kind === '제안' || event.kind === '보고' || event.kind === '사고' || ALERT.test(text) || ACTION.test(text));
|
|
150
|
+
if (systemOffered) {
|
|
151
|
+
offered.push(sample(day, 'offered', 'coord', event.id, event.at, event.summary, link));
|
|
152
|
+
} else if (event.refs.seat === 'CEO' && (event.kind === '요청' || ASKED.test(text))) {
|
|
153
|
+
asked.push(sample(day, 'asked', 'coord', event.id, event.at, event.summary, link));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!input.loopsError) {
|
|
159
|
+
const proactive = new Set(['queued', 'launched', 'asked', 'hitl', 'shadow', 'attempting']);
|
|
160
|
+
for (const row of input.loops ?? []) {
|
|
161
|
+
if (!inWindow(row.at)) continue;
|
|
162
|
+
const day = proactKstDay(row.at);
|
|
163
|
+
const id = row.id ?? `${row.seat}:${row.at}`;
|
|
164
|
+
const link = `loop://${row.file}#${id}`;
|
|
165
|
+
const title = row.title ?? `${row.seat} ${row.status}`;
|
|
166
|
+
if (proactive.has(row.status) || row.action === 'decision' || row.action === 'harness') {
|
|
167
|
+
offered.push(sample(day, 'offered', 'loop', id, row.at, `${proactKind(title, row.action)}:${title}`, link));
|
|
168
|
+
if (row.status === 'launched' || row.status === 'queued' || row.action === 'decision') {
|
|
169
|
+
adopted.push(sample(day, 'adopted', 'loop', id, row.at, title, link));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const tally = new Map<string, { day: string; offered: number; adopted: number; asked: number }>(window.days.map(day => [day, { day, offered: 0, adopted: 0, asked: 0 }]));
|
|
176
|
+
for (const row of offered) tally.get(row.day)!.offered += 1;
|
|
177
|
+
for (const row of adopted) {
|
|
178
|
+
const cell = tally.get(row.day);
|
|
179
|
+
if (cell) cell.adopted += 1;
|
|
180
|
+
}
|
|
181
|
+
for (const row of asked) tally.get(row.day)!.asked += 1;
|
|
182
|
+
const pick = (rows: ProactSample[]) => {
|
|
183
|
+
const seen = new Set<string>();
|
|
184
|
+
const out: ProactSample[] = [];
|
|
185
|
+
for (const source of ['decision', 'directive', 'coord', 'loop'] as const) {
|
|
186
|
+
for (const row of rows.filter(item => item.source === source).sort((a, b) => b.at.localeCompare(a.at) || a.id.localeCompare(b.id))) {
|
|
187
|
+
if (seen.has(row.link)) continue;
|
|
188
|
+
seen.add(row.link);
|
|
189
|
+
out.push(row);
|
|
190
|
+
if (out.length === 2) break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return out;
|
|
194
|
+
};
|
|
195
|
+
const status = (error: string | undefined, read: boolean): ProactSourceStatus => error ? { read: false, reason: error } : { read };
|
|
196
|
+
return {
|
|
197
|
+
since: window.since, until: window.until,
|
|
198
|
+
days: window.days.map(day => tally.get(day)!),
|
|
199
|
+
samples: [...pick(offered), ...pick(adopted), ...pick(asked)],
|
|
200
|
+
sources: {
|
|
201
|
+
decision: status(input.decisionsError, !input.decisionsError),
|
|
202
|
+
directive: status(input.directivesError, !input.directivesError),
|
|
203
|
+
coord: status(input.coordError, !input.coordError),
|
|
204
|
+
loop: status(input.loopsError, !input.loopsError),
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function readLoopRows(root: string, days: readonly string[]): ProactLoopRow[] {
|
|
210
|
+
const base = join(root, 'seat-loop');
|
|
211
|
+
const seats = readdirSync(base, { withFileTypes: true }).filter(entry => entry.isDirectory()).map(entry => entry.name);
|
|
212
|
+
const rows: ProactLoopRow[] = [];
|
|
213
|
+
for (const seat of seats) {
|
|
214
|
+
for (const day of days) {
|
|
215
|
+
const file = join(base, seat, `${day}.jsonl`);
|
|
216
|
+
let text = '';
|
|
217
|
+
try { text = readFileSync(file, 'utf8'); } catch (error) {
|
|
218
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue;
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
for (const line of text.split('\n')) {
|
|
222
|
+
if (!line.trim()) continue;
|
|
223
|
+
const parsed = JSON.parse(line) as { at?: string; ts?: string; status?: string; action?: string; item?: { id?: string; title?: string } };
|
|
224
|
+
const at = parsed.at ?? parsed.ts;
|
|
225
|
+
if (!at || !parsed.status) continue;
|
|
226
|
+
rows.push({ seat, at, status: parsed.status, action: parsed.action, id: parsed.item?.id, title: parsed.item?.title, file });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return rows;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface ProactMeterDeps {
|
|
234
|
+
readonly now?: Date;
|
|
235
|
+
readonly days?: number;
|
|
236
|
+
readonly stateDir?: string;
|
|
237
|
+
readonly instanceRoot?: string;
|
|
238
|
+
readonly readDecisions?: () => { decisions: DecisionEntry[]; seatDecisions: SeatDecisionRecord[] };
|
|
239
|
+
readonly readDirectives?: (since: string, until: string) => DirectiveRow[];
|
|
240
|
+
readonly readCoord?: (since: string) => CoordEvent[];
|
|
241
|
+
readonly readLoops?: (days: readonly string[]) => ProactLoopRow[];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Read the four ledgers. A source that cannot be opened is reported, never counted as zero evidence. */
|
|
245
|
+
export function readProactMeter(deps: ProactMeterDeps = {}): ProactMeter {
|
|
246
|
+
const now = deps.now ?? new Date();
|
|
247
|
+
const window = proactWindow(now, deps.days ?? 7);
|
|
248
|
+
const input: ProactMeterInput = {};
|
|
249
|
+
const stateDir = deps.stateDir;
|
|
250
|
+
try {
|
|
251
|
+
const read = deps.readDecisions ?? (() => {
|
|
252
|
+
const ledger = new DecisionLedger(stateDir ? { stateDir } : {});
|
|
253
|
+
return { decisions: ledger.list({ status: 'all', since: window.since }), seatDecisions: ledger.seatReport({ since: window.since }) };
|
|
254
|
+
});
|
|
255
|
+
const got = read();
|
|
256
|
+
input.decisions = got.decisions;
|
|
257
|
+
input.seatDecisions = got.seatDecisions;
|
|
258
|
+
} catch (error) { input.decisionsError = error instanceof Error ? error.message : String(error); }
|
|
259
|
+
try {
|
|
260
|
+
input.directives = (deps.readDirectives ?? ((since, until) => {
|
|
261
|
+
const index = openDirectiveIndex(stateDir);
|
|
262
|
+
try { return index.search('', { since, until, limit: 1000 }); }
|
|
263
|
+
finally { index.close(); }
|
|
264
|
+
}))(window.since, window.until);
|
|
265
|
+
} catch (error) { input.directivesError = error instanceof Error ? error.message : String(error); }
|
|
266
|
+
try {
|
|
267
|
+
input.coord = (deps.readCoord ?? ((since) => listCoordEvents({ since, channelOnly: true })))(window.since);
|
|
268
|
+
} catch (error) { input.coordError = error instanceof Error ? error.message : String(error); }
|
|
269
|
+
try {
|
|
270
|
+
const loopRoot = deps.instanceRoot ?? stateDir ?? effectiveInstanceRoot() ?? elanousStateRoot();
|
|
271
|
+
input.loops = (deps.readLoops ?? ((days) => readLoopRows(loopRoot, days)))(window.days);
|
|
272
|
+
} catch (error) { input.loopsError = error instanceof Error ? error.message : String(error); }
|
|
273
|
+
return measureProact(input, now, deps.days ?? 7);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function formatProactMeter(meter: ProactMeter): string {
|
|
277
|
+
const lines = [`선제성 ${meter.since} ~ ${meter.until}`, '날짜 · 먼저 냄 · 채택 · 물어서야'];
|
|
278
|
+
for (const day of meter.days) lines.push(`${day.day} · ${day.offered} · ${day.adopted} · ${day.asked}`);
|
|
279
|
+
const unread = (Object.entries(meter.sources) as Array<[ProactSource, ProactSourceStatus]>).filter(([, status]) => !status.read);
|
|
280
|
+
if (unread.length) lines.push(`못 읽음: ${unread.map(([name, status]) => `${name}(${status.reason})`).join(' · ')}`);
|
|
281
|
+
if (meter.samples.length) {
|
|
282
|
+
lines.push('표본:');
|
|
283
|
+
for (const row of meter.samples) lines.push(` ${row.bucket} · ${row.source} · ${row.id} · ${row.link}`);
|
|
284
|
+
}
|
|
285
|
+
return lines.join('\n');
|
|
286
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { DecisionEntry } from './decision-ledger.js';
|
|
2
|
+
import type { ChecklistItem } from '../release-loop/checklist.js';
|
|
3
|
+
import type { ReleaseSchedule } from '../release-loop/release-schedule.js';
|
|
4
|
+
import type { DraftSweepResult, SweepDraft } from '../self-dev/draft-sweep.js';
|
|
5
|
+
|
|
6
|
+
type ProactLoop = { readonly name: string; readonly state: 'alive' | 'late' | 'failing' | 'off' | 'unknown'; readonly scope?: string };
|
|
7
|
+
type ProactYellow = { readonly version: string; readonly item: Pick<ChecklistItem, 'id' | 'title' | 'status'> };
|
|
8
|
+
type ProactDrafts = { readonly drafts: readonly Pick<SweepDraft, 'number' | 'title'>[]; readonly sweep: Pick<DraftSweepResult, 'complete' | 'entries'> };
|
|
9
|
+
export interface ProactSignals {
|
|
10
|
+
/** A suspended run (resume) or an explicitly observed blocker, not merely an old open card. */
|
|
11
|
+
readonly decisions?: readonly (Pick<DecisionEntry, 'id' | 'title' | 'status' | 'raisedAt' | 'importedAt' | 'resume'> & { readonly blocked?: boolean })[];
|
|
12
|
+
readonly yellow?: readonly ProactYellow[];
|
|
13
|
+
readonly schedules?: readonly Pick<ReleaseSchedule, 'version' | 'landBy'>[];
|
|
14
|
+
readonly loops?: readonly ProactLoop[];
|
|
15
|
+
/** Only a complete, read-only runDraftSweep({ apply: false }) assessment proves draft staleness. */
|
|
16
|
+
readonly draftAssessment?: ProactDrafts;
|
|
17
|
+
}
|
|
18
|
+
type ProactSuggestion = {
|
|
19
|
+
readonly key: string;
|
|
20
|
+
readonly kind: 'decision' | 'yellow' | 'loop' | 'draft';
|
|
21
|
+
readonly title: string;
|
|
22
|
+
readonly reason: string;
|
|
23
|
+
readonly source: string;
|
|
24
|
+
readonly score: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const HOUR = 3_600_000;
|
|
28
|
+
const DECISION_AGE_HOURS = 24;
|
|
29
|
+
const LANDING_WINDOW_HOURS = 24;
|
|
30
|
+
const MAX_SUGGESTIONS = 3;
|
|
31
|
+
|
|
32
|
+
/** Rules-only, read-only selection. Missing/invalid observations are unknown, never evidence of urgency. */
|
|
33
|
+
export function selectProact1Lite(signals: ProactSignals, options: { now: Date; previouslySuggested?: ReadonlySet<string> }): ProactSuggestion[] {
|
|
34
|
+
const now = options.now.getTime();
|
|
35
|
+
if (!Number.isFinite(now)) throw new Error('valid now required');
|
|
36
|
+
const candidates: ProactSuggestion[] = [];
|
|
37
|
+
const elapsedHours = (iso: string | undefined): number => iso ? (now - Date.parse(iso)) / HOUR : NaN;
|
|
38
|
+
const valid = (text: string): boolean => text.trim().length > 0;
|
|
39
|
+
|
|
40
|
+
for (const card of signals.decisions ?? []) {
|
|
41
|
+
if (card.status !== 'open' || (card.blocked !== true && !card.resume) || !valid(card.id) || !valid(card.title)) continue;
|
|
42
|
+
// Imported historical cards have no known raise time; their import time is a lower bound on age.
|
|
43
|
+
const age = elapsedHours(card.raisedAt ?? card.importedAt);
|
|
44
|
+
if (!Number.isFinite(age) || age < DECISION_AGE_HOURS) continue;
|
|
45
|
+
candidates.push({ key: `decision:${card.id}`, kind: 'decision', title: card.title,
|
|
46
|
+
reason: `열린 결정 ${Math.floor(age)}시간 대기${card.raisedAt ? '' : ' (가져온 시각 기준)'}`,
|
|
47
|
+
source: card.id, score: 400 + Math.min(100, Math.floor(age / 24)) });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const deadlines = new Map((signals.schedules ?? []).map(schedule => [schedule.version, schedule.landBy]));
|
|
51
|
+
for (const { version, item } of signals.yellow ?? []) {
|
|
52
|
+
if (item.status !== 'yellow' || !valid(item.id) || !valid(item.title) || !valid(version)) continue;
|
|
53
|
+
const landBy = deadlines.get(version);
|
|
54
|
+
const remaining = landBy ? (Date.parse(landBy) - now) / HOUR : NaN;
|
|
55
|
+
if (!Number.isFinite(remaining) || remaining > LANDING_WINDOW_HOURS) continue;
|
|
56
|
+
candidates.push({ key: `yellow:${version}:${item.id}`, kind: 'yellow', title: item.title,
|
|
57
|
+
reason: remaining < 0 ? `착지 마감 ${Math.ceil(-remaining)}시간 경과` : `착지 마감 ${Math.ceil(remaining)}시간 전`,
|
|
58
|
+
source: `${version}/${item.id} · ${landBy}`, score: 500 + Math.min(100, Math.max(0, Math.ceil(-remaining))) });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const loop of signals.loops ?? []) {
|
|
62
|
+
if (loop.state !== 'late' || !valid(loop.name)) continue;
|
|
63
|
+
candidates.push({ key: `loop:${loop.scope ?? ''}:${loop.name}`, kind: 'loop', title: loop.name,
|
|
64
|
+
reason: '루프 실행 지연 관측', source: `${loop.scope ?? 'loop'}/${loop.name}`, score: 300 });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const assessment = signals.draftAssessment;
|
|
68
|
+
if (assessment?.sweep.complete) {
|
|
69
|
+
const drafts = new Map(assessment.drafts.map(draft => [draft.number, draft]));
|
|
70
|
+
for (const entry of assessment.sweep.entries) {
|
|
71
|
+
// A stale assessment is not a request to close: only surface the already-verified signal.
|
|
72
|
+
if (entry.action !== 'close' || entry.applied || !['stale-unobserved', 'claim-expired', 'claim-expired (self-implement.result final; worktree is not live)'].includes(entry.reason)) continue;
|
|
73
|
+
const draft = drafts.get(entry.number);
|
|
74
|
+
if (!draft || !valid(draft.title)) continue;
|
|
75
|
+
candidates.push({ key: `draft:${entry.number}`, kind: 'draft', title: draft.title,
|
|
76
|
+
reason: `draft PR 정체 (${entry.reason})`, source: `#${entry.number}`, score: 200 });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const selected = new Map<string, ProactSuggestion>();
|
|
81
|
+
for (const candidate of candidates) {
|
|
82
|
+
if (options.previouslySuggested?.has(candidate.key)) continue;
|
|
83
|
+
if (!selected.has(candidate.key) || selected.get(candidate.key)!.score < candidate.score) selected.set(candidate.key, candidate);
|
|
84
|
+
}
|
|
85
|
+
return [...selected.values()].sort((a, b) => b.score - a.score || a.key.localeCompare(b.key)).slice(0, MAX_SUGGESTIONS);
|
|
86
|
+
}
|
|
@@ -28,7 +28,7 @@ export function webPushDecisionTransport(
|
|
|
28
28
|
const recommendation = 'skipped' in entry.recommendation ? '추천안 없음' : `추천안: ${entry.recommendation.option.toUpperCase()}`;
|
|
29
29
|
const body = `${recommendation}${entry.dueAt ? ` · 기한: ${new Intl.DateTimeFormat('ko-KR', { timeZone: 'Asia/Seoul', month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }).format(new Date(entry.dueAt))}` : ''}`;
|
|
30
30
|
const result = await push({ title: title('대표 결정 · ', entry.title), body, url: url(id), tag: tag(id) });
|
|
31
|
-
debug.log('decisions.webpush', 'card-sent', { id, delivered: result.delivered });
|
|
31
|
+
if (result.attempted) debug.log('decisions.webpush', 'card-sent', { id, delivered: result.delivered });
|
|
32
32
|
return result.delivered ? { chat: 'webpush', message: id } : null;
|
|
33
33
|
},
|
|
34
34
|
edit: async (ref, _view) => {
|
|
@@ -66,6 +66,7 @@ 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
68
|
import type { ContextNowDeps } from './context-bus/context-now.js';
|
|
69
|
+
import { botUnavailableSlashReply, deriveTuiSlashAvailability, type TuiSlashAvailability } from './maturity/tui-slash-availability.js';
|
|
69
70
|
|
|
70
71
|
const SLASH_FOCUS_TURNS_DEFAULT = 8;
|
|
71
72
|
|
|
@@ -142,6 +143,8 @@ export interface DiscordSelfMessageDeps {
|
|
|
142
143
|
log?: (msg: string) => void;
|
|
143
144
|
cardFollowupDeps?: { ocrText?: (path: string) => Promise<string | null>; runGraph?: typeof runGraph; rootDir?: () => string };
|
|
144
145
|
nowDeps?: ContextNowDeps;
|
|
146
|
+
/** Override the TUI slash projection for isolated message-path tests. */
|
|
147
|
+
tuiSlashAvailability?: readonly TuiSlashAvailability[];
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
/** Compose the full discord self+interweave onMessage handler. */
|
|
@@ -399,6 +402,12 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
|
|
|
399
402
|
log(`session cmd: ${ctx.text.trim().slice(0, 40)}`);
|
|
400
403
|
return sessionReply;
|
|
401
404
|
}
|
|
405
|
+
const slashName = /^\/([a-z0-9_-]{1,32})(?:\s|$)/i.exec(ctx.text.trim())?.[1]?.toLowerCase();
|
|
406
|
+
if (slashName) {
|
|
407
|
+
const entry = (deps.tuiSlashAvailability ?? deriveTuiSlashAvailability()).find(({ name }) => name === slashName);
|
|
408
|
+
const unavailable = botUnavailableSlashReply(entry, 'discord');
|
|
409
|
+
if (unavailable) return unavailable;
|
|
410
|
+
}
|
|
402
411
|
log(`◀ ${ctx.userName ?? ctx.userId}: ${ctx.text.slice(0, 80)}${ctx.attachments.length ? ` (+첨부 ${ctx.attachments.length})` : ''}`);
|
|
403
412
|
debug.log('discord.self.turn', 'inbound', { channelId: ctx.channelId, chars: ctx.text.length, attachments: ctx.attachments.length });
|
|
404
413
|
// C2+ — 첨부 기억(사진만 먼저 올리는 패턴 지원). 명령 여부와 무관.
|