elanous 0.2.5 → 0.2.7
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/.env.example +10 -7
- package/README.md +9 -4
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5391-8793f88b03b30161.js → 2375-cb6debea56cce4fb.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{1751.50b96ea9c37b49b6.js → 2415.ed6428ff334c7ce2.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +25 -0
- package/apps/pwa/out/_next/static/chunks/{4daa2798-1363d725c79f3593.js → 2ea96958-e8c7e112de50b067.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8617.465ca22a319c95b3.js → 4323.c5dfb6f503304775.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/4836-2eba6079f31b88d4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8918-482b30ce2a8ebf4c.js → 5080-1613752891fcadb4.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5348-bbf838e119661eee.js → 5791-bb82743fa6299562.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{9704-3221c102623c8d73.js → 6062-73e68343a9814f6a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{4440-18bb160d95819f4e.js → 6090-85a3f05f954e5697.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{f6f3850d-7f324c6566002113.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8519-f6b5d8a5833a7603.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8894-e92491a164192cc1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9740-67051141115ce775.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +1 -0
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5ebde04844689e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-f89e9a303515a033.js → page-10ed24f413ed66e9.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/board/{page-4f403cd060442c95.js → page-211810b6c93ab66a.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-0ac5d053f0444d21.js → page-624f7a2f89b60c8f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-5a5fb385a4df0ffe.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-80497e93a878ddc5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-7457f52683562161.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-cee86394de407aa6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4ff8edab44196b8c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-92e4f44428d5e290.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-b7dcf5d48a854b76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-51480757e7d68b92.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-2a05dfe80c8bdf16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-d9979633aa82f8e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-30533117beb53625.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-f7857826c2f97ad7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-07267f1391b96aba.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-bccce0babceae30f.js → page-55d50e9b0006a91d.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-9699b1786be6171d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-43dd772aee8af75f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-a7e37af7d0399cb8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-973ab7af59c718b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a9f8d3865b91e436.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-15e5a15db84c56aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a2e572e4511f2a16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-dd107bf1c7d52a90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d42b99cdfb9f27c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-2764ff40dad5a995.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-c5f1152db116ff96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-f933bca81ea38a6f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-c1c95739c0bff3b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-a7aaffcf771662d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-71d18eec33b9c667.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-56adbe1a1f1379bd.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-2c4e59079ee357ac.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-a81f113f3ef89d72.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d422f38c052967e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fc27b00e20733146.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-2bf59c720fe9542c.js → webpack-e5bea36f183b25e6.js} +1 -1
- package/apps/pwa/out/_next/static/css/f537bdb19c69306a.css +1 -0
- package/apps/pwa/out/_next/static/keajW7DDyghEs1y8oJe6T/_buildManifest.js +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +22 -22
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +22 -22
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +22 -22
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +21 -21
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +21 -21
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +21 -21
- package/apps/pwa/out/consult/index.html +1 -0
- package/apps/pwa/out/consult/index.txt +32 -0
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +22 -22
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +22 -22
- package/apps/pwa/out/exec/index.html +1 -0
- package/apps/pwa/out/exec/index.txt +32 -0
- package/apps/pwa/out/field/index.html +1 -0
- package/apps/pwa/out/field/index.txt +32 -0
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/icons/elanous-16.png +0 -0
- package/apps/pwa/out/icons/elanous-180.png +0 -0
- package/apps/pwa/out/icons/elanous-192.png +0 -0
- package/apps/pwa/out/icons/elanous-32.png +0 -0
- package/apps/pwa/out/icons/elanous-512.png +0 -0
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +21 -21
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +22 -22
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +21 -21
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +21 -21
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +22 -22
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +22 -22
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +22 -22
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +21 -21
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +22 -22
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +22 -22
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +22 -22
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +22 -22
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +22 -22
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +21 -21
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +22 -22
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +22 -22
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +22 -22
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +22 -22
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +22 -22
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +22 -22
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +22 -22
- package/bin/elanous.cjs +107 -0
- package/graphs/field/field-feed.yaml +24 -0
- package/graphs/field/recipes.yaml +12 -0
- package/graphs/landing/landing-heal.yaml +3 -1
- package/graphs/landing/recipes.yaml +3 -0
- package/graphs/release/recipes.yaml +13 -0
- package/graphs/release/release-loop.yaml +13 -4
- package/package.json +5 -5
- package/plugins/card-followup/README.md +42 -0
- package/plugins/card-followup/examples/card-sample.json +7 -0
- package/plugins/card-followup/graphs/card-followup.yaml +18 -0
- package/plugins/card-followup/graphs/recipes.yaml +16 -0
- package/plugins/card-followup/graphs/run-step.ts +276 -0
- package/plugins/card-followup/graphs/sales-strategy.ts +173 -0
- package/plugins/card-followup/plugin.json +25 -0
- package/plugins/doc-draft/README.md +13 -0
- package/plugins/doc-draft/examples/brief-sample.json +8 -0
- package/plugins/doc-draft/graphs/doc-draft.yaml +14 -0
- package/plugins/doc-draft/graphs/recipes.yaml +9 -0
- package/plugins/doc-draft/graphs/run-step.ts +113 -0
- package/plugins/doc-draft/plugin.json +24 -0
- package/plugins/geo-check/README.md +15 -0
- package/plugins/geo-check/examples/brand-sample.json +8 -0
- package/plugins/geo-check/graphs/geo-check.yaml +20 -0
- package/plugins/geo-check/graphs/recipes.yaml +18 -0
- package/plugins/geo-check/graphs/run-step.ts +198 -0
- package/plugins/geo-check/plugin.json +24 -0
- package/plugins/job-coach/.codex-plugin/plugin.json +1 -1
- package/plugins/job-coach/README.md +31 -1
- package/plugins/job-coach/graphs/run-step.ts +2 -2
- package/plugins/job-coach/plugin.json +1 -1
- package/plugins/job-coach/plugin.ts +1 -1
- package/scripts/affinity/endcard.sh +3 -2
- package/scripts/affinity/reframe.sh +3 -1
- package/scripts/ci-public-export-leak-gate.ts +48 -0
- package/scripts/codex-quota-alert.ts +122 -258
- package/scripts/coord-tracks.json +4 -4
- package/scripts/daemon-port-baseline.txt +1 -2
- package/scripts/field-feed/deliver-node.ts +52 -0
- package/scripts/field-feed/feed-node.ts +67 -0
- package/scripts/field-feed/lib.ts +162 -0
- package/scripts/field-feed/reel-node.ts +24 -0
- package/scripts/field-feed/start.ts +62 -0
- package/scripts/fixtures/vflow-sample.ndjson +6 -0
- package/scripts/install.sh +92 -13
- package/scripts/landing-heal/clean-checkout.ts +114 -0
- package/scripts/landing-heal/run-mission.ts +38 -3
- package/scripts/lib/android-test-device.ts +32 -0
- package/scripts/lib/pwa-scenarios/scenarios.ts +2 -1
- package/scripts/public-export.ts +8 -4
- package/scripts/release-loop/baseline-from-logs.ts +92 -0
- package/scripts/release-loop/checklist-gate-node.ts +35 -0
- package/scripts/release-loop/env-known-failures.ts +31 -0
- package/scripts/release-loop/gate-diff.ts +29 -0
- package/scripts/release-loop/gate-node.ts +124 -29
- package/scripts/release-loop/known-issues-node.ts +159 -0
- package/scripts/release-loop/npm-publish-node.ts +118 -0
- package/scripts/release-loop/ops-upgrade-node.ts +87 -0
- package/scripts/release-loop/release-readiness.ts +58 -0
- package/scripts/release-loop/shard-plan.ts +47 -0
- package/scripts/release-loop/unattended-release.ts +93 -0
- package/scripts/ux/measure-first-text.ts +102 -0
- package/scripts/ux/record-page.ts +4 -1
- package/scripts/vflow-collect.ts +20 -7
- package/scripts/vflow-search.ts +73 -0
- package/scripts/video-free-line-test-walker.ts +36 -0
- package/scripts/video-pipeline.ts +2 -2
- package/skills/explainer-video/SKILL.md +24 -3
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -1
- package/skills/explainer-video/engine/build.mjs +2 -2
- package/skills/explainer-video/engine/reel.mjs +235 -0
- package/skills/explainer-video/engine/reel.sh +9 -0
- package/skills/explainer-video/engine/vo.ts +58 -16
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +1 -1
- package/skills/grill-me/SKILL.md +1 -1
- package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +1 -1
- package/src/a2a/server.ts +2 -0
- package/src/acp/elanous-aware-peers.ts +30 -0
- package/src/acp/server.ts +91 -15
- package/src/agent/monad-agent-turn.ts +25 -6
- package/src/agent/shared-app-tools.ts +5 -3
- package/src/agent-substrate/review-intent.ts +6 -2
- package/src/agent-substrate/shard-boundary-intent.ts +70 -0
- package/src/auth/github-app-token.ts +4 -1
- package/src/autopilot/mission-origin.ts +20 -4
- package/src/autopilot/mission-phase-track.ts +11 -1
- package/src/autopilot/track-agent-shadow.ts +151 -0
- package/src/autopilot/track-agent.ts +26 -6
- package/src/autopilot/track-courier.ts +2 -2
- package/src/boot/acp-server-help.ts +16 -0
- package/src/boot/daemon-prompt-turn.ts +31 -10
- package/src/boot/daemon-tools/index.ts +7 -1
- package/src/budget/codex-credit-pace.ts +84 -0
- package/src/budget/codex-credit-plan.ts +154 -0
- package/src/budget/codex-reset-credit-state.ts +2 -0
- package/src/chat/chat-cli.ts +4 -1
- package/src/chat/short-question.ts +40 -0
- package/src/cli/agent-cli.ts +24 -15
- package/src/cli/ask-bare.ts +42 -0
- package/src/cli/cli-user-error.ts +25 -0
- package/src/cli/codex-credit-plan-view.ts +25 -0
- package/src/cli/dependence-gauge.ts +237 -0
- package/src/cli/doctor-cli.ts +5 -0
- package/src/cli/doctor-modes.ts +48 -0
- package/src/cli/intake-cli.ts +12 -2
- package/src/cli/intake-source-cli.ts +58 -0
- package/src/cli/machine-cli.ts +23 -0
- package/src/cli/msg-cli.ts +137 -0
- package/src/cli/nexus-ios-bind.ts +25 -8
- package/src/cli/phone-link.ts +117 -0
- package/src/cli/plugin-cli.ts +52 -0
- package/src/cli/pr-cli.ts +36 -0
- package/src/cli/private-ledger-commands.ts +31 -0
- package/src/cli/provider-cli.ts +5 -0
- package/src/cli/release-cli.ts +56 -12
- package/src/cli/release-run-if-ready.ts +48 -0
- package/src/cli/setup-cli.ts +27 -6
- package/src/cli/setup-recommend.ts +8 -0
- package/src/cli/usage-cli.ts +11 -1
- package/src/codex/setup.ts +27 -8
- package/src/dashboard/index.ts +1 -1
- package/src/discord.ts +56 -6
- package/src/domains/core-tools.ts +37 -14
- package/src/domains/outbound-alert.ts +55 -16
- package/src/exec-requests/planner.ts +140 -0
- package/src/exec-requests/runner.ts +338 -0
- package/src/exec-requests/store.ts +76 -0
- package/src/field/field-media.ts +250 -0
- package/src/field/field-reel.ts +196 -0
- package/src/grok/credential.ts +7 -5
- package/src/harness/aside-run-status.ts +33 -0
- package/src/harness/browser-verify.ts +48 -17
- package/src/harness/harness-cli-command.ts +25 -3
- package/src/harness/image-load-state.ts +21 -0
- package/src/harness/process-garbage.ts +45 -0
- package/src/index.ts +104 -38
- package/src/instance/leader.ts +13 -1
- package/src/intake-plane/collect-github.ts +5 -3
- package/src/intake-plane/collect-telegram-saved.ts +15 -3
- package/src/intake-plane/discord-seat-work.ts +56 -0
- package/src/intake-plane/intake-sources.ts +202 -0
- package/src/intake-plane/items.ts +28 -12
- package/src/intake-plane/seat-doc-route.ts +207 -0
- package/src/intake-plane/submit-intake-work.ts +9 -1
- package/src/intake-plane/telegram-seat-work.ts +72 -0
- package/src/llm/model-defaults.ts +24 -1
- package/src/llm.ts +5 -4
- package/src/machines/machine-ledger.ts +127 -0
- package/src/market/pack-internal-refs.ts +28 -0
- package/src/market/publish.ts +31 -4
- package/src/maturity/feature-maturity.ts +65 -0
- package/src/model-tier/turn-tier.ts +24 -0
- package/src/msg/msg-store.ts +149 -0
- package/src/native-tool-catalog.ts +2 -1
- package/src/nexus/api/chat-fast-path-config.ts +28 -0
- package/src/nexus/api/consult-requests.ts +117 -0
- package/src/nexus/api/discord-trigger-bot.ts +3 -0
- package/src/nexus/api/exec-requests.ts +51 -0
- package/src/nexus/api/field-uploads.ts +180 -0
- package/src/nexus/api/graph-approvals-feed.ts +140 -0
- package/src/nexus/api/graph-approvals.ts +120 -4
- package/src/nexus/api/harness-api.ts +219 -5
- package/src/nexus/api/http-server.ts +68 -5
- package/src/nexus/api/seat-requests.ts +254 -0
- package/src/nexus/api/trace-run-scan.ts +37 -0
- package/src/nexus/api/trace.ts +19 -3
- package/src/nexus/index.ts +3 -0
- package/src/nexus/setup-status.ts +4 -5
- package/src/notify/llm-budget-alert.ts +125 -0
- package/src/oauth/codex-account-rotation.ts +26 -4
- package/src/oauth/codex-account-store.ts +20 -2
- package/src/oauth/codex-browser-login.ts +173 -0
- package/src/oauth/codex.ts +4 -1
- package/src/oauth/fallback-chain.ts +5 -5
- package/src/oauth/store.ts +42 -5
- package/src/onboarding/continue-here.ts +29 -0
- package/src/onboarding/entry-hints.ts +8 -0
- package/src/onboarding/full-screen-io.ts +35 -3
- package/src/onboarding/help/skills.md +1 -1
- package/src/onboarding/non-interactive.ts +1 -0
- package/src/onboarding/subscription-hint.ts +27 -0
- package/src/onboarding/web-first.ts +83 -0
- package/src/onboarding.ts +230 -23
- package/src/plugins/maker/node-install.ts +92 -0
- package/src/plugins/maker/node-maker.ts +139 -0
- package/src/plugins/maker/plugin-maker.ts +220 -0
- package/src/policy/llm-policy.ts +268 -0
- package/src/prompt-library/session-guidance.ts +7 -1
- package/src/prompt-library/universal-preamble.ts +2 -2
- package/src/relay/channel.ts +184 -0
- package/src/relay/mux.ts +182 -0
- package/src/relay/relay-cli.ts +20 -0
- package/src/relay/relay-server.ts +220 -0
- package/src/relay/test-vectors.json +29 -0
- package/src/release-loop/checklist.ts +36 -9
- package/src/seat-address/seat-address.ts +34 -0
- package/src/self-dev/decompose.ts +1 -0
- package/src/self-dev/dev-pipeline.ts +6 -3
- package/src/self-dev/run-record-mirror.ts +44 -0
- package/src/self-dev/run-store.ts +12 -3
- package/src/self-implement/harness-policy-gates.ts +4 -1
- package/src/self-implement/report-origin.ts +12 -1
- package/src/self-implement/run-ledger-compact.ts +1 -1
- package/src/session/chat.ts +17 -5
- package/src/session/index.ts +23 -10
- package/src/skills/shared-agent-skill-roots.ts +25 -0
- package/src/steward/failure-streak.ts +78 -0
- package/src/steward/steward-cards.ts +24 -0
- package/src/steward/triage-budget.ts +127 -0
- package/src/steward/triage-plan.ts +75 -0
- package/src/steward/triage.ts +244 -42
- package/src/task-cards/card-store.ts +1 -1
- package/src/task-orchestrator/surfaces/pod-bun-cache.ts +18 -0
- package/src/task-orchestrator/surfaces/pod-command-job.ts +12 -3
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
- package/src/task-orchestrator/surfaces/pod-ledger-events.ts +68 -0
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +78 -4
- package/src/telegram.ts +145 -2
- package/src/tool-runtime/elanous-skills-list-runtime.ts +3 -3
- package/src/tool-runtime/self-cognition-runtimes.ts +160 -2
- package/src/user-config.ts +84 -4
- package/src/version/packed-revision.json +1 -1
- package/src/video-pipeline/recipes/character.ts +42 -5
- package/src/video-pipeline/recipes/explainer.ts +213 -0
- package/src/video-pipeline/recipes/ffmpeg.ts +1 -1
- package/src/video-pipeline/recipes/free-line.ts +16 -3
- package/src/video-pipeline/recipes/remote.ts +7 -1
- package/src/video-pipeline/vflow-reference.ts +109 -0
- package/src/video-pipeline/walk-line.ts +3 -2
- package/apps/pwa/out/_next/static/chunks/06ef2eba-d1b99ac6d2ea663c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1413-7179a49332db5a80.js +0 -1
- package/apps/pwa/out/_next/static/chunks/150-25c277137a9638ab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1640-bc99ec23bbe29d12.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2300-de059d0ab2a16630.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2684-05b0a81e75298c16.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3026-2ed7e317d9b02102.js +0 -25
- package/apps/pwa/out/_next/static/chunks/3611-77496ada38cd6358.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3717-68957c4dc9ceec90.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3929-46ba9af6f9d895cc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4015-62c5a3744698a8da.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4217-3c4317a7905f9eeb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4240-3230f230fae2d7d5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4271-e53e075ebd48593b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4377.3b125fef79576dfd.js +0 -5
- package/apps/pwa/out/_next/static/chunks/4853-35fdbe6bdbcdc0fc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5007-6a3b30e23ff68a6e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5244-12b424c42070f5ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5611-df00af3e4e4a61bb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5890-dca2321773be829a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6123-37c9dc57ddaa7ab0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6588-b7975304a302ab91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6604-c03961962b6273b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7109-cf8c87e968e83a65.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7178-30f77877525a2efb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7262-871563fc9663730b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7294-f0eeebb92aa93459.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7429-881af44deed6958f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7527.943753b6ed829f09.js +0 -1
- package/apps/pwa/out/_next/static/chunks/764-a01e858525a190c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7868-e35d5f7985ecd3f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8320-51484af7b01fdfa5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9131-52f5fad51cceca04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9238.2cd5f47f4bf7a2c3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-70c404c7bb6d7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3ffd80166b1050aa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4bcc63e272ae8205.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-880734bd07d62882.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-72bd384898d88955.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1018bbc9586ecae5.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-de86f8c38076dd7b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-70c404c7bb6d7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-cd6447196d8bafe6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-e733630c47fa102b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-d1c1a8357b161875.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-60c514128e81d787.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8b637b7e325e13c1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-63301d11dbe73836.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-a8cf0dc472060db2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-02fba54881ccfa49.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d5e8e90f0038f8de.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-b1ef6824f494466a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d589bb0f233b1009.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c9bbaebef4cda059.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-3639ce7916b92fed.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7450ae7478955653.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-70c404c7bb6d7f0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b41ca4abfd618452.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b333dd79ebee3fa9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-8be1227492793655.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6e03e9371b552e0d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-911411e9fca18d03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-792170e649b37088.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c40463466937bc91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-509c798b80384536.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1cac2aa21d77c417.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9626799d9b4a2cd1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-84cd419494e16b0f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-361487d2da603917.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-607f5beb94cb2e07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-d0222d219e29dddf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-a26980a31126decc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-edb2b37decf23e4d.js +0 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +0 -1
- package/apps/pwa/out/_next/static/ktMqfrBbf8BMGac0jqLrb/_buildManifest.js +0 -1
- /package/apps/pwa/out/_next/static/{ktMqfrBbf8BMGac0jqLrb → keajW7DDyghEs1y8oJe6T}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
type Data = Record<string, unknown>;
|
|
6
|
+
const object = (v: unknown): Data => v !== null && typeof v === 'object' && !Array.isArray(v) ? v as Data : {};
|
|
7
|
+
const text = (v: unknown): string => typeof v === 'string' ? v.trim() : '';
|
|
8
|
+
const strings = (v: unknown): string[] => Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string' && !!x.trim()) : [];
|
|
9
|
+
const rows = (v: unknown): Data[] => Array.isArray(v) ? v.map(object) : [];
|
|
10
|
+
const line = (v: unknown): string => text(v).replace(/[\r\n|]+/g, ' ');
|
|
11
|
+
const errorLine = (e: unknown): string => line(e instanceof Error ? e.message : String(e)).slice(0, 240) || 'unknown error';
|
|
12
|
+
try {
|
|
13
|
+
const contextPath = process.env.ELANOUS_GRAPH_CONTEXT ?? '';
|
|
14
|
+
const context = object(JSON.parse(readFileSync(contextPath, 'utf8')));
|
|
15
|
+
const input = object(context.input);
|
|
16
|
+
const outputs = object(context.outputs);
|
|
17
|
+
const stateDirectory = dirname(contextPath);
|
|
18
|
+
if (!stateDirectory.endsWith('.json.contexts')) throw new Error('invalid graph run context');
|
|
19
|
+
const defaultOutDir = stateDirectory.slice(0, -'.json.contexts'.length);
|
|
20
|
+
const brand = text(input.brand);
|
|
21
|
+
if (!brand) throw new Error('brand is required');
|
|
22
|
+
const engines = input.engines === undefined ? ['openai-codex', 'grok'] : strings(input.engines);
|
|
23
|
+
if (!engines.length || engines.length > 3 || engines.length !== new Set(engines).size || (input.engines !== undefined && (!Array.isArray(input.engines) || strings(input.engines).length !== input.engines.length))) throw new Error('engines must contain 1 to 3 distinct names');
|
|
24
|
+
const cli = process.env.GEO_CHECK_ELANOUS_BIN || 'elanous';
|
|
25
|
+
|
|
26
|
+
// Ask from an empty folder: run inside a brand's own repository, `elanous ask` carries that project's
|
|
27
|
+
// context (AGENTS.md and the like) and the answers «know» the brand — a marketer's folder inflated mentions 0/10 → 9/10.
|
|
28
|
+
const neutralDir = mkdtempSync(join(tmpdir(), 'geo-check-'));
|
|
29
|
+
function neutralEnv(engine?: string): Record<string, string | undefined> {
|
|
30
|
+
const { ELANOUS_TOOL_CWD: _tool, ...rest } = process.env;
|
|
31
|
+
return { ...rest, PWD: neutralDir, ...(engine ? { ELANOUS_LLM_PROVIDER: engine } : {}) };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function invoke(args: string[], engine?: string): Promise<unknown> {
|
|
35
|
+
const proc = Bun.spawn([cli, ...args], {
|
|
36
|
+
cwd: neutralDir,
|
|
37
|
+
env: neutralEnv(engine),
|
|
38
|
+
stdout: 'pipe', stderr: 'pipe',
|
|
39
|
+
});
|
|
40
|
+
const [stdout, stderr, exit] = await Promise.all([new Response(proc.stdout).text(), new Response(proc.stderr).text(), proc.exited]);
|
|
41
|
+
if (exit !== 0) throw new Error(line(stderr || stdout) || `CLI exited ${exit}`);
|
|
42
|
+
const last = stdout.trim().split('\n').at(-1);
|
|
43
|
+
if (!last) throw new Error('CLI returned no JSON');
|
|
44
|
+
return JSON.parse(last) as unknown;
|
|
45
|
+
}
|
|
46
|
+
// An installed elanous older than `ask --bare` rejects the flag; ask the plain way and mark the row so the report can say so.
|
|
47
|
+
async function askEngine(question: string, engine: string): Promise<{ answer: string; bare: boolean }> {
|
|
48
|
+
try { return { answer: reply(await invoke(['ask', '--bare', '--json', question], engine)), bare: true }; }
|
|
49
|
+
catch (e) {
|
|
50
|
+
if (!/unknown option '--bare'/.test(errorLine(e))) throw e;
|
|
51
|
+
return { answer: reply(await invoke(['ask', '--json', question], engine)), bare: false };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function reply(raw: unknown): string {
|
|
55
|
+
const result = object(raw);
|
|
56
|
+
if (typeof result.reply !== 'string') throw new Error('ask returned no reply');
|
|
57
|
+
return result.reply;
|
|
58
|
+
}
|
|
59
|
+
function parseArray(raw: string): unknown {
|
|
60
|
+
const cleaned = raw.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
|
|
61
|
+
return JSON.parse(cleaned) as unknown;
|
|
62
|
+
}
|
|
63
|
+
function domains(v: string): string {
|
|
64
|
+
const raw = v.trim().replace(/^https?:\/\//i, '').replace(/^www\./i, '').replace(/\/+$/, '');
|
|
65
|
+
return raw.split('/')[0]?.toLowerCase().replace(/:\d+$/, '') ?? '';
|
|
66
|
+
}
|
|
67
|
+
function urls(v: unknown): string[] {
|
|
68
|
+
if (Array.isArray(v)) return v.flatMap(urls);
|
|
69
|
+
if (typeof v === 'string') return [...v.matchAll(/https?:\/\/[^\s<>"')\]]+/gi)].map(m => m[0]!.replace(/[.,;!?]+$/, ''));
|
|
70
|
+
if (v && typeof v === 'object') return Object.entries(v).flatMap(([key, item]) => key === 'url' && typeof item === 'string' ? [item] : urls(item));
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
// `elanous research --json` = { query, output, metadata } — result links are `- [title](url)` lines in `output`.
|
|
74
|
+
// Only those links count; the query string or other fields may name the brand domain without it being a result.
|
|
75
|
+
function resultUrls(raw: unknown): string[] {
|
|
76
|
+
const out = object(raw).output;
|
|
77
|
+
if (typeof out !== 'string') return [];
|
|
78
|
+
return [...out.matchAll(/^\s*-\s*\[[^\]]*\]\((https?:\/\/[^)\s]+)\)/gm)].map(m => m[1]!);
|
|
79
|
+
}
|
|
80
|
+
// A bare domain in the answer text («see elanous.ai») is a citation too, not only an https:// link.
|
|
81
|
+
function mentionsHost(answer: string, host: string): boolean {
|
|
82
|
+
if (!host) return false;
|
|
83
|
+
const escaped = host.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
84
|
+
return new RegExp(`(^|[^a-z0-9.-])(?:www\\.)?${escaped}(?![a-z0-9-])`, 'i').test(answer);
|
|
85
|
+
}
|
|
86
|
+
function cited(url: string, host: string): boolean {
|
|
87
|
+
try {
|
|
88
|
+
const name = new URL(url).hostname.toLowerCase().replace(/^www\./, '');
|
|
89
|
+
return name === host || name.endsWith(`.${host}`);
|
|
90
|
+
} catch { return false; }
|
|
91
|
+
}
|
|
92
|
+
function firstMention(answer: string, name: string): number | null {
|
|
93
|
+
const lower = answer.toLocaleLowerCase();
|
|
94
|
+
const needle = name.toLocaleLowerCase();
|
|
95
|
+
let at = lower.indexOf(needle);
|
|
96
|
+
while (at >= 0) {
|
|
97
|
+
const before = at ? lower[at - 1]! : '';
|
|
98
|
+
const after = lower[at + needle.length] ?? '';
|
|
99
|
+
if (!/[\p{L}\p{N}_]/u.test(before) && !/[\p{L}\p{N}_]/u.test(after)) return at;
|
|
100
|
+
at = lower.indexOf(needle, at + 1);
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function output(result: Data, calls = 0, errors = 0): void {
|
|
105
|
+
console.log(JSON.stringify({ ...result, calls, errors }));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function run(step: string): Promise<void> {
|
|
109
|
+
if (step === 'questions') {
|
|
110
|
+
if (input.questions !== undefined && !Array.isArray(input.questions)) throw new Error('questions must be an array');
|
|
111
|
+
const provided = strings(input.questions);
|
|
112
|
+
if (provided.length > 8 || (Array.isArray(input.questions) && provided.length !== input.questions.length)) throw new Error('questions must contain at most 8 nonempty strings');
|
|
113
|
+
if (provided.length) { output({ questions: provided }); return; }
|
|
114
|
+
try {
|
|
115
|
+
const response = reply(await invoke(['ask', '--json', `Generate exactly 5 potential customer questions about the category of ${brand}. Respond ONLY with a JSON array of 5 question strings. Do not include the brand name in any question.`]));
|
|
116
|
+
const generated = parseArray(response);
|
|
117
|
+
if (!Array.isArray(generated) || generated.length !== 5 || strings(generated).length !== 5 || strings(generated).some(q => q.toLocaleLowerCase().includes(brand.toLocaleLowerCase()))) throw new Error('ask returned invalid or branded questions');
|
|
118
|
+
output({ questions: generated }, 1);
|
|
119
|
+
} catch (e) { output({ outcome: 'fail', questions: [], error: errorLine(e) }, 1, 1); }
|
|
120
|
+
} else if (step === 'answers') {
|
|
121
|
+
const questions = strings(object(outputs.questions).questions);
|
|
122
|
+
if (!questions.length || questions.length > 8) throw new Error('missing questions');
|
|
123
|
+
const answers: Data[] = [];
|
|
124
|
+
let failures = 0;
|
|
125
|
+
for (const question of questions) for (const engine of engines) {
|
|
126
|
+
try {
|
|
127
|
+
const { answer, bare } = await askEngine(question, engine);
|
|
128
|
+
answers.push({ question, engine, answer, ...(bare ? {} : { bare: false }) });
|
|
129
|
+
}
|
|
130
|
+
catch (e) { failures++; answers.push({ question, engine, error: errorLine(e) }); }
|
|
131
|
+
}
|
|
132
|
+
output({ answers, outcome: failures === answers.length ? 'fail' : 'ok' }, answers.length, failures);
|
|
133
|
+
} else if (step === 'web') {
|
|
134
|
+
const questions = strings(object(outputs.questions).questions);
|
|
135
|
+
const web: Data[] = [];
|
|
136
|
+
let failures = 0;
|
|
137
|
+
for (const question of questions) {
|
|
138
|
+
try { web.push({ question, urls: resultUrls(await invoke(['research', '--json', '--limit', '5', question])).slice(0, 5) }); }
|
|
139
|
+
catch (e) { failures++; web.push({ question, urls: [], error: errorLine(e) }); }
|
|
140
|
+
}
|
|
141
|
+
output({ web }, questions.length, failures);
|
|
142
|
+
} else if (step === 'score') {
|
|
143
|
+
const answers = rows(object(outputs.answers).answers);
|
|
144
|
+
const web = rows(object(outputs.web).web);
|
|
145
|
+
if (!answers.length) throw new Error('missing answers');
|
|
146
|
+
const domain = domains(text(input.domain));
|
|
147
|
+
const table = answers.map(cell => {
|
|
148
|
+
const answer = text(cell.answer);
|
|
149
|
+
if (cell.error) return { question: cell.question, engine: cell.engine, error: cell.error, mention: false, citation: domain ? false : null, rank: null };
|
|
150
|
+
const position = firstMention(answer, brand);
|
|
151
|
+
const candidates = [...urls(answer), ...strings(web.find(row => row.question === cell.question)?.urls)];
|
|
152
|
+
return { question: cell.question, engine: cell.engine, mention: position !== null,
|
|
153
|
+
citation: domain ? candidates.some(url => cited(url, domain)) || mentionsHost(answer, domain) : null, rank: position === null ? null : answer.slice(0, position).trim().split(/\s+/).filter(Boolean).length + 1 };
|
|
154
|
+
});
|
|
155
|
+
output({ table }, 0, table.filter(cell => cell.error).length);
|
|
156
|
+
} else if (step === 'suggest') {
|
|
157
|
+
const table = rows(object(outputs.score).table);
|
|
158
|
+
if (!table.length) throw new Error('missing score table');
|
|
159
|
+
const gaps = table.filter(cell => cell.error || !cell.mention || cell.citation === false).map(cell => ({ question: cell.question, engine: cell.engine, error: cell.error ?? null, mention: cell.mention, citation: cell.citation }));
|
|
160
|
+
if (!gaps.length) { output({ suggestions: [] }); return; }
|
|
161
|
+
try {
|
|
162
|
+
const raw = parseArray(reply(await invoke(['ask', '--json', `Using ONLY this score table, return a JSON array of 3 to 5 improvement suggestions. Each must have "suggestion" (FAQ question, FAQPage/Organization structured data, comparison document, etc.) and "evidence" containing a question and engine from the missing cells. Evidence cells: ${JSON.stringify(gaps)}. Score table: ${JSON.stringify(table)}`])));
|
|
163
|
+
if (!Array.isArray(raw) || raw.length < 3 || raw.length > 5) throw new Error('invalid suggestion count');
|
|
164
|
+
const suggestions = raw.map(item => {
|
|
165
|
+
const candidate = object(item);
|
|
166
|
+
const evidence = object(candidate.evidence);
|
|
167
|
+
const match = gaps.find(cell => cell.question === evidence.question && cell.engine === evidence.engine);
|
|
168
|
+
if (!text(candidate.suggestion) || !match) throw new Error('suggestion missing valid score evidence');
|
|
169
|
+
return { suggestion: text(candidate.suggestion), evidence: { question: match.question, engine: match.engine } };
|
|
170
|
+
});
|
|
171
|
+
output({ suggestions }, 1);
|
|
172
|
+
} catch (e) { output({ outcome: 'fail', suggestions: [], error: errorLine(e) }, 1, 1); }
|
|
173
|
+
} else if (step === 'report') {
|
|
174
|
+
const table = rows(object(outputs.score).table);
|
|
175
|
+
const answers = rows(object(outputs.answers).answers);
|
|
176
|
+
const suggestions = rows(object(outputs.suggest).suggestions);
|
|
177
|
+
if (!table.length) throw new Error('missing score table');
|
|
178
|
+
const outDir = text(input.outDir) || defaultOutDir;
|
|
179
|
+
const success = table.filter(cell => !cell.error);
|
|
180
|
+
const citationSummary = text(input.domain) ? `${success.filter(cell => cell.citation).length}/${success.length} cite ${text(input.domain)}.` : 'Citation unknown (no domain provided).';
|
|
181
|
+
const summary = `${brand}: ${success.filter(cell => cell.mention).length}/${success.length} available answers mention the brand; ${citationSummary}`;
|
|
182
|
+
const markdown = [`# GEO check — ${line(brand)}`, '', summary, '', '| Question | Engine | Mention | Citation | Rank | Error |', '| --- | --- | --- | --- | --- | --- |',
|
|
183
|
+
...table.map(cell => `| ${line(cell.question)} | ${line(cell.engine)} | ${cell.mention ? 'yes' : 'no'} | ${cell.citation === null ? 'unknown' : cell.citation ? 'yes' : 'no'} | ${cell.rank ?? '—'} | ${line(cell.error)} |`),
|
|
184
|
+
'', '## Suggestions', ...suggestions.map(item => `- ${line(item.suggestion)} — ${line(object(item.evidence).question)} / ${line(object(item.evidence).engine)}`),
|
|
185
|
+
'', '## Answers', ...answers.map(cell => `### ${line(cell.question)} — ${line(cell.engine)}\n\n${line(cell.error) || text(cell.answer).slice(0, 400)}\n`), ''].join('\n');
|
|
186
|
+
mkdirSync(outDir, { recursive: true });
|
|
187
|
+
const md = join(outDir, 'report.md');
|
|
188
|
+
const json = join(outDir, 'report.json');
|
|
189
|
+
writeFileSync(md, markdown, { mode: 0o600 });
|
|
190
|
+
writeFileSync(json, JSON.stringify({ brand, domain: text(input.domain), summary, table, suggestions, answers }, null, 2) + '\n', { mode: 0o600 });
|
|
191
|
+
output({ report: md, reportJson: json, summary }, 0, table.filter(cell => cell.error).length);
|
|
192
|
+
} else throw new Error(`unknown geo-check step: ${step}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await run(process.argv[2] ?? '');
|
|
196
|
+
} catch (e) {
|
|
197
|
+
console.log(JSON.stringify({ outcome: 'fail', error: errorLine(e), calls: 0, errors: 1 }));
|
|
198
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://antigravity.google/schemas/v1/plugin.json",
|
|
3
|
+
"name": "geo-check",
|
|
4
|
+
"version": "0.1.2",
|
|
5
|
+
"description": "Measure brand mentions and citations in AI answers and web results.",
|
|
6
|
+
"extensions": {
|
|
7
|
+
"ai.elanous": {
|
|
8
|
+
"graphs": [
|
|
9
|
+
"./graphs/geo-check.yaml"
|
|
10
|
+
],
|
|
11
|
+
"capabilities": [
|
|
12
|
+
"fs:workdir",
|
|
13
|
+
"proc:bun",
|
|
14
|
+
"proc:elanous"
|
|
15
|
+
],
|
|
16
|
+
"connectors": [],
|
|
17
|
+
"pricing": {
|
|
18
|
+
"model": "free"
|
|
19
|
+
},
|
|
20
|
+
"trust": "official",
|
|
21
|
+
"category": "Marketing"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -12,11 +12,41 @@
|
|
|
12
12
|
elanous graph run /absolute/path/to/installed/job-coach/graphs/report-enterprise.yaml --input '{"interview":"/private/path/to/fictional-enterprise.md"}' --json
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
### 기업 모드 샘플 입력 (가상 정보만)
|
|
16
|
+
|
|
17
|
+
다음은 **가상 인물** 민서 하와 **가상 회사** 푸른달 스튜디오의 예시다. 이미지 경로는 실제 사진이 아닌 플레이스홀더이며, 인터뷰 안의 참고용 표기일 뿐이다. 그래프 입력 필드는 `interview` 하나로, 이미지 파일을 읽거나 전송하는 입력은 없다. 비공개 작업 디렉터리에 `fictional-enterprise.md`로 저장한 뒤 위 명령의 `interview` 경로를 그 파일의 절대 경로로 바꾼다.
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
## 기업 개요
|
|
21
|
+
푸른달 스튜디오 (가상 회사). 이미지 참고 경로: /placeholder/images/fictional-company.png
|
|
22
|
+
|
|
23
|
+
## 교육 대상
|
|
24
|
+
민서 하 (가상 인물), 문서 기획 담당자
|
|
25
|
+
|
|
26
|
+
## 현업 과제
|
|
27
|
+
가상 행사 안내 문서의 기획
|
|
28
|
+
|
|
29
|
+
## 교육 목표
|
|
30
|
+
행사 안내 문서를 요구사항에 맞게 기획한다
|
|
31
|
+
|
|
32
|
+
## 현재 역량
|
|
33
|
+
문서 작성 경험 (자기보고)
|
|
34
|
+
|
|
35
|
+
## 교육 제약
|
|
36
|
+
예산과 승인 여부 미확인
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 입력·출력과 프라이버시 경계
|
|
40
|
+
|
|
41
|
+
입력은 기업 모드 인터뷰 Markdown **파일의 비공개 절대 경로**를 담은 `--input '{"interview":"/private/path/to/fictional-enterprise.md"}'`이다. 위 여섯 `##` 섹션이 필요하다. 실제 직원 이름·사진·인사정보·사내 기밀을 샘플, 명령 인자 또는 공개 저장소에 넣지 않는다. 실제 인터뷰를 사용할 경우에도 민감 정보를 제거하고 접근 제한된 저장소에서만 실행한다. 그래프 러너는 입력·노드 stdout을 런 상태와 컨텍스트 JSON에 저장하므로 **파일 경로만 인자로 넘겨도 보고서와 런 상태에는 인터뷰에서 파생된 직무·역량이 남는다**. 파일 경로가 개인정보 비노출이나 무저장을 보장하는 것은 아니다.
|
|
42
|
+
|
|
43
|
+
출력은 CLI의 상태 JSON(`statePath`와 `done`/`failed`) 및 그 상태 경로와 같은 디렉터리의 `<run-id>/report.md`다. 보고서는 아래 일곱 섹션으로 구성된다. NCS 조회가 실패하면 `failed`로 끝나며 확인되지 않은 과정·URL 및 내부 승인 여부는 `미확인`으로 남긴다. **No-send boundary:** 이 그래프 실행은 보고서를 로컬에 작성하는 경로이며 메신저·이메일로 보고서를 보내거나 마켓에 게시하는 단계가 없다. `--no-send` 플래그를 지원한다고 가정하지 않는다. 다만 NCS API 조회 및 외부 과정 조사는 네트워크 요청이므로 오프라인·무외부전송 실행이 아니다. 보고서 배포나 마켓 게시/시장 출시는 이 플러그인 README의 범위 밖이다.
|
|
44
|
+
|
|
15
45
|
기업 보고서는 `기업 개요 및 교육 대상`, `교육 니즈`, `직무·과제 및 NCS`, `AI 적합성`, `역량 진단`, `교육 로드맵`, `교육 명세·운영 경로` 순서다. NCS 조회에 실패하면 매칭을 발명하지 않고 런을 실패로 종료한다. `run-report` 스킬은 개인 보고서 진입이며 기업 모드는 다섯 개 기업 스킬(`enterprise-needs`, `enterprise-task-analysis`, `enterprise-ai-fit`, `enterprise-competency`, `enterprise-roadmap`)과 기업 그래프를 이용한다.
|
|
16
46
|
|
|
17
47
|
## 실행
|
|
18
48
|
|
|
19
|
-
공공데이터포털 [한국산업인력공단 NCS 기준정보 조회](https://www.data.go.kr/data/15128213/openapi.do) 활용신청 후 키를 **환경변수 `NCS_SERVICE_KEY`** 로 설정한다. 설치 시 `credentials` 이벤트의 `ncs.serviceKey` 에 입력하는 경로는 플러그인 설치기가 담당한다. 저장소에는 키를 넣지 않는다. `.mcp.json`은 MCP stdio 서버를 번들로 제공한다. 레시피는 러너가 제공하는 `ELANOUS_GRAPH_DIR`을 기준으로 번들 스크립트를 찾고, MCP의 `${CODEX_PLUGIN_ROOT}`는 Codex가 설치한 플러그인의 절대 경로로 치환한다. 런타임에는 `bun`과 PATH의 `elanous research --json`가 필요하다. 중첩 elanous
|
|
49
|
+
공공데이터포털 [한국산업인력공단 NCS 기준정보 조회](https://www.data.go.kr/data/15128213/openapi.do) 활용신청 후 키를 **환경변수 `NCS_SERVICE_KEY`** 로 설정한다. 설치 시 `credentials` 이벤트의 `ncs.serviceKey` 에 입력하는 경로는 플러그인 설치기가 담당한다. 저장소에는 키를 넣지 않는다. `.mcp.json`은 MCP stdio 서버를 번들로 제공한다. 레시피는 러너가 제공하는 `ELANOUS_GRAPH_DIR`을 기준으로 번들 스크립트를 찾고, MCP의 `${CODEX_PLUGIN_ROOT}`는 Codex가 설치한 플러그인의 절대 경로로 치환한다. 런타임에는 `bun`과 PATH의 `elanous research --json`가 필요하다. 중첩 elanous 호출은 그래프를 돌린 우주를 그대로 따른다(`--test` 를 붙이지 않는다 — 저장소 밖에서는 `--test` 가 격리 루트를 못 정해 멈춘다). 인터뷰 파일은 `## 희망 직무`, `## 경험`, `## 보유 역량`을 포함해야 한다.
|
|
20
50
|
|
|
21
51
|
```sh
|
|
22
52
|
elanous graph run /absolute/path/to/installed/job-coach/graphs/report.yaml --input '{"interview":"/absolute/path/to/installed/job-coach/examples/interview-sample.md"}' --json
|
|
@@ -230,7 +230,7 @@ async function run(step: string) {
|
|
|
230
230
|
const profile = value(outputs.profile);
|
|
231
231
|
const query = `${list(profile.jobs).join(' ')} 역량 교육 과정 강의 공식 출처`;
|
|
232
232
|
if (!query.trim()) throw new Error('missing job for research');
|
|
233
|
-
const cli = Bun.spawn(['elanous', '
|
|
233
|
+
const cli = Bun.spawn(['elanous', 'research', query, '--json'], { env: process.env, stdout: 'pipe', stderr: 'pipe' });
|
|
234
234
|
const stdout = await new Response(cli.stdout).text();
|
|
235
235
|
const stderr = await new Response(cli.stderr).text();
|
|
236
236
|
if (await cli.exited !== 0) throw new Error(`research failed: ${stderr.slice(0, 300)}`);
|
|
@@ -253,7 +253,7 @@ async function run(step: string) {
|
|
|
253
253
|
return;
|
|
254
254
|
}
|
|
255
255
|
const gaps = await judgeGaps(units, interview, async prompt => {
|
|
256
|
-
const cli = Bun.spawn(['elanous', '
|
|
256
|
+
const cli = Bun.spawn(['elanous', 'ask', '--json', prompt], { env: process.env, stdout: 'pipe', stderr: 'pipe' });
|
|
257
257
|
const stdout = await new Response(cli.stdout).text();
|
|
258
258
|
await new Response(cli.stderr).text();
|
|
259
259
|
if (await cli.exited !== 0) throw new Error('gap LLM failed');
|
|
@@ -15,13 +15,14 @@ CFG=$(OUT="$OUT" W="$W" H="$H" T="$TITLE" G="$TAG" F="$FOOT" python3 -c '
|
|
|
15
15
|
import json, os
|
|
16
16
|
print(json.dumps({"out": os.environ["OUT"], "width": int(os.environ["W"]), "height": int(os.environ["H"]),
|
|
17
17
|
"title": os.environ["T"], "tagline": os.environ["G"], "footer": os.environ["F"]}))')
|
|
18
|
-
|
|
18
|
+
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/endcard.XXXXXX") || { echo "⛔ 임시 스크립트를 만들지 못했다" >&2; exit 1; }
|
|
19
|
+
TMP="$TMP_DIR/endcard.js"
|
|
19
20
|
CFG="$CFG" python3 -c '
|
|
20
21
|
import os, io, sys
|
|
21
22
|
io.open(sys.argv[2], "w", encoding="utf-8").write(
|
|
22
23
|
io.open(sys.argv[1], encoding="utf-8").read().replace("__CFG__", os.environ["CFG"]))' "$HERE/endcard.js" "$TMP"
|
|
23
24
|
out=$(python3 "$AFF" run "$TMP" 2>&1); rc=$?
|
|
24
|
-
rm -f "$TMP"; echo "$out"
|
|
25
|
+
rm -f "$TMP"; rmdir "$TMP_DIR"; echo "$out"
|
|
25
26
|
printf '%s' "$out" | grep -qE '^Error:|Uncaught|TypeError|ReferenceError' && exit 1
|
|
26
27
|
[ $rc -ne 0 ] && exit 1
|
|
27
28
|
[ -f "$OUT" ] || { echo "⛔ 산출이 «없다»: $OUT" >&2; exit 1; }
|
|
@@ -37,7 +37,8 @@ print(json.dumps({"src": os.environ["SRC"], "out": os.environ["OUT"],
|
|
|
37
37
|
"width": int(os.environ["W"]), "height": int(os.environ["H"]),
|
|
38
38
|
"blur": float(os.environ["BLUR"]), "mirror": os.environ["MIRROR"] == "1"}))')
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
TMP_DIR=$(mktemp -d "${TMPDIR:-/tmp}/reframe.XXXXXX") || { echo "⛔ 임시 스크립트를 만들지 못했다" >&2; exit 1; }
|
|
41
|
+
TMP="$TMP_DIR/reframe.js"
|
|
41
42
|
CFG="$CFG" python3 -c '
|
|
42
43
|
import os, io, sys
|
|
43
44
|
src = io.open(sys.argv[1], encoding="utf-8").read()
|
|
@@ -46,6 +47,7 @@ io.open(sys.argv[2], "w", encoding="utf-8").write(src.replace("__CFG__", os.envi
|
|
|
46
47
|
|
|
47
48
|
out=$(python3 "$AFF" run "$TMP" 2>&1); rc=$?
|
|
48
49
|
rm -f "$TMP"
|
|
50
|
+
rmdir "$TMP_DIR"
|
|
49
51
|
echo "$out"
|
|
50
52
|
# ⛔ aff.py 는 스크립트 «안»의 오류도 exit 0 으로 낼 수 있다 — 산출을 «읽어서» 가른다.
|
|
51
53
|
if printf '%s' "$out" | grep -qE '^Error:|Uncaught|TypeError|ReferenceError'; then exit 1; fi
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public export leak check on changed files (LEAK1) — the same check `release prepare` fails on:
|
|
3
|
+
* manifest transforms and private redactions applied, then the leak markers. A hit here fails the next prepare
|
|
4
|
+
* (0.2.7: one test title), so `pr land` and the harness policy gates run it before landing.
|
|
5
|
+
*
|
|
6
|
+
* bun scripts/ci-public-export-leak-gate.ts --changed-files a b … # exit 1 on a hit · 0 when clean or not measurable
|
|
7
|
+
*/
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { join, resolve } from 'node:path';
|
|
11
|
+
import { privateRedactionsPath } from './public-export.js';
|
|
12
|
+
|
|
13
|
+
export interface ExportLeakCheck {
|
|
14
|
+
readonly measured: boolean;
|
|
15
|
+
readonly hits: ReadonlyArray<{ readonly file: string; readonly line: number; readonly marker: string }>;
|
|
16
|
+
readonly detail?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function exportLeakCheck(changedFiles: readonly string[], root: string): ExportLeakCheck {
|
|
20
|
+
const script = join(root, 'scripts', 'public-export.ts');
|
|
21
|
+
if (!existsSync(script)) return { measured: false, hits: [], detail: 'scripts/public-export.ts 없음' };
|
|
22
|
+
// Without the private table (a Pod, a fresh machine) the markers still run; private identifiers are not checked.
|
|
23
|
+
const privateList = existsSync(privateRedactionsPath()) ? [] : ['--no-private-list'];
|
|
24
|
+
const r = spawnSync('bun', [script, '--leak-check', '--json', '--all', ...privateList, '--files', ...changedFiles], { cwd: root, encoding: 'utf-8', timeout: 180_000, maxBuffer: 64 * 1024 * 1024 });
|
|
25
|
+
if (r.status !== 0 && r.status !== 1) return { measured: false, hits: [], detail: `rc=${r.status ?? r.signal}` };
|
|
26
|
+
try {
|
|
27
|
+
const parsed = JSON.parse(String(r.stdout).trim().split('\n').at(-1) ?? '') as { hits?: Array<{ file: string; line: number; marker: string }> };
|
|
28
|
+
return { measured: true, hits: (parsed.hits ?? []).map((h) => ({ file: h.file, line: h.line, marker: h.marker })) };
|
|
29
|
+
} catch { return { measured: false, hits: [], detail: 'JSON 을 못 읽었다' }; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Gate form for the harness policy gates: 1 on a hit, 0 when clean or not measurable (said in a line). */
|
|
33
|
+
export function runPublicExportLeakGate(io: { args: readonly string[]; cwd: string; log: (line: string) => void; error: (line: string) => void }): number {
|
|
34
|
+
const at = io.args.indexOf('--changed-files');
|
|
35
|
+
const files = at >= 0 ? io.args.slice(at + 1).filter((a) => !a.startsWith('--')) : [];
|
|
36
|
+
if (files.length === 0) return 0;
|
|
37
|
+
const result = exportLeakCheck(files, io.cwd);
|
|
38
|
+
if (!result.measured) { io.log(`⚠ public-export-leak: 못 쟀다(${result.detail ?? '?'})`); return 0; }
|
|
39
|
+
if (result.hits.length === 0) return 0;
|
|
40
|
+
io.error(`✗ public-export-leak: 바뀐 파일 ${result.hits.length}곳이 공개본에 사적 흔적을 싣는다 — 다음 release prepare 가 여기서 실패한다:`);
|
|
41
|
+
for (const h of result.hits) io.error(` ${h.file}:${h.line} ${h.marker}`);
|
|
42
|
+
io.error(' 그 줄에서 표식·홈 경로·내부 문서 이름을 빼라(주석이면 공개본 변환이 지우는지 bun scripts/public-export.ts --leak-check --files <파일> 로 확인).');
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (import.meta.main) {
|
|
47
|
+
process.exit(runPublicExportLeakGate({ args: process.argv.slice(2), cwd: resolve(import.meta.dir, '..'), log: console.log, error: console.error }));
|
|
48
|
+
}
|