elanous 0.2.4 → 0.2.5
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/README.md +3 -3
- package/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/chunks/06ef2eba-d1b99ac6d2ea663c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1413-7179a49332db5a80.js +1 -0
- package/apps/pwa/out/_next/static/chunks/150-25c277137a9638ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1640-bc99ec23bbe29d12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 1751.50b96ea9c37b49b6.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2300-de059d0ab2a16630.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2684-05b0a81e75298c16.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3026-2ed7e317d9b02102.js +25 -0
- package/apps/pwa/out/_next/static/chunks/3611-77496ada38cd6358.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3717-68957c4dc9ceec90.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3929-46ba9af6f9d895cc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4015-62c5a3744698a8da.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4217-3c4317a7905f9eeb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4240-3230f230fae2d7d5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4271-e53e075ebd48593b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4377.3b125fef79576dfd.js +5 -0
- package/apps/pwa/out/_next/static/chunks/4440-18bb160d95819f4e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4853-35fdbe6bdbcdc0fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → 4daa2798-1363d725c79f3593.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5007-6a3b30e23ff68a6e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5244-12b424c42070f5ff.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{5791-bb82743fa6299562.js → 5348-bbf838e119661eee.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/5391-8793f88b03b30161.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5611-df00af3e4e4a61bb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5890-dca2321773be829a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6123-37c9dc57ddaa7ab0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6588-b7975304a302ab91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6604-c03961962b6273b6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7109-cf8c87e968e83a65.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7178-30f77877525a2efb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7262-871563fc9663730b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7294-f0eeebb92aa93459.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7429-881af44deed6958f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7527.943753b6ed829f09.js +1 -0
- package/apps/pwa/out/_next/static/chunks/764-a01e858525a190c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7868-e35d5f7985ecd3f1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8320-51484af7b01fdfa5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 8617.465ca22a319c95b3.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 8918-482b30ce2a8ebf4c.js} +3 -3
- package/apps/pwa/out/_next/static/chunks/9131-52f5fad51cceca04.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9238.2cd5f47f4bf7a2c3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 9704-3221c102623c8d73.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-3ffd80166b1050aa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f89e9a303515a033.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-4f403cd060442c95.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/{page-9fa879ebf5959096.js → page-0ac5d053f0444d21.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-4bcc63e272ae8205.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-880734bd07d62882.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/{page-4472ddf1e2458e69.js → page-72bd384898d88955.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-1018bbc9586ecae5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-de86f8c38076dd7b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-cd6447196d8bafe6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-e733630c47fa102b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/page-d1c1a8357b161875.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-60c514128e81d787.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8b637b7e325e13c1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/{page-112f7e477bda77fd.js → page-bccce0babceae30f.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-63301d11dbe73836.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-a8cf0dc472060db2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-02fba54881ccfa49.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-d5e8e90f0038f8de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-b1ef6824f494466a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-d589bb0f233b1009.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c9bbaebef4cda059.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-3639ce7916b92fed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7450ae7478955653.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-70c404c7bb6d7f0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-b41ca4abfd618452.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-b333dd79ebee3fa9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-8be1227492793655.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-6e03e9371b552e0d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-911411e9fca18d03.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-792170e649b37088.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c40463466937bc91.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-509c798b80384536.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-1cac2aa21d77c417.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-9626799d9b4a2cd1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-84cd419494e16b0f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6f3850d-7f324c6566002113.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/framework-361487d2da603917.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-607f5beb94cb2e07.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-d0222d219e29dddf.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-a26980a31126decc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-edb2b37decf23e4d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{webpack-e5bea36f183b25e6.js → webpack-2bf59c720fe9542c.js} +1 -1
- package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
- package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → ktMqfrBbf8BMGac0jqLrb}/_buildManifest.js +1 -1
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +29 -27
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +29 -27
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +29 -27
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +26 -24
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +26 -24
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +26 -24
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +29 -27
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +29 -27
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +29 -27
- 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 +26 -24
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +29 -27
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +29 -27
- package/apps/pwa/out/manifest.webmanifest +5 -2
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +26 -24
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +26 -24
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +29 -27
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +29 -27
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +27 -25
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +29 -27
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +26 -24
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +29 -27
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +29 -27
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +29 -27
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +29 -27
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +29 -27
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +26 -24
- package/apps/pwa/out/sw-strategies.js +44 -0
- package/apps/pwa/out/sw.js +6 -44
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +29 -27
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +29 -27
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +29 -27
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +29 -27
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +29 -27
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +29 -27
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +29 -27
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +29 -27
- package/catalog/resources.yaml +6 -6
- package/docs/goal-context/steward-directives-2026-09-28.md +35 -37
- package/graphs/landing/landing-heal.yaml +1 -1
- package/graphs/release/recipes.yaml +8 -1
- package/graphs/release/release-loop.yaml +6 -1
- package/graphs/video/README.md +1 -0
- package/graphs/video/explainer-line.yaml +58 -0
- package/package.json +3 -1
- package/plugins/job-coach/.codex-plugin/plugin.json +18 -1
- package/plugins/job-coach/README.md +12 -0
- package/plugins/job-coach/connectors/ncs/server.ts +4 -3
- package/plugins/job-coach/graphs/gap-judge.ts +44 -0
- package/plugins/job-coach/graphs/recipes.yaml +27 -0
- package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
- package/plugins/job-coach/graphs/run-step.ts +211 -17
- package/plugins/job-coach/plugin.json +16 -3
- package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
- package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
- package/plugins/job-coach/skills/interview-to-profile/SKILL.md +18 -1
- package/scripts/backup/elanous-backup.sh +4 -1
- package/scripts/build-docs-site.ts +4 -3
- package/scripts/coord-channel-watch.sh +23 -4
- package/scripts/coord-post.sh +49 -12
- package/scripts/coord-tracks.json +5 -5
- package/scripts/eval-bot-vs-grok.ts +79 -0
- package/scripts/eval-grok-live-baseline.ts +36 -0
- package/scripts/fixtures/front-route-corpus.sample.json +17 -0
- package/scripts/fleet/node-setup.sh +46 -7
- package/scripts/landing-heal/must-fix-match.ts +26 -0
- package/scripts/landing-heal/run-mission.ts +114 -20
- package/scripts/lib/front-route-measurement.ts +131 -0
- package/scripts/lib/tui-sim-keys.ts +42 -0
- package/scripts/measure-front-route-classifier.ts +56 -0
- package/scripts/media/ocr-leak-scan.sh +21 -0
- package/scripts/public-export.ts +2 -3
- package/scripts/public-import-pr.ts +80 -6
- package/scripts/publish-internal-dist.ts +2 -3
- package/scripts/red-sweep-diff.ts +1 -1
- package/scripts/release-loop/auto-approve-node.ts +72 -0
- package/scripts/release-loop/gate-node.ts +332 -44
- package/scripts/release-loop/publish-node.ts +7 -3
- package/scripts/release-loop/release-docs.ts +20 -13
- package/scripts/release-loop/test-durations.ts +63 -0
- package/scripts/release-loop/upgrade-node.ts +3 -2
- package/scripts/test-audit-coverage-per-file.sh +16 -0
- package/scripts/test-audit-meaningless.ts +88 -0
- package/scripts/test-audit-overlap-and-weak.ts +73 -0
- package/scripts/test-deterministic.ts +27 -5
- package/scripts/tui-sim.ts +59 -14
- package/scripts/ux/bump-installed.sh +20 -0
- package/scripts/ux/lib/cdp.ts +72 -0
- package/scripts/ux/record-page.ts +50 -0
- package/skills/explainer-video/SKILL.md +99 -0
- package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
- package/skills/explainer-video/engine/build.mjs +431 -0
- package/skills/explainer-video/engine/vo.ts +37 -0
- package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
- package/src/agent/nested-agent-env.ts +1 -0
- package/src/agent-mission/driver.ts +95 -16
- package/src/agent-mission/mission-cli.ts +6 -0
- package/src/agent-mission/resource-ladder.ts +73 -25
- package/src/auth/acp-token.ts +16 -0
- package/src/auth/github-app-token.ts +112 -0
- package/src/autopilot/gh-rest-fallback.ts +177 -0
- package/src/autopilot/pr-manager.ts +51 -4
- package/src/boot/acp-server.ts +2 -16
- package/src/boot/daemon-tools/web-search.ts +2 -1
- package/src/boot/ws-bridge.ts +11 -0
- package/src/chat/index.ts +37 -8
- package/src/chat/log-scroll-key.ts +12 -0
- package/src/chat/submit-trace.ts +30 -0
- package/src/cli/agent-cli.ts +7 -2
- package/src/cli/decisions-cli.ts +94 -0
- package/src/cli/doctor-cli.ts +40 -54
- package/src/cli/doctor-readiness.ts +16 -24
- package/src/cli/intake-cli.ts +1 -1
- package/src/cli/mcp-call.ts +9 -0
- package/src/cli/mcp-gateway-cli.ts +7 -3
- package/src/cli/plugin-cli.ts +109 -11
- package/src/cli/pr-lineage.ts +2 -1
- package/src/cli/pwa-start.ts +3 -0
- package/src/cli/release-cli.ts +94 -7
- package/src/cli/self-update.ts +7 -4
- package/src/cli/start-cli.ts +33 -14
- package/src/dashboard/acp-boot.ts +3 -0
- package/src/dashboard/index.ts +62 -13
- package/src/dashboard/input/no-provider-reply.ts +13 -0
- package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
- package/src/dashboard/stale-install-check.ts +77 -0
- package/src/decisions/decision-ledger.ts +404 -0
- package/src/domains/outbound-alert.ts +8 -2
- package/src/domains/telegram-kind-route.ts +21 -0
- package/src/execution-loop/dispatch-task.ts +139 -0
- package/src/execution-loop/gate-decision-summary.ts +29 -0
- package/src/execution-loop/gates.ts +197 -0
- package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
- package/src/graph-kinds/plugin-nodes.ts +10 -2
- package/src/graph-runner/runner.ts +35 -5
- package/src/grounding/token.ts +12 -6
- package/src/harness/harness-cli-command.ts +68 -9
- package/src/harness/harness-pod-dispatch.ts +8 -2
- package/src/harness/harness-stop.ts +147 -18
- package/src/harness/self-send-decide.ts +26 -17
- package/src/harness/self-send-target.ts +22 -1
- package/src/harness/worktree-branch-prefix.ts +4 -3
- package/src/index.ts +56 -13
- package/src/instance/resolve.ts +6 -0
- package/src/intake-plane/collect-telegram-saved.ts +5 -0
- package/src/intake-plane/digest.ts +11 -2
- package/src/live/detail-switch.ts +8 -3
- package/src/llm/usable-llm.ts +32 -0
- package/src/loops/registry.ts +27 -5
- package/src/market/publish.ts +59 -3
- package/src/native-tool-catalog.ts +2 -2
- package/src/nexus/api/graph-approvals.ts +125 -0
- package/src/nexus/api/http-server.ts +60 -6
- package/src/nexus/api/log-fabric.ts +10 -1
- package/src/nexus/api/mcp-oauth-hint.ts +10 -9
- package/src/nexus/api/merge-approvals.ts +2 -1
- package/src/nexus/api/meta-api.ts +14 -0
- package/src/nexus/api/plugins-credentials-api.ts +47 -0
- package/src/nexus/api/plugins-market.ts +87 -3
- package/src/nexus/api/pod-credential-api.ts +68 -14
- package/src/nexus/api/public-routes.ts +7 -0
- package/src/nexus/api/trace.ts +57 -6
- package/src/nexus/boot/register-mcp-clients.ts +99 -14
- package/src/nexus/index.ts +33 -32
- package/src/nexus/outbound/router.ts +1 -1
- package/src/onboarding.ts +25 -2
- package/src/plugins/core/host.ts +14 -0
- package/src/plugins/install/market-fetch.ts +4 -3
- package/src/plugins/install/plugin-credentials.ts +106 -0
- package/src/plugins/install/plugin-install.ts +163 -32
- package/src/prompt-library/universal-preamble.ts +8 -2
- package/src/release-loop/checklist.ts +191 -0
- package/src/release-loop/release-note.ts +6 -3
- package/src/self-dev/dev-cli.ts +40 -20
- package/src/self-dev/dev-pipeline.ts +77 -1
- package/src/self-implement/gate-baseline.ts +3 -1
- package/src/self-implement/goal-author.ts +18 -1
- package/src/self-implement/host-regate.ts +6 -2
- package/src/self-implement/orchestrator.ts +7 -2
- package/src/self-implement/seams.ts +4 -2
- package/src/skills/tools/dev-harness.ts +42 -0
- package/src/steward/steward-cards.ts +45 -0
- package/src/steward/triage.ts +50 -14
- package/src/task-orchestrator/surfaces/pod-artifact-return.ts +61 -3
- package/src/task-orchestrator/surfaces/pod-command-job.ts +27 -6
- package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +39 -9
- package/src/task-orchestrator/surfaces/pod-source-bundle.ts +16 -1
- package/src/task-orchestrator/surfaces/pod-source-receive.ts +51 -13
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +172 -28
- package/src/telegram-report.ts +12 -6
- package/src/tui-client/acp-transport-ws-client.ts +72 -5
- package/src/tui.ts +3 -2
- package/src/user-config.ts +28 -2
- package/src/util/spawn-sync-output.ts +15 -0
- package/src/version/code-revision.ts +1 -1
- package/src/version/packed-revision.json +1 -1
- package/src/web-search/free.ts +77 -0
- package/src/web-search/index.ts +2 -0
- package/src/workflow-runtime/deps-bridge.ts +4 -3
- package/src/workflow-runtime/plugin-kind-node.ts +32 -4
- package/src/workflow-runtime/types.ts +2 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1191-7b13a24117a689fa.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-3fcae5186c64fe6b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
- package/apps/pwa/out/_next/static/chunks/319-d37df1b6e0cbeff4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4836-3c93795ab834381e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6090-1bd6077591603947.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7457-f5cb1cc8db9f7453.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-011e1f474efb8b33.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8519-0059d82bdb4812bf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9740-52467f244755917f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-21c3261af6ab377c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1991445b8ea86274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-4ebfaee2369fe248.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ca7c3cc305c3285a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-9951930add3d2051.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-4de08d4b766f134b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7a975e2b9db1dfb7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-49270714ef35bb96.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-33fbdf353e2a5982.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/live/page-259f122e2dd64317.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-65b82726753658e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-4b8df3748dc06285.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-542a5c0006da637c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-63ffb347831262ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-98e4e359d97263b4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-97e24ac675ce576e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-ac6391f26391d89d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-9f45c9d45ffc43bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-36995b9cb5d98218.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-0c795b577988d63f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b4311e2f71c6735a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-eb2381074df20dba.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-b01336e64eba01ae.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-7af7f21c1f2858ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-a98ecd364586c89b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-57f0502dfbfa089b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-3467d2e1abd8a14a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-7842ca23d53ba682.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-ae9ac882912b0198.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-3a0d72e3f783bcef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-f3b39fe8358c2a97.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-5a22af3181ac2afb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-1457627bb302a22c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/css/e66b7f5efc8df3f3.css +0 -1
- /package/apps/pwa/out/_next/static/{zoQX5iTvXOsIvCjxCe1Ji → ktMqfrBbf8BMGac0jqLrb}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enterprise-needs
|
|
3
|
+
description: Extract evidence-backed organizational learning needs from a fictional enterprise interview without claiming that a requested course is the solution.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use after `interview-to-profile` in enterprise mode (`intake` → `needs`). Read the enterprise interview headings `## 기업 개요`, `## 교육 대상`, `## 현업 과제`, `## 교육 목표`, `## 현재 역량`, `## 교육 제약`. Distinguish what the organization requested from what its workflow evidence supports. For each need, record the affected learner group, task or bottleneck, desired observable outcome, evidence section and missing information. Keep unsupported causes and target levels `미확인`; ask for missing evidence rather than invent a baseline or learner count. A request for AI training is not proof that AI is the best intervention.
|
|
7
|
+
|
|
8
|
+
Use only fictional employer and learner details in examples. Summarize or cite interview sections instead of reproducing interview paragraphs; if an excerpt is essential, quote only a short relevant fragment without names or identifiers. Never print the raw interview or credentials into graph stdout. Company approval and data-use policy, NCS mappings, named courses and course URLs remain `미확인` until independently verified. Hand needs and unresolved questions to `task`, not a prematurely selected course.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enterprise-roadmap
|
|
3
|
+
description: Sequence evidence-backed enterprise learning and assessment into a conditional roadmap, not an approved company policy.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use after `enterprise-competency` at the `roadmap` stage. Order learning activities by prerequisite and task: baseline check, guided practice, task-based pilot with human review, then observable assessment. For each phase, show the learner group, supported task or verified NCS requirement, expected work artifact, success measure, responsible reviewer if known and prerequisites. If time, budget, data access, approval or baseline are absent from `## 교육 제약`, mark them `미확인` and describe the recommendation as conditional; do not invent durations, headcounts, policy approvals or guaranteed outcomes. Pass the ordered phases to `spec` for training specification and `routing` for delivery options. A course recommendation needs an inspected publisher page describing an actual course, direct URL and relevance to the cited unit; otherwise record course and URL as `미확인`, not a plausible link.
|
|
7
|
+
|
|
8
|
+
All examples must be fictional. Cite interview headings instead of copying raw interview text; any essential quote must be short, relevant and de-identified. NCS codes, course details and organization policy not independently confirmed remain `미확인`. Do not label this proposed roadmap an adopted company plan.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enterprise-task-analysis
|
|
3
|
+
description: Turn enterprise learning needs into traceable tasks and evidence requirements for NCS matching.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use at the enterprise `task` stage after `enterprise-needs`. For each interview-backed work task, capture the learner role, input, action, output, observable performance criterion and interview section that supports it. Separate a concrete work description from a hypothesized task; mark absent frequency, tools, access permissions and proficiency `미확인`. Return job candidate keywords and bounded task descriptions for the existing `ncs-match` stage. A job title is a search candidate, not a confirmed NCS classification. Let `ncs-match` verify a code, name, level and source from the NCS service; never manufacture a code or promote a loose search hit to a confirmed unit.
|
|
7
|
+
|
|
8
|
+
In examples use fictional roles and organizations. Do not pass raw interview paragraphs into graph stdout: cite the heading or use a brief de-identified excerpt only where necessary. Do not invent internal company policy, course names or course URLs; write `미확인` if absent. If no concrete task is documented, state that the task basis is `미확인` and request clarification instead of drawing an unsupported NCS match.
|
|
@@ -3,4 +3,21 @@ name: interview-to-profile
|
|
|
3
3
|
description: Extract career candidates, experience and self-reported skills from a Korean interview.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Read the interview Markdown
|
|
6
|
+
Read the interview Markdown without inventing evidence. Never print or log the original interview; keep quotations out of graph stdout and persistent run context. Where a report needs a quotation, use only a short, relevant excerpt necessary to support the claim, without identifying details; otherwise paraphrase and cite the interview section. Mark unknown NCS codes or units, course names or URLs, and company policy details `미확인` rather than guessing.
|
|
7
|
+
|
|
8
|
+
## Personal mode (existing contract)
|
|
9
|
+
|
|
10
|
+
Extract `jobs` (target job candidates), `skills` (self-reported capabilities) and `experienceCount` (number of concrete work or project descriptions). Do not persist the raw descriptions in graph stdout. Record unknowns as missing rather than extrapolating. For the personal graph, prepare headings `## 희망 직무`, `## 경험`, `## 보유 역량`; list items for experience and comma-separated skills. A job is a candidate, not a confirmed NCS code.
|
|
11
|
+
|
|
12
|
+
## Enterprise mode (interview-to-profile input)
|
|
13
|
+
|
|
14
|
+
Use a fictional organization in packaged examples. Prepare the interview under these headings, separating statements made by the organization from independently confirmed facts:
|
|
15
|
+
|
|
16
|
+
- `## 기업 개요`: fictional organization, sector and team; omit real employer and employee identifiers.
|
|
17
|
+
- `## 교육 대상`: roles, learner group and scope; do not infer headcount or proficiency.
|
|
18
|
+
- `## 현업 과제`: concrete tasks and workflow bottlenecks, not a presumed diagnosis.
|
|
19
|
+
- `## 교육 목표`: requested outcomes and observable success measures, if stated.
|
|
20
|
+
- `## 현재 역량`: self-reported capabilities and evidence; not a verified competency rating.
|
|
21
|
+
- `## 교육 제약`: time, budget, data access and approvals only when actually supplied; otherwise `미확인`.
|
|
22
|
+
|
|
23
|
+
For the enterprise graph, summarize only the minimum necessary organization context, job candidates, tasks, goals, current skills and constraints for downstream steps. Do not copy interview paragraphs or identifying quotations into run outputs. Keep a stated need distinct from an observed task and from a recommended intervention. An interview alone does not verify NCS classifications, course existence or URLs, applicability of AI to confidential data, or internal policy approval: mark each unknown `미확인` until independently verified. Do not present a suggested training plan as an approved company policy.
|
|
@@ -505,7 +505,10 @@ BACKUP_OK=1
|
|
|
505
505
|
if [ "${ELANOUS_BACKUP_NO_HEARTBEAT:-0}" = "1" ]; then
|
|
506
506
|
echo " (심박 끔 — ELANOUS_BACKUP_NO_HEARTBEAT=1 · 시험 전용)" >&2
|
|
507
507
|
elif [ -n "$HB_REPO" ] && [ -f "$HB_REPO/scripts/botlab/heartbeat-emit.sh" ]; then
|
|
508
|
-
|
|
508
|
+
# 심박은 «운영» 우주로 — 백업이 뜨는 것이 운영 ~/.elanous 이다. 크론은 환경변수가 없고 소스 트리는
|
|
509
|
+
# 트리 파생 시험 우주로 풀려(`where` layer=tree-derived) 심박이 «격리라 안 밀었다»로 3일 멎었다(09-30).
|
|
510
|
+
ELANOUS_STATE_DIR="${ELANOUS_STATE_DIR:-$HOME/.elanous}" ELANOUS_CONFIG_DIR="${ELANOUS_CONFIG_DIR:-$HOME/.elanous}" \
|
|
511
|
+
bash "$HB_REPO/scripts/botlab/heartbeat-emit.sh" backup "$BACKUP_OK" "$HB_REPO" "$BACKUP_SOURCE"
|
|
509
512
|
else
|
|
510
513
|
echo " ⚠️ 심박을 «못 보냈다» — 저장소 자리를 못 찾았다(HB_REPO=${HB_REPO:-없음})" >&2
|
|
511
514
|
fi
|
|
@@ -60,11 +60,12 @@ export const TRACK_SCRUBS: { pattern: RegExp; replace: string; why: string }[] =
|
|
|
60
60
|
/** 잔여 표식 — 하나라도 남으면 rc=1 */
|
|
61
61
|
const TRACK_REGISTRY = JSON.parse(
|
|
62
62
|
readFileSync(join(import.meta.dir, 'coord-tracks.json'), 'utf8'),
|
|
63
|
-
) as { tracks: { id: string; mark: string }[] };
|
|
63
|
+
) as { tracks: { id: string; mark: string; alias?: string }[] };
|
|
64
64
|
const TRACK_MARKS = TRACK_REGISTRY.tracks.map((t) => t.mark).join('');
|
|
65
|
-
|
|
65
|
+
/** 2글자 신원(OP·MK·TC·UX) ⊕ 옛 한 글자 별칭(S·T·O·F) — 둘 다 공개 문서에 남으면 안 된다. */
|
|
66
|
+
const TRACK_NAMES = [...new Set(TRACK_REGISTRY.tracks.flatMap((t) => [t.id, t.alias ?? '']).filter(Boolean))].join('|');
|
|
66
67
|
/** 잔여 표식 — 트랙 정본(`coord-tracks.json`)에서 파생한다. 트랙을 더해도 여기는 고치지 않는다. */
|
|
67
|
-
export const TRACK_RESIDUE = new RegExp(`[${TRACK_MARKS}]|(?<![\\w\\]])\\[
|
|
68
|
+
export const TRACK_RESIDUE = new RegExp(`[${TRACK_MARKS}]|(?<![\\w\\]])\\[(?:${TRACK_NAMES})\\](?!\\()`, 'gu');
|
|
68
69
|
|
|
69
70
|
export function scrubTrackMarks(md: string): { text: string; applied: string[] } {
|
|
70
71
|
let text = md;
|
|
@@ -107,13 +107,20 @@ fi
|
|
|
107
107
|
# 🆕 2026-09-24 (🅞 보고): 「나에게 온 «요청»」 표지가 🅢 로 고정돼 있었다 — `--track O` 감시에서 🅢·🅣 앞 요청이
|
|
108
108
|
# «🅞 에게 온 요청»으로 뜨고, 🅞 앞 요청은 일반어(부탁드립니다)가 없으면 안 떴다.
|
|
109
109
|
# ⇒ 표지를 트랙 정본(scripts/coord-tracks.json)에서 $TRACK 으로 찾아 식을 짓는다. 일반어는 «내 표지와 함께»일 때만.
|
|
110
|
-
|
|
110
|
+
# 🆕 09-30 역할 재편: 신원이 2글자 id(TC) ⊕ 직함(CTO) ⊕ 옛 alias(O) 셋이다 — --track 은 id·alias 어느 쪽도 받고,
|
|
111
|
+
# «내 글» 거르기와 «나에게 온 요청» 표지는 셋을 다 본다(전환기엔 옛 접두로 쓴 글도 내 글이다).
|
|
112
|
+
ENTRY=$(jq -c --arg t "$TRACK" '[.tracks[] | select(.id == $t or .alias == $t)][0] // empty' "$(dirname "$0")/coord-tracks.json" 2>/dev/null)
|
|
113
|
+
MARK=$(printf '%s' "$ENTRY" | jq -r '.mark // empty' 2>/dev/null)
|
|
114
|
+
SELF_ID=$(printf '%s' "$ENTRY" | jq -r '.id // empty' 2>/dev/null); SELF_ID=${SELF_ID:-$TRACK}
|
|
115
|
+
SELF_ALIAS=$(printf '%s' "$ENTRY" | jq -r '.alias // empty' 2>/dev/null); SELF_ALIAS=${SELF_ALIAS:-$SELF_ID}
|
|
116
|
+
SELF_TITLE=$(printf '%s' "$ENTRY" | jq -r '.title // empty' 2>/dev/null)
|
|
117
|
+
NAMES_ADDR="\\\\[$SELF_ID\\\\] 님|\\\\[$SELF_ALIAS\\\\] 님|$SELF_ID 님|$SELF_ID 께${SELF_TITLE:+|$SELF_TITLE 님|$SELF_TITLE 께}"
|
|
111
118
|
if [ -n "$MARK" ]; then
|
|
112
|
-
REQ_ADDR="$MARK 께|$MARK 에게|$MARK 께서|$MARK 에 요청|$MARK
|
|
119
|
+
REQ_ADDR="$MARK 께|$MARK 에게|$MARK 께서|$MARK 에 요청|$MARK 님|$NAMES_ADDR"
|
|
113
120
|
REQ_MARK="$MARK"
|
|
114
121
|
else
|
|
115
122
|
echo "⚠️ 트랙 정본에서 $TRACK 의 표지를 못 찾았다 — 요청 표지는 [$TRACK] 님 만 본다" >&2
|
|
116
|
-
REQ_ADDR="
|
|
123
|
+
REQ_ADDR="$NAMES_ADDR"
|
|
117
124
|
REQ_MARK="\\\\[$TRACK\\\\]"
|
|
118
125
|
fi
|
|
119
126
|
|
|
@@ -123,6 +130,9 @@ LOCKDIR="$TMP/ch${PR_NUM}-$TRACK.lock"
|
|
|
123
130
|
# 본문 기준선 — ⭐ 프로세스가 아니라 «파일»에 둔다. 그래서 감시가 죽어 있던 구간의 본문 변경도
|
|
124
131
|
# 부활 «첫 주기»에 뜬다(매뉴얼 §「부활 구간은 못 본다」의 **본문 축만** 닫힌다 — 코멘트 축은 그대로).
|
|
125
132
|
BODYFILE="$TMP/ch${PR_NUM}-$TRACK.body"
|
|
133
|
+
# 코멘트 기준선(since)도 «파일»에 둔다(RFC-coordination-cost §7a · C1 · 09-30) — 재기동·30분 만기 재무장 뒤에도
|
|
134
|
+
# 끊긴 자리부터 이어 읽는다. 24시간보다 오래된 기준선은 버리고 지금부터(한 번에 쏟아지는 것 방지).
|
|
135
|
+
SINCEFILE="$TMP/ch${PR_NUM}-$TRACK.since"
|
|
126
136
|
|
|
127
137
|
# 기동시각 — 소유 판정의 유일한 근거(PID 는 재사용된다). 공백은 정규화해서 비교한다.
|
|
128
138
|
proc_started() {
|
|
@@ -416,7 +426,7 @@ fetch_comments() { # $1=since(ISO8601) → stdout=포맷된 신규 행 · rc=0
|
|
|
416
426
|
# 그 글은 필터를 «통과»했고 창에도 떴다 — 그런데 다른 글 사이에 섞여 «요청»으로 안 보였다.
|
|
417
427
|
# 🔑 그러므로 결손은 「고르기」가 아니라 ***「고른 뒤 «구분»하지 않은 것」***이다.
|
|
418
428
|
# ⛔ 표지를 «본문 앞»에 둔다 — 뒤에 두면 BODY_CHARS 절단에 잘려 사라진다.
|
|
419
|
-
fmt="select(((.body | sub(\"^[[:space:]#]+\"; \"\")) | (startswith(\"**[$
|
|
429
|
+
fmt="select(((.body | sub(\"^[[:space:]#]+\"; \"\")) | (startswith(\"**[$SELF_ID]\") or startswith(\"[$SELF_ID]\") or startswith(\"**[$SELF_ALIAS]\") or startswith(\"[$SELF_ALIAS]\"))) | not)
|
|
420
430
|
| \"[#$PR_NUM 신규 id=\" + (.id|tostring) + \" \" + .created_at + \" @\" + .login + \"]\"
|
|
421
431
|
+ (if ((.body | test(\"$REQ_ADDR\")) or ((.body | test(\"$REQ_MARK\")) and (.body | test(\"부탁드립니다|부탁합니다\")))) then \" 🙋‼️ 나에게 온 «요청»\" else \"\" end)
|
|
422
432
|
+ \" \" + (.body[:$BODY_CHARS] | gsub(\"\n\"; \" ⏎ \"))"
|
|
@@ -501,6 +511,14 @@ watch_loop() {
|
|
|
501
511
|
echo " 📄 «본문» 감시 ON · 기준선 «없음» ⇒ 첫 주기를 기준선으로 삼고 이벤트를 안 낸다"
|
|
502
512
|
fi
|
|
503
513
|
last=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
514
|
+
if [ -s "$SINCEFILE" ]; then
|
|
515
|
+
local saved; saved=$(head -1 "$SINCEFILE" 2>/dev/null)
|
|
516
|
+
if [[ "$saved" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]] \
|
|
517
|
+
&& [ -z "$(find "$SINCEFILE" -mmin +1440 2>/dev/null)" ]; then
|
|
518
|
+
last="$saved"
|
|
519
|
+
echo " 🔁 코멘트 기준선 이어 읽기 since=$last (파일 $SINCEFILE) — 죽어 있던 구간도 첫 주기에 뜬다"
|
|
520
|
+
fi
|
|
521
|
+
fi
|
|
504
522
|
while true; do
|
|
505
523
|
sleep "$INTERVAL"
|
|
506
524
|
now=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
@@ -530,6 +548,7 @@ watch_loop() {
|
|
|
530
548
|
fi
|
|
531
549
|
[ -n "$out" ] && printf '%s\n' "$out"
|
|
532
550
|
last="$now"
|
|
551
|
+
printf '%s\n' "$last" > "$SINCEFILE" 2>/dev/null || true
|
|
533
552
|
# ⭐ 실패하다 «돌아왔으면» 한 줄로 말한다 — 조용히 복구되면 아무도 «공백 구간»이 있었음을 모른다.
|
|
534
553
|
if [ "$WATCH_FAILS" -gt 0 ]; then
|
|
535
554
|
printf '✅ [#%s 조회 «회복» %s] 연속 실패 %s회 뒤 성공 — 그 사이도 since=%s 로 «이어 붙였다»(놓친 것 없음)\n' \
|
package/scripts/coord-post.sh
CHANGED
|
@@ -41,25 +41,44 @@ grep -q '{{TS}}' "$BODY" || echo "⚠️ 본문에 {{TS}} 자리표시가 «없
|
|
|
41
41
|
|
|
42
42
|
# 트랙 신원은 «정본 한 곳»에서 읽는다 — scripts/coord-tracks.json. 못 읽으면 추측하지 않고 선다.
|
|
43
43
|
TRACKS_FILE="$(dirname "$0")/coord-tracks.json"
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
# 신원 = 2글자 id(OP·MK·TC·UX…) ⊕ 옛 한 글자 alias(S·T·O·F — 전환기엔 받되 경고). 목록은 정본에서만.
|
|
45
|
+
IDS=$(grep -o '"id": *"[A-Z][A-Z]*"' "$TRACKS_FILE" 2>/dev/null | sed 's/.*"\([A-Z]*\)"$/\1/' | tr '\n' ' ')
|
|
46
|
+
ALIASES=$(grep -o '"alias": *"[A-Z][A-Z]*"' "$TRACKS_FILE" 2>/dev/null | sed 's/.*"\([A-Z]*\)"$/\1/' | tr '\n' ' ')
|
|
47
|
+
[ -n "$IDS" ] || { echo "⛔ 트랙 정본을 읽지 못했다: $TRACKS_FILE — 발신하지 않는다" >&2; exit 4; }
|
|
46
48
|
ID="${COORD_ID:-S}"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
case " $IDS " in
|
|
50
|
+
*" $ID "*) ;;
|
|
51
|
+
*) case " $ALIASES " in
|
|
52
|
+
*" $ID "*) NEW=$(grep -o "\"id\": *\"[A-Z]*\", *\"title\": *\"[A-Z]*\", *\"alias\": *\"$ID\"" "$TRACKS_FILE" | sed 's/^"id": *"\([A-Z]*\)".*/\1/')
|
|
53
|
+
echo "⚠️ 옛 한 글자 신원 [$ID] — 새 신원은 [${NEW:-?}] 다(전환기라 받는다)" >&2 ;;
|
|
54
|
+
*) echo "⛔ COORD_ID 는 트랙 정본($TRACKS_FILE)의 신원 [$IDS] (옛 [$ALIASES]) 중 하나여야 한다(받은 값: $ID)" >&2; exit 4 ;;
|
|
55
|
+
esac ;;
|
|
56
|
+
esac
|
|
50
57
|
FIRST=$(head -1 "$BODY")
|
|
58
|
+
OTHER=""
|
|
59
|
+
for T in $IDS $ALIASES; do
|
|
60
|
+
[ "$T" = "$ID" ] && continue
|
|
61
|
+
case "$FIRST" in "**[$T]**"*) OTHER="$T" ;; esac
|
|
62
|
+
done
|
|
51
63
|
case "$FIRST" in
|
|
52
64
|
"**[$ID]**"*) ;;
|
|
53
|
-
\*\*\[[$TRACKS]\]\*\**)
|
|
54
|
-
echo "⛔ 신원 접두가 «내 것이 아니다» — 첫 줄이 ${FIRST%%\*\* *}** 로 시작한다(내 신원 = [$ID])." >&2
|
|
55
|
-
echo " ⛔ 감시자는 startswith 로 고른다 ⇒ 상대는 이 글을 «자기 글»로 보고 건너뛴다. 발신하지 않는다." >&2
|
|
56
|
-
echo " ✅ 수신자는 본문에 적어라: **[$ID]** … [T] 님께 …" >&2
|
|
57
|
-
exit 5 ;;
|
|
58
65
|
*)
|
|
66
|
+
if [ -n "$OTHER" ]; then
|
|
67
|
+
echo "⛔ 신원 접두가 «내 것이 아니다» — 첫 줄이 **[$OTHER]** 로 시작한다(내 신원 = [$ID])." >&2
|
|
68
|
+
echo " ⛔ 감시자는 startswith 로 고른다 ⇒ 상대는 이 글을 «자기 글»로 보고 건너뛴다. 발신하지 않는다." >&2
|
|
69
|
+
echo " ✅ 수신자는 본문에 적어라: **[$ID]** … MK 님께 …" >&2
|
|
70
|
+
exit 5
|
|
71
|
+
fi
|
|
59
72
|
echo "⛔ 첫 줄이 «**[$ID]**» 신원 접두로 시작하지 않는다 — 상대 감시자가 이 글을 못 고른다. 발신하지 않는다." >&2
|
|
60
73
|
exit 5 ;;
|
|
61
74
|
esac
|
|
62
75
|
|
|
76
|
+
# 봉투 v2(RFC-coordination-cost §2 · C1 · 09-30): 첫 줄에 종류 다섯 중 하나 — 막지 않고 «알린다».
|
|
77
|
+
case "$FIRST" in
|
|
78
|
+
*요청*|*결정*|*사고*|*보고*|*정정*) ;;
|
|
79
|
+
*) echo "⚠️ 첫 줄에 글 종류(요청·결정·사고·보고·정정)가 없다 — 받는 쪽 감시자가 즉시/모아 읽기를 못 가른다(발신은 한다)" >&2 ;;
|
|
80
|
+
esac
|
|
81
|
+
|
|
63
82
|
TS=$(TZ=Asia/Seoul date '+%Y-%m-%d %H:%M KST')
|
|
64
83
|
TMP=$(mktemp "${TMPDIR:-/tmp}/coord-post.XXXXXX") || { echo "⛔ mktemp 실패 — 발신하지 않는다" >&2; exit 7; }
|
|
65
84
|
DATA=$(mktemp "${TMPDIR:-/tmp}/coord-post-data.XXXXXX") || { rm -f "$TMP"; echo "⛔ mktemp 실패 — 발신하지 않는다" >&2; exit 7; }
|
|
@@ -125,8 +144,26 @@ fi
|
|
|
125
144
|
POST_OUTPUT=$(bun bin/elanous.mjs gh pr comment "$PR" --body-file "$TMP" 2>&1)
|
|
126
145
|
RC=$?
|
|
127
146
|
printf '%s\n' "$POST_OUTPUT" | tail -2
|
|
147
|
+
VIA=""
|
|
148
|
+
# `gh pr comment` goes through GraphQL; its secondary rate limit can block while REST still answers (GIT-S83).
|
|
149
|
+
# 09-30: 채널 코멘트 1,500 뒤 GraphQL 이 «Something went wrong» 으로 연속 실패하고 REST 는 201 — 이 내부 오류도 REST 로 물러선다.
|
|
150
|
+
# ⛔ 다른 실패(PR 못 찾음 등)는 물러서지 않는다. 물러서기 전에 같은 첫 줄이 이미 올라갔는지 본다(부분 성공 → 이중 발신 방지).
|
|
151
|
+
if [ "$RC" -ne 0 ] && printf '%s' "$POST_OUTPUT" | grep -qiE 'rate limit|Something went wrong'; then
|
|
152
|
+
echo "⚠️ GraphQL 실패(한도·내부 오류) — REST 로 한 번 물러선다(GIT-S83)" >&2
|
|
153
|
+
FIRSTOUT=$(head -1 "$TMP")
|
|
154
|
+
# issues 코멘트 목록은 오래된 순이고 정렬 인자를 안 받는다 ⇒ 최근 10분만 since 로 자른다.
|
|
155
|
+
SINCE10=$(date -u -v-10M +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '-10 min' +%Y-%m-%dT%H:%M:%SZ)
|
|
156
|
+
if bun bin/elanous.mjs gh api "repos/{owner}/{repo}/issues/$PR/comments?since=$SINCE10&per_page=100" --jq '.[].body | split("\n")[0]' 2>/dev/null | grep -qxF -- "$FIRSTOUT"; then
|
|
157
|
+
echo "[coord-post] 같은 첫 줄이 이미 채널에 있다 — GraphQL 이 실제로는 올렸다(이중 발신 안 함)" >&2
|
|
158
|
+
exit 0
|
|
159
|
+
fi
|
|
160
|
+
POST_OUTPUT=$(bun bin/elanous.mjs gh api -X POST "repos/{owner}/{repo}/issues/$PR/comments" -F "body=@$TMP" -q .html_url 2>&1)
|
|
161
|
+
RC=$?
|
|
162
|
+
printf '%s\n' "$POST_OUTPUT" | tail -2
|
|
163
|
+
VIA=" · REST 폴백"
|
|
164
|
+
fi
|
|
128
165
|
if [ "$RC" -ne 0 ]; then
|
|
129
|
-
echo "⛔ 발신 실패 rc=$RC — «보냈다고 읽지 마라»" >&2
|
|
166
|
+
echo "⛔ 발신 실패 rc=$RC$VIA — «보냈다고 읽지 마라»" >&2
|
|
130
167
|
exit "$RC"
|
|
131
168
|
fi
|
|
132
|
-
echo "[coord-post] 발신
|
|
169
|
+
echo "[coord-post] 발신 성공$VIA · 채널 #$PR · 신원 [$ID] · 시각 $TS" >&2
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "조율 채널
|
|
2
|
+
"_comment": "조율 채널 세션 신원의 정본(09-30 역할 재편). id = 2글자 신원(발신 접두 **[TC]**) · title = 직함 · alias = 옛 한 글자(기록 읽기·전환기 발신용) · mark = 옛 표지. 세션을 더하려면 여기 한 줄만 더한다 — coord-post.sh · coord-channel-watch.sh · build-docs-site.ts · mission-phase-track.ts 가 이 파일을 읽는다.",
|
|
3
3
|
"tracks": [
|
|
4
|
-
{ "id": "S", "mark": "🅢", "owns": ["기획", "총괄", "전략", "
|
|
5
|
-
{ "id": "T", "mark": "🅣", "owns": ["영상", "비디오", "티저", "하이퍼프레임", "
|
|
6
|
-
{ "id": "
|
|
7
|
-
{ "id": "
|
|
4
|
+
{ "id": "OP", "title": "COO", "alias": "S", "mark": "🅢", "owns": ["판올림", "컷", "체크리스트", "분배", "통합", "로드맵", "기획", "총괄", "전략", "인계", "release cut", "roadmap", "plan", "strategy"] },
|
|
5
|
+
{ "id": "MK", "title": "CMO", "alias": "T", "mark": "🅣", "owns": ["블로그", "콘텐츠", "발행", "광고", "프로모션", "소구점", "마케팅", "영상", "비디오", "티저", "하이퍼프레임", "렌더", "blog", "content", "marketing", "ads", "video", "teaser", "render", "B-roll", "상표", "변리사", "브랜드", "trademark", "brand"] },
|
|
6
|
+
{ "id": "TC", "title": "CTO", "alias": "O", "mark": "🅞", "owns": ["하니스", "게이트", "원장", "리뷰", "관측", "릴리스 루프", "흡수", "Obsidian", "매뉴얼", "문서", "기술", "harness", "gate", "ledger", "review", "observability", "intake", "manual", "docs"] },
|
|
7
|
+
{ "id": "UX", "title": "CXO", "alias": "F", "mark": "🅕", "owns": ["PWA", "웹", "웹사이트", "elanous.ai", "탭", "화면", "UI", "프론트엔드", "안드로이드", "아이폰", "데스크톱", "텔레그램", "디스코드", "메신저", "봇", "frontend", "web", "tab", "android", "ios", "desktop", "telegram", "discord", "bot"] },
|
|
8
8
|
{ "id": "E", "mark": "🅔", "owns": [] }
|
|
9
9
|
]
|
|
10
10
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// 봇 UX «그록봇 수준» 판별 — 같은 질문 묶음을 elanous chat(글자) · elanous agent(도구) · xAI Grok 에 던지고 원문·시간을 남긴다.
|
|
2
|
+
// 사용: bun scripts/eval-bot-vs-grok.ts <outDir> <repoCwd> · Grok 모델 = 사다리 `budget` 칸(tierModel) (키 값은 출력하지 않는다)
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { tierModel } from '../src/llm/model-defaults.js';
|
|
7
|
+
|
|
8
|
+
const [,, outDir, repoCwd] = process.argv;
|
|
9
|
+
mkdirSync(outDir, { recursive: true });
|
|
10
|
+
|
|
11
|
+
type Q = { id: string; axis: string; text: string; session?: string; needsTools?: boolean };
|
|
12
|
+
const QUESTIONS: Q[] = [
|
|
13
|
+
{ id: 'q01', axis: 'smalltalk', text: '안녕, 오늘 뭐 도와줄 수 있어? 세 줄 안으로.' },
|
|
14
|
+
{ id: 'q02', axis: 'smalltalk', text: '요즘 좀 지쳤어. 짧게 한마디 해줘.' },
|
|
15
|
+
{ id: 'q03', axis: 'fact', text: '서울에서 부산까지 KTX 로 대략 몇 시간 걸려? 한 줄로.' },
|
|
16
|
+
{ id: 'q04', axis: 'fact', text: 'HWPX 파일 형식이 뭐야? 두 줄로.' },
|
|
17
|
+
{ id: 'q05', axis: 'context', session: 'ctx', text: '내 프로젝트 이름은 elanous 야. 기억해 둬. «알겠다» 한 마디만.' },
|
|
18
|
+
{ id: 'q06', axis: 'context', session: 'ctx', text: '우리 팀은 네 세션으로 나뉘어 있어: OP, MK, TC, UX. 이것도 기억해. 한 마디만.' },
|
|
19
|
+
{ id: 'q07', axis: 'context', session: 'ctx', text: '내 프로젝트 이름이랑 팀 세션 약자 넷을 한 줄로 말해 봐.' },
|
|
20
|
+
{ id: 'q08', axis: 'tools', needsTools: true, text: '지금 작업 폴더(git 저장소)에서 가장 최근 커밋 제목 세 개를 알려줘.' },
|
|
21
|
+
{ id: 'q09', axis: 'tools', needsTools: true, text: 'apps/pwa 폴더에 파일이 대략 몇 개야? 숫자 하나로.' },
|
|
22
|
+
{ id: 'q10', axis: 'tools', needsTools: true, text: '오늘 날짜를 한국 시간으로 알려줘.' },
|
|
23
|
+
{ id: 'q11', axis: 'recovery', needsTools: true, text: 'docs/NOPE-does-not-exist-xyz.md 파일을 요약해줘.' },
|
|
24
|
+
{ id: 'q12', axis: 'recovery', text: '내가 어제 너한테 부탁한 일 결과 알려줘.' },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
type Row = { id: string; axis: string; target: string; ms: number; ok: boolean; reply: string; meta?: Record<string, unknown> };
|
|
28
|
+
const rows: Row[] = [];
|
|
29
|
+
|
|
30
|
+
function runElanous(mode: 'chat' | 'agent', q: Q, session?: string): Row {
|
|
31
|
+
const args = [mode, '--json', ...(session ? ['--session', session] : ['--new']), q.text];
|
|
32
|
+
const t0 = Date.now();
|
|
33
|
+
const r = spawnSync('elanous', args, { cwd: repoCwd, encoding: 'utf8', timeout: 240_000, maxBuffer: 64 << 20 });
|
|
34
|
+
const ms = Date.now() - t0;
|
|
35
|
+
const line = (r.stdout ?? '').trim().split('\n').reverse().find((l) => l.startsWith('{')) ?? '';
|
|
36
|
+
let parsed: Record<string, unknown> = {};
|
|
37
|
+
try { parsed = JSON.parse(line); } catch { /* keep empty */ }
|
|
38
|
+
const reply = typeof parsed.reply === 'string' ? parsed.reply : ((r.stderr ?? '').slice(-400) || '(no reply)');
|
|
39
|
+
return { id: q.id, axis: q.axis, target: `elanous-${mode}`, ms, ok: r.status === 0 && typeof parsed.reply === 'string', reply, meta: { sessionId: parsed.sessionId, provider: parsed.provider, model: parsed.model, status: r.status } };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function xaiKey(): string {
|
|
43
|
+
const env = readFileSync(join(process.env.HOME!, '.claude/skills/omni-crawl/.env'), 'utf8');
|
|
44
|
+
return /^XAI_API_KEY=(.*)$/m.exec(env)?.[1]?.trim().replace(/^['"]|['"]$/g, '') ?? '';
|
|
45
|
+
}
|
|
46
|
+
const grokHistory = new Map<string, Array<{ role: string; content: string }>>();
|
|
47
|
+
async function runGrok(q: Q): Promise<Row> {
|
|
48
|
+
if (q.needsTools) return { id: q.id, axis: q.axis, target: 'grok', ms: 0, ok: false, reply: 'n/a — 작업 폴더·도구 없음(대조 제외)' };
|
|
49
|
+
const history = q.session ? (grokHistory.get(q.session) ?? []) : [];
|
|
50
|
+
const messages = [...history, { role: 'user', content: q.text }];
|
|
51
|
+
const t0 = Date.now();
|
|
52
|
+
const res = await fetch('https://api.x.ai/v1/chat/completions', {
|
|
53
|
+
method: 'POST', headers: { 'content-type': 'application/json', authorization: `Bearer ${xaiKey()}` },
|
|
54
|
+
body: JSON.stringify({ model: tierModel('budget', 'grok'), messages }),
|
|
55
|
+
});
|
|
56
|
+
const ms = Date.now() - t0;
|
|
57
|
+
const body = await res.json().catch(() => ({})) as { choices?: Array<{ message?: { content?: string } }> };
|
|
58
|
+
const reply = body.choices?.[0]?.message?.content ?? `(http ${res.status})`;
|
|
59
|
+
if (q.session) grokHistory.set(q.session, [...messages, { role: 'assistant', content: reply }]);
|
|
60
|
+
return { id: q.id, axis: q.axis, target: 'grok', ms, ok: res.ok, reply };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const mode of ['chat', 'agent'] as const) {
|
|
64
|
+
const sessions = new Map<string, string>();
|
|
65
|
+
for (const q of QUESTIONS) {
|
|
66
|
+
const row = runElanous(mode, q, q.session ? sessions.get(q.session) : undefined);
|
|
67
|
+
if (q.session && !sessions.has(q.session) && typeof row.meta?.sessionId === 'string') sessions.set(q.session, row.meta.sessionId as string);
|
|
68
|
+
rows.push(row);
|
|
69
|
+
console.log(`${mode} ${q.id} ${row.ok ? 'ok' : 'FAIL'} ${(row.ms / 1000).toFixed(1)}s`);
|
|
70
|
+
writeFileSync(join(outDir, 'rows.json'), JSON.stringify(rows, null, 2));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (const q of QUESTIONS) {
|
|
74
|
+
const row = await runGrok(q);
|
|
75
|
+
rows.push(row);
|
|
76
|
+
console.log(`grok ${q.id} ${row.ok ? 'ok' : 'skip/FAIL'} ${(row.ms / 1000).toFixed(1)}s`);
|
|
77
|
+
}
|
|
78
|
+
writeFileSync(join(outDir, 'rows.json'), JSON.stringify(rows, null, 2));
|
|
79
|
+
console.log('done', rows.length);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// 대조군 v2 — xAI Responses API · 추론 모델(저장소 GROK_SEARCH_MODEL) · web_search ⊕ x_search 켬. 그록봇 «제품» 은 아니다.
|
|
2
|
+
// 사용: bun eval-grok-live.ts <repo-checkout> <outDir> (키 값은 출력하지 않는다)
|
|
3
|
+
import { mkdirSync, writeFileSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const [,, repo, outDir] = process.argv;
|
|
7
|
+
mkdirSync(outDir, { recursive: true });
|
|
8
|
+
const { grokAgentSearch, GROK_SEARCH_MODEL } = await import(join(repo, 'src/grok/agent-search.js'));
|
|
9
|
+
const env = readFileSync(join(process.env.HOME!, '.claude/skills/omni-crawl/.env'), 'utf8');
|
|
10
|
+
const apiKey = /^XAI_API_KEY=(.*)$/m.exec(env)?.[1]?.trim().replace(/^['"]|['"]$/g, '') ?? '';
|
|
11
|
+
|
|
12
|
+
type Q = { id: string; text: string; session?: string };
|
|
13
|
+
const QS: Q[] = [
|
|
14
|
+
{ id: 'q01', text: '안녕, 오늘 뭐 도와줄 수 있어? 세 줄 안으로.' },
|
|
15
|
+
{ id: 'q02', text: '요즘 좀 지쳤어. 짧게 한마디 해줘.' },
|
|
16
|
+
{ id: 'q03', text: '서울에서 부산까지 KTX 로 대략 몇 시간 걸려? 한 줄로.' },
|
|
17
|
+
{ id: 'q04', text: 'HWPX 파일 형식이 뭐야? 두 줄로.' },
|
|
18
|
+
{ id: 'q05', session: 'ctx', text: '내 프로젝트 이름은 elanous 야. 기억해 둬. «알겠다» 한 마디만.' },
|
|
19
|
+
{ id: 'q06', session: 'ctx', text: '우리 팀은 네 세션으로 나뉘어 있어: OP, MK, TC, UX. 이것도 기억해. 한 마디만.' },
|
|
20
|
+
{ id: 'q07', session: 'ctx', text: '내 프로젝트 이름이랑 팀 세션 약자 넷을 한 줄로 말해 봐.' },
|
|
21
|
+
{ id: 'q10', text: '오늘 날짜를 한국 시간으로 알려줘.' },
|
|
22
|
+
{ id: 'q12', text: '내가 어제 너한테 부탁한 일 결과 알려줘.' },
|
|
23
|
+
];
|
|
24
|
+
const history: string[] = [];
|
|
25
|
+
const rows: unknown[] = [];
|
|
26
|
+
for (const q of QS) {
|
|
27
|
+
const systemPrompt = q.session && history.length ? `이전 대화:\n${history.join('\n')}` : undefined;
|
|
28
|
+
const t0 = Date.now();
|
|
29
|
+
const r = await grokAgentSearch(q.text, { apiKey, tools: ['web_search', 'x_search'], ...(systemPrompt ? { systemPrompt } : {}), timeoutMs: 90_000 });
|
|
30
|
+
const ms = Date.now() - t0;
|
|
31
|
+
if (q.session) history.push(`사용자: ${q.text}`, `어시스턴트: ${r.text}`);
|
|
32
|
+
rows.push({ id: q.id, target: 'grok-live', model: GROK_SEARCH_MODEL, ms, ok: r.ok, reply: r.text, citations: (r.citations ?? []).length, error: r.error });
|
|
33
|
+
console.log(`grok-live ${q.id} ${r.ok ? 'ok' : 'FAIL'} ${(ms / 1000).toFixed(1)}s cites=${(r.citations ?? []).length}`);
|
|
34
|
+
}
|
|
35
|
+
writeFileSync(join(outDir, 'grok-live.rows.json'), JSON.stringify(rows, null, 2));
|
|
36
|
+
console.log('done', rows.length);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "손으로 쓴 개인정보 없는 앞 분류기 예시 — 규칙 미판정 문장",
|
|
3
|
+
"items": [
|
|
4
|
+
{ "id": "absorb-1", "text": "이 기술 발표에서 핵심 논점이 뭔지 알고 싶어.", "expected": "absorb" },
|
|
5
|
+
{ "id": "absorb-2", "text": "긴 글의 주요 주장만 파악하고 싶어.", "expected": "absorb" },
|
|
6
|
+
{ "id": "absorb-3", "text": "이 자료는 어떤 배경에서 나왔는지 살펴봐.", "expected": "absorb" },
|
|
7
|
+
{ "id": "tasks-1", "text": "문서 제목 확인하고 표 오타 검토한 다음 회의실 예약도 챙겨야 해.", "expected": "tasks" },
|
|
8
|
+
{ "id": "tasks-2", "text": "청구서 확인, 일정 변경, 메일 답장 각각 챙기자.", "expected": "tasks" },
|
|
9
|
+
{ "id": "tasks-3", "text": "오늘은 재고 파악하고 전등 교체 일정도 따로 잡아야겠어.", "expected": "tasks" },
|
|
10
|
+
{ "id": "graph-1", "text": "이 입력부터 결과까지 이어지는 처리 과정을 자동화하고 싶어.", "expected": "graph" },
|
|
11
|
+
{ "id": "graph-2", "text": "데이터가 들어오면 검사 후 분류해서 다음 단계로 넘기는 흐름이 필요해.", "expected": "graph" },
|
|
12
|
+
{ "id": "graph-3", "text": "요청 접수부터 승인과 배포까지 한 흐름으로 묶자.", "expected": "graph" },
|
|
13
|
+
{ "id": "ask-human-1", "text": "이거 어떻게 할까?", "expected": "ask-human" },
|
|
14
|
+
{ "id": "ask-human-2", "text": "지금은 어느 방향이 좋을지 모르겠어.", "expected": "ask-human" },
|
|
15
|
+
{ "id": "ask-human-3", "text": "그건 상황을 보고 결정하자.", "expected": "ask-human" }
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -71,11 +71,21 @@ else R "k3d registry create $REGISTRY --port 127.0.0.1:$REG_PORT >/tmp/fleet-reg
|
|
|
71
71
|
# `bridge` 에만 붙어 kubelet 이 `lookup k3d-elanous-registry: no such host` → Pod `ImagePullBackOff`(🅕 첫 Pod 사용에서 발견). 멱등.
|
|
72
72
|
[ $CHECK = 1 ] || R "docker network inspect k3d-$CLUSTER >/dev/null 2>&1 && (docker network connect k3d-$CLUSTER k3d-$REGISTRY 2>/dev/null || true)" >/dev/null 2>&1
|
|
73
73
|
fi
|
|
74
|
-
#
|
|
74
|
+
# 🪞 호스트 git 미러(대표 09-29) — Pod 는 GitHub 대신 이 미러에서 clone 한다(`ELANOUS_POD_HOST_MIRROR` · #21929).
|
|
75
|
+
# bare 미러 = 원격 ~/mirror/elanous-agent.git(없으면 만든다 · 갱신은 이 맥의 크론이 push). 클러스터 노드에 읽기 전용으로 붙인다.
|
|
76
|
+
MIRROR_DIR='$HOME/mirror'; MIRROR_MOUNT=/mirror-host
|
|
77
|
+
if R "test -d ~/mirror/elanous-agent.git"; then ok "호스트 git 미러 ~/mirror/elanous-agent.git"
|
|
78
|
+
elif [ $CHECK = 1 ]; then todo "호스트 git 미러 ~/mirror/elanous-agent.git 생성(git init --bare) ⊕ 이 맥에서 첫 push"
|
|
79
|
+
else R "mkdir -p ~/mirror && git init -q --bare ~/mirror/elanous-agent.git" && ok "호스트 git 미러 (생성 · 첫 push 는 이 맥 크론)" || { bad "미러 생성 실패"; exit 1; }; fi
|
|
80
|
+
# 클러스터가 그 레지스트리를 쓰나 ⊕ 미러가 붙어 있나 — 둘 중 하나라도 아니면(옛 클러스터) 돌고 있는 Job 이 없을 때만 다시 만든다.
|
|
81
|
+
# ⛔ k3d 는 볼륨을 «클러스터 생성 때만» 붙인다 — 붙이려면 다시 만들 수밖에 없다.
|
|
75
82
|
REG_WIRED=0; R "docker exec k3d-$CLUSTER-server-0 cat /etc/rancher/k3s/registries.yaml 2>/dev/null | grep -q k3d-$REGISTRY" && REG_WIRED=1
|
|
83
|
+
MIRROR_WIRED=0; R "docker exec k3d-$CLUSTER-server-0 test -d $MIRROR_MOUNT/elanous-agent.git" && MIRROR_WIRED=1
|
|
84
|
+
[ $MIRROR_WIRED = 1 ] || { [ $REG_WIRED = 1 ] && REG_WIRED=0 && MIRROR_ONLY=1; }
|
|
76
85
|
if R "k3d cluster list $CLUSTER" >/dev/null 2>&1 && [ $REG_WIRED = 0 ]; then
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
# «실행 중»만 센다 — 실패로 끝난 Job 은 Complete 가 아니어도 도는 것이 아니다(09-29: `grep -vc Complete` 는 Failed 까지 세서 영원히 0 이 안 됐다).
|
|
87
|
+
RUNNING="$(R "KUBECONFIG=\$(k3d kubeconfig write $CLUSTER) kubectl get jobs -A -o jsonpath='{range .items[*]}{.status.active}{\"\\n\"}{end}' 2>/dev/null | awk '\$1>0' | wc -l | tr -d ' '" 2>/dev/null | tail -1)"; RUNNING="${RUNNING:-0}"
|
|
88
|
+
if [ $CHECK = 1 ]; then todo "클러스터 $CLUSTER 가 $([ "${MIRROR_ONLY:-0}" = 1 ] && echo "미러($MIRROR_MOUNT)를 안 붙였다" || echo "레지스트리를 안 쓴다") — 다시 만든다(도는 Job ${RUNNING:-?}개)"
|
|
79
89
|
elif [ "${RUNNING:-0}" != "0" ]; then bad "클러스터 $CLUSTER 를 다시 만들어야 하는데 도는 Job 이 ${RUNNING}개 — 끝난 뒤 다시"; exit 1
|
|
80
90
|
else R "k3d cluster delete $CLUSTER >/dev/null 2>&1" && ok "클러스터 $CLUSTER (레지스트리 연결 위해 지움)"; fi
|
|
81
91
|
fi
|
|
@@ -83,7 +93,7 @@ if R "k3d cluster list $CLUSTER" >/dev/null 2>&1; then ok "클러스터 $CLUSTER
|
|
|
83
93
|
elif [ $CHECK = 1 ]; then todo "클러스터 $CLUSTER 생성($K3S_IMAGE · API :$API_PORT · SAN $HOST ${TSNAME:-} · 레지스트리 k3d-$REGISTRY:$REG_PORT)"
|
|
84
94
|
else
|
|
85
95
|
SAN="--k3s-arg --tls-san=$HOST@server:0"; [ -n "$TSNAME" ] && SAN="$SAN --k3s-arg --tls-san=$TSNAME@server:0"
|
|
86
|
-
R "k3d cluster create $CLUSTER --image $K3S_IMAGE --no-lb --api-port 0.0.0.0:$API_PORT $SAN --registry-use k3d-$REGISTRY:$REG_PORT --wait --timeout 300s >/tmp/fleet-k3d-create.log 2>&1" && ok "클러스터 $CLUSTER (생성 · 레지스트리 k3d-$REGISTRY)" || { bad "클러스터 생성 실패 — 원격 /tmp/fleet-k3d-create.log"; exit 1; }
|
|
96
|
+
R "k3d cluster create $CLUSTER --image $K3S_IMAGE --no-lb --api-port 0.0.0.0:$API_PORT $SAN --registry-use k3d-$REGISTRY:$REG_PORT --volume $MIRROR_DIR:$MIRROR_MOUNT:ro@server:0 --wait --timeout 300s >/tmp/fleet-k3d-create.log 2>&1" && ok "클러스터 $CLUSTER (생성 · 레지스트리 k3d-$REGISTRY)" || { bad "클러스터 생성 실패 — 원격 /tmp/fleet-k3d-create.log"; exit 1; }
|
|
87
97
|
fi
|
|
88
98
|
# 7. 이 맥 kubeconfig 에 컨텍스트 pool-<호스트>
|
|
89
99
|
# ⛔ «있다»로 끝내지 않는다 — 클러스터를 다시 만들면 인증서가 바뀌어 옛 컨텍스트가 x509 로 죽는다(2026-09-26 node-b:
|
|
@@ -114,9 +124,38 @@ PY
|
|
|
114
124
|
KUBECONFIG=~/.kube/config:"$TMP/kc.yaml" kubectl config view --flatten > "$TMP/merged.yaml" && chmod 600 "$TMP/merged.yaml" && mv "$TMP/merged.yaml" ~/.kube/config && ok "kubeconfig 컨텍스트 $CTX (추가 · 현재 컨텍스트는 그대로)" || bad "kubeconfig 병합 실패"
|
|
115
125
|
fi
|
|
116
126
|
# 8. 네임스페이스 ⊕ 격리 정책
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
apply_base() {
|
|
128
|
+
if [ "$CHECK" = 1 ]; then
|
|
129
|
+
if ! kubectl --context "$CTX" --request-timeout=10s get ns elanous-test >/dev/null 2>&1; then
|
|
130
|
+
todo "base.yaml ⊕ policy-internet.yaml 적용"
|
|
131
|
+
elif kubectl --context "$CTX" diff -f "$ROOT/docker/h1/base.yaml" -f "$ROOT/docker/h1/policy-internet.yaml" >/dev/null 2>&1; then
|
|
132
|
+
ok "elanous-test 네임스페이스 ⊕ 정책"
|
|
133
|
+
else
|
|
134
|
+
local diff_status=$?
|
|
135
|
+
if [ "$diff_status" = 1 ]; then todo "base.yaml ⊕ policy-internet.yaml 적용"
|
|
136
|
+
else todo "base.yaml ⊕ policy-internet.yaml 적용 (못 쟀다)"; fi
|
|
137
|
+
fi
|
|
138
|
+
return
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
local attempts=0 output retries
|
|
142
|
+
while [ "$attempts" -lt 6 ]; do
|
|
143
|
+
attempts=$((attempts+1))
|
|
144
|
+
if output=$(kubectl --context "$CTX" apply -f "$ROOT/docker/h1/base.yaml" -f "$ROOT/docker/h1/policy-internet.yaml" 2>&1); then
|
|
145
|
+
retries=$((attempts-1))
|
|
146
|
+
if [ "$retries" -eq 0 ]; then ok "elanous-test 네임스페이스 ⊕ 정책 (적용)"
|
|
147
|
+
else ok "elanous-test 네임스페이스 ⊕ 정책 (적용 · 재시도 $retries)"; fi
|
|
148
|
+
return
|
|
149
|
+
fi
|
|
150
|
+
if [[ "$output" != *'serviceaccount "default" not found'* ]] || [ "$attempts" -eq 6 ]; then
|
|
151
|
+
[ -z "$output" ] || printf '%s\n' "$output" >&2
|
|
152
|
+
bad "적용 실패 (재시도 $((attempts-1)))"
|
|
153
|
+
return
|
|
154
|
+
fi
|
|
155
|
+
sleep "${FLEET_APPLY_RETRY_SLEEP:-5}"
|
|
156
|
+
done
|
|
157
|
+
}
|
|
158
|
+
apply_base
|
|
120
159
|
# 9. 판 대조 — k3s · 노드 준비
|
|
121
160
|
v="$(kubectl --context "$CTX" --request-timeout=10s get nodes -o jsonpath='{.items[0].status.nodeInfo.kubeletVersion}' 2>/dev/null)"
|
|
122
161
|
want="${K3S_IMAGE##*:}"; want="${want/-k3s/+k3s}"
|
|
@@ -1,4 +1,30 @@
|
|
|
1
|
+
import { parse } from '../../src/agent-substrate/pr-comment-meta.js';
|
|
2
|
+
|
|
1
3
|
export type MustFixVerdict = 'resolved' | 'unresolved' | 'unknown';
|
|
4
|
+
export interface ReviewRoundMatch {
|
|
5
|
+
text: string;
|
|
6
|
+
verdict: 'rereviewed' | 'open';
|
|
7
|
+
run: string;
|
|
8
|
+
round: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** A later reviewer round in the same run is evidence of rereview, not proof of repair. */
|
|
12
|
+
const MUST_FIX_ROUND = /^Round \d+: reviewer requested \d+ must-fix change\(s\)\.$/m;
|
|
13
|
+
|
|
14
|
+
export function classifyReviewRounds(comments: Array<{ body?: string }>): ReviewRoundMatch[] {
|
|
15
|
+
const reviews = comments.flatMap(({ body }) => {
|
|
16
|
+
if (!body) return [];
|
|
17
|
+
const meta = parse(body);
|
|
18
|
+
return meta?.role === 'reviewer' && meta.run && meta.round !== undefined
|
|
19
|
+
? [{ body, run: meta.run, round: meta.round }] : [];
|
|
20
|
+
});
|
|
21
|
+
// 항목은 «must-fix 요청» 라운드에서만 센다 — «review completed (warn)» 의 요약 본문에도 «- » 글머리가 들어 있다(#21983 실물).
|
|
22
|
+
return reviews.flatMap(({ body, run, round }) => !MUST_FIX_ROUND.test(body) ? [] :
|
|
23
|
+
[...body.matchAll(/^- (.+)$/gm)].map((item) => ({
|
|
24
|
+
text: item[1]!, run, round,
|
|
25
|
+
verdict: reviews.some((review) => review.run === run && review.round > round) ? 'rereviewed' as const : 'open' as const,
|
|
26
|
+
})));
|
|
27
|
+
}
|
|
2
28
|
|
|
3
29
|
/** Match only named evidence in a PR's final patch, never an earlier review round. */
|
|
4
30
|
export function matchMustFix(mustFix: string, finalDiff: string): MustFixVerdict {
|