@quickchatai/quickreview 0.0.0-stage → 1.1.0
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/LICENSE +23 -0
- package/README.md +385 -2
- package/THIRD-PARTY-NOTICES.md +134 -0
- package/agents/pass-abstraction.md +64 -0
- package/agents/pass-ask-human.md +134 -0
- package/agents/pass-backcompat.md +67 -0
- package/agents/pass-baseline.md +61 -0
- package/agents/pass-behaviour-delta.md +64 -0
- package/agents/pass-bounds.md +36 -0
- package/agents/pass-claims.md +31 -0
- package/agents/pass-concurrency.md +168 -0
- package/agents/pass-consistency.md +57 -0
- package/agents/pass-contracts.md +30 -0
- package/agents/pass-data-safety.md +54 -0
- package/agents/pass-deferred.md +35 -0
- package/agents/pass-delivery.md +37 -0
- package/agents/pass-duplication.md +60 -0
- package/agents/pass-intent.md +63 -0
- package/agents/pass-lint-conformance.md +46 -0
- package/agents/pass-maintainability.md +67 -0
- package/agents/pass-placement.md +68 -0
- package/agents/pass-prod-risk.md +24 -0
- package/agents/pass-rollout.md +32 -0
- package/agents/pass-roundtrip.md +46 -0
- package/agents/pass-security.md +31 -0
- package/agents/pass-simplicity.md +68 -0
- package/agents/pass-statelessness.md +27 -0
- package/agents/pass-style.md +37 -0
- package/agents/pass-sweep.md +57 -0
- package/agents/pass-tests.md +58 -0
- package/agents/pass-ui.md +23 -0
- package/agents/pass-validate.md +50 -0
- package/agents/pass-widened.md +46 -0
- package/agents/scan/pass-scan.md +86 -0
- package/bin/qcr.js +11 -0
- package/config.example.toml +1067 -0
- package/deploy/ansible/README.md +356 -0
- package/deploy/ansible/group_vars/all.yml +212 -0
- package/deploy/ansible/inventory.example.ini +25 -0
- package/deploy/ansible/playbook.yml +360 -0
- package/deploy/collect-secrets.sh +289 -0
- package/deploy/collect-slack-secrets.mjs +1190 -0
- package/deploy/github-app-manifest.json +31 -0
- package/deploy/manifest.schema.json +75 -0
- package/deploy/qcr-update.sh +803 -0
- package/deploy/reviewer/AGENTS.override.md +12 -0
- package/dist/extensions/cli/bash-guard-ambient.js +42 -0
- package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
- package/dist/extensions/cli/bash-guard.js +5 -0
- package/dist/extensions/cli/bash-guard.js.map +1 -0
- package/dist/extensions/cli/companion-guard.js +5 -0
- package/dist/extensions/cli/companion-guard.js.map +1 -0
- package/dist/extensions/cli/debug-round.js +8 -0
- package/dist/extensions/cli/debug-round.js.map +1 -0
- package/dist/extensions/cli/inject-bridge.js +5 -0
- package/dist/extensions/cli/inject-bridge.js.map +1 -0
- package/dist/extensions/cli/ledger-pin.js +5 -0
- package/dist/extensions/cli/ledger-pin.js.map +1 -0
- package/dist/extensions/cli/lens.js +13 -0
- package/dist/extensions/cli/lens.js.map +1 -0
- package/dist/extensions/cli/muted-theme.js +5 -0
- package/dist/extensions/cli/muted-theme.js.map +1 -0
- package/dist/extensions/cli/onboarding.js +5 -0
- package/dist/extensions/cli/onboarding.js.map +1 -0
- package/dist/extensions/cli/setup.js +5 -0
- package/dist/extensions/cli/setup.js.map +1 -0
- package/dist/extensions/cli/tui-view.js +5 -0
- package/dist/extensions/cli/tui-view.js.map +1 -0
- package/dist/extensions/companion-guard.js +361 -0
- package/dist/extensions/companion-guard.js.map +1 -0
- package/dist/extensions/debug-round.js +204 -0
- package/dist/extensions/debug-round.js.map +1 -0
- package/dist/extensions/inject-bridge.js +603 -0
- package/dist/extensions/inject-bridge.js.map +1 -0
- package/dist/extensions/muted-theme.js +12 -0
- package/dist/extensions/muted-theme.js.map +1 -0
- package/dist/extensions/onboarding.js +175 -0
- package/dist/extensions/onboarding.js.map +1 -0
- package/dist/extensions/setup-replay.js +269 -0
- package/dist/extensions/setup-replay.js.map +1 -0
- package/dist/extensions/setup-scan.js +998 -0
- package/dist/extensions/setup-scan.js.map +1 -0
- package/dist/extensions/setup-triage.js +175 -0
- package/dist/extensions/setup-triage.js.map +1 -0
- package/dist/extensions/setup.js +1160 -0
- package/dist/extensions/setup.js.map +1 -0
- package/dist/extensions/tui-view.js +389 -0
- package/dist/extensions/tui-view.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/client.js +376 -0
- package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/config.js +189 -0
- package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/index.js +421 -0
- package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
- package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
- package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
- package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
- package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
- package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
- package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
- package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
- package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
- package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
- package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
- package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
- package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
- package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
- package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
- package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
- package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
- package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
- package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
- package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
- package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
- package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
- package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
- package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
- package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
- package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
- package/dist/src/anomalies.js +483 -0
- package/dist/src/anomalies.js.map +1 -0
- package/dist/src/answerworker.js +180 -0
- package/dist/src/answerworker.js.map +1 -0
- package/dist/src/appstamp.js +68 -0
- package/dist/src/appstamp.js.map +1 -0
- package/dist/src/archive/admission.js +3 -0
- package/dist/src/archive/admission.js.map +1 -0
- package/dist/src/archive/archivedb.js +474 -0
- package/dist/src/archive/archivedb.js.map +1 -0
- package/dist/src/archive/archiver.js +1770 -0
- package/dist/src/archive/archiver.js.map +1 -0
- package/dist/src/archive/archivewrite.js +198 -0
- package/dist/src/archive/archivewrite.js.map +1 -0
- package/dist/src/archive/bundle.js +266 -0
- package/dist/src/archive/bundle.js.map +1 -0
- package/dist/src/archive/dbmove.js +638 -0
- package/dist/src/archive/dbmove.js.map +1 -0
- package/dist/src/archive/duplicatecounts.js +80 -0
- package/dist/src/archive/duplicatecounts.js.map +1 -0
- package/dist/src/archive/eligibility.js +113 -0
- package/dist/src/archive/eligibility.js.map +1 -0
- package/dist/src/archive/extract.js +288 -0
- package/dist/src/archive/extract.js.map +1 -0
- package/dist/src/archive/fsutil.js +330 -0
- package/dist/src/archive/fsutil.js.map +1 -0
- package/dist/src/archive/gzfile.js +277 -0
- package/dist/src/archive/gzfile.js.map +1 -0
- package/dist/src/archive/kinds.js +22 -0
- package/dist/src/archive/kinds.js.map +1 -0
- package/dist/src/archive/livecheckpoint.js +178 -0
- package/dist/src/archive/livecheckpoint.js.map +1 -0
- package/dist/src/archive/liveretention.js +145 -0
- package/dist/src/archive/liveretention.js.map +1 -0
- package/dist/src/archive/manifest.js +284 -0
- package/dist/src/archive/manifest.js.map +1 -0
- package/dist/src/archive/overseer.js +283 -0
- package/dist/src/archive/overseer.js.map +1 -0
- package/dist/src/archive/piglobal.js +129 -0
- package/dist/src/archive/piglobal.js.map +1 -0
- package/dist/src/archive/plan.js +398 -0
- package/dist/src/archive/plan.js.map +1 -0
- package/dist/src/archive/prarchive.js +831 -0
- package/dist/src/archive/prarchive.js.map +1 -0
- package/dist/src/archive/prfiles.js +303 -0
- package/dist/src/archive/prfiles.js.map +1 -0
- package/dist/src/archive/report.js +169 -0
- package/dist/src/archive/report.js.map +1 -0
- package/dist/src/archive/restore.js +276 -0
- package/dist/src/archive/restore.js.map +1 -0
- package/dist/src/archive/rows.js +106 -0
- package/dist/src/archive/rows.js.map +1 -0
- package/dist/src/archive/seal.js +480 -0
- package/dist/src/archive/seal.js.map +1 -0
- package/dist/src/archive/status.js +528 -0
- package/dist/src/archive/status.js.map +1 -0
- package/dist/src/archive/storepass.js +409 -0
- package/dist/src/archive/storepass.js.map +1 -0
- package/dist/src/backfill.js +217 -0
- package/dist/src/backfill.js.map +1 -0
- package/dist/src/bench/cost.js +87 -0
- package/dist/src/bench/exec.js +76 -0
- package/dist/src/bench/values.js +14 -0
- package/dist/src/botcontrol.js +127 -0
- package/dist/src/botcontrol.js.map +1 -0
- package/dist/src/botworker.js +805 -0
- package/dist/src/botworker.js.map +1 -0
- package/dist/src/capture.js +234 -0
- package/dist/src/capture.js.map +1 -0
- package/dist/src/cases.js +1491 -0
- package/dist/src/cases.js.map +1 -0
- package/dist/src/channelfs.js +27 -0
- package/dist/src/channelfs.js.map +1 -0
- package/dist/src/checklist.js +312 -0
- package/dist/src/checklist.js.map +1 -0
- package/dist/src/ciretry.js +91 -0
- package/dist/src/ciretry.js.map +1 -0
- package/dist/src/cli.js +1916 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/cliops.js +333 -0
- package/dist/src/cliops.js.map +1 -0
- package/dist/src/clonedir.js +15 -0
- package/dist/src/clonedir.js.map +1 -0
- package/dist/src/commandeffects.js +43 -0
- package/dist/src/commandeffects.js.map +1 -0
- package/dist/src/commands.js +1312 -0
- package/dist/src/commands.js.map +1 -0
- package/dist/src/companioninbox.js +203 -0
- package/dist/src/companioninbox.js.map +1 -0
- package/dist/src/compare.js +673 -0
- package/dist/src/compare.js.map +1 -0
- package/dist/src/config.js +1027 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/configcheck.js +111 -0
- package/dist/src/configcheck.js.map +1 -0
- package/dist/src/coreext.js +43 -0
- package/dist/src/coreext.js.map +1 -0
- package/dist/src/daemon.js +929 -0
- package/dist/src/daemon.js.map +1 -0
- package/dist/src/debuground.js +731 -0
- package/dist/src/debuground.js.map +1 -0
- package/dist/src/debugroundpi.js +219 -0
- package/dist/src/debugroundpi.js.map +1 -0
- package/dist/src/diagnostics.js +14 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/digest.js +139 -0
- package/dist/src/digest.js.map +1 -0
- package/dist/src/dispatch.js +1726 -0
- package/dist/src/dispatch.js.map +1 -0
- package/dist/src/engine/answerguard.js +18 -0
- package/dist/src/engine/answerguard.js.map +1 -0
- package/dist/src/engine/botsdk.js +262 -0
- package/dist/src/engine/botsdk.js.map +1 -0
- package/dist/src/engine/caserunner-sdk.js +332 -0
- package/dist/src/engine/caserunner-sdk.js.map +1 -0
- package/dist/src/engine/debugpi.js +194 -0
- package/dist/src/engine/debugpi.js.map +1 -0
- package/dist/src/eod.js +473 -0
- package/dist/src/eod.js.map +1 -0
- package/dist/src/eodpinwatch.js +391 -0
- package/dist/src/eodpinwatch.js.map +1 -0
- package/dist/src/escalation.js +1009 -0
- package/dist/src/escalation.js.map +1 -0
- package/dist/src/eventloop.js +779 -0
- package/dist/src/eventloop.js.map +1 -0
- package/dist/src/execboundary.js +430 -0
- package/dist/src/execboundary.js.map +1 -0
- package/dist/src/feedback.js +915 -0
- package/dist/src/feedback.js.map +1 -0
- package/dist/src/findingscheck.js +200 -0
- package/dist/src/findingscheck.js.map +1 -0
- package/dist/src/fsdenied.js +26 -0
- package/dist/src/fsdenied.js.map +1 -0
- package/dist/src/ghread.js +181 -0
- package/dist/src/ghread.js.map +1 -0
- package/dist/src/github.js +1063 -0
- package/dist/src/github.js.map +1 -0
- package/dist/src/homes.js +103 -0
- package/dist/src/homes.js.map +1 -0
- package/dist/src/hostroute.js +24 -0
- package/dist/src/hostroute.js.map +1 -0
- package/dist/src/improvements.js +625 -0
- package/dist/src/improvements.js.map +1 -0
- package/dist/src/install.js +946 -0
- package/dist/src/install.js.map +1 -0
- package/dist/src/installkind.js +40 -0
- package/dist/src/installkind.js.map +1 -0
- package/dist/src/ipc/capability.js +286 -0
- package/dist/src/ipc/capability.js.map +1 -0
- package/dist/src/ipc/client.js +226 -0
- package/dist/src/ipc/client.js.map +1 -0
- package/dist/src/ipc/control.js +70 -0
- package/dist/src/ipc/control.js.map +1 -0
- package/dist/src/ipc/ghreadshim.js +77 -0
- package/dist/src/ipc/ghreadshim.js.map +1 -0
- package/dist/src/ipc/launcher.js +65 -0
- package/dist/src/ipc/launcher.js.map +1 -0
- package/dist/src/ipc/permits.js +293 -0
- package/dist/src/ipc/permits.js.map +1 -0
- package/dist/src/ipc/postshim.js +137 -0
- package/dist/src/ipc/postshim.js.map +1 -0
- package/dist/src/ipc/protocol.js +239 -0
- package/dist/src/ipc/protocol.js.map +1 -0
- package/dist/src/ipc/server.js +312 -0
- package/dist/src/ipc/server.js.map +1 -0
- package/dist/src/ipc/workerspec.js +148 -0
- package/dist/src/ipc/workerspec.js.map +1 -0
- package/dist/src/learner/learner.js +1272 -0
- package/dist/src/learner/learner.js.map +1 -0
- package/dist/src/learner/miner.js +948 -0
- package/dist/src/learner/miner.js.map +1 -0
- package/dist/src/learner/packet.js +216 -0
- package/dist/src/learner/packet.js.map +1 -0
- package/dist/src/learner/receipt.js +135 -0
- package/dist/src/learner/receipt.js.map +1 -0
- package/dist/src/learner/status.js +38 -0
- package/dist/src/learner/status.js.map +1 -0
- package/dist/src/ledgercheck.js +120 -0
- package/dist/src/ledgercheck.js.map +1 -0
- package/dist/src/lessonledger.js +451 -0
- package/dist/src/lessonledger.js.map +1 -0
- package/dist/src/lessonselect.js +639 -0
- package/dist/src/lessonselect.js.map +1 -0
- package/dist/src/lessonshadow.js +540 -0
- package/dist/src/lessonshadow.js.map +1 -0
- package/dist/src/levers.js +302 -0
- package/dist/src/levers.js.map +1 -0
- package/dist/src/live/projection.js +450 -0
- package/dist/src/live/projection.js.map +1 -0
- package/dist/src/live/publish.js +83 -0
- package/dist/src/live/publish.js.map +1 -0
- package/dist/src/live/read.js +60 -0
- package/dist/src/live/read.js.map +1 -0
- package/dist/src/livecli.js +288 -0
- package/dist/src/livecli.js.map +1 -0
- package/dist/src/loginhint.js +42 -0
- package/dist/src/loginhint.js.map +1 -0
- package/dist/src/loginrefresh/atomic.js +66 -0
- package/dist/src/loginrefresh/atomic.js.map +1 -0
- package/dist/src/loginrefresh/lock.js +93 -0
- package/dist/src/loginrefresh/lock.js.map +1 -0
- package/dist/src/loginrefresh/main.js +58 -0
- package/dist/src/loginrefresh/main.js.map +1 -0
- package/dist/src/loginrefresh/pirefresh.js +49 -0
- package/dist/src/loginrefresh/pirefresh.js.map +1 -0
- package/dist/src/loginrefresh/refresher.js +286 -0
- package/dist/src/loginrefresh/refresher.js.map +1 -0
- package/dist/src/loginrefresh/supervisor.js +253 -0
- package/dist/src/loginrefresh/supervisor.js.map +1 -0
- package/dist/src/loginrefresh/types.js +4 -0
- package/dist/src/loginrefresh/types.js.map +1 -0
- package/dist/src/memory/context.js +251 -0
- package/dist/src/memory/context.js.map +1 -0
- package/dist/src/memory/datafence.js +5 -0
- package/dist/src/memory/datafence.js.map +1 -0
- package/dist/src/memory/delivery.js +131 -0
- package/dist/src/memory/delivery.js.map +1 -0
- package/dist/src/memory/eventtags.js +81 -0
- package/dist/src/memory/eventtags.js.map +1 -0
- package/dist/src/memory/health.js +713 -0
- package/dist/src/memory/health.js.map +1 -0
- package/dist/src/memory/import.js +504 -0
- package/dist/src/memory/import.js.map +1 -0
- package/dist/src/memory/layout.js +252 -0
- package/dist/src/memory/layout.js.map +1 -0
- package/dist/src/memory/legacy-file.js +100 -0
- package/dist/src/memory/legacy-file.js.map +1 -0
- package/dist/src/memory/lessons.js +370 -0
- package/dist/src/memory/lessons.js.map +1 -0
- package/dist/src/memory/ovsession.js +449 -0
- package/dist/src/memory/ovsession.js.map +1 -0
- package/dist/src/memory/relayout.js +259 -0
- package/dist/src/memory/relayout.js.map +1 -0
- package/dist/src/memory/roundactivity.js +180 -0
- package/dist/src/memory/roundactivity.js.map +1 -0
- package/dist/src/memory/selection.js +690 -0
- package/dist/src/memory/selection.js.map +1 -0
- package/dist/src/memory/shadowstatus.js +69 -0
- package/dist/src/memory/shadowstatus.js.map +1 -0
- package/dist/src/memory/store.js +83 -0
- package/dist/src/memory/store.js.map +1 -0
- package/dist/src/memory/textidentity.js +8 -0
- package/dist/src/memory/textidentity.js.map +1 -0
- package/dist/src/metrics.js +773 -0
- package/dist/src/metrics.js.map +1 -0
- package/dist/src/missingconfig.js +8 -0
- package/dist/src/missingconfig.js.map +1 -0
- package/dist/src/npmverbs.js +226 -0
- package/dist/src/npmverbs.js.map +1 -0
- package/dist/src/onboard/choices.js +191 -0
- package/dist/src/onboard/choices.js.map +1 -0
- package/dist/src/onboard/engine.js +344 -0
- package/dist/src/onboard/engine.js.map +1 -0
- package/dist/src/onboard/launch.js +83 -0
- package/dist/src/onboard/launch.js.map +1 -0
- package/dist/src/onboard/steps/appslug.js +211 -0
- package/dist/src/onboard/steps/appslug.js.map +1 -0
- package/dist/src/onboard/steps/bringup.js +408 -0
- package/dist/src/onboard/steps/bringup.js.map +1 -0
- package/dist/src/onboard/steps/compose.js +343 -0
- package/dist/src/onboard/steps/compose.js.map +1 -0
- package/dist/src/onboard/steps/configdelivery.js +50 -0
- package/dist/src/onboard/steps/configdelivery.js.map +1 -0
- package/dist/src/onboard/steps/configfill.js +1148 -0
- package/dist/src/onboard/steps/configfill.js.map +1 -0
- package/dist/src/onboard/steps/deploysh.js +80 -0
- package/dist/src/onboard/steps/deploysh.js.map +1 -0
- package/dist/src/onboard/steps/embeddings.js +472 -0
- package/dist/src/onboard/steps/embeddings.js.map +1 -0
- package/dist/src/onboard/steps/escalation.js +571 -0
- package/dist/src/onboard/steps/escalation.js.map +1 -0
- package/dist/src/onboard/steps/githubapp.js +1092 -0
- package/dist/src/onboard/steps/githubapp.js.map +1 -0
- package/dist/src/onboard/steps/golive.js +506 -0
- package/dist/src/onboard/steps/golive.js.map +1 -0
- package/dist/src/onboard/steps/index.js +67 -0
- package/dist/src/onboard/steps/index.js.map +1 -0
- package/dist/src/onboard/steps/ingress.js +762 -0
- package/dist/src/onboard/steps/ingress.js.map +1 -0
- package/dist/src/onboard/steps/inventory.js +420 -0
- package/dist/src/onboard/steps/inventory.js.map +1 -0
- package/dist/src/onboard/steps/llmauth.js +893 -0
- package/dist/src/onboard/steps/llmauth.js.map +1 -0
- package/dist/src/onboard/steps/memory.js +1060 -0
- package/dist/src/onboard/steps/memory.js.map +1 -0
- package/dist/src/onboard/steps/memorykeys.js +43 -0
- package/dist/src/onboard/steps/memorykeys.js.map +1 -0
- package/dist/src/onboard/steps/preflight.js +172 -0
- package/dist/src/onboard/steps/preflight.js.map +1 -0
- package/dist/src/onboard/steps/proof.js +502 -0
- package/dist/src/onboard/steps/proof.js.map +1 -0
- package/dist/src/onboard/steps/ssh.js +300 -0
- package/dist/src/onboard/steps/ssh.js.map +1 -0
- package/dist/src/onboard/steps/topology.js +291 -0
- package/dist/src/onboard/steps/topology.js.map +1 -0
- package/dist/src/onboard/steps/transport.js +511 -0
- package/dist/src/onboard/steps/transport.js.map +1 -0
- package/dist/src/onboard/steps/verifyup.js +362 -0
- package/dist/src/onboard/steps/verifyup.js.map +1 -0
- package/dist/src/onboard/steps/vmdeploy.js +368 -0
- package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
- package/dist/src/onboard/toml.js +53 -0
- package/dist/src/onboard/toml.js.map +1 -0
- package/dist/src/opencli.js +508 -0
- package/dist/src/opencli.js.map +1 -0
- package/dist/src/openpr.js +457 -0
- package/dist/src/openpr.js.map +1 -0
- package/dist/src/openviking/client.js +821 -0
- package/dist/src/openviking/client.js.map +1 -0
- package/dist/src/openviking/context.js +218 -0
- package/dist/src/openviking/context.js.map +1 -0
- package/dist/src/openviking/embedder.js +457 -0
- package/dist/src/openviking/embedder.js.map +1 -0
- package/dist/src/openviking/gapsink.js +38 -0
- package/dist/src/openviking/gapsink.js.map +1 -0
- package/dist/src/openviking/health.js +69 -0
- package/dist/src/openviking/health.js.map +1 -0
- package/dist/src/openviking/index.js +24 -0
- package/dist/src/openviking/index.js.map +1 -0
- package/dist/src/openviking/lessonfind.js +54 -0
- package/dist/src/openviking/lessonfind.js.map +1 -0
- package/dist/src/openviking/probe.js +89 -0
- package/dist/src/openviking/probe.js.map +1 -0
- package/dist/src/openviking/proc.js +95 -0
- package/dist/src/openviking/proc.js.map +1 -0
- package/dist/src/openviking/provisionreport.js +144 -0
- package/dist/src/openviking/provisionreport.js.map +1 -0
- package/dist/src/openviking/reembed.js +317 -0
- package/dist/src/openviking/reembed.js.map +1 -0
- package/dist/src/openviking/relayout.js +153 -0
- package/dist/src/openviking/relayout.js.map +1 -0
- package/dist/src/openviking/sidecar-main.js +39 -0
- package/dist/src/openviking/sidecar-main.js.map +1 -0
- package/dist/src/openviking/sidecar.js +79 -0
- package/dist/src/openviking/sidecar.js.map +1 -0
- package/dist/src/openviking/store.js +1758 -0
- package/dist/src/openviking/store.js.map +1 -0
- package/dist/src/openviking/supervisor.js +422 -0
- package/dist/src/openviking/supervisor.js.map +1 -0
- package/dist/src/openviking/types.js +5 -0
- package/dist/src/openviking/types.js.map +1 -0
- package/dist/src/operator-recovery.js +172 -0
- package/dist/src/operator-recovery.js.map +1 -0
- package/dist/src/operatormessage.js +44 -0
- package/dist/src/operatormessage.js.map +1 -0
- package/dist/src/outcomes.js +1361 -0
- package/dist/src/outcomes.js.map +1 -0
- package/dist/src/piauthcopy.js +5 -0
- package/dist/src/piauthcopy.js.map +1 -0
- package/dist/src/picli.js +45 -0
- package/dist/src/picli.js.map +1 -0
- package/dist/src/picwd.js +25 -0
- package/dist/src/picwd.js.map +1 -0
- package/dist/src/pilogin.js +35 -0
- package/dist/src/pilogin.js.map +1 -0
- package/dist/src/pingpong.js +1028 -0
- package/dist/src/pingpong.js.map +1 -0
- package/dist/src/posting-journal.js +441 -0
- package/dist/src/posting-journal.js.map +1 -0
- package/dist/src/posting-outbox.js +662 -0
- package/dist/src/posting-outbox.js.map +1 -0
- package/dist/src/posting-receipt.js +203 -0
- package/dist/src/posting-receipt.js.map +1 -0
- package/dist/src/posting.js +348 -0
- package/dist/src/posting.js.map +1 -0
- package/dist/src/postops.js +637 -0
- package/dist/src/postops.js.map +1 -0
- package/dist/src/pr-activity.js +11 -0
- package/dist/src/pr-activity.js.map +1 -0
- package/dist/src/pr-author.js +5 -0
- package/dist/src/pr-author.js.map +1 -0
- package/dist/src/pr-conversation.js +355 -0
- package/dist/src/pr-conversation.js.map +1 -0
- package/dist/src/pr-session-lifecycle.js +542 -0
- package/dist/src/pr-session-lifecycle.js.map +1 -0
- package/dist/src/preflight.js +341 -0
- package/dist/src/preflight.js.map +1 -0
- package/dist/src/priorresolve.js +275 -0
- package/dist/src/priorresolve.js.map +1 -0
- package/dist/src/qcrbin.js +180 -0
- package/dist/src/qcrbin.js.map +1 -0
- package/dist/src/recheck.js +1398 -0
- package/dist/src/recheck.js.map +1 -0
- package/dist/src/reconcile.js +820 -0
- package/dist/src/reconcile.js.map +1 -0
- package/dist/src/recordedfindings.js +15 -0
- package/dist/src/recordedfindings.js.map +1 -0
- package/dist/src/regression.js +546 -0
- package/dist/src/regression.js.map +1 -0
- package/dist/src/replay.js +598 -0
- package/dist/src/replay.js.map +1 -0
- package/dist/src/requiredpasses.js +113 -0
- package/dist/src/requiredpasses.js.map +1 -0
- package/dist/src/resumecheck.js +168 -0
- package/dist/src/resumecheck.js.map +1 -0
- package/dist/src/reviewcli.js +671 -0
- package/dist/src/reviewcli.js.map +1 -0
- package/dist/src/reviewcontext.js +22 -0
- package/dist/src/reviewcontext.js.map +1 -0
- package/dist/src/reviewdetach.js +574 -0
- package/dist/src/reviewdetach.js.map +1 -0
- package/dist/src/reviewhistory.js +284 -0
- package/dist/src/reviewhistory.js.map +1 -0
- package/dist/src/reviewhome.js +26 -0
- package/dist/src/reviewhome.js.map +1 -0
- package/dist/src/reviewmemory.js +110 -0
- package/dist/src/reviewmemory.js.map +1 -0
- package/dist/src/reviewrendercli.js +104 -0
- package/dist/src/reviewrendercli.js.map +1 -0
- package/dist/src/round-work.js +20 -0
- package/dist/src/round-work.js.map +1 -0
- package/dist/src/runhost.js +72 -0
- package/dist/src/runhost.js.map +1 -0
- package/dist/src/scanauth.js +25 -0
- package/dist/src/scanauth.js.map +1 -0
- package/dist/src/scanbox.js +846 -0
- package/dist/src/scanbox.js.map +1 -0
- package/dist/src/scancli.js +1074 -0
- package/dist/src/scancli.js.map +1 -0
- package/dist/src/scandriverlog.js +30 -0
- package/dist/src/scandriverlog.js.map +1 -0
- package/dist/src/session-bridge.js +75 -0
- package/dist/src/session-bridge.js.map +1 -0
- package/dist/src/session-command-payload.js +29 -0
- package/dist/src/session-command-payload.js.map +1 -0
- package/dist/src/session-command-supervisor.js +939 -0
- package/dist/src/session-command-supervisor.js.map +1 -0
- package/dist/src/sessionjsonl.js +85 -0
- package/dist/src/sessionjsonl.js.map +1 -0
- package/dist/src/sessions.js +541 -0
- package/dist/src/sessions.js.map +1 -0
- package/dist/src/setup/adapters.js +330 -0
- package/dist/src/setup/adapters.js.map +1 -0
- package/dist/src/setup/baselines.js +64 -0
- package/dist/src/setup/baselines.js.map +1 -0
- package/dist/src/setup/caserunner.js +554 -0
- package/dist/src/setup/caserunner.js.map +1 -0
- package/dist/src/setup/configcommit.js +358 -0
- package/dist/src/setup/configcommit.js.map +1 -0
- package/dist/src/setup/engine.js +672 -0
- package/dist/src/setup/engine.js.map +1 -0
- package/dist/src/setup/facts.js +593 -0
- package/dist/src/setup/facts.js.map +1 -0
- package/dist/src/setup/findings.js +948 -0
- package/dist/src/setup/findings.js.map +1 -0
- package/dist/src/setup/history.js +338 -0
- package/dist/src/setup/history.js.map +1 -0
- package/dist/src/setup/interview.js +185 -0
- package/dist/src/setup/interview.js.map +1 -0
- package/dist/src/setup/ledger.js +207 -0
- package/dist/src/setup/ledger.js.map +1 -0
- package/dist/src/setup/pins.js +43 -0
- package/dist/src/setup/pins.js.map +1 -0
- package/dist/src/setup/portrait.js +432 -0
- package/dist/src/setup/portrait.js.map +1 -0
- package/dist/src/setup/presentation.js +9 -0
- package/dist/src/setup/presentation.js.map +1 -0
- package/dist/src/setup/profile.js +417 -0
- package/dist/src/setup/profile.js.map +1 -0
- package/dist/src/setup/replay.js +240 -0
- package/dist/src/setup/replay.js.map +1 -0
- package/dist/src/setup/replaybuild.js +311 -0
- package/dist/src/setup/replaybuild.js.map +1 -0
- package/dist/src/setup/replayrun.js +806 -0
- package/dist/src/setup/replayrun.js.map +1 -0
- package/dist/src/setup/replaysource.js +169 -0
- package/dist/src/setup/replaysource.js.map +1 -0
- package/dist/src/setup/retune.js +333 -0
- package/dist/src/setup/retune.js.map +1 -0
- package/dist/src/setup/retunelog.js +51 -0
- package/dist/src/setup/retunelog.js.map +1 -0
- package/dist/src/setup/scan.js +725 -0
- package/dist/src/setup/scan.js.map +1 -0
- package/dist/src/setup/scanbuild.js +133 -0
- package/dist/src/setup/scanbuild.js.map +1 -0
- package/dist/src/setup/scanfacts.js +96 -0
- package/dist/src/setup/scanfacts.js.map +1 -0
- package/dist/src/setup/scanlease.js +251 -0
- package/dist/src/setup/scanlease.js.map +1 -0
- package/dist/src/setup/scanreport.js +289 -0
- package/dist/src/setup/scanreport.js.map +1 -0
- package/dist/src/setup/scanrun.js +1733 -0
- package/dist/src/setup/scanrun.js.map +1 -0
- package/dist/src/setup/scantree.js +257 -0
- package/dist/src/setup/scantree.js.map +1 -0
- package/dist/src/setup/stage2.js +617 -0
- package/dist/src/setup/stage2.js.map +1 -0
- package/dist/src/setup/stage2request.js +259 -0
- package/dist/src/setup/stage2request.js.map +1 -0
- package/dist/src/setup/survey.js +307 -0
- package/dist/src/setup/survey.js.map +1 -0
- package/dist/src/setup/surveyrun.js +549 -0
- package/dist/src/setup/surveyrun.js.map +1 -0
- package/dist/src/setup/textsniff.js +89 -0
- package/dist/src/setup/textsniff.js.map +1 -0
- package/dist/src/setup/trackersink.js +395 -0
- package/dist/src/setup/trackersink.js.map +1 -0
- package/dist/src/setup/triage.js +636 -0
- package/dist/src/setup/triage.js.map +1 -0
- package/dist/src/setup/tuning.js +585 -0
- package/dist/src/setup/tuning.js.map +1 -0
- package/dist/src/setup/unitcut.js +437 -0
- package/dist/src/setup/unitcut.js.map +1 -0
- package/dist/src/singleflight.js +72 -0
- package/dist/src/singleflight.js.map +1 -0
- package/dist/src/slack/announce.js +725 -0
- package/dist/src/slack/announce.js.map +1 -0
- package/dist/src/slack/api.js +227 -0
- package/dist/src/slack/api.js.map +1 -0
- package/dist/src/slack/messages.js +158 -0
- package/dist/src/slack/messages.js.map +1 -0
- package/dist/src/slack/origin.js +550 -0
- package/dist/src/slack/origin.js.map +1 -0
- package/dist/src/slack/policy.js +225 -0
- package/dist/src/slack/policy.js.map +1 -0
- package/dist/src/slack/reply.js +153 -0
- package/dist/src/slack/reply.js.map +1 -0
- package/dist/src/slack/router.js +189 -0
- package/dist/src/slack/router.js.map +1 -0
- package/dist/src/slack/socket.js +323 -0
- package/dist/src/slack/socket.js.map +1 -0
- package/dist/src/slack/types.js +11 -0
- package/dist/src/slack/types.js.map +1 -0
- package/dist/src/sqlitehint.js +30 -0
- package/dist/src/sqlitehint.js.map +1 -0
- package/dist/src/ssh-observation.js +25 -0
- package/dist/src/ssh-observation.js.map +1 -0
- package/dist/src/state.js +5869 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/statemachine.js +220 -0
- package/dist/src/statemachine.js.map +1 -0
- package/dist/src/stubs.js +52 -0
- package/dist/src/stubs.js.map +1 -0
- package/dist/src/takeover.js +506 -0
- package/dist/src/takeover.js.map +1 -0
- package/dist/src/takeoverpi.js +249 -0
- package/dist/src/takeoverpi.js.map +1 -0
- package/dist/src/terminal-failure.js +26 -0
- package/dist/src/terminal-failure.js.map +1 -0
- package/dist/src/thread-resolution-refusal.js +4 -0
- package/dist/src/thread-resolution-refusal.js.map +1 -0
- package/dist/src/tmux.js +126 -0
- package/dist/src/tmux.js.map +1 -0
- package/dist/src/trackerid.js +43 -0
- package/dist/src/trackerid.js.map +1 -0
- package/dist/src/transcriptindex.js +512 -0
- package/dist/src/transcriptindex.js.map +1 -0
- package/dist/src/tui/actions.js +404 -0
- package/dist/src/tui/actions.js.map +1 -0
- package/dist/src/tui/alerts.js +523 -0
- package/dist/src/tui/alerts.js.map +1 -0
- package/dist/src/tui/app.js +1835 -0
- package/dist/src/tui/app.js.map +1 -0
- package/dist/src/tui/archiveops.js +164 -0
- package/dist/src/tui/archiveops.js.map +1 -0
- package/dist/src/tui/autofile.js +191 -0
- package/dist/src/tui/autofile.js.map +1 -0
- package/dist/src/tui/board.js +1143 -0
- package/dist/src/tui/board.js.map +1 -0
- package/dist/src/tui/candidates.js +699 -0
- package/dist/src/tui/candidates.js.map +1 -0
- package/dist/src/tui/cellwidth.js +151 -0
- package/dist/src/tui/cellwidth.js.map +1 -0
- package/dist/src/tui/changelog-store.js +404 -0
- package/dist/src/tui/changelog-store.js.map +1 -0
- package/dist/src/tui/chord.js +26 -0
- package/dist/src/tui/chord.js.map +1 -0
- package/dist/src/tui/clipboard.js +81 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/cmdbar.js +253 -0
- package/dist/src/tui/cmdbar.js.map +1 -0
- package/dist/src/tui/companion.js +1275 -0
- package/dist/src/tui/companion.js.map +1 -0
- package/dist/src/tui/companionplan.js +200 -0
- package/dist/src/tui/companionplan.js.map +1 -0
- package/dist/src/tui/companiontab.js +61 -0
- package/dist/src/tui/companiontab.js.map +1 -0
- package/dist/src/tui/confighome.js +105 -0
- package/dist/src/tui/confighome.js.map +1 -0
- package/dist/src/tui/connect.js +44 -0
- package/dist/src/tui/connect.js.map +1 -0
- package/dist/src/tui/deploy-ceremony.js +75 -0
- package/dist/src/tui/deploy-ceremony.js.map +1 -0
- package/dist/src/tui/deploy-drain.js +93 -0
- package/dist/src/tui/deploy-drain.js.map +1 -0
- package/dist/src/tui/deploy-process.js +234 -0
- package/dist/src/tui/deploy-process.js.map +1 -0
- package/dist/src/tui/deploytab.js +1263 -0
- package/dist/src/tui/deploytab.js.map +1 -0
- package/dist/src/tui/design.js +111 -0
- package/dist/src/tui/design.js.map +1 -0
- package/dist/src/tui/digesttab.js +148 -0
- package/dist/src/tui/digesttab.js.map +1 -0
- package/dist/src/tui/emptystate.js +32 -0
- package/dist/src/tui/emptystate.js.map +1 -0
- package/dist/src/tui/evidence.js +790 -0
- package/dist/src/tui/evidence.js.map +1 -0
- package/dist/src/tui/feed.js +415 -0
- package/dist/src/tui/feed.js.map +1 -0
- package/dist/src/tui/feedbacktab.js +907 -0
- package/dist/src/tui/feedbacktab.js.map +1 -0
- package/dist/src/tui/filters.js +72 -0
- package/dist/src/tui/filters.js.map +1 -0
- package/dist/src/tui/ghread.js +272 -0
- package/dist/src/tui/ghread.js.map +1 -0
- package/dist/src/tui/historypull.js +61 -0
- package/dist/src/tui/historypull.js.map +1 -0
- package/dist/src/tui/inboxpull.js +197 -0
- package/dist/src/tui/inboxpull.js.map +1 -0
- package/dist/src/tui/index.js +1425 -0
- package/dist/src/tui/index.js.map +1 -0
- package/dist/src/tui/issuesink.js +206 -0
- package/dist/src/tui/issuesink.js.map +1 -0
- package/dist/src/tui/jsonfile.js +32 -0
- package/dist/src/tui/jsonfile.js.map +1 -0
- package/dist/src/tui/keys.js +97 -0
- package/dist/src/tui/keys.js.map +1 -0
- package/dist/src/tui/layout.js +70 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/learning.js +469 -0
- package/dist/src/tui/learning.js.map +1 -0
- package/dist/src/tui/lessonswitch.js +103 -0
- package/dist/src/tui/lessonswitch.js.map +1 -0
- package/dist/src/tui/livecontrol.js +167 -0
- package/dist/src/tui/livecontrol.js.map +1 -0
- package/dist/src/tui/memforward.js +310 -0
- package/dist/src/tui/memforward.js.map +1 -0
- package/dist/src/tui/memincident.js +287 -0
- package/dist/src/tui/memincident.js.map +1 -0
- package/dist/src/tui/memory.js +485 -0
- package/dist/src/tui/memory.js.map +1 -0
- package/dist/src/tui/memtab.js +513 -0
- package/dist/src/tui/memtab.js.map +1 -0
- package/dist/src/tui/metricstab.js +979 -0
- package/dist/src/tui/metricstab.js.map +1 -0
- package/dist/src/tui/model.js +104 -0
- package/dist/src/tui/model.js.map +1 -0
- package/dist/src/tui/mouse.js +83 -0
- package/dist/src/tui/mouse.js.map +1 -0
- package/dist/src/tui/msg.js +196 -0
- package/dist/src/tui/msg.js.map +1 -0
- package/dist/src/tui/nativecompanion.js +321 -0
- package/dist/src/tui/nativecompanion.js.map +1 -0
- package/dist/src/tui/openurl.js +17 -0
- package/dist/src/tui/openurl.js.map +1 -0
- package/dist/src/tui/paint.js +501 -0
- package/dist/src/tui/paint.js.map +1 -0
- package/dist/src/tui/panes.js +74 -0
- package/dist/src/tui/panes.js.map +1 -0
- package/dist/src/tui/pipane.js +166 -0
- package/dist/src/tui/pipane.js.map +1 -0
- package/dist/src/tui/poll-hooks.js +11 -0
- package/dist/src/tui/poll-hooks.js.map +1 -0
- package/dist/src/tui/poll.js +113 -0
- package/dist/src/tui/poll.js.map +1 -0
- package/dist/src/tui/proposals.js +715 -0
- package/dist/src/tui/proposals.js.map +1 -0
- package/dist/src/tui/prpause.js +132 -0
- package/dist/src/tui/prpause.js.map +1 -0
- package/dist/src/tui/reload.js +351 -0
- package/dist/src/tui/reload.js.map +1 -0
- package/dist/src/tui/remote.js +312 -0
- package/dist/src/tui/remote.js.map +1 -0
- package/dist/src/tui/rollover.js +767 -0
- package/dist/src/tui/rollover.js.map +1 -0
- package/dist/src/tui/scantab.js +1053 -0
- package/dist/src/tui/scantab.js.map +1 -0
- package/dist/src/tui/setupentry.js +81 -0
- package/dist/src/tui/setupentry.js.map +1 -0
- package/dist/src/tui/sidebar.js +602 -0
- package/dist/src/tui/sidebar.js.map +1 -0
- package/dist/src/tui/sshtriage.js +393 -0
- package/dist/src/tui/sshtriage.js.map +1 -0
- package/dist/src/tui/startup.js +172 -0
- package/dist/src/tui/startup.js.map +1 -0
- package/dist/src/tui/tabs.js +41 -0
- package/dist/src/tui/tabs.js.map +1 -0
- package/dist/src/tui/termmirror.js +217 -0
- package/dist/src/tui/termmirror.js.map +1 -0
- package/dist/src/tui/theme.js +95 -0
- package/dist/src/tui/theme.js.map +1 -0
- package/dist/src/tui/ticketstab.js +224 -0
- package/dist/src/tui/ticketstab.js.map +1 -0
- package/dist/src/tui/tmuxsocket.js +101 -0
- package/dist/src/tui/tmuxsocket.js.map +1 -0
- package/dist/src/tui/trackertickets.js +77 -0
- package/dist/src/tui/trackertickets.js.map +1 -0
- package/dist/src/tui/unwedge.js +245 -0
- package/dist/src/tui/unwedge.js.map +1 -0
- package/dist/src/tui/viewer.js +479 -0
- package/dist/src/tui/viewer.js.map +1 -0
- package/dist/src/tui/viewerinput.js +125 -0
- package/dist/src/tui/viewerinput.js.map +1 -0
- package/dist/src/updatenotice.js +143 -0
- package/dist/src/updatenotice.js.map +1 -0
- package/dist/src/usage.js +1007 -0
- package/dist/src/usage.js.map +1 -0
- package/dist/src/verdicts.js +3848 -0
- package/dist/src/verdicts.js.map +1 -0
- package/dist/src/version.js +58 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/wakes.js +411 -0
- package/dist/src/wakes.js.map +1 -0
- package/dist/src/webhook.js +725 -0
- package/dist/src/webhook.js.map +1 -0
- package/dist/src/webhookheal.js +238 -0
- package/dist/src/webhookheal.js.map +1 -0
- package/dist/src/wire.js +1801 -0
- package/dist/src/wire.js.map +1 -0
- package/dist/src/worker-session-host.js +531 -0
- package/dist/src/worker-session-host.js.map +1 -0
- package/dist/src/workerbreaker.js +112 -0
- package/dist/src/workerbreaker.js.map +1 -0
- package/dist/src/workercontrol.js +49 -0
- package/dist/src/workercontrol.js.map +1 -0
- package/dist/src/workerlease.js +351 -0
- package/dist/src/workerlease.js.map +1 -0
- package/dist/src/workerliveness.js +84 -0
- package/dist/src/workerliveness.js.map +1 -0
- package/dist/src/workerlog.js +65 -0
- package/dist/src/workerlog.js.map +1 -0
- package/dist/src/workerruntime.js +10 -0
- package/dist/src/workerruntime.js.map +1 -0
- package/dist/src/workersupervisor.js +1206 -0
- package/dist/src/workersupervisor.js.map +1 -0
- package/dist/src/workerusage.js +23 -0
- package/dist/src/workerusage.js.map +1 -0
- package/dist/src/workerworld.js +332 -0
- package/dist/src/workerworld.js.map +1 -0
- package/dist/src/worktrees.js +984 -0
- package/dist/src/worktrees.js.map +1 -0
- package/docker/Dockerfile +284 -0
- package/docker/Dockerfile.dockerignore +35 -0
- package/docker/README.md +221 -0
- package/docker/companion/Dockerfile +139 -0
- package/docker/companion/agents/evidence.md +34 -0
- package/docker/companion/agents/explore.md +26 -0
- package/docker/companion/agents/pass-survey.md +49 -0
- package/docker/companion/agents/study.md +26 -0
- package/docker/companion/entrypoint.sh +343 -0
- package/docker/companion/memory-health.mjs +269 -0
- package/docker/entrypoint.sh +782 -0
- package/docker/memory/Dockerfile +191 -0
- package/docker/memory/Dockerfile.dockerignore +20 -0
- package/docker/memory/README.md +60 -0
- package/docker/memory/entrypoint.sh +163 -0
- package/docker/setid/qcr-setid.c +109 -0
- package/docker/tunnel/Dockerfile.cloudflared +51 -0
- package/docker/tunnel/Dockerfile.ngrok +40 -0
- package/docker/tunnel/sidecar.mjs +411 -0
- package/docker/vm/tmux.conf +26 -0
- package/docker-compose.yml +348 -0
- package/extensions/cli/bash-guard-ambient.ts +71 -0
- package/extensions/cli/bash-guard.ts +12 -0
- package/extensions/cli/companion-guard.ts +8 -0
- package/extensions/cli/debug-round.ts +9 -0
- package/extensions/cli/inject-bridge.ts +8 -0
- package/extensions/cli/ledger-pin.ts +13 -0
- package/extensions/cli/lens.ts +22 -0
- package/extensions/cli/muted-theme.ts +9 -0
- package/extensions/cli/onboarding.ts +8 -0
- package/extensions/cli/setup.ts +8 -0
- package/extensions/cli/tui-view.ts +8 -0
- package/extensions/companion-guard.ts +411 -0
- package/extensions/debug-round.ts +281 -0
- package/extensions/inject-bridge.ts +630 -0
- package/extensions/inline-bash.ts +102 -0
- package/extensions/muted-theme.json +68 -0
- package/extensions/muted-theme.ts +23 -0
- package/extensions/onboarding.ts +233 -0
- package/extensions/setup-replay.ts +336 -0
- package/extensions/setup-scan.ts +1236 -0
- package/extensions/setup-triage.ts +190 -0
- package/extensions/setup.ts +1428 -0
- package/extensions/tui-view.ts +489 -0
- package/extensions/vendor/openviking-pi/LICENSE +201 -0
- package/extensions/vendor/openviking-pi/NOTICE +68 -0
- package/extensions/vendor/openviking-pi/README.md +550 -0
- package/extensions/vendor/openviking-pi/client.ts +588 -0
- package/extensions/vendor/openviking-pi/config.json +28 -0
- package/extensions/vendor/openviking-pi/config.ts +271 -0
- package/extensions/vendor/openviking-pi/index.ts +523 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
- package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
- package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
- package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
- package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
- package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
- package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
- package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
- package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
- package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
- package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
- package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
- package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
- package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
- package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
- package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
- package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
- package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
- package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
- package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
- package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
- package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
- package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
- package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
- package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
- package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
- package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
- package/extensions/vendor/openviking-pi/recall.ts +149 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
- package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
- package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
- package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
- package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
- package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
- package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
- package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
- package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
- package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
- package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
- package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
- package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
- package/extensions/vendor/openviking-pi/sync.ts +163 -0
- package/extensions/vendor/openviking-pi/takeover.ts +36 -0
- package/extensions/vendor/openviking-pi/tools.ts +479 -0
- package/extensions/vendor/openviking-pi/upstream.json +48 -0
- package/package.json +138 -4
- package/profiles/default/README.md +21 -0
- package/profiles/default/addenda/.gitkeep +0 -0
- package/profiles/default/profile.toml +17 -0
- package/profiles/example-django/README.md +21 -0
- package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
- package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
- package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
- package/profiles/example-django/addenda/40-orm-usage.md +19 -0
- package/profiles/example-django/addenda/50-declared-shape.md +10 -0
- package/profiles/example-django/addenda/60-config-placement.md +14 -0
- package/profiles/example-django/heuristics.toml +24 -0
- package/profiles/example-django/profile.toml +9 -0
- package/profiles/fast/README.md +24 -0
- package/profiles/fast/profile.toml +59 -0
- package/profiles/minimal/README.md +17 -0
- package/profiles/minimal/addenda/.gitkeep +0 -0
- package/profiles/minimal/profile.toml +8 -0
- package/profiles/snappy/README.md +24 -0
- package/profiles/snappy/addenda/10-race-as-question.md +6 -0
- package/profiles/snappy/profile.toml +46 -0
- package/prompts/companion-day-open.md +46 -0
- package/prompts/companion-first-run.md +104 -0
- package/prompts/companion.md +821 -0
- package/prompts/conversation-system.md +117 -0
- package/prompts/eod-sweep.md +120 -0
- package/prompts/onboarding.md +74 -0
- package/prompts/pass-common.md +52 -0
- package/prompts/pass-quality-common.md +22 -0
- package/prompts/round-first.md +874 -0
- package/prompts/round-follow-up.md +991 -0
- package/prompts/round-re-review.md +889 -0
- package/prompts/round-recheck.md +196 -0
- package/prompts/round-staleness.md +856 -0
- package/schemas/review-result.v1.json +391 -0
- package/schemas/scan-result.v1.json +421 -0
- package/schemas/version.v1.json +57 -0
- package/scripts/approve-pr.sh +16 -0
- package/scripts/build-stamp.mjs +67 -0
- package/scripts/commit-verdict.sh +155 -0
- package/scripts/companion-e2e.sh +250 -0
- package/scripts/companion-memory-forward.mjs +1966 -0
- package/scripts/config-fingerprint.sh +28 -0
- package/scripts/deploy-manifest.mjs +141 -0
- package/scripts/deploy.sh +1435 -0
- package/scripts/directory-lock.mjs +193 -0
- package/scripts/heartbeat-fresh.mjs +54 -0
- package/scripts/install-locator.sh +328 -0
- package/scripts/install-wrapper.sh +76 -0
- package/scripts/memory-search-check.mjs +98 -0
- package/scripts/mint-token.sh +212 -0
- package/scripts/openviking-embedder.py +392 -0
- package/scripts/openviking-ov-conf.mjs +161 -0
- package/scripts/openviking-provision.mjs +1122 -0
- package/scripts/openviking-reembed.mjs +1154 -0
- package/scripts/post-review.sh +16 -0
- package/scripts/pr-checks.sh +256 -0
- package/scripts/pr-context.py +174 -0
- package/scripts/pr-context.sh +63 -0
- package/scripts/pr-self.sh +144 -0
- package/scripts/pr-threads.sh +145 -0
- package/scripts/qcr +15 -0
- package/scripts/qcr-host.sh +769 -0
- package/scripts/render-review.sh +24 -0
- package/scripts/reply-thread.sh +16 -0
- package/scripts/required-passes.py +112 -0
- package/scripts/smoke-companion.sh +211 -0
- package/scripts/tui-deploy-runner.mjs +61 -0
- package/scripts/verify-dist.mjs +201 -0
- package/scripts/vm-ghcr-login.sh +188 -0
- package/seeds/agents/pass-baseline.md +61 -0
- package/seeds/agents/pass-contracts.md +30 -0
- package/seeds/agents/pass-data-safety.md +54 -0
- package/seeds/agents/pass-delivery.md +37 -0
- package/seeds/agents/pass-prod-risk.md +24 -0
- package/seeds/agents/pass-security.md +31 -0
- package/seeds/agents/pass-statelessness.md +27 -0
- package/seeds/agents/pass-ui.md +23 -0
- package/seeds/agents/pass-validate.md +50 -0
- package/seeds/prompts/conversation-system.md +117 -0
- package/seeds/prompts/eod-sweep.md +120 -0
- package/seeds/prompts/pass-common.md +52 -0
- package/skills/companion/adjudicate/SKILL.md +247 -0
- package/skills/companion/evidence/SKILL.md +150 -0
- package/skills/companion/land/SKILL.md +200 -0
- package/skills/companion/replay/SKILL.md +138 -0
- package/skills/companion/setup/SKILL.md +167 -0
- package/skills/companion/survey/SKILL.md +160 -0
- package/skills/selfimprove/README.md +34 -0
- package/skills/selfimprove/SKILL.md +127 -0
- package/skills/selfimprove/agents/openai.yaml +2 -0
- package/skills/selfimprove/commands/selfimprove.md +6 -0
- package/skills/selfimprove/references/fix-loop.md +49 -0
- package/skills/selfimprove/references/ledger.md +20 -0
- package/skills/selfimprove/references/setup.md +40 -0
- package/src/anomalies.ts +566 -0
- package/src/answerworker.ts +251 -0
- package/src/appstamp.ts +65 -0
- package/src/archive/admission.ts +3 -0
- package/src/archive/archivedb.ts +548 -0
- package/src/archive/archiver.ts +1963 -0
- package/src/archive/archivewrite.ts +279 -0
- package/src/archive/bundle.ts +333 -0
- package/src/archive/dbmove.ts +911 -0
- package/src/archive/duplicatecounts.ts +98 -0
- package/src/archive/eligibility.ts +181 -0
- package/src/archive/extract.ts +342 -0
- package/src/archive/fsutil.ts +415 -0
- package/src/archive/gzfile.ts +370 -0
- package/src/archive/kinds.ts +21 -0
- package/src/archive/livecheckpoint.ts +217 -0
- package/src/archive/liveretention.ts +180 -0
- package/src/archive/manifest.ts +519 -0
- package/src/archive/overseer.ts +336 -0
- package/src/archive/piglobal.ts +174 -0
- package/src/archive/plan.ts +612 -0
- package/src/archive/prarchive.ts +984 -0
- package/src/archive/prfiles.ts +406 -0
- package/src/archive/report.ts +252 -0
- package/src/archive/restore.ts +330 -0
- package/src/archive/rows.ts +187 -0
- package/src/archive/seal.ts +630 -0
- package/src/archive/status.ts +719 -0
- package/src/archive/storepass.ts +581 -0
- package/src/backfill.ts +362 -0
- package/src/bench/cost.ts +113 -0
- package/src/bench/exec.ts +90 -0
- package/src/bench/values.ts +13 -0
- package/src/botcontrol.ts +176 -0
- package/src/botworker.ts +953 -0
- package/src/capture.ts +321 -0
- package/src/cases.ts +1789 -0
- package/src/channelfs.ts +26 -0
- package/src/checklist.ts +506 -0
- package/src/ciretry.ts +141 -0
- package/src/cli.ts +2271 -0
- package/src/cliops.ts +384 -0
- package/src/clonedir.ts +15 -0
- package/src/commandeffects.ts +57 -0
- package/src/commands.ts +1678 -0
- package/src/companioninbox.ts +239 -0
- package/src/compare.ts +957 -0
- package/src/config.ts +2050 -0
- package/src/configcheck.ts +133 -0
- package/src/coreext.ts +47 -0
- package/src/daemon.ts +1302 -0
- package/src/debuground.ts +927 -0
- package/src/debugroundpi.ts +262 -0
- package/src/diagnostics.ts +14 -0
- package/src/digest.ts +183 -0
- package/src/dispatch.ts +2250 -0
- package/src/engine/answerguard.ts +27 -0
- package/src/engine/botsdk.ts +336 -0
- package/src/engine/caserunner-sdk.ts +423 -0
- package/src/engine/debugpi.ts +235 -0
- package/src/eod.ts +669 -0
- package/src/eodpinwatch.ts +556 -0
- package/src/escalation.ts +1253 -0
- package/src/eventloop.ts +1045 -0
- package/src/execboundary.ts +633 -0
- package/src/feedback.ts +1162 -0
- package/src/findingscheck.ts +298 -0
- package/src/fsdenied.ts +24 -0
- package/src/ghread.ts +242 -0
- package/src/github.ts +1497 -0
- package/src/homes.ts +175 -0
- package/src/hostroute.ts +53 -0
- package/src/improvements.ts +787 -0
- package/src/install.ts +1103 -0
- package/src/installkind.ts +50 -0
- package/src/ipc/capability.ts +333 -0
- package/src/ipc/client.ts +269 -0
- package/src/ipc/control.ts +90 -0
- package/src/ipc/ghreadshim.ts +94 -0
- package/src/ipc/launcher.ts +65 -0
- package/src/ipc/permits.ts +362 -0
- package/src/ipc/postshim.ts +164 -0
- package/src/ipc/protocol.ts +342 -0
- package/src/ipc/server.ts +371 -0
- package/src/ipc/workerspec.ts +218 -0
- package/src/learner/learner.ts +1655 -0
- package/src/learner/miner.ts +1290 -0
- package/src/learner/packet.ts +340 -0
- package/src/learner/receipt.ts +168 -0
- package/src/learner/status.ts +107 -0
- package/src/ledgercheck.ts +157 -0
- package/src/lessonledger.ts +703 -0
- package/src/lessonselect.ts +888 -0
- package/src/lessonshadow.ts +810 -0
- package/src/levers.ts +393 -0
- package/src/live/projection.ts +491 -0
- package/src/live/publish.ts +92 -0
- package/src/live/read.ts +86 -0
- package/src/livecli.ts +336 -0
- package/src/loginhint.ts +56 -0
- package/src/loginrefresh/atomic.ts +76 -0
- package/src/loginrefresh/lock.ts +104 -0
- package/src/loginrefresh/main.ts +59 -0
- package/src/loginrefresh/pirefresh.ts +57 -0
- package/src/loginrefresh/refresher.ts +326 -0
- package/src/loginrefresh/supervisor.ts +273 -0
- package/src/loginrefresh/types.ts +35 -0
- package/src/memory/context.ts +385 -0
- package/src/memory/datafence.ts +9 -0
- package/src/memory/delivery.ts +171 -0
- package/src/memory/eventtags.ts +80 -0
- package/src/memory/health.ts +953 -0
- package/src/memory/import.ts +664 -0
- package/src/memory/layout.ts +261 -0
- package/src/memory/legacy-file.ts +104 -0
- package/src/memory/lessons.ts +447 -0
- package/src/memory/ovsession.ts +601 -0
- package/src/memory/relayout.ts +335 -0
- package/src/memory/roundactivity.ts +209 -0
- package/src/memory/selection.ts +989 -0
- package/src/memory/shadowstatus.ts +125 -0
- package/src/memory/store.ts +275 -0
- package/src/memory/textidentity.ts +8 -0
- package/src/metrics.ts +1056 -0
- package/src/missingconfig.ts +7 -0
- package/src/npmverbs.ts +264 -0
- package/src/onboard/choices.ts +222 -0
- package/src/onboard/engine.ts +518 -0
- package/src/onboard/launch.ts +115 -0
- package/src/onboard/steps/appslug.ts +265 -0
- package/src/onboard/steps/bringup.ts +482 -0
- package/src/onboard/steps/compose.ts +459 -0
- package/src/onboard/steps/configdelivery.ts +58 -0
- package/src/onboard/steps/configfill.ts +1352 -0
- package/src/onboard/steps/deploysh.ts +105 -0
- package/src/onboard/steps/embeddings.ts +550 -0
- package/src/onboard/steps/escalation.ts +640 -0
- package/src/onboard/steps/githubapp.ts +1414 -0
- package/src/onboard/steps/golive.ts +577 -0
- package/src/onboard/steps/index.ts +71 -0
- package/src/onboard/steps/ingress.ts +907 -0
- package/src/onboard/steps/inventory.ts +526 -0
- package/src/onboard/steps/llmauth.ts +1095 -0
- package/src/onboard/steps/memory.ts +1290 -0
- package/src/onboard/steps/memorykeys.ts +85 -0
- package/src/onboard/steps/preflight.ts +191 -0
- package/src/onboard/steps/proof.ts +624 -0
- package/src/onboard/steps/ssh.ts +377 -0
- package/src/onboard/steps/topology.ts +337 -0
- package/src/onboard/steps/transport.ts +665 -0
- package/src/onboard/steps/verifyup.ts +448 -0
- package/src/onboard/steps/vmdeploy.ts +442 -0
- package/src/onboard/toml.ts +50 -0
- package/src/opencli.ts +591 -0
- package/src/openpr.ts +568 -0
- package/src/openviking/client.ts +1131 -0
- package/src/openviking/context.ts +263 -0
- package/src/openviking/embedder.ts +516 -0
- package/src/openviking/gapsink.ts +63 -0
- package/src/openviking/health.ts +83 -0
- package/src/openviking/index.ts +119 -0
- package/src/openviking/lessonfind.ts +92 -0
- package/src/openviking/probe.ts +129 -0
- package/src/openviking/proc.ts +110 -0
- package/src/openviking/provisionreport.ts +160 -0
- package/src/openviking/reembed.ts +412 -0
- package/src/openviking/relayout.ts +177 -0
- package/src/openviking/sidecar-main.ts +38 -0
- package/src/openviking/sidecar.ts +106 -0
- package/src/openviking/store.ts +2025 -0
- package/src/openviking/supervisor.ts +474 -0
- package/src/openviking/types.ts +174 -0
- package/src/operator-recovery.ts +237 -0
- package/src/operatormessage.ts +52 -0
- package/src/outcomes.ts +1670 -0
- package/src/piauthcopy.ts +12 -0
- package/src/picli.ts +49 -0
- package/src/picwd.ts +25 -0
- package/src/pilogin.ts +37 -0
- package/src/pingpong.ts +1339 -0
- package/src/posting-journal.ts +549 -0
- package/src/posting-outbox.ts +802 -0
- package/src/posting-receipt.ts +324 -0
- package/src/posting.ts +603 -0
- package/src/postops.ts +832 -0
- package/src/pr-activity.ts +20 -0
- package/src/pr-author.ts +6 -0
- package/src/pr-conversation.ts +432 -0
- package/src/pr-session-lifecycle.ts +631 -0
- package/src/preflight.ts +391 -0
- package/src/priorresolve.ts +343 -0
- package/src/qcrbin.ts +186 -0
- package/src/recheck.ts +1775 -0
- package/src/reconcile.ts +1030 -0
- package/src/recordedfindings.ts +27 -0
- package/src/regression.ts +833 -0
- package/src/replay.ts +816 -0
- package/src/requiredpasses.ts +148 -0
- package/src/resumecheck.ts +246 -0
- package/src/reviewcli.ts +787 -0
- package/src/reviewcontext.ts +35 -0
- package/src/reviewdetach.ts +734 -0
- package/src/reviewhistory.ts +427 -0
- package/src/reviewhome.ts +36 -0
- package/src/reviewmemory.ts +133 -0
- package/src/reviewrendercli.ts +139 -0
- package/src/round-work.ts +42 -0
- package/src/runhost.ts +108 -0
- package/src/scanauth.ts +36 -0
- package/src/scanbox.ts +1081 -0
- package/src/scancli.ts +1296 -0
- package/src/scandriverlog.ts +32 -0
- package/src/session-bridge.ts +139 -0
- package/src/session-command-payload.ts +35 -0
- package/src/session-command-supervisor.ts +1142 -0
- package/src/sessionjsonl.ts +88 -0
- package/src/sessions.ts +659 -0
- package/src/setup/adapters.ts +335 -0
- package/src/setup/baselines.ts +110 -0
- package/src/setup/caserunner.ts +611 -0
- package/src/setup/configcommit.ts +443 -0
- package/src/setup/engine.ts +1001 -0
- package/src/setup/facts.ts +627 -0
- package/src/setup/findings.ts +1264 -0
- package/src/setup/history.ts +443 -0
- package/src/setup/interview.ts +243 -0
- package/src/setup/ledger.ts +324 -0
- package/src/setup/pins.ts +66 -0
- package/src/setup/portrait.ts +635 -0
- package/src/setup/presentation.ts +18 -0
- package/src/setup/profile.ts +508 -0
- package/src/setup/replay.ts +370 -0
- package/src/setup/replaybuild.ts +392 -0
- package/src/setup/replayrun.ts +1096 -0
- package/src/setup/replaysource.ts +198 -0
- package/src/setup/retune.ts +415 -0
- package/src/setup/retunelog.ts +95 -0
- package/src/setup/scan.ts +1033 -0
- package/src/setup/scanbuild.ts +168 -0
- package/src/setup/scanfacts.ts +107 -0
- package/src/setup/scanlease.ts +297 -0
- package/src/setup/scanreport.ts +373 -0
- package/src/setup/scanrun.ts +2268 -0
- package/src/setup/scantree.ts +259 -0
- package/src/setup/stage2.ts +926 -0
- package/src/setup/stage2request.ts +347 -0
- package/src/setup/survey.ts +458 -0
- package/src/setup/surveyrun.ts +758 -0
- package/src/setup/textsniff.ts +95 -0
- package/src/setup/trackersink.ts +433 -0
- package/src/setup/triage.ts +768 -0
- package/src/setup/tuning.ts +766 -0
- package/src/setup/unitcut.ts +523 -0
- package/src/singleflight.ts +86 -0
- package/src/slack/announce.ts +911 -0
- package/src/slack/api.ts +303 -0
- package/src/slack/messages.ts +288 -0
- package/src/slack/origin.ts +599 -0
- package/src/slack/policy.ts +354 -0
- package/src/slack/reply.ts +201 -0
- package/src/slack/router.ts +285 -0
- package/src/slack/socket.ts +384 -0
- package/src/slack/types.ts +108 -0
- package/src/sqlitehint.ts +32 -0
- package/src/ssh-observation.ts +30 -0
- package/src/state.ts +8243 -0
- package/src/statemachine.ts +369 -0
- package/src/stubs.ts +68 -0
- package/src/takeover.ts +634 -0
- package/src/takeoverpi.ts +293 -0
- package/src/terminal-failure.ts +41 -0
- package/src/thread-resolution-refusal.ts +2 -0
- package/src/tmux.ts +153 -0
- package/src/trackerid.ts +41 -0
- package/src/transcriptindex.ts +665 -0
- package/src/tui/actions.ts +484 -0
- package/src/tui/alerts.ts +666 -0
- package/src/tui/app.ts +1979 -0
- package/src/tui/archiveops.ts +192 -0
- package/src/tui/autofile.ts +233 -0
- package/src/tui/board.ts +1432 -0
- package/src/tui/candidates.ts +889 -0
- package/src/tui/cellwidth.ts +148 -0
- package/src/tui/changelog-store.ts +503 -0
- package/src/tui/chord.ts +25 -0
- package/src/tui/clipboard.ts +92 -0
- package/src/tui/cmdbar.ts +296 -0
- package/src/tui/companion.ts +1427 -0
- package/src/tui/companionplan.ts +247 -0
- package/src/tui/companiontab.ts +83 -0
- package/src/tui/confighome.ts +155 -0
- package/src/tui/connect.ts +68 -0
- package/src/tui/deploy-ceremony.ts +104 -0
- package/src/tui/deploy-drain.ts +129 -0
- package/src/tui/deploy-process.ts +265 -0
- package/src/tui/deploytab.ts +1523 -0
- package/src/tui/design.ts +125 -0
- package/src/tui/digesttab.ts +214 -0
- package/src/tui/emptystate.ts +39 -0
- package/src/tui/evidence.ts +999 -0
- package/src/tui/feed.ts +604 -0
- package/src/tui/feedbacktab.ts +1077 -0
- package/src/tui/filters.ts +90 -0
- package/src/tui/ghread.ts +321 -0
- package/src/tui/historypull.ts +81 -0
- package/src/tui/inboxpull.ts +236 -0
- package/src/tui/index.ts +1565 -0
- package/src/tui/issuesink.ts +238 -0
- package/src/tui/jsonfile.ts +42 -0
- package/src/tui/keys.ts +109 -0
- package/src/tui/layout.ts +108 -0
- package/src/tui/learning.ts +627 -0
- package/src/tui/lessonswitch.ts +118 -0
- package/src/tui/livecontrol.ts +228 -0
- package/src/tui/memforward.ts +443 -0
- package/src/tui/memincident.ts +363 -0
- package/src/tui/memory.ts +620 -0
- package/src/tui/memtab.ts +606 -0
- package/src/tui/metricstab.ts +1177 -0
- package/src/tui/model.ts +131 -0
- package/src/tui/mouse.ts +98 -0
- package/src/tui/msg.ts +268 -0
- package/src/tui/nativecompanion.ts +417 -0
- package/src/tui/openurl.ts +21 -0
- package/src/tui/paint.ts +511 -0
- package/src/tui/panes.ts +98 -0
- package/src/tui/pipane.ts +219 -0
- package/src/tui/poll-hooks.ts +12 -0
- package/src/tui/poll.ts +147 -0
- package/src/tui/proposals.ts +838 -0
- package/src/tui/prpause.ts +150 -0
- package/src/tui/reload.ts +436 -0
- package/src/tui/remote.ts +413 -0
- package/src/tui/rollover.ts +969 -0
- package/src/tui/scantab.ts +1290 -0
- package/src/tui/setupentry.ts +93 -0
- package/src/tui/sidebar.ts +749 -0
- package/src/tui/sshtriage.ts +510 -0
- package/src/tui/startup.ts +231 -0
- package/src/tui/tabs.ts +45 -0
- package/src/tui/termmirror.ts +224 -0
- package/src/tui/theme.ts +95 -0
- package/src/tui/ticketstab.ts +310 -0
- package/src/tui/tmuxsocket.ts +129 -0
- package/src/tui/trackertickets.ts +84 -0
- package/src/tui/unwedge.ts +286 -0
- package/src/tui/viewer.ts +575 -0
- package/src/tui/viewerinput.ts +173 -0
- package/src/updatenotice.ts +178 -0
- package/src/usage.ts +1154 -0
- package/src/verdicts.ts +4689 -0
- package/src/version.ts +80 -0
- package/src/wakes.ts +476 -0
- package/src/webhook.ts +942 -0
- package/src/webhookheal.ts +276 -0
- package/src/wire.ts +2153 -0
- package/src/worker-session-host.ts +602 -0
- package/src/workerbreaker.ts +156 -0
- package/src/workercontrol.ts +104 -0
- package/src/workerlease.ts +441 -0
- package/src/workerliveness.ts +103 -0
- package/src/workerlog.ts +79 -0
- package/src/workerruntime.ts +30 -0
- package/src/workersupervisor.ts +1538 -0
- package/src/workerusage.ts +42 -0
- package/src/workerworld.ts +427 -0
- package/src/worktrees.ts +1138 -0
- package/tests/fixtures/regression-cases.json +795 -0
- package/tests/fixtures/seed-manifest.json +19 -0
|
@@ -0,0 +1,1206 @@
|
|
|
1
|
+
// The WorkerSupervisor: the daemon's end of the per-round worker.
|
|
2
|
+
//
|
|
3
|
+
// One Node process serves one round of one PR (src/botworker.ts). This module
|
|
4
|
+
// starts it, watches it and ends it:
|
|
5
|
+
//
|
|
6
|
+
// - SPAWN. `node dist/src/botworker.js --command <id> --mode round|answer`, in
|
|
7
|
+
// a process group of its own, with the command's capability on an inherited
|
|
8
|
+
// fd (stdio[3]) and a small env built from an ALLOWLIST. No GitHub credential
|
|
9
|
+
// of any name is in it (GITHUB_TOKEN_ENVS, from packages/pi-plugin/src/envstrip.ts, so this
|
|
10
|
+
// module never imports src/engine/), and the capability is on the fd only.
|
|
11
|
+
// - LISTEN. It serves `worker.spec` and the worker's events over the IPC socket
|
|
12
|
+
// (src/ipc/server.ts). A request or event is accepted only for the command's
|
|
13
|
+
// live generation (the claim's attempt count); a worker from an earlier
|
|
14
|
+
// generation is refused, and its capability is dead anyway.
|
|
15
|
+
// - WATCH. The worker's heartbeats (`progress` events) are its liveness; the
|
|
16
|
+
// deadlines tolerate the 0.7-2.5 s a wave's session creation blocks its event
|
|
17
|
+
// loop (tests/cpu-gate: staggered grants will shorten it, not remove it). A
|
|
18
|
+
// worker is ended when it stops talking, when a stage runs over, or at its
|
|
19
|
+
// wall clock: SIGTERM to the group, SIGKILL after the grace.
|
|
20
|
+
// - LEASE. A file per session (src/workerlease.ts) names the leader's pid, pgid,
|
|
21
|
+
// start time and the boot/container id, fenced by an owner token. Pids and
|
|
22
|
+
// pgids repeat in a recreated container, so the daemon's boot signals a
|
|
23
|
+
// leased group only when the leader still has the recorded start time in the
|
|
24
|
+
// recorded boot and container.
|
|
25
|
+
// - WRITE. This module is the single writer of session_commands rows for a
|
|
26
|
+
// worker-hosted PR: it creates them, claims them, marks them started on the
|
|
27
|
+
// worker's `accepted`, finishes them from the exit, and requeues a round whose
|
|
28
|
+
// worker died. The one-active index (`session_commands_one_active`) gives one
|
|
29
|
+
// worker per session; a second spawn is refused before the row is claimed.
|
|
30
|
+
// - BREAKER (src/workerbreaker.ts). Every exit is counted: a worker that
|
|
31
|
+
// served its round resets its PR's count; a non-zero exit, a `failed` event or
|
|
32
|
+
// a deadline kill adds one. Two in a row park the PR (no further spawn, a
|
|
33
|
+
// `worker_crash_loop` anomaly, the last error in status.json); failures on three
|
|
34
|
+
// PRs within 15 minutes drain the bot. A drained bot or a parked PR starts no
|
|
35
|
+
// worker, of any mode: `launch` refuses and `launchPending` skips. An end by the
|
|
36
|
+
// daemon's own stop or by the operator is not a failure.
|
|
37
|
+
// - CONTROL. `control` forwards the operator's steer and follow-up to the
|
|
38
|
+
// round's worker over its own connection and answers with the worker's ack
|
|
39
|
+
// (queued, handled or refused); `abortRound` and the recovery `fence` ask the
|
|
40
|
+
// worker to abort and, when it has not gone within the grace period, SIGKILL its
|
|
41
|
+
// group (a lease the process table no longer confirms is never signalled).
|
|
42
|
+
// - TAKEOVER LEASE (src/takeover.ts). While the operator holds a PR's
|
|
43
|
+
// session no worker starts for it, of any mode (`hostGate`): the command waits
|
|
44
|
+
// as it does behind a drain, and the lease is freed only once the takeover pi
|
|
45
|
+
// is confirmed gone. The lease is granted only when no worker command is active.
|
|
46
|
+
// - HOSTED COMMANDS. A round the debug round runs in interactive pi
|
|
47
|
+
// (src/debuground.ts) has no worker process, but its row is still this module's to
|
|
48
|
+
// write: the debug manager asks for the claim, the start mark and the settlement
|
|
49
|
+
// (`claimHosted`, `markHostedStarted`, `settleHosted`).
|
|
50
|
+
// - RESTART. On a daemon restart every leased group is killed and its round is
|
|
51
|
+
// requeued under the same command id (`recover`), so the fresh worker resumes
|
|
52
|
+
// the JSONL and continues instead of sending the order again.
|
|
53
|
+
//
|
|
54
|
+
// The session host over it is src/worker-session-host.ts: the SessionHooks
|
|
55
|
+
// the daemon's state machine speaks, served by this supervisor.
|
|
56
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
57
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
58
|
+
import { closeSync, mkdirSync, openSync, renameSync, writeFileSync } from "node:fs";
|
|
59
|
+
import { dirname, join } from "node:path";
|
|
60
|
+
import { GITHUB_TOKEN_ENVS } from "@quickchatai/pi-quickreview/src/envstrip.js";
|
|
61
|
+
import { createCapabilityFd, ipcEnv, stdioWithCapability, } from "./ipc/capability.js";
|
|
62
|
+
import { IpcError, } from "./ipc/protocol.js";
|
|
63
|
+
import { CONTROL_ACK_OP, controlText, parseControlAck, } from "./ipc/control.js";
|
|
64
|
+
import { ghReadOps } from "./ghread.js";
|
|
65
|
+
import { permitOps } from "./ipc/permits.js";
|
|
66
|
+
import { WAKES_ACK_OP, WAKES_TAKE_OP, WORKER_SPEC_OP, parseWorkerSpec, } from "./ipc/workerspec.js";
|
|
67
|
+
import { DEFAULT_ANSWER_CAP_USD, DEFAULT_ANSWER_TIMEOUT_S } from "./config.js";
|
|
68
|
+
import { operatorMayPost } from "./operatormessage.js";
|
|
69
|
+
import { postOps } from "./postops.js";
|
|
70
|
+
import { messagePayload, verifiedPayload } from "./session-command-payload.js";
|
|
71
|
+
import { DEFAULT_WORKER_LOG_LIMITS, rotateWorkerLogs, workerLogPath, } from "./workerlog.js";
|
|
72
|
+
import { WorkerBreaker } from "./workerbreaker.js";
|
|
73
|
+
import { WorkerLeases, judgeLease, killLease, realProc, sendSignal, } from "./workerlease.js";
|
|
74
|
+
/** The env names a worker process may inherit from the daemon. Everything else is
|
|
75
|
+
* dropped, whatever the daemon's own env holds: a worker carries the daemon's
|
|
76
|
+
* locale, path and proxies, the tool-uid split the image arranged (the worker
|
|
77
|
+
* reads them to run the model's tools under it), and nothing that is a secret. */
|
|
78
|
+
export const WORKER_ENV_ALLOW = [
|
|
79
|
+
"PATH",
|
|
80
|
+
"HOME",
|
|
81
|
+
"LANG",
|
|
82
|
+
"LC_ALL",
|
|
83
|
+
"LC_CTYPE",
|
|
84
|
+
"TZ",
|
|
85
|
+
"TMPDIR",
|
|
86
|
+
"TERM",
|
|
87
|
+
"NODE_EXTRA_CA_CERTS",
|
|
88
|
+
"SSL_CERT_FILE",
|
|
89
|
+
"SSL_CERT_DIR",
|
|
90
|
+
"HTTP_PROXY",
|
|
91
|
+
"HTTPS_PROXY",
|
|
92
|
+
"NO_PROXY",
|
|
93
|
+
"http_proxy",
|
|
94
|
+
"https_proxy",
|
|
95
|
+
"no_proxy",
|
|
96
|
+
"PI_CODING_AGENT_DIR",
|
|
97
|
+
// The separate tool uid (packages/pi-plugin/src/engine/tooluid.ts TOOL_UID_ENVS; named here
|
|
98
|
+
// because the daemon never imports src/engine/).
|
|
99
|
+
"QCR_TOOL_UID",
|
|
100
|
+
"QCR_TOOL_GID",
|
|
101
|
+
"QCR_TOOL_UID_REQUIRED",
|
|
102
|
+
"QCR_ALLOW_SHARED_TOOL_UID",
|
|
103
|
+
// The bot's setid helper, which the worker reaches the tool uid through
|
|
104
|
+
// (tooluid.ts setidHelperOf): a path, no secret.
|
|
105
|
+
"QCR_SETPRIV_BIN",
|
|
106
|
+
];
|
|
107
|
+
export const DEFAULT_WORKER_LIMITS = {
|
|
108
|
+
readyMs: 120_000,
|
|
109
|
+
acceptMs: 120_000,
|
|
110
|
+
heartbeatMs: 15_000,
|
|
111
|
+
missedBeats: 4,
|
|
112
|
+
stallAllowanceMs: 5_000,
|
|
113
|
+
wallGraceMs: 30_000,
|
|
114
|
+
killGraceMs: 10_000,
|
|
115
|
+
abortGraceMs: 5_000,
|
|
116
|
+
controlAckMs: 5_000,
|
|
117
|
+
maxRequeues: 3,
|
|
118
|
+
tickMs: 1_000,
|
|
119
|
+
fenceWaitMs: 15_000,
|
|
120
|
+
};
|
|
121
|
+
/** How long, once silent, a worker has before it is ended. */
|
|
122
|
+
export const staleAfterMs = (heartbeatMs, l) => l.missedBeats * heartbeatMs + l.stallAllowanceMs;
|
|
123
|
+
/**
|
|
124
|
+
* The exit decides the row. Exit 0 is a served round (a verdict may or may not
|
|
125
|
+
* have been written: that is the round's own business). A worker that failed in
|
|
126
|
+
* a way a fresh one would repeat (a refused login, a turn that ran out of time or
|
|
127
|
+
* money, a bad payload) fails the command; one that was lost (killed, crashed,
|
|
128
|
+
* its daemon went away) hands the round to a fresh worker, which resumes the
|
|
129
|
+
* JSONL. Ended for its wall clock, or by the operator: failed, not retried. Ended
|
|
130
|
+
* by a recovery fence: left as it is, for the caller that fenced it.
|
|
131
|
+
*/
|
|
132
|
+
export function decideOutcome(x) {
|
|
133
|
+
if (x.spawnError !== undefined)
|
|
134
|
+
return { kind: "failed", reason: `could not start the worker: ${x.spawnError}` };
|
|
135
|
+
if (x.killedFor === "wall_clock")
|
|
136
|
+
return { kind: "failed", reason: "the worker ran past its wall clock and was ended" };
|
|
137
|
+
if (x.killedFor === "fence")
|
|
138
|
+
return { kind: "leave" };
|
|
139
|
+
if (x.killedFor === "operator")
|
|
140
|
+
return { kind: "failed", reason: "the worker was ended by the operator" };
|
|
141
|
+
if (x.killedFor !== undefined)
|
|
142
|
+
return { kind: "requeue", reason: `the worker was ended: ${x.killedFor}` };
|
|
143
|
+
if (x.code === 0)
|
|
144
|
+
return { kind: "acked" };
|
|
145
|
+
if (x.code !== null && x.code >= 1 && x.code <= 6)
|
|
146
|
+
return { kind: "failed", reason: x.failedReason ?? `the worker exited ${x.code}` };
|
|
147
|
+
return {
|
|
148
|
+
kind: "requeue",
|
|
149
|
+
reason: x.signal ? `the worker died of ${x.signal}` : `the worker exited ${x.code}`,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** The env of one worker process: the allowlisted part of the daemon's env, the
|
|
153
|
+
* daemon's explicit additions, and the IPC names (the fd NUMBER, never the
|
|
154
|
+
* capability). A GitHub credential is dropped last, by name, so no addition can
|
|
155
|
+
* carry one in. */
|
|
156
|
+
export function workerProcessEnv(parent, extra, ipc) {
|
|
157
|
+
const env = {};
|
|
158
|
+
for (const name of WORKER_ENV_ALLOW) {
|
|
159
|
+
const value = parent[name];
|
|
160
|
+
if (value !== undefined && value !== "")
|
|
161
|
+
env[name] = value;
|
|
162
|
+
}
|
|
163
|
+
for (const [name, value] of Object.entries(extra))
|
|
164
|
+
if (!name.startsWith("QCR_IPC_"))
|
|
165
|
+
env[name] = value;
|
|
166
|
+
Object.assign(env, ipc);
|
|
167
|
+
for (const name of GITHUB_TOKEN_ENVS)
|
|
168
|
+
delete env[name];
|
|
169
|
+
return env;
|
|
170
|
+
}
|
|
171
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
172
|
+
export class WorkerLaunchError extends Error {
|
|
173
|
+
constructor(message) {
|
|
174
|
+
super(message);
|
|
175
|
+
this.name = "WorkerLaunchError";
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export class WorkerSupervisor {
|
|
179
|
+
deps;
|
|
180
|
+
limits;
|
|
181
|
+
leases;
|
|
182
|
+
/** The crash-loop breaker: also the question every host asks before a spawn. */
|
|
183
|
+
breaker;
|
|
184
|
+
byCommand = new Map();
|
|
185
|
+
bySession = new Map();
|
|
186
|
+
/** Sessions with a launch under way: the second spawn is refused before any await. */
|
|
187
|
+
launching = new Set();
|
|
188
|
+
spawnFn;
|
|
189
|
+
proc;
|
|
190
|
+
signal;
|
|
191
|
+
now;
|
|
192
|
+
log;
|
|
193
|
+
timer = null;
|
|
194
|
+
stopping = false;
|
|
195
|
+
constructor(deps) {
|
|
196
|
+
this.deps = deps;
|
|
197
|
+
this.limits = { ...DEFAULT_WORKER_LIMITS, ...deps.limits };
|
|
198
|
+
this.leases = new WorkerLeases(join(deps.runDir, "workers"));
|
|
199
|
+
this.spawnFn = deps.spawn ?? nodeSpawn;
|
|
200
|
+
this.proc = deps.proc ?? realProc;
|
|
201
|
+
this.signal = deps.signal ?? sendSignal;
|
|
202
|
+
this.now = deps.now ?? Date.now;
|
|
203
|
+
this.log = deps.log ?? (() => { });
|
|
204
|
+
this.breaker = new WorkerBreaker(deps.store, {
|
|
205
|
+
...(deps.breaker ? { limits: deps.breaker } : {}),
|
|
206
|
+
now: this.now,
|
|
207
|
+
log: this.log,
|
|
208
|
+
});
|
|
209
|
+
deps.permits?.attach(deps.capabilities);
|
|
210
|
+
}
|
|
211
|
+
// ---- what the IPC server asks the daemon ---------------------------------
|
|
212
|
+
/** The server's view of a command, read fresh for every request. `active` is
|
|
213
|
+
* true only while THIS supervisor holds a live worker for the row, so a worker
|
|
214
|
+
* that outlived its row (or its supervisor) is refused as inactive; the
|
|
215
|
+
* generation is the live worker's, so an earlier one is refused as stale. */
|
|
216
|
+
lookup = (commandId) => {
|
|
217
|
+
const row = this.deps.store.getSessionCommand(commandId);
|
|
218
|
+
if (!row)
|
|
219
|
+
return null;
|
|
220
|
+
const rec = this.byCommand.get(commandId);
|
|
221
|
+
const live = rec !== undefined &&
|
|
222
|
+
!rec.settled &&
|
|
223
|
+
row.status === "running" &&
|
|
224
|
+
row.owner_token === rec.token;
|
|
225
|
+
const facts = this.deps.facts?.(row) ?? {};
|
|
226
|
+
const answer = row.kind === "answer";
|
|
227
|
+
const state = {
|
|
228
|
+
pr: row.pr,
|
|
229
|
+
generation: rec?.generation ?? row.attempt,
|
|
230
|
+
active: live,
|
|
231
|
+
// An answer worker posts only when the operator marked its message, and
|
|
232
|
+
// the daemon's word for that is the payload it dispatched.
|
|
233
|
+
may_post: answer ? rec?.mayPost === true : facts.may_post === true,
|
|
234
|
+
lease: facts.lease ?? "none",
|
|
235
|
+
};
|
|
236
|
+
if (answer)
|
|
237
|
+
state.answer = true;
|
|
238
|
+
return state;
|
|
239
|
+
};
|
|
240
|
+
/** The ops the daemon serves a worker: its spec, its post ops, and
|
|
241
|
+
* its held wakes. */
|
|
242
|
+
ops() {
|
|
243
|
+
const ops = {
|
|
244
|
+
[WORKER_SPEC_OP]: {
|
|
245
|
+
roles: ["worker"],
|
|
246
|
+
handle: (ctx) => {
|
|
247
|
+
const rec = this.byCommand.get(ctx.commandId);
|
|
248
|
+
if (!rec || rec.settled || rec.generation !== ctx.generation)
|
|
249
|
+
throw new IpcError("command_inactive", "no live worker for this command");
|
|
250
|
+
return rec.spec;
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
ops[CONTROL_ACK_OP] = {
|
|
255
|
+
roles: ["worker"],
|
|
256
|
+
handle: (ctx, body) => {
|
|
257
|
+
const ack = parseControlAck(body);
|
|
258
|
+
const rec = this.byCommand.get(ctx.commandId);
|
|
259
|
+
// An ack for a control nobody waits for any more is not an error.
|
|
260
|
+
rec?.controls.get(ack.id)?.(ack);
|
|
261
|
+
return {};
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
if (this.deps.post)
|
|
265
|
+
Object.assign(ops, postOps(this.deps.post));
|
|
266
|
+
if (this.deps.ghRead)
|
|
267
|
+
Object.assign(ops, ghReadOps({ reader: this.deps.ghRead, log: this.log }));
|
|
268
|
+
if (this.deps.permits)
|
|
269
|
+
Object.assign(ops, permitOps(this.deps.permits));
|
|
270
|
+
const wakes = this.deps.wakes;
|
|
271
|
+
if (wakes) {
|
|
272
|
+
ops[WAKES_TAKE_OP] = {
|
|
273
|
+
roles: ["worker"],
|
|
274
|
+
handle: (ctx) => ({ batches: wakes.take(ctx) }),
|
|
275
|
+
};
|
|
276
|
+
ops[WAKES_ACK_OP] = {
|
|
277
|
+
roles: ["worker"],
|
|
278
|
+
handle: (ctx, body) => {
|
|
279
|
+
const id = body?.id;
|
|
280
|
+
if (typeof id !== "string" || id === "")
|
|
281
|
+
throw new IpcError("bad_request", "wakes.ack: `id` must be a string");
|
|
282
|
+
wakes.ack(ctx, id);
|
|
283
|
+
return {};
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return ops;
|
|
288
|
+
}
|
|
289
|
+
/** The worker's events. Any event is a sign of life; `accepted` is the row's
|
|
290
|
+
* start; the rest is kept for the exit and handed to the observer. */
|
|
291
|
+
onEvent = (ctx, kind, body) => {
|
|
292
|
+
const rec = this.byCommand.get(ctx.commandId);
|
|
293
|
+
if (!rec || rec.settled || rec.generation !== ctx.generation)
|
|
294
|
+
return;
|
|
295
|
+
const t = this.now();
|
|
296
|
+
rec.lastSeen = t;
|
|
297
|
+
const b = (typeof body === "object" && body !== null ? body : {});
|
|
298
|
+
switch (kind) {
|
|
299
|
+
case "ready":
|
|
300
|
+
rec.readyAt = t;
|
|
301
|
+
if (typeof b["session_id"] === "string")
|
|
302
|
+
rec.sessionId = b["session_id"];
|
|
303
|
+
break;
|
|
304
|
+
case "accepted":
|
|
305
|
+
if (rec.acceptedAt === undefined) {
|
|
306
|
+
rec.acceptedAt = t;
|
|
307
|
+
this.deps.store.markSessionCommandStarted(rec.row.id, rec.token, this.iso(this.hardDeadline(rec)));
|
|
308
|
+
}
|
|
309
|
+
break;
|
|
310
|
+
case "verdict_written":
|
|
311
|
+
rec.verdictWritten = true;
|
|
312
|
+
break;
|
|
313
|
+
case "posted":
|
|
314
|
+
rec.posted++;
|
|
315
|
+
break;
|
|
316
|
+
case "failed":
|
|
317
|
+
if (typeof b["reason"] === "string")
|
|
318
|
+
rec.failedReason = b["reason"].slice(0, 500);
|
|
319
|
+
break;
|
|
320
|
+
case "done":
|
|
321
|
+
rec.report = b;
|
|
322
|
+
break;
|
|
323
|
+
default:
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
this.deps.observe?.({
|
|
328
|
+
kind,
|
|
329
|
+
command_id: rec.row.id,
|
|
330
|
+
pr: rec.row.pr,
|
|
331
|
+
session: rec.row.session,
|
|
332
|
+
mode: rec.mode,
|
|
333
|
+
generation: rec.generation,
|
|
334
|
+
body,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
catch (e) {
|
|
338
|
+
this.log(`worker observer failed: ${String(e)}`);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
// ---- the single writer of session_commands -------------------------------
|
|
342
|
+
/** Create a worker-hosted command: the saved payload and its row. The inject
|
|
343
|
+
* path is a name only (`worker:<id>`): no bridge reads it. Refused while the
|
|
344
|
+
* session has work owed, and for a round that is no longer running. */
|
|
345
|
+
submit(input) {
|
|
346
|
+
const { store } = this.deps;
|
|
347
|
+
const session = `pr-${input.pr}`;
|
|
348
|
+
const blocking = store.blockingSessionCommand(session);
|
|
349
|
+
if (blocking)
|
|
350
|
+
throw new Error(`session command ${blocking.id} is ${blocking.status}`);
|
|
351
|
+
if (input.kind === "round" && input.round_no !== null) {
|
|
352
|
+
const status = store.roundStatus(input.pr, input.round_no);
|
|
353
|
+
if (status !== undefined && status !== "running")
|
|
354
|
+
throw new Error(`round ${input.round_no} of #${input.pr} is ${status}; not submitting its order`);
|
|
355
|
+
}
|
|
356
|
+
const id = randomUUID();
|
|
357
|
+
const payload = messagePayload(input.payload);
|
|
358
|
+
const payloadPath = join(this.deps.payloadDir, `${id}.md`);
|
|
359
|
+
mkdirSync(dirname(payloadPath), { recursive: true });
|
|
360
|
+
const tmp = `${payloadPath}.tmp-${process.pid}`;
|
|
361
|
+
writeFileSync(tmp, payload);
|
|
362
|
+
renameSync(tmp, payloadPath);
|
|
363
|
+
return store.createSessionCommand({
|
|
364
|
+
id,
|
|
365
|
+
pr: input.pr,
|
|
366
|
+
session,
|
|
367
|
+
kind: input.kind,
|
|
368
|
+
round_no: input.round_no,
|
|
369
|
+
payload_sha256: createHash("sha256").update(payload).digest("hex"),
|
|
370
|
+
payload_path: payloadPath,
|
|
371
|
+
inject_path: `worker:${id}`,
|
|
372
|
+
next_check_at: this.iso(this.now() + this.limits.readyMs),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/** Start a worker for a pending command. Refused (a `WorkerLaunchError`) when
|
|
376
|
+
* the session already has a worker, a lease held by a live process, or a login
|
|
377
|
+
* rewrite that does not end; the row is left as it was. */
|
|
378
|
+
async launch(commandId) {
|
|
379
|
+
if (this.stopping)
|
|
380
|
+
throw new WorkerLaunchError("the supervisor is stopping");
|
|
381
|
+
const first = this.deps.store.getSessionCommand(commandId);
|
|
382
|
+
if (!first)
|
|
383
|
+
throw new WorkerLaunchError(`no such command: ${commandId}`);
|
|
384
|
+
const session = first.session;
|
|
385
|
+
this.mayStart(first);
|
|
386
|
+
if (this.bySession.has(session) || this.launching.has(session))
|
|
387
|
+
throw new WorkerLaunchError(`${session} already has a worker`);
|
|
388
|
+
this.launching.add(session);
|
|
389
|
+
try {
|
|
390
|
+
return await this.launchHeld(commandId, session);
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
this.launching.delete(session);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/** Launch every pending command whose session has no worker, oldest first. */
|
|
397
|
+
async launchPending() {
|
|
398
|
+
const out = [];
|
|
399
|
+
for (const row of this.deps.store.activeSessionCommands()) {
|
|
400
|
+
if (row.status !== "pending" || row.kind === "legacy")
|
|
401
|
+
continue;
|
|
402
|
+
if (this.bySession.has(row.session) || this.launching.has(row.session))
|
|
403
|
+
continue;
|
|
404
|
+
// An answer is started the moment it is submitted or not at all: one still
|
|
405
|
+
// pending here was left by a daemon that went away. The operator asks again.
|
|
406
|
+
if (row.kind === "answer") {
|
|
407
|
+
this.deps.store.cancelAnswerCommand(row.id, "not started before the daemon stopped; ask again");
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
// A drained bot or a parked PR starts nothing; the command waits, and the
|
|
411
|
+
// refusal is not logged every pass.
|
|
412
|
+
if (!this.breaker.gate(row.pr).ok)
|
|
413
|
+
continue;
|
|
414
|
+
if (this.deps.hostGate?.(row.pr))
|
|
415
|
+
continue; // a takeover holds the session: the command waits
|
|
416
|
+
try {
|
|
417
|
+
out.push(await this.launch(row.id));
|
|
418
|
+
}
|
|
419
|
+
catch (e) {
|
|
420
|
+
this.log(`worker launch for ${row.id} failed: ${String(e)}`);
|
|
421
|
+
this.deps.store.logEvent("worker_launch_refused", row.pr, {
|
|
422
|
+
command_id: row.id,
|
|
423
|
+
error: String(e).slice(0, 300),
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return out;
|
|
428
|
+
}
|
|
429
|
+
/** The no of the breaker (a drained bot, a parked PR: no worker of any mode; the
|
|
430
|
+
* debug round asks `breaker.gate` itself) and of the host lease (a PR under a
|
|
431
|
+
* takeover). The row is left as it was. */
|
|
432
|
+
mayStart(row) {
|
|
433
|
+
const verdict = this.breaker.gate(row.pr);
|
|
434
|
+
if (!verdict.ok)
|
|
435
|
+
throw new WorkerLaunchError(verdict.reason);
|
|
436
|
+
// A takeover holds the PR's session: two writers must never append to its JSONL.
|
|
437
|
+
const held = this.deps.hostGate?.(row.pr);
|
|
438
|
+
if (held)
|
|
439
|
+
throw new WorkerLaunchError(held);
|
|
440
|
+
}
|
|
441
|
+
async launchHeld(commandId, session) {
|
|
442
|
+
const { store, capabilities } = this.deps;
|
|
443
|
+
const gate = this.deps.loginGate;
|
|
444
|
+
if (gate?.paused() && !(await gate.whenResumed(this.deps.loginWaitMs ?? 30_000)))
|
|
445
|
+
throw new WorkerLaunchError("a login holds the refresher paused: not starting a worker");
|
|
446
|
+
if (this.stopping)
|
|
447
|
+
throw new WorkerLaunchError("the supervisor is stopping");
|
|
448
|
+
const row = store.getSessionCommand(commandId);
|
|
449
|
+
if (!row || row.status !== "pending" || row.kind === "legacy")
|
|
450
|
+
throw new WorkerLaunchError(`command ${commandId} is not a pending worker command`);
|
|
451
|
+
this.mayStart(row); // a login wait or a failing neighbour may have drained the bot meanwhile
|
|
452
|
+
// A lease still held by a live worker of ours (another daemon, a worker the
|
|
453
|
+
// boot could not kill) refuses the spawn; one that names nothing is cleared.
|
|
454
|
+
const held = this.leases.read(session);
|
|
455
|
+
if (held !== undefined) {
|
|
456
|
+
const verdict = held === null ? { kind: "dead" } : judgeLease(held, this.proc);
|
|
457
|
+
if (verdict.kind === "group" || verdict.kind === "members")
|
|
458
|
+
throw new WorkerLaunchError(`${session} has a live worker (lease of ${held?.command_id})`);
|
|
459
|
+
this.leases.forget(session);
|
|
460
|
+
}
|
|
461
|
+
// A wake outside a round is the PR's main session carrying on, so it runs as a
|
|
462
|
+
// round worker does; only an operator's message to an idle PR is an answer.
|
|
463
|
+
const mode = row.kind === "answer" ? "answer" : "round";
|
|
464
|
+
const payload = verifiedPayload(row, "the command's payload");
|
|
465
|
+
if (!payload.ok) {
|
|
466
|
+
if (row.kind === "answer")
|
|
467
|
+
store.cancelAnswerCommand(row.id, payload.why);
|
|
468
|
+
else
|
|
469
|
+
store.failPendingSessionCommand(row.id, payload.why);
|
|
470
|
+
throw new WorkerLaunchError(payload.why);
|
|
471
|
+
}
|
|
472
|
+
const mayPost = mode === "answer" && operatorMayPost(payload.payload.toString("utf8"));
|
|
473
|
+
const spec = this.checkedSpec(row, mode, mayPost);
|
|
474
|
+
const token = WorkerLeases.newToken();
|
|
475
|
+
const t0 = this.now();
|
|
476
|
+
const hardDeadline = t0 + spec.wall_clock_ms + this.limits.wallGraceMs + this.limits.killGraceMs;
|
|
477
|
+
const claimed = store.claimSessionCommand(row.id, token, this.iso(hardDeadline));
|
|
478
|
+
if (!claimed)
|
|
479
|
+
throw new WorkerLaunchError(`command ${commandId} was claimed by another`);
|
|
480
|
+
const generation = claimed.attempt;
|
|
481
|
+
const lease = {
|
|
482
|
+
v: 1,
|
|
483
|
+
owner_token: token,
|
|
484
|
+
command_id: row.id,
|
|
485
|
+
session,
|
|
486
|
+
pr: row.pr,
|
|
487
|
+
generation,
|
|
488
|
+
pid: null,
|
|
489
|
+
pgid: null,
|
|
490
|
+
start_time: null,
|
|
491
|
+
boot_id: this.proc.bootId(),
|
|
492
|
+
command: `botworker --command ${row.id} --mode ${mode}`,
|
|
493
|
+
created_at: new Date(t0).toISOString(),
|
|
494
|
+
};
|
|
495
|
+
let capFd = -1;
|
|
496
|
+
let logFd = -1;
|
|
497
|
+
let leased = false;
|
|
498
|
+
let child;
|
|
499
|
+
try {
|
|
500
|
+
this.leases.create(lease);
|
|
501
|
+
leased = true;
|
|
502
|
+
const capability = capabilities.mint(row.id);
|
|
503
|
+
// In run/caps, never run/ itself: run/ is the socket's dir, which the tool
|
|
504
|
+
// uid must still traverse (0710) when the model's tools run as another uid.
|
|
505
|
+
capFd = createCapabilityFd(join(this.deps.runDir, "caps"), capability);
|
|
506
|
+
mkdirSync(this.deps.logDir, { recursive: true });
|
|
507
|
+
rotateWorkerLogs(this.deps.logDir, row.id, t0, {
|
|
508
|
+
...DEFAULT_WORKER_LOG_LIMITS,
|
|
509
|
+
...this.deps.logLimits,
|
|
510
|
+
});
|
|
511
|
+
logFd = openSync(workerLogPath(this.deps.logDir, row.id), "a", 0o600);
|
|
512
|
+
const env = workerProcessEnv(this.deps.parentEnv ?? process.env, this.deps.extraEnv ?? {}, ipcEnv({ socketPath: this.deps.socketPath, commandId: row.id, generation }));
|
|
513
|
+
child = this.spawnFn(this.deps.nodeBin ?? process.execPath, [this.deps.workerMain, "--command", row.id, "--mode", mode], {
|
|
514
|
+
cwd: spec.root,
|
|
515
|
+
env,
|
|
516
|
+
detached: true,
|
|
517
|
+
stdio: stdioWithCapability(["ignore", logFd, logFd], capFd),
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
catch (e) {
|
|
521
|
+
this.abandon(row.id, token, session, leased, `could not start the worker: ${String(e)}`);
|
|
522
|
+
throw new WorkerLaunchError(`could not start the worker: ${String(e)}`);
|
|
523
|
+
}
|
|
524
|
+
finally {
|
|
525
|
+
if (capFd >= 0)
|
|
526
|
+
closeSync(capFd); // the child holds its own copy
|
|
527
|
+
if (logFd >= 0)
|
|
528
|
+
closeSync(logFd);
|
|
529
|
+
}
|
|
530
|
+
const pid = child.pid;
|
|
531
|
+
const identity = pid === undefined ? undefined : this.proc.identity(pid);
|
|
532
|
+
if (pid !== undefined)
|
|
533
|
+
this.leases.update(session, token, {
|
|
534
|
+
pid,
|
|
535
|
+
pgid: identity?.pgid ?? pid,
|
|
536
|
+
start_time: identity?.start ?? null,
|
|
537
|
+
});
|
|
538
|
+
let finish = () => { };
|
|
539
|
+
const done = new Promise((resolve) => (finish = resolve));
|
|
540
|
+
const rec = {
|
|
541
|
+
row: claimed,
|
|
542
|
+
spec,
|
|
543
|
+
mode,
|
|
544
|
+
token,
|
|
545
|
+
generation,
|
|
546
|
+
child,
|
|
547
|
+
lease: { ...lease, pid: pid ?? null, pgid: pid ?? null, start_time: identity?.start ?? null },
|
|
548
|
+
spawnedAt: t0,
|
|
549
|
+
wallDeadline: t0 + spec.wall_clock_ms + this.limits.wallGraceMs,
|
|
550
|
+
staleMs: staleAfterMs(spec.heartbeat_ms ?? this.limits.heartbeatMs, this.limits),
|
|
551
|
+
lastSeen: t0,
|
|
552
|
+
readyAt: undefined,
|
|
553
|
+
acceptedAt: undefined,
|
|
554
|
+
sessionId: undefined,
|
|
555
|
+
verdictWritten: false,
|
|
556
|
+
posted: 0,
|
|
557
|
+
failedReason: undefined,
|
|
558
|
+
spawnError: undefined,
|
|
559
|
+
report: undefined,
|
|
560
|
+
mayPost,
|
|
561
|
+
killedFor: undefined,
|
|
562
|
+
killTimer: undefined,
|
|
563
|
+
controls: new Map(),
|
|
564
|
+
forceKilled: false,
|
|
565
|
+
settled: false,
|
|
566
|
+
done,
|
|
567
|
+
finish,
|
|
568
|
+
};
|
|
569
|
+
this.byCommand.set(row.id, rec);
|
|
570
|
+
this.bySession.set(session, rec);
|
|
571
|
+
child.once("exit", (code, signal) => void this.settle(rec, code, signal));
|
|
572
|
+
child.once("error", (e) => {
|
|
573
|
+
if (pid === undefined) {
|
|
574
|
+
rec.spawnError = e.message;
|
|
575
|
+
void this.settle(rec, null, null);
|
|
576
|
+
}
|
|
577
|
+
else
|
|
578
|
+
this.log(`worker ${row.id}: ${e.message}`);
|
|
579
|
+
});
|
|
580
|
+
store.logEvent("worker_spawned", row.pr, {
|
|
581
|
+
command_id: row.id,
|
|
582
|
+
mode,
|
|
583
|
+
generation,
|
|
584
|
+
pid: pid ?? null,
|
|
585
|
+
});
|
|
586
|
+
return { command_id: row.id, session, pid: pid ?? -1, generation };
|
|
587
|
+
}
|
|
588
|
+
/** The spec as the worker will read it: from the builder, checked against the
|
|
589
|
+
* row (a spec for another command, PR or payload is a bug, not a request), with
|
|
590
|
+
* no GitHub credential, no IPC name, and the beat interval filled in. */
|
|
591
|
+
checkedSpec(row, mode, mayPost) {
|
|
592
|
+
const built = this.deps.buildSpec(row, mode);
|
|
593
|
+
const env = {};
|
|
594
|
+
for (const [name, value] of Object.entries(built.env)) {
|
|
595
|
+
if (name.startsWith("QCR_IPC_"))
|
|
596
|
+
throw new WorkerLaunchError(`the spec's env names ${name}: the IPC names are the supervisor's`);
|
|
597
|
+
if (!GITHUB_TOKEN_ENVS.includes(name))
|
|
598
|
+
env[name] = value;
|
|
599
|
+
}
|
|
600
|
+
// An answer is a short worker with caps of its own: the builder's round budget,
|
|
601
|
+
// its planned wave and its lens dir do not apply, and the may-post mark is the
|
|
602
|
+
// daemon's own reading of the payload, whatever the builder put.
|
|
603
|
+
const answer = mode === "answer"
|
|
604
|
+
? {
|
|
605
|
+
wall_clock_ms: this.deps.answer?.timeoutMs ?? Math.round(DEFAULT_ANSWER_TIMEOUT_S * 1000),
|
|
606
|
+
cap_usd: this.deps.answer?.capUsd ?? DEFAULT_ANSWER_CAP_USD,
|
|
607
|
+
planned_wave: false,
|
|
608
|
+
may_post: mayPost,
|
|
609
|
+
// The newest round's live log is where a viewer following the PR looks.
|
|
610
|
+
...(this.newestRound(row.pr) !== undefined
|
|
611
|
+
? { live_round: this.newestRound(row.pr) }
|
|
612
|
+
: {}),
|
|
613
|
+
}
|
|
614
|
+
: {};
|
|
615
|
+
const spec = parseWorkerSpec(JSON.parse(JSON.stringify({
|
|
616
|
+
...built,
|
|
617
|
+
...answer,
|
|
618
|
+
env,
|
|
619
|
+
heartbeat_ms: built.heartbeat_ms ?? this.limits.heartbeatMs,
|
|
620
|
+
})));
|
|
621
|
+
if (spec.command_id !== row.id ||
|
|
622
|
+
spec.pr !== row.pr ||
|
|
623
|
+
spec.session !== row.session ||
|
|
624
|
+
spec.mode !== mode ||
|
|
625
|
+
spec.payload_path !== row.payload_path ||
|
|
626
|
+
spec.payload_sha256 !== row.payload_sha256)
|
|
627
|
+
throw new WorkerLaunchError("the spec is not for this command");
|
|
628
|
+
return spec;
|
|
629
|
+
}
|
|
630
|
+
newestRound(pr) {
|
|
631
|
+
return this.deps.store.roundsFor(pr).at(-1)?.round_no;
|
|
632
|
+
}
|
|
633
|
+
/** Undo a launch that never produced a process. */
|
|
634
|
+
abandon(id, token, session, leased, why) {
|
|
635
|
+
this.deps.capabilities.revoke(id);
|
|
636
|
+
if (leased)
|
|
637
|
+
this.leases.remove(session, token);
|
|
638
|
+
this.finishFailed(id, token, why);
|
|
639
|
+
}
|
|
640
|
+
// ---- commands another host runs ---------------------------------
|
|
641
|
+
/** A round order claimed for a host that is no worker process of ours: the debug
|
|
642
|
+
* round's interactive pi (src/debuground.ts). The supervisor stays the single writer
|
|
643
|
+
* of the rows, so the host asks for the claim, the start mark and the settlement
|
|
644
|
+
* here. `ownerToken` is the host's lease token; the row follows it as a worker's
|
|
645
|
+
* follows its spawn's. Undefined when another claimed the row first. */
|
|
646
|
+
claimHosted(commandId, ownerToken, nextCheckAt) {
|
|
647
|
+
return this.deps.store.claimSessionCommand(commandId, ownerToken, nextCheckAt);
|
|
648
|
+
}
|
|
649
|
+
/** The hosted round's order reached its session: the row is started. */
|
|
650
|
+
markHostedStarted(commandId, ownerToken, nextCheckAt) {
|
|
651
|
+
return this.deps.store.markSessionCommandStarted(commandId, ownerToken, nextCheckAt);
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* The hosted command's host is gone (confirmed by the caller). Served: the round's
|
|
655
|
+
* verdict was committed, so the row is acked. Otherwise the row goes back to
|
|
656
|
+
* `pending` for a worker, which resumes the session and continues, or
|
|
657
|
+
* fails once the requeues are spent or its round has ended. Nothing counts against
|
|
658
|
+
* the PR's crash-loop breaker: a person ended it.
|
|
659
|
+
*/
|
|
660
|
+
settleHosted(commandId, ownerToken, outcome) {
|
|
661
|
+
const { store } = this.deps;
|
|
662
|
+
const row = store.getSessionCommand(commandId);
|
|
663
|
+
if (!row || row.status !== "running" || row.owner_token !== ownerToken)
|
|
664
|
+
return "gone";
|
|
665
|
+
if (outcome.served) {
|
|
666
|
+
store.markSessionCommandStarted(commandId, ownerToken, this.iso(this.now()));
|
|
667
|
+
if (store.finishSessionCommand(commandId, ownerToken, "acked"))
|
|
668
|
+
return "acked";
|
|
669
|
+
}
|
|
670
|
+
const reason = outcome.served ? "the round could not be marked served" : outcome.reason;
|
|
671
|
+
return this.requeueOrFail(commandId, ownerToken, reason) ? "requeued" : "failed";
|
|
672
|
+
}
|
|
673
|
+
// ---- watching ------------------------------------------------------------
|
|
674
|
+
/** Begin the periodic `tick`. */
|
|
675
|
+
start() {
|
|
676
|
+
if (this.timer)
|
|
677
|
+
return;
|
|
678
|
+
this.timer = setInterval(() => this.tick(), this.limits.tickMs);
|
|
679
|
+
this.timer.unref();
|
|
680
|
+
this.deps.permits?.start(this.lookup);
|
|
681
|
+
}
|
|
682
|
+
/** One pass of the deadlines. Pure of I/O but for the signals it sends. */
|
|
683
|
+
tick() {
|
|
684
|
+
const t = this.now();
|
|
685
|
+
for (const rec of [...this.byCommand.values()]) {
|
|
686
|
+
if (rec.settled || rec.killedFor !== undefined)
|
|
687
|
+
continue;
|
|
688
|
+
if (t >= rec.wallDeadline)
|
|
689
|
+
this.end(rec, "wall_clock");
|
|
690
|
+
else if (rec.readyAt === undefined) {
|
|
691
|
+
if (t - rec.spawnedAt > this.limits.readyMs)
|
|
692
|
+
this.end(rec, "stage_ready");
|
|
693
|
+
}
|
|
694
|
+
else if (rec.acceptedAt === undefined && t - rec.readyAt > this.limits.acceptMs)
|
|
695
|
+
this.end(rec, "stage_accept");
|
|
696
|
+
else if (t - rec.lastSeen > rec.staleMs)
|
|
697
|
+
this.end(rec, "heartbeat_lost");
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
/** End one worker's group (TERM, then KILL after the grace). The row follows
|
|
701
|
+
* the exit: an operator's end fails it, a lost worker requeues it. */
|
|
702
|
+
kill(commandId, why = "operator") {
|
|
703
|
+
const rec = this.byCommand.get(commandId);
|
|
704
|
+
if (!rec || rec.settled)
|
|
705
|
+
return false;
|
|
706
|
+
this.end(rec, why);
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
end(rec, why) {
|
|
710
|
+
if (rec.settled || rec.killedFor !== undefined)
|
|
711
|
+
return;
|
|
712
|
+
rec.killedFor = why;
|
|
713
|
+
this.log(`ending worker ${rec.row.id} (${why})`);
|
|
714
|
+
this.deps.store.logEvent("worker_ending", rec.row.pr, { command_id: rec.row.id, why });
|
|
715
|
+
this.signalGroup(rec, "SIGTERM");
|
|
716
|
+
rec.killTimer = setTimeout(() => {
|
|
717
|
+
if (!rec.settled)
|
|
718
|
+
this.signalGroup(rec, "SIGKILL");
|
|
719
|
+
}, this.limits.killGraceMs);
|
|
720
|
+
rec.killTimer.unref();
|
|
721
|
+
}
|
|
722
|
+
/** Signal the live child's group. Safe by construction: the child has not been
|
|
723
|
+
* reaped, so its pid cannot belong to anyone else. */
|
|
724
|
+
signalGroup(rec, sig) {
|
|
725
|
+
const pid = rec.child.pid;
|
|
726
|
+
if (pid === undefined)
|
|
727
|
+
return;
|
|
728
|
+
if (this.signal(-pid, sig) === "denied")
|
|
729
|
+
this.log(`worker ${rec.row.id}: ${sig} to the group was refused`);
|
|
730
|
+
}
|
|
731
|
+
async settle(rec, code, signal) {
|
|
732
|
+
if (rec.settled)
|
|
733
|
+
return;
|
|
734
|
+
rec.settled = true;
|
|
735
|
+
if (rec.killTimer)
|
|
736
|
+
clearTimeout(rec.killTimer);
|
|
737
|
+
for (const [id, resolve] of [...rec.controls])
|
|
738
|
+
resolve({ id, ack: "refused", reason: "the worker ended" });
|
|
739
|
+
const { store } = this.deps;
|
|
740
|
+
let survivors = false;
|
|
741
|
+
try {
|
|
742
|
+
// The leader is gone; whatever it left in its group goes with it.
|
|
743
|
+
const leftovers = await killLease(rec.lease, {
|
|
744
|
+
proc: this.proc,
|
|
745
|
+
signal: this.signal,
|
|
746
|
+
graceMs: 0,
|
|
747
|
+
pollMs: 10,
|
|
748
|
+
});
|
|
749
|
+
if (leftovers.verdict === "members")
|
|
750
|
+
this.log(`worker ${rec.row.id}: left members in its group; ended`);
|
|
751
|
+
if (leftovers.survivors) {
|
|
752
|
+
survivors = true;
|
|
753
|
+
this.log(`worker ${rec.row.id}: group members survived the kill`);
|
|
754
|
+
}
|
|
755
|
+
this.deps.capabilities.revoke(rec.row.id);
|
|
756
|
+
const outcome = decideOutcome({
|
|
757
|
+
code,
|
|
758
|
+
signal,
|
|
759
|
+
killedFor: rec.killedFor,
|
|
760
|
+
failedReason: rec.failedReason,
|
|
761
|
+
spawnError: rec.spawnError,
|
|
762
|
+
});
|
|
763
|
+
this.apply(rec, outcome);
|
|
764
|
+
store.logEvent("worker_exited", rec.row.pr, {
|
|
765
|
+
command_id: rec.row.id,
|
|
766
|
+
generation: rec.generation,
|
|
767
|
+
code,
|
|
768
|
+
signal,
|
|
769
|
+
outcome: outcome.kind,
|
|
770
|
+
...(outcome.kind === "acked" || outcome.kind === "leave" ? {} : { reason: outcome.reason }),
|
|
771
|
+
verdict_written: rec.verdictWritten,
|
|
772
|
+
posted: rec.posted,
|
|
773
|
+
});
|
|
774
|
+
this.count(rec, outcome, code);
|
|
775
|
+
this.tell(rec, outcome, code, signal);
|
|
776
|
+
}
|
|
777
|
+
catch (e) {
|
|
778
|
+
this.log(`settling worker ${rec.row.id} failed: ${String(e)}`);
|
|
779
|
+
}
|
|
780
|
+
finally {
|
|
781
|
+
// A lease whose group members survived stays: it is what keeps a successor
|
|
782
|
+
// from launching beside them (launchHeld) until the process table shows them gone.
|
|
783
|
+
if (!survivors)
|
|
784
|
+
this.leases.remove(rec.row.session, rec.token);
|
|
785
|
+
this.byCommand.delete(rec.row.id);
|
|
786
|
+
if (this.bySession.get(rec.row.session) === rec)
|
|
787
|
+
this.bySession.delete(rec.row.session);
|
|
788
|
+
rec.finish();
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
/** Tell the breaker how the worker ended. A served round is a success; a
|
|
792
|
+
* non-zero exit, a `failed` event or a deadline kill is a failure; the daemon's
|
|
793
|
+
* own stop, the operator's end and a recovery fence are neither. A PR parked by this failure has
|
|
794
|
+
* no round to retry, so a requeue it just got is failed instead. */
|
|
795
|
+
count(rec, outcome, code) {
|
|
796
|
+
const { pr } = rec.row;
|
|
797
|
+
// An answer is the operator's chat, not the PR's round: its failures do not park
|
|
798
|
+
// the PR or feed the drain, and its success must not hide a round's crash loop.
|
|
799
|
+
if (rec.mode === "answer")
|
|
800
|
+
return;
|
|
801
|
+
// A fenced worker was ended by the caller, who owns the row's fate: not a worker failure.
|
|
802
|
+
if (rec.killedFor === "shutdown" || rec.killedFor === "operator" || outcome.kind === "leave")
|
|
803
|
+
return;
|
|
804
|
+
if (outcome.kind === "acked" && rec.failedReason === undefined) {
|
|
805
|
+
this.breaker.success(pr);
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const error = rec.failedReason ??
|
|
809
|
+
(outcome.kind === "acked"
|
|
810
|
+
? `the worker reported a failure and exited ${code}`
|
|
811
|
+
: outcome.reason);
|
|
812
|
+
const res = this.breaker.failure(pr, rec.row.id, error);
|
|
813
|
+
if (res.parked && outcome.kind === "requeue")
|
|
814
|
+
this.deps.store.failPendingSessionCommand(rec.row.id, `PR #${pr} is parked after ${res.failures} consecutive worker failures: ${error}`.slice(0, 500));
|
|
815
|
+
}
|
|
816
|
+
/** Hand the host that submitted the command what the worker came to. */
|
|
817
|
+
tell(rec, outcome, code, signal) {
|
|
818
|
+
if (!this.deps.settled)
|
|
819
|
+
return;
|
|
820
|
+
try {
|
|
821
|
+
this.deps.settled({
|
|
822
|
+
command_id: rec.row.id,
|
|
823
|
+
pr: rec.row.pr,
|
|
824
|
+
session: rec.row.session,
|
|
825
|
+
mode: rec.mode,
|
|
826
|
+
kind: rec.row.kind,
|
|
827
|
+
outcome: outcome.kind,
|
|
828
|
+
reason: outcome.kind === "failed" || outcome.kind === "requeue"
|
|
829
|
+
? (rec.failedReason ?? outcome.reason)
|
|
830
|
+
: undefined,
|
|
831
|
+
code,
|
|
832
|
+
signal,
|
|
833
|
+
report: rec.report,
|
|
834
|
+
posted: rec.posted,
|
|
835
|
+
verdict_written: rec.verdictWritten,
|
|
836
|
+
may_post: rec.mayPost,
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
catch (e) {
|
|
840
|
+
this.log(`worker settled hook failed: ${String(e)}`);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
apply(rec, outcome) {
|
|
844
|
+
const { store } = this.deps;
|
|
845
|
+
const id = rec.row.id;
|
|
846
|
+
if (outcome.kind === "leave")
|
|
847
|
+
return;
|
|
848
|
+
if (outcome.kind === "acked") {
|
|
849
|
+
// An exit 0 is the round served, whether or not `accepted` got through.
|
|
850
|
+
store.markSessionCommandStarted(id, rec.token, this.iso(this.now()));
|
|
851
|
+
store.finishSessionCommand(id, rec.token, "acked");
|
|
852
|
+
}
|
|
853
|
+
else if (outcome.kind === "failed") {
|
|
854
|
+
this.finishFailed(id, rec.token, outcome.reason);
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
this.requeueOrFail(id, rec.token, outcome.reason);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
/** Hand the command to a fresh worker, or fail it once the requeues are spent
|
|
861
|
+
* (or its round has ended meanwhile). */
|
|
862
|
+
requeueOrFail(id, token, reason) {
|
|
863
|
+
const { store } = this.deps;
|
|
864
|
+
const row = store.getSessionCommand(id);
|
|
865
|
+
if (!row || row.status !== "running")
|
|
866
|
+
return false;
|
|
867
|
+
// A lost answer worker is not replayed: the operator is told (the reply file
|
|
868
|
+
// says the worker was lost) and asks again if they still want the answer.
|
|
869
|
+
if (row.kind === "answer") {
|
|
870
|
+
store.cancelAnswerCommand(id, reason);
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
if (row.recovery_attempts >= this.limits.maxRequeues) {
|
|
874
|
+
this.failRow(row, token, `${reason}; requeued ${row.recovery_attempts} times already`);
|
|
875
|
+
return false;
|
|
876
|
+
}
|
|
877
|
+
if (store.requeueWorkerCommand(id, token, this.iso(this.now())))
|
|
878
|
+
return true;
|
|
879
|
+
this.failRow(row, token, `${reason}; its round is no longer running`);
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
failRow(row, token, why) {
|
|
883
|
+
const { store } = this.deps;
|
|
884
|
+
if (token !== null)
|
|
885
|
+
this.finishFailed(row.id, token, why);
|
|
886
|
+
else {
|
|
887
|
+
store.failActiveSessionCommand(row.id, why);
|
|
888
|
+
this.endAnswer(row.id, why);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/** The row's `failed`, except that an answer command never ends failed: a failed
|
|
892
|
+
* non-round command is owed work that blocks the PR's next round, and an
|
|
893
|
+
* operator's message owes nothing (`cancelAnswerCommand`). */
|
|
894
|
+
finishFailed(id, token, why) {
|
|
895
|
+
const done = this.deps.store.finishSessionCommand(id, token, "failed", why);
|
|
896
|
+
if (done)
|
|
897
|
+
this.endAnswer(id, why);
|
|
898
|
+
return done;
|
|
899
|
+
}
|
|
900
|
+
endAnswer(id, why) {
|
|
901
|
+
if (this.deps.store.getSessionCommand(id)?.kind === "answer")
|
|
902
|
+
this.deps.store.cancelAnswerCommand(id, why);
|
|
903
|
+
}
|
|
904
|
+
// ---- restart -------------------------------------------------------------
|
|
905
|
+
/**
|
|
906
|
+
* The daemon's boot: no worker of a previous daemon is wanted. Every
|
|
907
|
+
* leased group is killed (only when the leader still has the lease's start time
|
|
908
|
+
* in the lease's boot/container), the leases are removed, and every command a
|
|
909
|
+
* dead worker held is handed back to `pending` under its own id, so the fresh
|
|
910
|
+
* worker resumes the JSONL and continues. A command whose group could not be
|
|
911
|
+
* ended is failed, not requeued: two workers must never serve one session.
|
|
912
|
+
* Run before any `launch`.
|
|
913
|
+
*/
|
|
914
|
+
async recover() {
|
|
915
|
+
const result = { killed: [], stale: [], requeued: [], failed: [] };
|
|
916
|
+
const unkillable = new Set();
|
|
917
|
+
const entries = this.leases.list();
|
|
918
|
+
await Promise.all(entries.map(async ({ session, lease }) => {
|
|
919
|
+
if (lease === null) {
|
|
920
|
+
this.leases.forget(session);
|
|
921
|
+
result.stale.push(session);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
const out = await killLease(lease, {
|
|
925
|
+
proc: this.proc,
|
|
926
|
+
signal: this.signal,
|
|
927
|
+
graceMs: this.limits.killGraceMs,
|
|
928
|
+
});
|
|
929
|
+
if (out.verdict === "group" || out.verdict === "members")
|
|
930
|
+
result.killed.push(session);
|
|
931
|
+
else
|
|
932
|
+
result.stale.push(session);
|
|
933
|
+
if (out.survivors)
|
|
934
|
+
unkillable.add(lease.command_id);
|
|
935
|
+
else
|
|
936
|
+
this.leases.forget(session);
|
|
937
|
+
this.deps.store.logEvent("worker_lease_recovered", lease.pr, {
|
|
938
|
+
command_id: lease.command_id,
|
|
939
|
+
verdict: out.verdict,
|
|
940
|
+
killed: out.killed,
|
|
941
|
+
denied: out.denied,
|
|
942
|
+
survivors: out.survivors,
|
|
943
|
+
});
|
|
944
|
+
}));
|
|
945
|
+
for (const row of this.deps.store.activeSessionCommands()) {
|
|
946
|
+
if (row.status !== "running" || this.byCommand.has(row.id))
|
|
947
|
+
continue;
|
|
948
|
+
if (unkillable.has(row.id)) {
|
|
949
|
+
const why = "the previous worker's group could not be ended";
|
|
950
|
+
this.failRow(row, null, why);
|
|
951
|
+
this.tellLostAnswer(row, why);
|
|
952
|
+
result.failed.push(row.id);
|
|
953
|
+
}
|
|
954
|
+
else if (this.requeueOrFail(row.id, null, "the daemon restarted"))
|
|
955
|
+
result.requeued.push(row.id);
|
|
956
|
+
else {
|
|
957
|
+
this.tellLostAnswer(row, "the daemon restarted");
|
|
958
|
+
result.failed.push(row.id);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return result;
|
|
962
|
+
}
|
|
963
|
+
/** An answer command a restart cancelled has no worker to settle it: tell the
|
|
964
|
+
* `settled` hook, which owes the operator the result file it advertised. */
|
|
965
|
+
tellLostAnswer(row, reason) {
|
|
966
|
+
if (row.kind !== "answer" || !this.deps.settled)
|
|
967
|
+
return;
|
|
968
|
+
const payload = verifiedPayload(row, "the command's payload");
|
|
969
|
+
try {
|
|
970
|
+
this.deps.settled({
|
|
971
|
+
command_id: row.id,
|
|
972
|
+
pr: row.pr,
|
|
973
|
+
session: row.session,
|
|
974
|
+
mode: "answer",
|
|
975
|
+
kind: row.kind,
|
|
976
|
+
outcome: "failed",
|
|
977
|
+
reason: `the worker was lost: ${reason}`,
|
|
978
|
+
code: null,
|
|
979
|
+
signal: null,
|
|
980
|
+
report: undefined,
|
|
981
|
+
posted: 0,
|
|
982
|
+
verdict_written: false,
|
|
983
|
+
may_post: payload.ok && operatorMayPost(payload.payload.toString("utf8")),
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
catch (e) {
|
|
987
|
+
this.log(`worker settled hook failed: ${String(e)}`);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
/** The daemon is going down: end every worker; their commands requeue. */
|
|
991
|
+
async stop() {
|
|
992
|
+
this.stopping = true;
|
|
993
|
+
if (this.timer)
|
|
994
|
+
clearInterval(this.timer);
|
|
995
|
+
this.timer = null;
|
|
996
|
+
this.deps.permits?.stop();
|
|
997
|
+
const live = [...this.byCommand.values()];
|
|
998
|
+
for (const rec of live)
|
|
999
|
+
this.end(rec, "shutdown");
|
|
1000
|
+
await Promise.all(live.map((rec) => rec.done));
|
|
1001
|
+
}
|
|
1002
|
+
// ---- the session host's questions -----------------------------------
|
|
1003
|
+
/**
|
|
1004
|
+
* Does a worker of this session run? Ours (a live child, or a launch under way),
|
|
1005
|
+
* or one a lease file still names that the process table confirms. The host's
|
|
1006
|
+
* liveness answer: a lease alone never says "alive", since pids repeat.
|
|
1007
|
+
*/
|
|
1008
|
+
runs(session) {
|
|
1009
|
+
if (this.bySession.has(session) || this.launching.has(session))
|
|
1010
|
+
return true;
|
|
1011
|
+
const held = this.leases.read(session);
|
|
1012
|
+
if (!held)
|
|
1013
|
+
return false;
|
|
1014
|
+
const verdict = judgeLease(held, this.proc);
|
|
1015
|
+
return verdict.kind === "group" || verdict.kind === "members";
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* End the session's worker and answer only once it is proven gone: true when no
|
|
1019
|
+
* worker of the session runs (it had none, or its exit has been reaped), false
|
|
1020
|
+
* when it outlived `fenceWaitMs`. The command's row is left as it is (`fence`):
|
|
1021
|
+
* the caller fenced the session to decide the row itself. A launch that is
|
|
1022
|
+
* still under way is waited out first, so a worker cannot start behind the fence.
|
|
1023
|
+
*/
|
|
1024
|
+
async fence(session) {
|
|
1025
|
+
// Real time, not the injected clock: this waits on processes.
|
|
1026
|
+
const end = Date.now() + this.limits.fenceWaitMs;
|
|
1027
|
+
while (this.launching.has(session) && Date.now() < end)
|
|
1028
|
+
await sleep(5);
|
|
1029
|
+
const rec = this.bySession.get(session);
|
|
1030
|
+
if (rec) {
|
|
1031
|
+
// Abort first (the worker stops its session and the passes it started, and
|
|
1032
|
+
// exits); the group is sent SIGKILL when that has not happened in the grace.
|
|
1033
|
+
this.startAbort(rec, "fence");
|
|
1034
|
+
await this.exited(rec, Math.max(0, end - Date.now()));
|
|
1035
|
+
}
|
|
1036
|
+
else {
|
|
1037
|
+
// No child of ours, but a lease may still name a group (a worker the boot
|
|
1038
|
+
// could not end, another daemon's). It is killed only while its leader still
|
|
1039
|
+
// has the lease's start time in the lease's boot and container (judgeLease):
|
|
1040
|
+
// a recycled pid or pgid is a stranger's and is left alone.
|
|
1041
|
+
const held = this.leases.read(session);
|
|
1042
|
+
if (held) {
|
|
1043
|
+
const out = await killLease(held, {
|
|
1044
|
+
proc: this.proc,
|
|
1045
|
+
signal: this.signal,
|
|
1046
|
+
graceMs: 0,
|
|
1047
|
+
pollMs: 10,
|
|
1048
|
+
});
|
|
1049
|
+
if (!out.survivors)
|
|
1050
|
+
this.leases.forget(session);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return !this.runs(session);
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Hand a command whose worker has been fenced and is gone back to `pending`
|
|
1057
|
+
* under the same id, for a fresh worker (`launch`) to resume. The CAS is on the
|
|
1058
|
+
* attempt the caller read, so a row that moved since is not replayed.
|
|
1059
|
+
*/
|
|
1060
|
+
replay(commandId, expectedAttempt) {
|
|
1061
|
+
return this.deps.store.retrySessionCommandAfterFence(commandId, expectedAttempt, this.iso(this.now() + this.limits.readyMs));
|
|
1062
|
+
}
|
|
1063
|
+
// ---- the operator's controls ---------------------------------------
|
|
1064
|
+
/** The PR's worker, if it can be controlled: running, past `ready`, and not
|
|
1065
|
+
* already on its way out. A refusal says why. */
|
|
1066
|
+
controllable(pr) {
|
|
1067
|
+
const rec = this.bySession.get(`pr-${pr}`);
|
|
1068
|
+
if (!rec || rec.settled)
|
|
1069
|
+
return `no worker is running for #${pr}`;
|
|
1070
|
+
if (rec.killedFor !== undefined)
|
|
1071
|
+
return `the worker of #${pr} is ending`;
|
|
1072
|
+
if (rec.readyAt === undefined)
|
|
1073
|
+
return `the worker of #${pr} has not opened its session yet`;
|
|
1074
|
+
return rec;
|
|
1075
|
+
}
|
|
1076
|
+
/** Push `kind` to the worker and say when its ack comes: null when the worker
|
|
1077
|
+
* is not reachable, the ack otherwise (a settled worker acks `refused`). */
|
|
1078
|
+
forward(rec, kind, text, waitMs) {
|
|
1079
|
+
const id = randomUUID();
|
|
1080
|
+
let timer;
|
|
1081
|
+
const ack = new Promise((resolve) => {
|
|
1082
|
+
rec.controls.set(id, (a) => resolve(a));
|
|
1083
|
+
timer = setTimeout(() => resolve(null), waitMs);
|
|
1084
|
+
timer.unref();
|
|
1085
|
+
}).finally(() => {
|
|
1086
|
+
clearTimeout(timer);
|
|
1087
|
+
rec.controls.delete(id);
|
|
1088
|
+
});
|
|
1089
|
+
const sent = this.deps.push?.(rec.row.id, kind, {
|
|
1090
|
+
id,
|
|
1091
|
+
...(text !== undefined ? { text } : {}),
|
|
1092
|
+
});
|
|
1093
|
+
if (!sent)
|
|
1094
|
+
rec.controls.get(id)?.({ id, ack: "refused", reason: "the worker is not connected" });
|
|
1095
|
+
return { sent: sent ?? 0, ack, id };
|
|
1096
|
+
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Forward the operator's steer or follow-up to the PR's running worker and answer
|
|
1099
|
+
* with the worker's word: `queued` (the session has it and acts when its turn
|
|
1100
|
+
* allows), `handled` (it acted now), or a refusal with the reason. The text is
|
|
1101
|
+
* validated here as well as by the command's schema. A worker that does not answer
|
|
1102
|
+
* in `controlAckMs` is reported unacknowledged, never as delivered.
|
|
1103
|
+
*/
|
|
1104
|
+
async control(pr, kind, text) {
|
|
1105
|
+
let checked;
|
|
1106
|
+
try {
|
|
1107
|
+
checked = controlText(text);
|
|
1108
|
+
}
|
|
1109
|
+
catch (e) {
|
|
1110
|
+
return { ok: false, reason: e instanceof Error ? e.message : String(e) };
|
|
1111
|
+
}
|
|
1112
|
+
if (!this.deps.push)
|
|
1113
|
+
return { ok: false, reason: "this daemon cannot reach its workers" };
|
|
1114
|
+
const rec = this.controllable(pr);
|
|
1115
|
+
if (typeof rec === "string")
|
|
1116
|
+
return { ok: false, reason: rec };
|
|
1117
|
+
const { ack } = this.forward(rec, kind, checked, this.limits.controlAckMs);
|
|
1118
|
+
const got = await ack;
|
|
1119
|
+
if (got === null)
|
|
1120
|
+
return { ok: false, reason: `the worker of #${pr} did not answer; check the session` };
|
|
1121
|
+
if (got.ack === "refused")
|
|
1122
|
+
return { ok: false, reason: got.reason ?? `the worker of #${pr} refused it` };
|
|
1123
|
+
return { ok: true, ack: got.ack, command_id: rec.row.id };
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* The operator's abort-round: ask the worker to stop its session (and the lens
|
|
1127
|
+
* passes it started) and end. The command's row fails as ended by the operator
|
|
1128
|
+
* (`decideOutcome`). A worker that has not exited within `abortGraceMs`, or that
|
|
1129
|
+
* cannot be reached, has its group sent SIGKILL.
|
|
1130
|
+
*/
|
|
1131
|
+
async abortRound(pr) {
|
|
1132
|
+
const rec = this.bySession.get(`pr-${pr}`);
|
|
1133
|
+
if (!rec || rec.settled)
|
|
1134
|
+
return { ok: false, reason: `no worker is running for #${pr}` };
|
|
1135
|
+
if (rec.killedFor !== undefined)
|
|
1136
|
+
return { ok: true, ack: "handled", command_id: rec.row.id, killed: rec.forceKilled };
|
|
1137
|
+
const { ack, sent } = this.startAbort(rec, "operator");
|
|
1138
|
+
// An ack ends the wait. So does the exit. No ack in the grace period (or a worker
|
|
1139
|
+
// that could not be asked) means the SIGKILL is due: wait for it to land.
|
|
1140
|
+
const got = sent > 0 ? await Promise.race([ack, rec.done]) : null;
|
|
1141
|
+
if (got === null || got === undefined)
|
|
1142
|
+
await this.exited(rec, this.limits.fenceWaitMs);
|
|
1143
|
+
return { ok: true, ack: "handled", command_id: rec.row.id, killed: rec.forceKilled };
|
|
1144
|
+
}
|
|
1145
|
+
/** Resolve when `rec` has been reaped or after `ms`, whichever is first. */
|
|
1146
|
+
async exited(rec, ms) {
|
|
1147
|
+
let timer;
|
|
1148
|
+
await Promise.race([
|
|
1149
|
+
rec.done,
|
|
1150
|
+
new Promise((resolve) => {
|
|
1151
|
+
timer = setTimeout(resolve, ms);
|
|
1152
|
+
timer.unref();
|
|
1153
|
+
}),
|
|
1154
|
+
]);
|
|
1155
|
+
clearTimeout(timer);
|
|
1156
|
+
}
|
|
1157
|
+
/** Ask `rec`'s worker to abort and arm the SIGKILL of its group for when it has
|
|
1158
|
+
* not exited in the grace period (at once, when it cannot be asked). The row
|
|
1159
|
+
* follows the exit as `why` says. */
|
|
1160
|
+
startAbort(rec, why) {
|
|
1161
|
+
const fresh = rec.killedFor === undefined;
|
|
1162
|
+
if (fresh) {
|
|
1163
|
+
rec.killedFor = why;
|
|
1164
|
+
this.log(`aborting worker ${rec.row.id} (${why})`);
|
|
1165
|
+
this.deps.store.logEvent("worker_ending", rec.row.pr, { command_id: rec.row.id, why });
|
|
1166
|
+
}
|
|
1167
|
+
const { sent, ack } = this.forward(rec, "abort", undefined, this.limits.abortGraceMs);
|
|
1168
|
+
if (fresh) {
|
|
1169
|
+
rec.killTimer = setTimeout(() => {
|
|
1170
|
+
if (rec.settled)
|
|
1171
|
+
return;
|
|
1172
|
+
rec.forceKilled = true;
|
|
1173
|
+
this.log(`worker ${rec.row.id} did not stop on abort: SIGKILL to its group`);
|
|
1174
|
+
this.signalGroup(rec, "SIGKILL");
|
|
1175
|
+
}, sent > 0 ? this.limits.abortGraceMs : 0);
|
|
1176
|
+
rec.killTimer.unref();
|
|
1177
|
+
}
|
|
1178
|
+
return { ack, sent };
|
|
1179
|
+
}
|
|
1180
|
+
// ---- views ---------------------------------------------------------------
|
|
1181
|
+
isLive(session) {
|
|
1182
|
+
return this.bySession.has(session);
|
|
1183
|
+
}
|
|
1184
|
+
workers() {
|
|
1185
|
+
const t = this.now();
|
|
1186
|
+
return [...this.byCommand.values()].map((rec) => ({
|
|
1187
|
+
command_id: rec.row.id,
|
|
1188
|
+
pr: rec.row.pr,
|
|
1189
|
+
session: rec.row.session,
|
|
1190
|
+
mode: rec.mode,
|
|
1191
|
+
pid: rec.child.pid ?? -1,
|
|
1192
|
+
generation: rec.generation,
|
|
1193
|
+
stage: rec.readyAt === undefined ? "starting" : rec.acceptedAt === undefined ? "ready" : "running",
|
|
1194
|
+
age_ms: t - rec.spawnedAt,
|
|
1195
|
+
silent_ms: t - rec.lastSeen,
|
|
1196
|
+
ending: rec.killedFor ?? null,
|
|
1197
|
+
}));
|
|
1198
|
+
}
|
|
1199
|
+
hardDeadline(rec) {
|
|
1200
|
+
return rec.wallDeadline + this.limits.killGraceMs;
|
|
1201
|
+
}
|
|
1202
|
+
iso(ms) {
|
|
1203
|
+
return new Date(ms).toISOString();
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
//# sourceMappingURL=workersupervisor.js.map
|